authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-04-10 01:51:31-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-04-10 01:51:31-07:00
log3405a64dd8fdf3affe14c057692484fffb63b0af
tree7a9d4be187f3f4b47e6108867bc917b71dca1886
parent1cba2f64d39073bb77faac7234e88f52aa395643
signature Signed by PGP key B5690EEEBB952194

bump effect to latest beta (#1866)

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Julius Marminge <julius@macmini.local> Co-authored-by: codex <codex@users.noreply.github.com>

174 files changed, 1853 insertions(+), 1870 deletions(-)

apps/desktop/src/clientPersistence.test.ts+1-1
...@@ -58,7 +58,7 @@ const clientSettings: ClientSettings = {...@@ -58,7 +58,7 @@ const clientSettings: ClientSettings = {
58};58};
5959
60const savedRegistryRecord: PersistedSavedEnvironmentRecord = {60const savedRegistryRecord: PersistedSavedEnvironmentRecord = {
61 environmentId: EnvironmentId.makeUnsafe("environment-1"),61 environmentId: EnvironmentId.make("environment-1"),
62 label: "Remote environment",62 label: "Remote environment",
63 httpBaseUrl: "https://remote.example.com/",63 httpBaseUrl: "https://remote.example.com/",
64 wsBaseUrl: "wss://remote.example.com/",64 wsBaseUrl: "wss://remote.example.com/",
apps/server/integration/OrchestrationEngineHarness.integration.ts+1-1
...@@ -438,7 +438,7 @@ export const makeOrchestrationIntegrationHarness = (...@@ -438,7 +438,7 @@ export const makeOrchestrationIntegrationHarness = (
438 ) =>438 ) =>
439 waitFor(439 waitFor(
440 pendingApprovalRepository440 pendingApprovalRepository
441 .getByRequestId({ requestId: ApprovalRequestId.makeUnsafe(requestId) })441 .getByRequestId({ requestId: ApprovalRequestId.make(requestId) })
442 .pipe(442 .pipe(
443 Effect.map((row) =>443 Effect.map((row) =>
444 Option.match(row, {444 Option.match(row, {
apps/server/integration/TestProviderAdapter.integration.ts+3-3
...@@ -289,7 +289,7 @@ export const makeTestProviderAdapterHarness = (options?: MakeTestProviderAdapter...@@ -289,7 +289,7 @@ export const makeTestProviderAdapterHarness = (options?: MakeTestProviderAdapter
289289
290 state.turnCount += 1;290 state.turnCount += 1;
291 const turnCount = state.turnCount;291 const turnCount = state.turnCount;
292 const turnId = TurnId.makeUnsafe(`turn-${turnCount}`);292 const turnId = TurnId.make(`turn-${turnCount}`);
293293
294 const response = state.queuedResponses.shift();294 const response = state.queuedResponses.shift();
295 if (!response) {295 if (!response) {
...@@ -307,7 +307,7 @@ export const makeTestProviderAdapterHarness = (options?: MakeTestProviderAdapter...@@ -307,7 +307,7 @@ export const makeTestProviderAdapterHarness = (options?: MakeTestProviderAdapter
307 ...(fixtureEvent as Record<string, unknown>),307 ...(fixtureEvent as Record<string, unknown>),
308 eventId: randomUUID(),308 eventId: randomUUID(),
309 provider,309 provider,
310 sessionId: RuntimeSessionId.makeUnsafe(String(input.threadId)),310 sessionId: RuntimeSessionId.make(String(input.threadId)),
311 createdAt: nowIso(),311 createdAt: nowIso(),
312 };312 };
313 rawEvent.threadId = state.snapshot.threadId;313 rawEvent.threadId = state.snapshot.threadId;
...@@ -363,7 +363,7 @@ export const makeTestProviderAdapterHarness = (options?: MakeTestProviderAdapter...@@ -363,7 +363,7 @@ export const makeTestProviderAdapterHarness = (options?: MakeTestProviderAdapter
363 if (deferredTurnCompletedEvents.length === 0) {363 if (deferredTurnCompletedEvents.length === 0) {
364 yield* emit({364 yield* emit({
365 type: "turn.completed",365 type: "turn.completed",
366 eventId: EventId.makeUnsafe(randomUUID()),366 eventId: EventId.make(randomUUID()),
367 provider,367 provider,
368 createdAt: nowIso(),368 createdAt: nowIso(),
369 threadId: state.snapshot.threadId,369 threadId: state.snapshot.threadId,
apps/server/integration/fixtures/providerRuntime.ts+2-2
...@@ -5,14 +5,14 @@ const PROVIDER = "codex" as const;...@@ -5,14 +5,14 @@ const PROVIDER = "codex" as const;
5const SESSION_ID = "fixture-session";5const SESSION_ID = "fixture-session";
6const THREAD_ID = "fixture-thread";6const THREAD_ID = "fixture-thread";
7const TURN_ID = "fixture-turn";7const TURN_ID = "fixture-turn";
8const REQUEST_ID = RuntimeRequestId.makeUnsafe("req-1");8const REQUEST_ID = RuntimeRequestId.make("req-1");
99
10function baseEvent(10function baseEvent(
11 eventId: string,11 eventId: string,
12 createdAt: string,12 createdAt: string,
13): Pick<LegacyProviderRuntimeEvent, "eventId" | "provider" | "sessionId" | "createdAt"> {13): Pick<LegacyProviderRuntimeEvent, "eventId" | "provider" | "sessionId" | "createdAt"> {
14 return {14 return {
15 eventId: EventId.makeUnsafe(eventId),15 eventId: EventId.make(eventId),
16 provider: PROVIDER,16 provider: PROVIDER,
17 sessionId: SESSION_ID,17 sessionId: SESSION_ID,
18 createdAt,18 createdAt,
apps/server/integration/orchestrationEngine.integration.test.ts+18-19
...@@ -30,14 +30,13 @@ import type {...@@ -30,14 +30,13 @@ import type {
30} from "../src/orchestration/Services/RuntimeReceiptBus.ts";30} from "../src/orchestration/Services/RuntimeReceiptBus.ts";
31import * as NodeServices from "@effect/platform-node/NodeServices";31import * as NodeServices from "@effect/platform-node/NodeServices";
3232
33const asMessageId = (value: string): MessageId => MessageId.makeUnsafe(value);33const asMessageId = (value: string): MessageId => MessageId.make(value);
34const asProjectId = (value: string): ProjectId => ProjectId.makeUnsafe(value);34const asProjectId = (value: string): ProjectId => ProjectId.make(value);
35const asEventId = (value: string): EventId => EventId.makeUnsafe(value);35const asEventId = (value: string): EventId => EventId.make(value);
36const asApprovalRequestId = (value: string): ApprovalRequestId =>36const asApprovalRequestId = (value: string): ApprovalRequestId => ApprovalRequestId.make(value);
37 ApprovalRequestId.makeUnsafe(value);
3837
39const PROJECT_ID = asProjectId("project-1");38const PROJECT_ID = asProjectId("project-1");
40const THREAD_ID = ThreadId.makeUnsafe("thread-1");39const THREAD_ID = ThreadId.make("thread-1");
41const FIXTURE_TURN_ID = "fixture-turn";40const FIXTURE_TURN_ID = "fixture-turn";
42const APPROVAL_REQUEST_ID = asApprovalRequestId("req-approval-1");41const APPROVAL_REQUEST_ID = asApprovalRequestId("req-approval-1");
43type IntegrationProvider = ProviderKind;42type IntegrationProvider = ProviderKind;
...@@ -112,7 +111,7 @@ const seedProjectAndThread = (harness: OrchestrationIntegrationHarness) =>...@@ -112,7 +111,7 @@ const seedProjectAndThread = (harness: OrchestrationIntegrationHarness) =>
112111
113 yield* harness.engine.dispatch({112 yield* harness.engine.dispatch({
114 type: "project.create",113 type: "project.create",
115 commandId: CommandId.makeUnsafe("cmd-project-create"),114 commandId: CommandId.make("cmd-project-create"),
116 projectId: PROJECT_ID,115 projectId: PROJECT_ID,
117 title: "Integration Project",116 title: "Integration Project",
118 workspaceRoot: harness.workspaceDir,117 workspaceRoot: harness.workspaceDir,
...@@ -125,7 +124,7 @@ const seedProjectAndThread = (harness: OrchestrationIntegrationHarness) =>...@@ -125,7 +124,7 @@ const seedProjectAndThread = (harness: OrchestrationIntegrationHarness) =>
125124
126 yield* harness.engine.dispatch({125 yield* harness.engine.dispatch({
127 type: "thread.create",126 type: "thread.create",
128 commandId: CommandId.makeUnsafe("cmd-thread-create"),127 commandId: CommandId.make("cmd-thread-create"),
129 threadId: THREAD_ID,128 threadId: THREAD_ID,
130 projectId: PROJECT_ID,129 projectId: PROJECT_ID,
131 title: "Integration Thread",130 title: "Integration Thread",
...@@ -150,7 +149,7 @@ const startTurn = (input: {...@@ -150,7 +149,7 @@ const startTurn = (input: {
150}) =>149}) =>
151 input.harness.engine.dispatch({150 input.harness.engine.dispatch({
152 type: "thread.turn.start",151 type: "thread.turn.start",
153 commandId: CommandId.makeUnsafe(input.commandId),152 commandId: CommandId.make(input.commandId),
154 threadId: THREAD_ID,153 threadId: THREAD_ID,
155 message: {154 message: {
156 messageId: asMessageId(input.messageId),155 messageId: asMessageId(input.messageId),
...@@ -261,7 +260,7 @@ it.live.skipIf(!process.env.CODEX_BINARY_PATH)(...@@ -261,7 +260,7 @@ it.live.skipIf(!process.env.CODEX_BINARY_PATH)(
261260
262 yield* harness.engine.dispatch({261 yield* harness.engine.dispatch({
263 type: "project.create",262 type: "project.create",
264 commandId: CommandId.makeUnsafe("cmd-project-create-real-codex"),263 commandId: CommandId.make("cmd-project-create-real-codex"),
265 projectId: PROJECT_ID,264 projectId: PROJECT_ID,
266 title: "Integration Project",265 title: "Integration Project",
267 workspaceRoot: harness.workspaceDir,266 workspaceRoot: harness.workspaceDir,
...@@ -274,7 +273,7 @@ it.live.skipIf(!process.env.CODEX_BINARY_PATH)(...@@ -274,7 +273,7 @@ it.live.skipIf(!process.env.CODEX_BINARY_PATH)(
274273
275 yield* harness.engine.dispatch({274 yield* harness.engine.dispatch({
276 type: "thread.create",275 type: "thread.create",
277 commandId: CommandId.makeUnsafe("cmd-thread-create-real-codex"),276 commandId: CommandId.make("cmd-thread-create-real-codex"),
278 threadId: THREAD_ID,277 threadId: THREAD_ID,
279 projectId: PROJECT_ID,278 projectId: PROJECT_ID,
280 title: "Integration Thread",279 title: "Integration Thread",
...@@ -291,7 +290,7 @@ it.live.skipIf(!process.env.CODEX_BINARY_PATH)(...@@ -291,7 +290,7 @@ it.live.skipIf(!process.env.CODEX_BINARY_PATH)(
291290
292 yield* harness.engine.dispatch({291 yield* harness.engine.dispatch({
293 type: "thread.turn.start",292 type: "thread.turn.start",
294 commandId: CommandId.makeUnsafe("cmd-turn-start-real-codex-1"),293 commandId: CommandId.make("cmd-turn-start-real-codex-1"),
295 threadId: THREAD_ID,294 threadId: THREAD_ID,
296 message: {295 message: {
297 messageId: asMessageId("msg-real-codex-1"),296 messageId: asMessageId("msg-real-codex-1"),
...@@ -318,7 +317,7 @@ it.live.skipIf(!process.env.CODEX_BINARY_PATH)(...@@ -318,7 +317,7 @@ it.live.skipIf(!process.env.CODEX_BINARY_PATH)(
318317
319 yield* harness.engine.dispatch({318 yield* harness.engine.dispatch({
320 type: "thread.turn.start",319 type: "thread.turn.start",
321 commandId: CommandId.makeUnsafe("cmd-turn-start-real-codex-2"),320 commandId: CommandId.make("cmd-turn-start-real-codex-2"),
322 threadId: THREAD_ID,321 threadId: THREAD_ID,
323 message: {322 message: {
324 messageId: asMessageId("msg-real-codex-2"),323 messageId: asMessageId("msg-real-codex-2"),
...@@ -583,7 +582,7 @@ it.live("tracks approval requests and resolves pending approvals on user respons...@@ -583,7 +582,7 @@ it.live("tracks approval requests and resolves pending approvals on user respons
583582
584 yield* harness.engine.dispatch({583 yield* harness.engine.dispatch({
585 type: "thread.approval.respond",584 type: "thread.approval.respond",
586 commandId: CommandId.makeUnsafe("cmd-approval-respond"),585 commandId: CommandId.make("cmd-approval-respond"),
587 threadId: THREAD_ID,586 threadId: THREAD_ID,
588 requestId: APPROVAL_REQUEST_ID,587 requestId: APPROVAL_REQUEST_ID,
589 decision: "accept",588 decision: "accept",
...@@ -815,7 +814,7 @@ it.live("reverts to an earlier checkpoint and trims checkpoint projections + git...@@ -815,7 +814,7 @@ it.live("reverts to an earlier checkpoint and trims checkpoint projections + git
815814
816 yield* harness.engine.dispatch({815 yield* harness.engine.dispatch({
817 type: "thread.checkpoint.revert",816 type: "thread.checkpoint.revert",
818 commandId: CommandId.makeUnsafe("cmd-checkpoint-revert"),817 commandId: CommandId.make("cmd-checkpoint-revert"),
819 threadId: THREAD_ID,818 threadId: THREAD_ID,
820 turnCount: 1,819 turnCount: 1,
821 createdAt: nowIso(),820 createdAt: nowIso(),
...@@ -875,7 +874,7 @@ it.live(...@@ -875,7 +874,7 @@ it.live(
875874
876 yield* harness.engine.dispatch({875 yield* harness.engine.dispatch({
877 type: "thread.checkpoint.revert",876 type: "thread.checkpoint.revert",
878 commandId: CommandId.makeUnsafe("cmd-checkpoint-revert-no-session"),877 commandId: CommandId.make("cmd-checkpoint-revert-no-session"),
879 threadId: THREAD_ID,878 threadId: THREAD_ID,
880 turnCount: 0,879 turnCount: 0,
881 createdAt: nowIso(),880 createdAt: nowIso(),
...@@ -1119,7 +1118,7 @@ it.live("forwards claudeAgent approval responses to the provider session", () =>...@@ -1119,7 +1118,7 @@ it.live("forwards claudeAgent approval responses to the provider session", () =>
11191118
1120 yield* harness.engine.dispatch({1119 yield* harness.engine.dispatch({
1121 type: "thread.approval.respond",1120 type: "thread.approval.respond",
1122 commandId: CommandId.makeUnsafe("cmd-claude-approval-respond"),1121 commandId: CommandId.make("cmd-claude-approval-respond"),
1123 threadId: THREAD_ID,1122 threadId: THREAD_ID,
1124 requestId: APPROVAL_REQUEST_ID,1123 requestId: APPROVAL_REQUEST_ID,
1125 decision: "accept",1124 decision: "accept",
...@@ -1192,7 +1191,7 @@ it.live("forwards thread.turn.interrupt to claudeAgent provider sessions", () =>...@@ -1192,7 +1191,7 @@ it.live("forwards thread.turn.interrupt to claudeAgent provider sessions", () =>
11921191
1193 yield* harness.engine.dispatch({1192 yield* harness.engine.dispatch({
1194 type: "thread.turn.interrupt",1193 type: "thread.turn.interrupt",
1195 commandId: CommandId.makeUnsafe("cmd-turn-interrupt-claude"),1194 commandId: CommandId.make("cmd-turn-interrupt-claude"),
1196 threadId: THREAD_ID,1195 threadId: THREAD_ID,
1197 createdAt: nowIso(),1196 createdAt: nowIso(),
1198 });1197 });
...@@ -1309,7 +1308,7 @@ it.live("reverts claudeAgent turns and rolls back provider conversation state",...@@ -1309,7 +1308,7 @@ it.live("reverts claudeAgent turns and rolls back provider conversation state",
13091308
1310 yield* harness.engine.dispatch({1309 yield* harness.engine.dispatch({
1311 type: "thread.checkpoint.revert",1310 type: "thread.checkpoint.revert",
1312 commandId: CommandId.makeUnsafe("cmd-checkpoint-revert-claude"),1311 commandId: CommandId.make("cmd-checkpoint-revert-claude"),
1313 threadId: THREAD_ID,1312 threadId: THREAD_ID,
1314 turnCount: 1,1313 turnCount: 1,
1315 createdAt: nowIso(),1314 createdAt: nowIso(),
apps/server/integration/providerService.integration.test.ts+24-36
...@@ -122,15 +122,12 @@ it.live("replays typed runtime fixture events", () =>...@@ -122,15 +122,12 @@ it.live("replays typed runtime fixture events", () =>
122122
123 yield* Effect.gen(function* () {123 yield* Effect.gen(function* () {
124 const provider = yield* ProviderService;124 const provider = yield* ProviderService;
125 const session = yield* provider.startSession(125 const session = yield* provider.startSession(ThreadId.make("thread-integration-typed"), {
126 ThreadId.makeUnsafe("thread-integration-typed"),126 threadId: ThreadId.make("thread-integration-typed"),
127 {127 provider: "codex",
128 threadId: ThreadId.makeUnsafe("thread-integration-typed"),128 cwd: fixture.cwd,
129 provider: "codex",129 runtimeMode: "full-access",
130 cwd: fixture.cwd,130 });
131 runtimeMode: "full-access",
132 },
133 );
134 assert.equal((session.threadId ?? "").length > 0, true);131 assert.equal((session.threadId ?? "").length > 0, true);
135132
136 const observedEvents = yield* runTurn({133 const observedEvents = yield* runTurn({
...@@ -157,15 +154,12 @@ it.live("replays file-changing fixture turn events", () =>...@@ -157,15 +154,12 @@ it.live("replays file-changing fixture turn events", () =>
157154
158 yield* Effect.gen(function* () {155 yield* Effect.gen(function* () {
159 const provider = yield* ProviderService;156 const provider = yield* ProviderService;
160 const session = yield* provider.startSession(157 const session = yield* provider.startSession(ThreadId.make("thread-integration-tools"), {
161 ThreadId.makeUnsafe("thread-integration-tools"),158 threadId: ThreadId.make("thread-integration-tools"),
162 {159 provider: "codex",
163 threadId: ThreadId.makeUnsafe("thread-integration-tools"),160 cwd: fixture.cwd,
164 provider: "codex",161 runtimeMode: "full-access",
165 cwd: fixture.cwd,162 });
166 runtimeMode: "full-access",
167 },
168 );
169 assert.equal((session.threadId ?? "").length > 0, true);163 assert.equal((session.threadId ?? "").length > 0, true);
170164
171 const observedEvents = yield* runTurn({165 const observedEvents = yield* runTurn({
...@@ -196,15 +190,12 @@ it.live("runs multi-turn tool/approval flow", () =>...@@ -196,15 +190,12 @@ it.live("runs multi-turn tool/approval flow", () =>
196190
197 yield* Effect.gen(function* () {191 yield* Effect.gen(function* () {
198 const provider = yield* ProviderService;192 const provider = yield* ProviderService;
199 const session = yield* provider.startSession(193 const session = yield* provider.startSession(ThreadId.make("thread-integration-multi"), {
200 ThreadId.makeUnsafe("thread-integration-multi"),194 threadId: ThreadId.make("thread-integration-multi"),
201 {195 provider: "codex",
202 threadId: ThreadId.makeUnsafe("thread-integration-multi"),196 cwd: fixture.cwd,
203 provider: "codex",197 runtimeMode: "full-access",
204 cwd: fixture.cwd,198 });
205 runtimeMode: "full-access",
206 },
207 );
208 assert.equal((session.threadId ?? "").length > 0, true);199 assert.equal((session.threadId ?? "").length > 0, true);
209200
210 const firstTurnEvents = yield* runTurn({201 const firstTurnEvents = yield* runTurn({
...@@ -250,15 +241,12 @@ it.live("rolls back provider conversation state only", () =>...@@ -250,15 +241,12 @@ it.live("rolls back provider conversation state only", () =>
250241
251 yield* Effect.gen(function* () {242 yield* Effect.gen(function* () {
252 const provider = yield* ProviderService;243 const provider = yield* ProviderService;
253 const session = yield* provider.startSession(244 const session = yield* provider.startSession(ThreadId.make("thread-integration-rollback"), {
254 ThreadId.makeUnsafe("thread-integration-rollback"),245 threadId: ThreadId.make("thread-integration-rollback"),
255 {246 provider: "codex",
256 threadId: ThreadId.makeUnsafe("thread-integration-rollback"),247 cwd: fixture.cwd,
257 provider: "codex",248 runtimeMode: "full-access",
258 cwd: fixture.cwd,249 });
259 runtimeMode: "full-access",
260 },
261 );
262 assert.equal((session.threadId ?? "").length > 0, true);250 assert.equal((session.threadId ?? "").length > 0, true);
263251
264 yield* runTurn({252 yield* runTurn({
apps/server/src/auth/Layers/SessionCredentialService.ts+1-1
...@@ -188,7 +188,7 @@ export const makeSessionCredentialService = Effect.gen(function* () {...@@ -188,7 +188,7 @@ export const makeSessionCredentialService = Effect.gen(function* () {
188188
189 const issue: SessionCredentialServiceShape["issue"] = (input) =>189 const issue: SessionCredentialServiceShape["issue"] = (input) =>
190 Effect.gen(function* () {190 Effect.gen(function* () {
191 const sessionId = AuthSessionId.makeUnsafe(crypto.randomUUID());191 const sessionId = AuthSessionId.make(crypto.randomUUID());
192 const issuedAt = yield* DateTime.now;192 const issuedAt = yield* DateTime.now;
193 const expiresAt = DateTime.add(issuedAt, {193 const expiresAt = DateTime.add(issuedAt, {
194 milliseconds: Duration.toMillis(input?.ttl ?? DEFAULT_SESSION_TTL),194 milliseconds: Duration.toMillis(input?.ttl ?? DEFAULT_SESSION_TTL),
apps/server/src/auth/Services/AuthControlPlane.ts+2-2
...@@ -4,7 +4,7 @@ import type {...@@ -4,7 +4,7 @@ import type {
4 AuthPairingLink,4 AuthPairingLink,
5 AuthSessionId,5 AuthSessionId,
6} from "@t3tools/contracts";6} from "@t3tools/contracts";
7import { Data, DateTime, Duration, Effect, ServiceMap } from "effect";7import { Data, DateTime, Duration, Effect, Context } from "effect";
8import { SessionRole } from "./SessionCredentialService";8import { SessionRole } from "./SessionCredentialService";
99
10export const DEFAULT_SESSION_SUBJECT = "cli-issued-session";10export const DEFAULT_SESSION_SUBJECT = "cli-issued-session";
...@@ -64,6 +64,6 @@ export interface AuthControlPlaneShape {...@@ -64,6 +64,6 @@ export interface AuthControlPlaneShape {
64 ) => Effect.Effect<number, AuthControlPlaneError>;64 ) => Effect.Effect<number, AuthControlPlaneError>;
65}65}
6666
67export class AuthControlPlane extends ServiceMap.Service<AuthControlPlane, AuthControlPlaneShape>()(67export class AuthControlPlane extends Context.Service<AuthControlPlane, AuthControlPlaneShape>()(
68 "t3/AuthControlPlane",68 "t3/AuthControlPlane",
69) {}69) {}
apps/server/src/auth/Services/BootstrapCredentialService.ts+2-2
...@@ -1,5 +1,5 @@...@@ -1,5 +1,5 @@
1import type { AuthPairingLink, ServerAuthBootstrapMethod } from "@t3tools/contracts";1import type { AuthPairingLink, ServerAuthBootstrapMethod } from "@t3tools/contracts";
2import { Data, DateTime, Duration, ServiceMap } from "effect";2import { Data, DateTime, Duration, Context } from "effect";
3import type { Effect, Stream } from "effect";3import type { Effect, Stream } from "effect";
44
5export type BootstrapCredentialRole = "owner" | "client";5export type BootstrapCredentialRole = "owner" | "client";
...@@ -51,7 +51,7 @@ export interface BootstrapCredentialServiceShape {...@@ -51,7 +51,7 @@ export interface BootstrapCredentialServiceShape {
51 readonly consume: (credential: string) => Effect.Effect<BootstrapGrant, BootstrapCredentialError>;51 readonly consume: (credential: string) => Effect.Effect<BootstrapGrant, BootstrapCredentialError>;
52}52}
5353
54export class BootstrapCredentialService extends ServiceMap.Service<54export class BootstrapCredentialService extends Context.Service<
55 BootstrapCredentialService,55 BootstrapCredentialService,
56 BootstrapCredentialServiceShape56 BootstrapCredentialServiceShape
57>()("t3/auth/Services/BootstrapCredentialService") {}57>()("t3/auth/Services/BootstrapCredentialService") {}
apps/server/src/auth/Services/ServerAuth.ts+2-2
...@@ -12,7 +12,7 @@ import type {...@@ -12,7 +12,7 @@ import type {
12 ServerAuthSessionMethod,12 ServerAuthSessionMethod,
13 AuthWebSocketTokenResult,13 AuthWebSocketTokenResult,
14} from "@t3tools/contracts";14} from "@t3tools/contracts";
15import { Data, DateTime, ServiceMap } from "effect";15import { Data, DateTime, Context } from "effect";
16import type { Effect } from "effect";16import type { Effect } from "effect";
17import type * as HttpServerRequest from "effect/unstable/http/HttpServerRequest";17import type * as HttpServerRequest from "effect/unstable/http/HttpServerRequest";
18import type { SessionRole } from "./SessionCredentialService.ts";18import type { SessionRole } from "./SessionCredentialService.ts";
...@@ -79,6 +79,6 @@ export interface ServerAuthShape {...@@ -79,6 +79,6 @@ export interface ServerAuthShape {
79 readonly issueStartupPairingUrl: (baseUrl: string) => Effect.Effect<string, AuthError>;79 readonly issueStartupPairingUrl: (baseUrl: string) => Effect.Effect<string, AuthError>;
80}80}
8181
82export class ServerAuth extends ServiceMap.Service<ServerAuth, ServerAuthShape>()(82export class ServerAuth extends Context.Service<ServerAuth, ServerAuthShape>()(
83 "t3/auth/Services/ServerAuth",83 "t3/auth/Services/ServerAuth",
84) {}84) {}
apps/server/src/auth/Services/ServerAuthPolicy.ts+2-2
...@@ -1,11 +1,11 @@...@@ -1,11 +1,11 @@
1import type { ServerAuthDescriptor } from "@t3tools/contracts";1import type { ServerAuthDescriptor } from "@t3tools/contracts";
2import { ServiceMap } from "effect";2import { Context } from "effect";
3import type { Effect } from "effect";3import type { Effect } from "effect";
44
5export interface ServerAuthPolicyShape {5export interface ServerAuthPolicyShape {
6 readonly getDescriptor: () => Effect.Effect<ServerAuthDescriptor>;6 readonly getDescriptor: () => Effect.Effect<ServerAuthDescriptor>;
7}7}
88
9export class ServerAuthPolicy extends ServiceMap.Service<ServerAuthPolicy, ServerAuthPolicyShape>()(9export class ServerAuthPolicy extends Context.Service<ServerAuthPolicy, ServerAuthPolicyShape>()(
10 "t3/auth/Services/ServerAuthPolicy",10 "t3/auth/Services/ServerAuthPolicy",
11) {}11) {}
apps/server/src/auth/Services/ServerSecretStore.ts+4-5
...@@ -1,4 +1,4 @@...@@ -1,4 +1,4 @@
1import { Data, ServiceMap } from "effect";1import { Data, Context } from "effect";
2import type { Effect } from "effect";2import type { Effect } from "effect";
33
4export class SecretStoreError extends Data.TaggedError("SecretStoreError")<{4export class SecretStoreError extends Data.TaggedError("SecretStoreError")<{
...@@ -16,7 +16,6 @@ export interface ServerSecretStoreShape {...@@ -16,7 +16,6 @@ export interface ServerSecretStoreShape {
16 readonly remove: (name: string) => Effect.Effect<void, SecretStoreError>;16 readonly remove: (name: string) => Effect.Effect<void, SecretStoreError>;
17}17}
1818
19export class ServerSecretStore extends ServiceMap.Service<19export class ServerSecretStore extends Context.Service<ServerSecretStore, ServerSecretStoreShape>()(
20 ServerSecretStore,20 "t3/auth/Services/ServerSecretStore",
21 ServerSecretStoreShape21) {}
22>()("t3/auth/Services/ServerSecretStore") {}
apps/server/src/auth/Services/SessionCredentialService.ts+2-2
...@@ -4,7 +4,7 @@ import type {...@@ -4,7 +4,7 @@ import type {
4 AuthSessionId,4 AuthSessionId,
5 ServerAuthSessionMethod,5 ServerAuthSessionMethod,
6} from "@t3tools/contracts";6} from "@t3tools/contracts";
7import { Data, DateTime, Duration, ServiceMap } from "effect";7import { Data, DateTime, Duration, Context } from "effect";
8import type { Effect, Stream } from "effect";8import type { Effect, Stream } from "effect";
99
10export type SessionRole = "owner" | "client";10export type SessionRole = "owner" | "client";
...@@ -81,7 +81,7 @@ export interface SessionCredentialServiceShape {...@@ -81,7 +81,7 @@ export interface SessionCredentialServiceShape {
81 readonly markDisconnected: (sessionId: AuthSessionId) => Effect.Effect<void, never>;81 readonly markDisconnected: (sessionId: AuthSessionId) => Effect.Effect<void, never>;
82}82}
8383
84export class SessionCredentialService extends ServiceMap.Service<84export class SessionCredentialService extends Context.Service<
85 SessionCredentialService,85 SessionCredentialService,
86 SessionCredentialServiceShape86 SessionCredentialServiceShape
87>()("t3/auth/Services/SessionCredentialService") {}87>()("t3/auth/Services/SessionCredentialService") {}
apps/server/src/checkpointing/Layers/CheckpointDiffQuery.test.ts+4-4
...@@ -26,7 +26,7 @@ function makeThreadCheckpointContext(input: {...@@ -26,7 +26,7 @@ function makeThreadCheckpointContext(input: {
26 worktreePath: input.worktreePath,26 worktreePath: input.worktreePath,
27 checkpoints: [27 checkpoints: [
28 {28 {
29 turnId: TurnId.makeUnsafe("turn-1"),29 turnId: TurnId.make("turn-1"),
30 checkpointTurnCount: input.checkpointTurnCount,30 checkpointTurnCount: input.checkpointTurnCount,
31 checkpointRef: input.checkpointRef,31 checkpointRef: input.checkpointRef,
32 status: "ready",32 status: "ready",
...@@ -40,8 +40,8 @@ function makeThreadCheckpointContext(input: {...@@ -40,8 +40,8 @@ function makeThreadCheckpointContext(input: {
4040
41describe("CheckpointDiffQueryLive", () => {41describe("CheckpointDiffQueryLive", () => {
42 it("computes diffs using canonical turn-0 checkpoint refs", async () => {42 it("computes diffs using canonical turn-0 checkpoint refs", async () => {
43 const projectId = ProjectId.makeUnsafe("project-1");43 const projectId = ProjectId.make("project-1");
44 const threadId = ThreadId.makeUnsafe("thread-1");44 const threadId = ThreadId.make("thread-1");
45 const toCheckpointRef = checkpointRefForThreadTurn(threadId, 1);45 const toCheckpointRef = checkpointRefForThreadTurn(threadId, 1);
46 const hasCheckpointRefCalls: Array<CheckpointRef> = [];46 const hasCheckpointRefCalls: Array<CheckpointRef> = [];
47 const diffCheckpointsCalls: Array<{47 const diffCheckpointsCalls: Array<{
...@@ -119,7 +119,7 @@ describe("CheckpointDiffQueryLive", () => {...@@ -119,7 +119,7 @@ describe("CheckpointDiffQueryLive", () => {
119 });119 });
120120
121 it("fails when the thread is missing from the snapshot", async () => {121 it("fails when the thread is missing from the snapshot", async () => {
122 const threadId = ThreadId.makeUnsafe("thread-missing");122 const threadId = ThreadId.make("thread-missing");
123123
124 const checkpointStore: CheckpointStoreShape = {124 const checkpointStore: CheckpointStoreShape = {
125 isGitRepository: () => Effect.succeed(true),125 isGitRepository: () => Effect.succeed(true),
apps/server/src/checkpointing/Layers/CheckpointStore.test.ts+1-1
...@@ -93,7 +93,7 @@ it.layer(TestLayer)("CheckpointStoreLive", (it) => {...@@ -93,7 +93,7 @@ it.layer(TestLayer)("CheckpointStoreLive", (it) => {
93 const tmp = yield* makeTmpDir();93 const tmp = yield* makeTmpDir();
94 yield* initRepoWithCommit(tmp);94 yield* initRepoWithCommit(tmp);
95 const checkpointStore = yield* CheckpointStore;95 const checkpointStore = yield* CheckpointStore;
96 const threadId = ThreadId.makeUnsafe("thread-checkpoint-store");96 const threadId = ThreadId.make("thread-checkpoint-store");
97 const fromCheckpointRef = checkpointRefForThreadTurn(threadId, 0);97 const fromCheckpointRef = checkpointRefForThreadTurn(threadId, 0);
98 const toCheckpointRef = checkpointRefForThreadTurn(threadId, 1);98 const toCheckpointRef = checkpointRefForThreadTurn(threadId, 1);
9999
apps/server/src/checkpointing/Services/CheckpointDiffQuery.ts+2-2
...@@ -12,7 +12,7 @@ import type {...@@ -12,7 +12,7 @@ import type {
12 OrchestrationGetTurnDiffInput,12 OrchestrationGetTurnDiffInput,
13 OrchestrationGetTurnDiffResult,13 OrchestrationGetTurnDiffResult,
14} from "@t3tools/contracts";14} from "@t3tools/contracts";
15import { ServiceMap } from "effect";15import { Context } from "effect";
16import type { Effect } from "effect";16import type { Effect } from "effect";
1717
18import type { CheckpointServiceError } from "../Errors.ts";18import type { CheckpointServiceError } from "../Errors.ts";
...@@ -43,7 +43,7 @@ export interface CheckpointDiffQueryShape {...@@ -43,7 +43,7 @@ export interface CheckpointDiffQueryShape {
43/**43/**
44 * CheckpointDiffQuery - Service tag for checkpoint diff queries.44 * CheckpointDiffQuery - Service tag for checkpoint diff queries.
45 */45 */
46export class CheckpointDiffQuery extends ServiceMap.Service<46export class CheckpointDiffQuery extends Context.Service<
47 CheckpointDiffQuery,47 CheckpointDiffQuery,
48 CheckpointDiffQueryShape48 CheckpointDiffQueryShape
49>()("t3/checkpointing/Services/CheckpointDiffQuery") {}49>()("t3/checkpointing/Services/CheckpointDiffQuery") {}
apps/server/src/checkpointing/Services/CheckpointStore.ts+3-3
...@@ -5,12 +5,12 @@...@@ -5,12 +5,12 @@
5 * workspace thread timeline. It does not store user-facing checkpoint metadata5 * workspace thread timeline. It does not store user-facing checkpoint metadata
6 * and does not coordinate provider conversation rollback.6 * and does not coordinate provider conversation rollback.
7 *7 *
8 * Uses Effect `ServiceMap.Service` for dependency injection and exposes typed8 * Uses Effect `Context.Service` for dependency injection and exposes typed
9 * domain errors for checkpoint storage operations.9 * domain errors for checkpoint storage operations.
10 *10 *
11 * @module CheckpointStore11 * @module CheckpointStore
12 */12 */
13import { ServiceMap } from "effect";13import { Context } from "effect";
14import type { Effect } from "effect";14import type { Effect } from "effect";
1515
16import type { CheckpointStoreError } from "../Errors.ts";16import type { CheckpointStoreError } from "../Errors.ts";
...@@ -95,6 +95,6 @@ export interface CheckpointStoreShape {...@@ -95,6 +95,6 @@ export interface CheckpointStoreShape {
95/**95/**
96 * CheckpointStore - Service tag for checkpoint persistence and restore operations.96 * CheckpointStore - Service tag for checkpoint persistence and restore operations.
97 */97 */
98export class CheckpointStore extends ServiceMap.Service<CheckpointStore, CheckpointStoreShape>()(98export class CheckpointStore extends Context.Service<CheckpointStore, CheckpointStoreShape>()(
99 "t3/checkpointing/Services/CheckpointStore",99 "t3/checkpointing/Services/CheckpointStore",
100) {}100) {}
apps/server/src/checkpointing/Utils.ts+1-1
...@@ -4,7 +4,7 @@ import { CheckpointRef, ProjectId, type ThreadId } from "@t3tools/contracts";...@@ -4,7 +4,7 @@ import { CheckpointRef, ProjectId, type ThreadId } from "@t3tools/contracts";
4export const CHECKPOINT_REFS_PREFIX = "refs/t3/checkpoints";4export const CHECKPOINT_REFS_PREFIX = "refs/t3/checkpoints";
55
6export function checkpointRefForThreadTurn(threadId: ThreadId, turnCount: number): CheckpointRef {6export function checkpointRefForThreadTurn(threadId: ThreadId, turnCount: number): CheckpointRef {
7 return CheckpointRef.makeUnsafe(7 return CheckpointRef.make(
8 `${CHECKPOINT_REFS_PREFIX}/${Encoding.encodeBase64Url(threadId)}/turn/${turnCount}`,8 `${CHECKPOINT_REFS_PREFIX}/${Encoding.encodeBase64Url(threadId)}/turn/${turnCount}`,
9 );9 );
10}10}
apps/server/src/cli-config.test.ts+63
...@@ -153,6 +153,69 @@ it.layer(NodeServices.layer)("cli config resolution", (it) => {...@@ -153,6 +153,69 @@ it.layer(NodeServices.layer)("cli config resolution", (it) => {
153 }),153 }),
154 );154 );
155155
156 it.effect("preserves explicit false CLI boolean flags over env and bootstrap values", () =>
157 Effect.gen(function* () {
158 const { join } = yield* Path.Path;
159 const baseDir = join(os.tmpdir(), "t3-cli-config-false-flags");
160 const fd = yield* openBootstrapFd({
161 noBrowser: true,
162 autoBootstrapProjectFromCwd: true,
163 logWebSocketEvents: true,
164 });
165 const derivedPaths = yield* deriveServerPaths(baseDir, new URL("http://127.0.0.1:4173"));
166
167 const resolved = yield* resolveServerConfig(
168 {
169 mode: Option.some("web"),
170 port: Option.some(8788),
171 host: Option.some("127.0.0.1"),
172 baseDir: Option.some(baseDir),
173 cwd: Option.none(),
174 devUrl: Option.some(new URL("http://127.0.0.1:4173")),
175 noBrowser: Option.some(false),
176 bootstrapFd: Option.none(),
177 autoBootstrapProjectFromCwd: Option.some(false),
178 logWebSocketEvents: Option.some(false),
179 },
180 Option.none(),
181 ).pipe(
182 Effect.provide(
183 Layer.mergeAll(
184 ConfigProvider.layer(
185 ConfigProvider.fromEnv({
186 env: {
187 T3CODE_BOOTSTRAP_FD: String(fd),
188 T3CODE_NO_BROWSER: "true",
189 T3CODE_AUTO_BOOTSTRAP_PROJECT_FROM_CWD: "true",
190 T3CODE_LOG_WS_EVENTS: "true",
191 },
192 }),
193 ),
194 NetService.layer,
195 ),
196 ),
197 );
198
199 expect(resolved).toEqual({
200 logLevel: "Info",
201 ...defaultObservabilityConfig,
202 mode: "web",
203 port: 8788,
204 cwd: process.cwd(),
205 baseDir,
206 ...derivedPaths,
207 host: "127.0.0.1",
208 staticDir: undefined,
209 devUrl: new URL("http://127.0.0.1:4173"),
210 noBrowser: false,
211 startupPresentation: "browser",
212 desktopBootstrapToken: undefined,
213 autoBootstrapProjectFromCwd: false,
214 logWebSocketEvents: false,
215 });
216 }),
217 );
218
156 it.effect("uses bootstrap envelope values as fallbacks when flags and env are absent", () =>219 it.effect("uses bootstrap envelope values as fallbacks when flags and env are absent", () =>
157 Effect.gen(function* () {220 Effect.gen(function* () {
158 const { join } = yield* Path.Path;221 const { join } = yield* Path.Path;
apps/server/src/cli.test.ts+4
...@@ -152,6 +152,10 @@ it.layer(NodeServices.layer)("cli log-level parsing", (it) => {...@@ -152,6 +152,10 @@ it.layer(NodeServices.layer)("cli log-level parsing", (it) => {
152 runCliWithRuntime(["--log-level", "debug", "--version"]),152 runCliWithRuntime(["--log-level", "debug", "--version"]),
153 );153 );
154154
155 it.effect("accepts canonical --no-<flag> boolean negation", () =>
156 runCliWithRuntime(["--no-log-websocket-events", "--version"]),
157 );
158
155 it.effect("rejects invalid log-level casing before launching the server", () =>159 it.effect("rejects invalid log-level casing before launching the server", () =>
156 Effect.gen(function* () {160 Effect.gen(function* () {
157 const error = yield* runCliWithRuntime(["--log-level", "Debug"]).pipe(Effect.flip);161 const error = yield* runCliWithRuntime(["--log-level", "Debug"]).pipe(Effect.flip);
apps/server/src/cli.ts+37-68
...@@ -193,9 +193,6 @@ interface CliAuthLocationFlags {...@@ -193,9 +193,6 @@ interface CliAuthLocationFlags {
193 readonly devUrl?: Option.Option<URL>;193 readonly devUrl?: Option.Option<URL>;
194}194}
195195
196const resolveBooleanFlag = (flag: Option.Option<boolean>, envValue: boolean) =>
197 Option.getOrElse(Option.filter(flag, Boolean), () => envValue);
198
199const resolveOptionPrecedence = <Value>(196const resolveOptionPrecedence = <Value>(
200 ...values: ReadonlyArray<Option.Option<Value>>197 ...values: ReadonlyArray<Option.Option<Value>>
201): Option.Option<Value> => Option.firstSomeOf(values);198): Option.Option<Value> => Option.firstSomeOf(values);
...@@ -241,12 +238,13 @@ export const resolveServerConfig = (...@@ -241,12 +238,13 @@ export const resolveServerConfig = (
241 bootstrapFd !== undefined238 bootstrapFd !== undefined
242 ? yield* readBootstrapEnvelope(BootstrapEnvelopeSchema, bootstrapFd)239 ? yield* readBootstrapEnvelope(BootstrapEnvelopeSchema, bootstrapFd)
243 : Option.none();240 : Option.none();
241 const bootstrap = Option.getOrUndefined(bootstrapEnvelope);
244242
245 const mode: RuntimeMode = Option.getOrElse(243 const mode: RuntimeMode = Option.getOrElse(
246 resolveOptionPrecedence(244 resolveOptionPrecedence(
247 normalizedFlags.mode,245 normalizedFlags.mode,
248 Option.fromUndefinedOr(env.mode),246 Option.fromUndefinedOr(env.mode),
249 Option.flatMap(bootstrapEnvelope, (bootstrap) => Option.fromUndefinedOr(bootstrap.mode)),247 Option.fromUndefinedOr(bootstrap?.mode),
250 ),248 ),
251 () => "web",249 () => "web",
252 );250 );
...@@ -255,7 +253,7 @@ export const resolveServerConfig = (...@@ -255,7 +253,7 @@ export const resolveServerConfig = (
255 resolveOptionPrecedence(253 resolveOptionPrecedence(
256 normalizedFlags.port,254 normalizedFlags.port,
257 Option.fromUndefinedOr(env.port),255 Option.fromUndefinedOr(env.port),
258 Option.flatMap(bootstrapEnvelope, (bootstrap) => Option.fromUndefinedOr(bootstrap.port)),256 Option.fromUndefinedOr(bootstrap?.port),
259 ),257 ),
260 {258 {
261 onSome: (value) => Effect.succeed(value),259 onSome: (value) => Effect.succeed(value),
...@@ -271,7 +269,7 @@ export const resolveServerConfig = (...@@ -271,7 +269,7 @@ export const resolveServerConfig = (
271 resolveOptionPrecedence(269 resolveOptionPrecedence(
272 normalizedFlags.devUrl,270 normalizedFlags.devUrl,
273 Option.fromUndefinedOr(env.devUrl),271 Option.fromUndefinedOr(env.devUrl),
274 Option.flatMap(bootstrapEnvelope, (bootstrap) => Option.fromUndefinedOr(bootstrap.devUrl)),272 Option.fromUndefinedOr(bootstrap?.devUrl),
275 ),273 ),
276 () => undefined,274 () => undefined,
277 );275 );
...@@ -280,9 +278,7 @@ export const resolveServerConfig = (...@@ -280,9 +278,7 @@ export const resolveServerConfig = (
280 resolveOptionPrecedence(278 resolveOptionPrecedence(
281 normalizedFlags.baseDir,279 normalizedFlags.baseDir,
282 Option.fromUndefinedOr(env.t3Home),280 Option.fromUndefinedOr(env.t3Home),
283 Option.flatMap(bootstrapEnvelope, (bootstrap) =>281 Option.fromUndefinedOr(bootstrap?.t3Home),
284 Option.fromUndefinedOr(bootstrap.t3Home),
285 ),
286 ),282 ),
287 ),283 ),
288 );284 );
...@@ -297,60 +293,41 @@ export const resolveServerConfig = (...@@ -297,60 +293,41 @@ export const resolveServerConfig = (
297 const serverTracePath = env.traceFile ?? derivedPaths.serverTracePath;293 const serverTracePath = env.traceFile ?? derivedPaths.serverTracePath;
298 yield* fs.makeDirectory(path.dirname(serverTracePath), { recursive: true });294 yield* fs.makeDirectory(path.dirname(serverTracePath), { recursive: true });
299 const startupPresentation = options?.startupPresentation ?? "browser";295 const startupPresentation = options?.startupPresentation ?? "browser";
300 const noBrowser =296 const isHeadlessStartup = startupPresentation === "headless";
301 startupPresentation === "headless"297 const noBrowser = Option.getOrElse(
302 ? true298 resolveOptionPrecedence(
303 : resolveBooleanFlag(299 isHeadlessStartup ? Option.some(true) : Option.none(),
304 normalizedFlags.noBrowser,300 normalizedFlags.noBrowser,
305 Option.getOrElse(301 Option.fromUndefinedOr(env.noBrowser),
306 resolveOptionPrecedence(302 Option.fromUndefinedOr(bootstrap?.noBrowser),
307 Option.fromUndefinedOr(env.noBrowser),
308 Option.flatMap(bootstrapEnvelope, (bootstrap) =>
309 Option.fromUndefinedOr(bootstrap.noBrowser),
310 ),
311 ),
312 () => mode === "desktop",
313 ),
314 );
315 const desktopBootstrapToken = Option.getOrUndefined(
316 Option.flatMap(bootstrapEnvelope, (bootstrap) =>
317 Option.fromUndefinedOr(bootstrap.desktopBootstrapToken),
318 ),303 ),
304 () => mode === "desktop",
319 );305 );
320 const autoBootstrapProjectFromCwd =306 const desktopBootstrapToken = bootstrap?.desktopBootstrapToken;
321 options?.forceAutoBootstrapProjectFromCwd ??307 const autoBootstrapProjectFromCwd = Option.getOrElse(
322 (startupPresentation === "headless"308 resolveOptionPrecedence(
323 ? false309 Option.fromUndefinedOr(options?.forceAutoBootstrapProjectFromCwd),
324 : resolveBooleanFlag(310 isHeadlessStartup ? Option.some(false) : Option.none(),
325 normalizedFlags.autoBootstrapProjectFromCwd,311 normalizedFlags.autoBootstrapProjectFromCwd,
326 Option.getOrElse(312 Option.fromUndefinedOr(env.autoBootstrapProjectFromCwd),
327 resolveOptionPrecedence(313 Option.fromUndefinedOr(bootstrap?.autoBootstrapProjectFromCwd),
328 Option.fromUndefinedOr(env.autoBootstrapProjectFromCwd),314 ),
329 Option.flatMap(bootstrapEnvelope, (bootstrap) =>315 () => mode === "web",
330 Option.fromUndefinedOr(bootstrap.autoBootstrapProjectFromCwd),316 );
331 ),317 const logWebSocketEvents = Option.getOrElse(
332 ),318 resolveOptionPrecedence(
333 () => mode === "web",319 normalizedFlags.logWebSocketEvents,
334 ),320 Option.fromUndefinedOr(env.logWebSocketEvents),
335 ));321 Option.fromUndefinedOr(bootstrap?.logWebSocketEvents),
336 const logWebSocketEvents = resolveBooleanFlag(
337 normalizedFlags.logWebSocketEvents,
338 Option.getOrElse(
339 resolveOptionPrecedence(
340 Option.fromUndefinedOr(env.logWebSocketEvents),
341 Option.flatMap(bootstrapEnvelope, (bootstrap) =>
342 Option.fromUndefinedOr(bootstrap.logWebSocketEvents),
343 ),
344 ),
345 () => Boolean(devUrl),
346 ),322 ),
323 () => Boolean(devUrl),
347 );324 );
348 const staticDir = devUrl ? undefined : yield* resolveStaticDir();325 const staticDir = devUrl ? undefined : yield* resolveStaticDir();
349 const host = Option.getOrElse(326 const host = Option.getOrElse(
350 resolveOptionPrecedence(327 resolveOptionPrecedence(
351 normalizedFlags.host,328 normalizedFlags.host,
352 Option.fromUndefinedOr(env.host),329 Option.fromUndefinedOr(env.host),
353 Option.flatMap(bootstrapEnvelope, (bootstrap) => Option.fromUndefinedOr(bootstrap.host)),330 Option.fromUndefinedOr(bootstrap?.host),
354 ),331 ),
355 () => (mode === "desktop" ? "127.0.0.1" : undefined),332 () => (mode === "desktop" ? "127.0.0.1" : undefined),
356 );333 );
...@@ -365,19 +342,11 @@ export const resolveServerConfig = (...@@ -365,19 +342,11 @@ export const resolveServerConfig = (
365 traceMaxFiles: env.traceMaxFiles,342 traceMaxFiles: env.traceMaxFiles,
366 otlpTracesUrl:343 otlpTracesUrl:
367 env.otlpTracesUrl ??344 env.otlpTracesUrl ??
368 Option.getOrUndefined(345 bootstrap?.otlpTracesUrl ??
369 Option.flatMap(bootstrapEnvelope, (bootstrap) =>
370 Option.fromUndefinedOr(bootstrap.otlpTracesUrl),
371 ),
372 ) ??
373 persistedObservabilitySettings.otlpTracesUrl,346 persistedObservabilitySettings.otlpTracesUrl,
374 otlpMetricsUrl:347 otlpMetricsUrl:
375 env.otlpMetricsUrl ??348 env.otlpMetricsUrl ??
376 Option.getOrUndefined(349 bootstrap?.otlpMetricsUrl ??
377 Option.flatMap(bootstrapEnvelope, (bootstrap) =>
378 Option.fromUndefinedOr(bootstrap.otlpMetricsUrl),
379 ),
380 ) ??
381 persistedObservabilitySettings.otlpMetricsUrl,350 persistedObservabilitySettings.otlpMetricsUrl,
382 otlpExportIntervalMs: env.otlpExportIntervalMs,351 otlpExportIntervalMs: env.otlpExportIntervalMs,
383 otlpServiceName: env.otlpServiceName,352 otlpServiceName: env.otlpServiceName,
...@@ -1030,10 +999,10 @@ const projectAddCommand = Command.make("add", {...@@ -1030,10 +999,10 @@ const projectAddCommand = Command.make("add", {
1030 }999 }
10311000
1032 const title = yield* resolveProjectTitle(workspaceRoot, Option.getOrUndefined(flags.title));1001 const title = yield* resolveProjectTitle(workspaceRoot, Option.getOrUndefined(flags.title));
1033 const projectId = ProjectId.makeUnsafe(crypto.randomUUID());1002 const projectId = ProjectId.make(crypto.randomUUID());
1034 yield* dispatch({1003 yield* dispatch({
1035 type: "project.create",1004 type: "project.create",
1036 commandId: CommandId.makeUnsafe(crypto.randomUUID()),1005 commandId: CommandId.make(crypto.randomUUID()),
1037 projectId,1006 projectId,
1038 title,1007 title,
1039 workspaceRoot,1008 workspaceRoot,
...@@ -1071,7 +1040,7 @@ const projectRemoveCommand = Command.make("remove", {...@@ -1071,7 +1040,7 @@ const projectRemoveCommand = Command.make("remove", {
1071 });1040 });
1072 yield* dispatch({1041 yield* dispatch({
1073 type: "project.delete",1042 type: "project.delete",
1074 commandId: CommandId.makeUnsafe(crypto.randomUUID()),1043 commandId: CommandId.make(crypto.randomUUID()),
1075 projectId: project.id,1044 projectId: project.id,
1076 });1045 });
1077 return `Removed project ${project.id} (${project.title}).`;1046 return `Removed project ${project.id} (${project.title}).`;
...@@ -1111,7 +1080,7 @@ const projectRenameCommand = Command.make("rename", {...@@ -1111,7 +1080,7 @@ const projectRenameCommand = Command.make("rename", {
11111080
1112 yield* dispatch({1081 yield* dispatch({
1113 type: "project.meta.update",1082 type: "project.meta.update",
1114 commandId: CommandId.makeUnsafe(crypto.randomUUID()),1083 commandId: CommandId.make(crypto.randomUUID()),
1115 projectId: project.id,1084 projectId: project.id,
1116 title: nextTitle,1085 title: nextTitle,
1117 });1086 });
apps/server/src/codexAppServerManager.test.ts+5-5
...@@ -17,7 +17,7 @@ import {...@@ -17,7 +17,7 @@ import {
17 resolveCodexModelForAccount,17 resolveCodexModelForAccount,
18} from "./codexAppServerManager";18} from "./codexAppServerManager";
1919
20const asThreadId = (value: string): ThreadId => ThreadId.makeUnsafe(value);20const asThreadId = (value: string): ThreadId => ThreadId.make(value);
2121
22function createSendTurnHarness() {22function createSendTurnHarness() {
23 const manager = new CodexAppServerManager();23 const manager = new CodexAppServerManager();
...@@ -111,9 +111,9 @@ function createPendingUserInputHarness() {...@@ -111,9 +111,9 @@ function createPendingUserInputHarness() {
111 },111 },
112 pendingUserInputs: new Map([112 pendingUserInputs: new Map([
113 [113 [
114 ApprovalRequestId.makeUnsafe("req-user-input-1"),114 ApprovalRequestId.make("req-user-input-1"),
115 {115 {
116 requestId: ApprovalRequestId.makeUnsafe("req-user-input-1"),116 requestId: ApprovalRequestId.make("req-user-input-1"),
117 jsonRpcId: 42,117 jsonRpcId: 42,
118 threadId: asThreadId("thread_1"),118 threadId: asThreadId("thread_1"),
119 },119 },
...@@ -743,7 +743,7 @@ describe("respondToUserInput", () => {...@@ -743,7 +743,7 @@ describe("respondToUserInput", () => {
743743
744 await manager.respondToUserInput(744 await manager.respondToUserInput(
745 asThreadId("thread_1"),745 asThreadId("thread_1"),
746 ApprovalRequestId.makeUnsafe("req-user-input-1"),746 ApprovalRequestId.make("req-user-input-1"),
747 {747 {
748 scope: "All request methods",748 scope: "All request methods",
749 compat: "Keep current envelope",749 compat: "Keep current envelope",
...@@ -780,7 +780,7 @@ describe("respondToUserInput", () => {...@@ -780,7 +780,7 @@ describe("respondToUserInput", () => {
780780
781 await manager.respondToUserInput(781 await manager.respondToUserInput(
782 asThreadId("thread_1"),782 asThreadId("thread_1"),
783 ApprovalRequestId.makeUnsafe("req-user-input-1"),783 ApprovalRequestId.make("req-user-input-1"),
784 {784 {
785 scope: [],785 scope: [],
786 },786 },
apps/server/src/codexAppServerManager.ts+16-16
...@@ -19,7 +19,7 @@ import {...@@ -19,7 +19,7 @@ import {
19 ProviderInteractionMode,19 ProviderInteractionMode,
20} from "@t3tools/contracts";20} from "@t3tools/contracts";
21import { normalizeModelSlug } from "@t3tools/shared/model";21import { normalizeModelSlug } from "@t3tools/shared/model";
22import { Effect, ServiceMap } from "effect";22import { Effect, Context } from "effect";
2323
24import {24import {
25 formatCodexCliUpgradeMessage,25 formatCodexCliUpgradeMessage,
...@@ -439,7 +439,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve...@@ -439,7 +439,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve
439 private readonly sessions = new Map<ThreadId, CodexSessionContext>();439 private readonly sessions = new Map<ThreadId, CodexSessionContext>();
440440
441 private runPromise: (effect: Effect.Effect<unknown, never>) => Promise<unknown>;441 private runPromise: (effect: Effect.Effect<unknown, never>) => Promise<unknown>;
442 constructor(services?: ServiceMap.ServiceMap<never>) {442 constructor(services?: Context.Context<never>) {
443 super();443 super();
444 this.runPromise = services ? Effect.runPromiseWith(services) : Effect.runPromise;444 this.runPromise = services ? Effect.runPromiseWith(services) : Effect.runPromise;
445 }445 }
...@@ -640,7 +640,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve...@@ -640,7 +640,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve
640 this.stopSession(threadId);640 this.stopSession(threadId);
641 } else {641 } else {
642 this.emitEvent({642 this.emitEvent({
643 id: EventId.makeUnsafe(randomUUID()),643 id: EventId.make(randomUUID()),
644 kind: "error",644 kind: "error",
645 provider: "codex",645 provider: "codex",
646 threadId,646 threadId,
...@@ -739,7 +739,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve...@@ -739,7 +739,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve
739 if (!turnIdRaw) {739 if (!turnIdRaw) {
740 throw new Error("turn/start response did not include a turn id.");740 throw new Error("turn/start response did not include a turn id.");
741 }741 }
742 const turnId = TurnId.makeUnsafe(turnIdRaw);742 const turnId = TurnId.make(turnIdRaw);
743743
744 this.updateSession(context, {744 this.updateSession(context, {
745 status: "running",745 status: "running",
...@@ -840,7 +840,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve...@@ -840,7 +840,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve
840 });840 });
841841
842 this.emitEvent({842 this.emitEvent({
843 id: EventId.makeUnsafe(randomUUID()),843 id: EventId.make(randomUUID()),
844 kind: "notification",844 kind: "notification",
845 provider: "codex",845 provider: "codex",
846 threadId: context.session.threadId,846 threadId: context.session.threadId,
...@@ -879,7 +879,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve...@@ -879,7 +879,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve
879 });879 });
880880
881 this.emitEvent({881 this.emitEvent({
882 id: EventId.makeUnsafe(randomUUID()),882 id: EventId.make(randomUUID()),
883 kind: "notification",883 kind: "notification",
884 provider: "codex",884 provider: "codex",
885 threadId: context.session.threadId,885 threadId: context.session.threadId,
...@@ -1061,7 +1061,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve...@@ -1061,7 +1061,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve
1061 : undefined;1061 : undefined;
10621062
1063 this.emitEvent({1063 this.emitEvent({
1064 id: EventId.makeUnsafe(randomUUID()),1064 id: EventId.make(randomUUID()),
1065 kind: "notification",1065 kind: "notification",
1066 provider: "codex",1066 provider: "codex",
1067 threadId: context.session.threadId,1067 threadId: context.session.threadId,
...@@ -1134,7 +1134,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve...@@ -1134,7 +1134,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve
1134 const requestKind = this.requestKindForMethod(request.method);1134 const requestKind = this.requestKindForMethod(request.method);
1135 let requestId: ApprovalRequestId | undefined;1135 let requestId: ApprovalRequestId | undefined;
1136 if (requestKind) {1136 if (requestKind) {
1137 requestId = ApprovalRequestId.makeUnsafe(randomUUID());1137 requestId = ApprovalRequestId.make(randomUUID());
1138 const pendingRequest: PendingApprovalRequest = {1138 const pendingRequest: PendingApprovalRequest = {
1139 requestId,1139 requestId,
1140 jsonRpcId: request.id,1140 jsonRpcId: request.id,
...@@ -1153,7 +1153,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve...@@ -1153,7 +1153,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve
1153 }1153 }
11541154
1155 if (request.method === "item/tool/requestUserInput") {1155 if (request.method === "item/tool/requestUserInput") {
1156 requestId = ApprovalRequestId.makeUnsafe(randomUUID());1156 requestId = ApprovalRequestId.make(randomUUID());
1157 context.pendingUserInputs.set(requestId, {1157 context.pendingUserInputs.set(requestId, {
1158 requestId,1158 requestId,
1159 jsonRpcId: request.id,1159 jsonRpcId: request.id,
...@@ -1164,7 +1164,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve...@@ -1164,7 +1164,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve
1164 }1164 }
11651165
1166 this.emitEvent({1166 this.emitEvent({
1167 id: EventId.makeUnsafe(randomUUID()),1167 id: EventId.make(randomUUID()),
1168 kind: "request",1168 kind: "request",
1169 provider: "codex",1169 provider: "codex",
1170 threadId: context.session.threadId,1170 threadId: context.session.threadId,
...@@ -1254,7 +1254,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve...@@ -1254,7 +1254,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve
12541254
1255 private emitLifecycleEvent(context: CodexSessionContext, method: string, message: string): void {1255 private emitLifecycleEvent(context: CodexSessionContext, method: string, message: string): void {
1256 this.emitEvent({1256 this.emitEvent({
1257 id: EventId.makeUnsafe(randomUUID()),1257 id: EventId.make(randomUUID()),
1258 kind: "session",1258 kind: "session",
1259 provider: "codex",1259 provider: "codex",
1260 threadId: context.session.threadId,1260 threadId: context.session.threadId,
...@@ -1266,7 +1266,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve...@@ -1266,7 +1266,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve
12661266
1267 private emitErrorEvent(context: CodexSessionContext, method: string, message: string): void {1267 private emitErrorEvent(context: CodexSessionContext, method: string, message: string): void {
1268 this.emitEvent({1268 this.emitEvent({
1269 id: EventId.makeUnsafe(randomUUID()),1269 id: EventId.make(randomUUID()),
1270 kind: "error",1270 kind: "error",
1271 provider: "codex",1271 provider: "codex",
1272 threadId: context.session.threadId,1272 threadId: context.session.threadId,
...@@ -1282,7 +1282,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve...@@ -1282,7 +1282,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve
1282 message: string,1282 message: string,
1283 ): void {1283 ): void {
1284 this.emitEvent({1284 this.emitEvent({
1285 id: EventId.makeUnsafe(randomUUID()),1285 id: EventId.make(randomUUID()),
1286 kind: "notification",1286 kind: "notification",
1287 provider: "codex",1287 provider: "codex",
1288 threadId: context.session.threadId,1288 threadId: context.session.threadId,
...@@ -1341,7 +1341,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve...@@ -1341,7 +1341,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve
1341 const turns = turnsRaw.map((turnValue, index) => {1341 const turns = turnsRaw.map((turnValue, index) => {
1342 const turn = this.readObject(turnValue);1342 const turn = this.readObject(turnValue);
1343 const turnIdRaw = this.readString(turn, "id") ?? `${threadIdRaw}:turn:${index + 1}`;1343 const turnIdRaw = this.readString(turn, "id") ?? `${threadIdRaw}:turn:${index + 1}`;
1344 const turnId = TurnId.makeUnsafe(turnIdRaw);1344 const turnId = TurnId.make(turnIdRaw);
1345 const items = this.readArray(turn, "items") ?? [];1345 const items = this.readArray(turn, "items") ?? [];
1346 return {1346 return {
1347 id: turnId,1347 id: turnId,
...@@ -1590,9 +1590,9 @@ function readResumeThreadId(input: {...@@ -1590,9 +1590,9 @@ function readResumeThreadId(input: {
1590}1590}
15911591
1592function toTurnId(value: string | undefined): TurnId | undefined {1592function toTurnId(value: string | undefined): TurnId | undefined {
1593 return brandIfNonEmpty(value, TurnId.makeUnsafe);1593 return brandIfNonEmpty(value, TurnId.make);
1594}1594}
15951595
1596function toProviderItemId(value: string | undefined): ProviderItemId | undefined {1596function toProviderItemId(value: string | undefined): ProviderItemId | undefined {
1597 return brandIfNonEmpty(value, ProviderItemId.makeUnsafe);1597 return brandIfNonEmpty(value, ProviderItemId.make);
1598}1598}
apps/server/src/config.ts+2-2
...@@ -6,7 +6,7 @@...@@ -6,7 +6,7 @@
6 *6 *
7 * @module ServerConfig7 * @module ServerConfig
8 */8 */
9import { Effect, FileSystem, Layer, LogLevel, Path, Schema, ServiceMap } from "effect";9import { Effect, FileSystem, Layer, LogLevel, Path, Schema, Context } from "effect";
1010
11export const DEFAULT_PORT = 3773;11export const DEFAULT_PORT = 3773;
1212
...@@ -120,7 +120,7 @@ export const ensureServerDirectories = Effect.fn(function* (derivedPaths: Server...@@ -120,7 +120,7 @@ export const ensureServerDirectories = Effect.fn(function* (derivedPaths: Server
120/**120/**
121 * ServerConfig - Service tag for server runtime configuration.121 * ServerConfig - Service tag for server runtime configuration.
122 */122 */
123export class ServerConfig extends ServiceMap.Service<ServerConfig, ServerConfigShape>()(123export class ServerConfig extends Context.Service<ServerConfig, ServerConfigShape>()(
124 "t3/config/ServerConfig",124 "t3/config/ServerConfig",
125) {125) {
126 static readonly layerTest = (cwd: string, baseDirOrPrefix: string | { prefix: string }) =>126 static readonly layerTest = (cwd: string, baseDirOrPrefix: string | { prefix: string }) =>
apps/server/src/environment/Layers/ServerEnvironment.ts+1-1
...@@ -64,7 +64,7 @@ export const makeServerEnvironment = Effect.fn("makeServerEnvironment")(function...@@ -64,7 +64,7 @@ export const makeServerEnvironment = Effect.fn("makeServerEnvironment")(function
64 return generated;64 return generated;
65 });65 });
6666
67 const environmentId = EnvironmentId.makeUnsafe(environmentIdRaw);67 const environmentId = EnvironmentId.make(environmentIdRaw);
68 const cwdBaseName = path.basename(serverConfig.cwd).trim();68 const cwdBaseName = path.basename(serverConfig.cwd).trim();
69 const label = yield* resolveServerEnvironmentLabel({69 const label = yield* resolveServerEnvironmentLabel({
70 cwdBaseName,70 cwdBaseName,
apps/server/src/environment/Layers/ServerEnvironmentLabel.ts+7-5
...@@ -51,11 +51,13 @@ const runFriendlyLabelCommand = Effect.fn("runFriendlyLabelCommand")(function* (...@@ -51,11 +51,13 @@ const runFriendlyLabelCommand = Effect.fn("runFriendlyLabelCommand")(function* (
51 command: string,51 command: string,
52 args: readonly string[],52 args: readonly string[],
53) {53) {
54 const result = yield* Effect.tryPromise(() =>54 const result = yield* Effect.tryPromise({
55 runProcess(command, args, {55 try: () =>
56 allowNonZeroExit: true,56 runProcess(command, args, {
57 }),57 allowNonZeroExit: true,
58 ).pipe(Effect.orElseSucceed(() => null));58 }),
59 catch: () => null,
60 }).pipe(Effect.orElseSucceed(() => null));
5961
60 if (!result || result.code !== 0) {62 if (!result || result.code !== 0) {
61 return null;63 return null;
apps/server/src/environment/Services/ServerEnvironment.ts+4-5
...@@ -1,5 +1,5 @@...@@ -1,5 +1,5 @@
1import type { EnvironmentId, ExecutionEnvironmentDescriptor } from "@t3tools/contracts";1import type { EnvironmentId, ExecutionEnvironmentDescriptor } from "@t3tools/contracts";
2import { ServiceMap } from "effect";2import { Context } from "effect";
3import type { Effect } from "effect";3import type { Effect } from "effect";
44
5export interface ServerEnvironmentShape {5export interface ServerEnvironmentShape {
...@@ -7,7 +7,6 @@ export interface ServerEnvironmentShape {...@@ -7,7 +7,6 @@ export interface ServerEnvironmentShape {
7 readonly getDescriptor: Effect.Effect<ExecutionEnvironmentDescriptor>;7 readonly getDescriptor: Effect.Effect<ExecutionEnvironmentDescriptor>;
8}8}
99
10export class ServerEnvironment extends ServiceMap.Service<10export class ServerEnvironment extends Context.Service<ServerEnvironment, ServerEnvironmentShape>()(
11 ServerEnvironment,11 "t3/environment/Services/ServerEnvironment",
12 ServerEnvironmentShape12) {}
13>()("t3/environment/Services/ServerEnvironment") {}
apps/server/src/git/Layers/GitCore.test.ts+2-2
...@@ -649,8 +649,8 @@ it.layer(TestLayer)("git integration", (it) => {...@@ -649,8 +649,8 @@ it.layer(TestLayer)("git integration", (it) => {
649649
650 it.effect("refreshes upstream behind count after checkout when remote branch advanced", () =>650 it.effect("refreshes upstream behind count after checkout when remote branch advanced", () =>
651 Effect.gen(function* () {651 Effect.gen(function* () {
652 const services = yield* Effect.services();652 const context = yield* Effect.context<never>();
653 const runPromise = Effect.runPromiseWith(services);653 const runPromise = Effect.runPromiseWith(context);
654654
655 const remote = yield* makeTmpDir();655 const remote = yield* makeTmpDir();
656 const source = yield* makeTmpDir();656 const source = yield* makeTmpDir();
apps/server/src/git/Layers/GitCore.ts+1-2
...@@ -933,9 +933,8 @@ export const makeGitCore = Effect.fn("makeGitCore")(function* (options?: {...@@ -933,9 +933,8 @@ export const makeGitCore = Effect.fn("makeGitCore")(function* (options?: {
933 return true as const;933 return true as const;
934 });934 });
935935
936 const statusUpstreamRefreshCache = yield* Cache.makeWith({936 const statusUpstreamRefreshCache = yield* Cache.makeWith(refreshStatusUpstreamCacheEntry, {
937 capacity: STATUS_UPSTREAM_REFRESH_CACHE_CAPACITY,937 capacity: STATUS_UPSTREAM_REFRESH_CACHE_CAPACITY,
938 lookup: refreshStatusUpstreamCacheEntry,
939 // Keep successful refreshes warm and briefly back off failed refreshes to avoid retry storms.938 // Keep successful refreshes warm and briefly back off failed refreshes to avoid retry storms.
940 timeToLive: (exit) =>939 timeToLive: (exit) =>
941 Exit.isSuccess(exit)940 Exit.isSuccess(exit)
apps/server/src/git/Layers/GitManager.ts+2-4
...@@ -731,9 +731,8 @@ export const makeGitManager = Effect.fn("makeGitManager")(function* () {...@@ -731,9 +731,8 @@ export const makeGitManager = Effect.fn("makeGitManager")(function* () {
731 workingTree: details.workingTree,731 workingTree: details.workingTree,
732 } satisfies GitStatusLocalResult;732 } satisfies GitStatusLocalResult;
733 });733 });
734 const localStatusResultCache = yield* Cache.makeWith({734 const localStatusResultCache = yield* Cache.makeWith(readLocalStatus, {
735 capacity: STATUS_RESULT_CACHE_CAPACITY,735 capacity: STATUS_RESULT_CACHE_CAPACITY,
736 lookup: readLocalStatus,
737 timeToLive: (exit) => (Exit.isSuccess(exit) ? STATUS_RESULT_CACHE_TTL : Duration.zero),736 timeToLive: (exit) => (Exit.isSuccess(exit) ? STATUS_RESULT_CACHE_TTL : Duration.zero),
738 });737 });
739 const invalidateLocalStatusResultCache = (cwd: string) =>738 const invalidateLocalStatusResultCache = (cwd: string) =>
...@@ -764,9 +763,8 @@ export const makeGitManager = Effect.fn("makeGitManager")(function* () {...@@ -764,9 +763,8 @@ export const makeGitManager = Effect.fn("makeGitManager")(function* () {
764 pr,763 pr,
765 } satisfies GitStatusRemoteResult;764 } satisfies GitStatusRemoteResult;
766 });765 });
767 const remoteStatusResultCache = yield* Cache.makeWith({766 const remoteStatusResultCache = yield* Cache.makeWith(readRemoteStatus, {
768 capacity: STATUS_RESULT_CACHE_CAPACITY,767 capacity: STATUS_RESULT_CACHE_CAPACITY,
769 lookup: readRemoteStatus,
770 timeToLive: (exit) => (Exit.isSuccess(exit) ? STATUS_RESULT_CACHE_TTL : Duration.zero),768 timeToLive: (exit) => (Exit.isSuccess(exit) ? STATUS_RESULT_CACHE_TTL : Duration.zero),
771 });769 });
772 const invalidateRemoteStatusResultCache = (cwd: string) =>770 const invalidateRemoteStatusResultCache = (cwd: string) =>
apps/server/src/git/Layers/RoutingTextGeneration.ts+3-3
...@@ -9,7 +9,7 @@...@@ -9,7 +9,7 @@
9 *9 *
10 * @module RoutingTextGeneration10 * @module RoutingTextGeneration
11 */11 */
12import { Effect, Layer, ServiceMap } from "effect";12import { Effect, Layer, Context } from "effect";
1313
14import {14import {
15 TextGeneration,15 TextGeneration,
...@@ -23,11 +23,11 @@ import { ClaudeTextGenerationLive } from "./ClaudeTextGeneration.ts";...@@ -23,11 +23,11 @@ import { ClaudeTextGenerationLive } from "./ClaudeTextGeneration.ts";
23// Internal service tags so both concrete layers can coexist.23// Internal service tags so both concrete layers can coexist.
24// ---------------------------------------------------------------------------24// ---------------------------------------------------------------------------
2525
26class CodexTextGen extends ServiceMap.Service<CodexTextGen, TextGenerationShape>()(26class CodexTextGen extends Context.Service<CodexTextGen, TextGenerationShape>()(
27 "t3/git/Layers/RoutingTextGeneration/CodexTextGen",27 "t3/git/Layers/RoutingTextGeneration/CodexTextGen",
28) {}28) {}
2929
30class ClaudeTextGen extends ServiceMap.Service<ClaudeTextGen, TextGenerationShape>()(30class ClaudeTextGen extends Context.Service<ClaudeTextGen, TextGenerationShape>()(
31 "t3/git/Layers/RoutingTextGeneration/ClaudeTextGen",31 "t3/git/Layers/RoutingTextGeneration/ClaudeTextGen",
32) {}32) {}
3333
apps/server/src/git/Services/GitCore.ts+2-4
...@@ -6,7 +6,7 @@...@@ -6,7 +6,7 @@
6 *6 *
7 * @module GitCore7 * @module GitCore
8 */8 */
9import { ServiceMap } from "effect";9import { Context } from "effect";
10import type { Effect } from "effect";10import type { Effect } from "effect";
11import type {11import type {
12 GitCheckoutInput,12 GitCheckoutInput,
...@@ -310,6 +310,4 @@ export interface GitCoreShape {...@@ -310,6 +310,4 @@ export interface GitCoreShape {
310/**310/**
311 * GitCore - Service tag for low-level Git repository operations.311 * GitCore - Service tag for low-level Git repository operations.
312 */312 */
313export class GitCore extends ServiceMap.Service<GitCore, GitCoreShape>()(313export class GitCore extends Context.Service<GitCore, GitCoreShape>()("t3/git/Services/GitCore") {}
314 "t3/git/Services/GitCore",
315) {}
apps/server/src/git/Services/GitHubCli.ts+2-2
...@@ -5,7 +5,7 @@...@@ -5,7 +5,7 @@
5 *5 *
6 * @module GitHubCli6 * @module GitHubCli
7 */7 */
8import { ServiceMap } from "effect";8import { Context } from "effect";
9import type { Effect } from "effect";9import type { Effect } from "effect";
1010
11import type { ProcessRunResult } from "../../processRunner";11import type { ProcessRunResult } from "../../processRunner";
...@@ -98,6 +98,6 @@ export interface GitHubCliShape {...@@ -98,6 +98,6 @@ export interface GitHubCliShape {
98/**98/**
99 * GitHubCli - Service tag for GitHub CLI process execution.99 * GitHubCli - Service tag for GitHub CLI process execution.
100 */100 */
101export class GitHubCli extends ServiceMap.Service<GitHubCli, GitHubCliShape>()(101export class GitHubCli extends Context.Service<GitHubCli, GitHubCliShape>()(
102 "t3/git/Services/GitHubCli",102 "t3/git/Services/GitHubCli",
103) {}103) {}
apps/server/src/git/Services/GitManager.ts+2-2
...@@ -19,7 +19,7 @@ import {...@@ -19,7 +19,7 @@ import {
19 GitStatusInput,19 GitStatusInput,
20 GitStatusResult,20 GitStatusResult,
21} from "@t3tools/contracts";21} from "@t3tools/contracts";
22import { ServiceMap } from "effect";22import { Context } from "effect";
23import type { Effect } from "effect";23import type { Effect } from "effect";
24import type { GitManagerServiceError } from "@t3tools/contracts";24import type { GitManagerServiceError } from "@t3tools/contracts";
2525
...@@ -99,6 +99,6 @@ export interface GitManagerShape {...@@ -99,6 +99,6 @@ export interface GitManagerShape {
99/**99/**
100 * GitManager - Service tag for stacked Git workflow orchestration.100 * GitManager - Service tag for stacked Git workflow orchestration.
101 */101 */
102export class GitManager extends ServiceMap.Service<GitManager, GitManagerShape>()(102export class GitManager extends Context.Service<GitManager, GitManagerShape>()(
103 "t3/git/Services/GitManager",103 "t3/git/Services/GitManager",
104) {}104) {}
apps/server/src/git/Services/GitStatusBroadcaster.ts+2-2
...@@ -1,4 +1,4 @@...@@ -1,4 +1,4 @@
1import { ServiceMap } from "effect";1import { Context } from "effect";
2import type { Effect, Stream } from "effect";2import type { Effect, Stream } from "effect";
3import type {3import type {
4 GitManagerServiceError,4 GitManagerServiceError,
...@@ -21,7 +21,7 @@ export interface GitStatusBroadcasterShape {...@@ -21,7 +21,7 @@ export interface GitStatusBroadcasterShape {
21 ) => Stream.Stream<GitStatusStreamEvent, GitManagerServiceError>;21 ) => Stream.Stream<GitStatusStreamEvent, GitManagerServiceError>;
22}22}
2323
24export class GitStatusBroadcaster extends ServiceMap.Service<24export class GitStatusBroadcaster extends Context.Service<
25 GitStatusBroadcaster,25 GitStatusBroadcaster,
26 GitStatusBroadcasterShape26 GitStatusBroadcasterShape
27>()("t3/git/Services/GitStatusBroadcaster") {}27>()("t3/git/Services/GitStatusBroadcaster") {}
apps/server/src/git/Services/TextGeneration.ts+2-2
...@@ -6,7 +6,7 @@...@@ -6,7 +6,7 @@
6 *6 *
7 * @module TextGeneration7 * @module TextGeneration
8 */8 */
9import { ServiceMap } from "effect";9import { Context } from "effect";
10import type { Effect } from "effect";10import type { Effect } from "effect";
11import type { ChatAttachment, ModelSelection } from "@t3tools/contracts";11import type { ChatAttachment, ModelSelection } from "@t3tools/contracts";
1212
...@@ -118,6 +118,6 @@ export interface TextGenerationShape {...@@ -118,6 +118,6 @@ export interface TextGenerationShape {
118/**118/**
119 * TextGeneration - Service tag for commit and PR text generation.119 * TextGeneration - Service tag for commit and PR text generation.
120 */120 */
121export class TextGeneration extends ServiceMap.Service<TextGeneration, TextGenerationShape>()(121export class TextGeneration extends Context.Service<TextGeneration, TextGenerationShape>()(
122 "t3/git/Services/TextGeneration",122 "t3/git/Services/TextGeneration",
123) {}123) {}
apps/server/src/keybindings.ts+2-2
...@@ -39,7 +39,7 @@ import {...@@ -39,7 +39,7 @@ import {
39 SchemaIssue,39 SchemaIssue,
40 SchemaTransformation,40 SchemaTransformation,
41 Ref,41 Ref,
42 ServiceMap,42 Context,
43 Scope,43 Scope,
44 Stream,44 Stream,
45} from "effect";45} from "effect";
...@@ -521,7 +521,7 @@ export interface KeybindingsShape {...@@ -521,7 +521,7 @@ export interface KeybindingsShape {
521/**521/**
522 * Keybindings - Service tag for keybinding configuration operations.522 * Keybindings - Service tag for keybinding configuration operations.
523 */523 */
524export class Keybindings extends ServiceMap.Service<Keybindings, KeybindingsShape>()(524export class Keybindings extends Context.Service<Keybindings, KeybindingsShape>()(
525 "t3/keybindings",525 "t3/keybindings",
526) {}526) {}
527527
apps/server/src/observability/Services/BrowserTraceCollector.ts+2-2
...@@ -1,4 +1,4 @@...@@ -1,4 +1,4 @@
1import { ServiceMap } from "effect";1import { Context } from "effect";
2import type { Effect } from "effect";2import type { Effect } from "effect";
33
4import type { TraceRecord } from "../TraceRecord.ts";4import type { TraceRecord } from "../TraceRecord.ts";
...@@ -7,7 +7,7 @@ export interface BrowserTraceCollectorShape {...@@ -7,7 +7,7 @@ export interface BrowserTraceCollectorShape {
7 readonly record: (records: ReadonlyArray<TraceRecord>) => Effect.Effect<void>;7 readonly record: (records: ReadonlyArray<TraceRecord>) => Effect.Effect<void>;
8}8}
99
10export class BrowserTraceCollector extends ServiceMap.Service<10export class BrowserTraceCollector extends Context.Service<
11 BrowserTraceCollector,11 BrowserTraceCollector,
12 BrowserTraceCollectorShape12 BrowserTraceCollectorShape
13>()("t3/observability/Services/BrowserTraceCollector") {}13>()("t3/observability/Services/BrowserTraceCollector") {}
apps/server/src/open.ts+2-2
...@@ -11,7 +11,7 @@ import { accessSync, constants, statSync } from "node:fs";...@@ -11,7 +11,7 @@ import { accessSync, constants, statSync } from "node:fs";
11import { extname, join } from "node:path";11import { extname, join } from "node:path";
1212
13import { EDITORS, OpenError, type EditorId } from "@t3tools/contracts";13import { EDITORS, OpenError, type EditorId } from "@t3tools/contracts";
14import { ServiceMap, Effect, Layer } from "effect";14import { Context, Effect, Layer } from "effect";
1515
16// ==============================16// ==============================
17// Definitions17// Definitions
...@@ -247,7 +247,7 @@ export interface OpenShape {...@@ -247,7 +247,7 @@ export interface OpenShape {
247/**247/**
248 * Open - Service tag for browser/editor launch operations.248 * Open - Service tag for browser/editor launch operations.
249 */249 */
250export class Open extends ServiceMap.Service<Open, OpenShape>()("t3/open") {}250export class Open extends Context.Service<Open, OpenShape>()("t3/open") {}
251251
252// ==============================252// ==============================
253// Implementations253// Implementations
apps/server/src/orchestration/Layers/CheckpointReactor.test.ts+118-120
...@@ -44,8 +44,8 @@ import { ServerConfig } from "../../config.ts";...@@ -44,8 +44,8 @@ import { ServerConfig } from "../../config.ts";
44import { WorkspaceEntriesLive } from "../../workspace/Layers/WorkspaceEntries.ts";44import { WorkspaceEntriesLive } from "../../workspace/Layers/WorkspaceEntries.ts";
45import { WorkspacePathsLive } from "../../workspace/Layers/WorkspacePaths.ts";45import { WorkspacePathsLive } from "../../workspace/Layers/WorkspacePaths.ts";
4646
47const asProjectId = (value: string): ProjectId => ProjectId.makeUnsafe(value);47const asProjectId = (value: string): ProjectId => ProjectId.make(value);
48const asTurnId = (value: string): TurnId => TurnId.makeUnsafe(value);48const asTurnId = (value: string): TurnId => TurnId.make(value);
4949
50type LegacyProviderRuntimeEvent = {50type LegacyProviderRuntimeEvent = {
51 readonly type: string;51 readonly type: string;
...@@ -81,7 +81,7 @@ function createProviderServiceHarness(...@@ -81,7 +81,7 @@ function createProviderServiceHarness(
81 provider: providerName,81 provider: providerName,
82 status: "ready",82 status: "ready",
83 runtimeMode: "full-access",83 runtimeMode: "full-access",
84 threadId: ThreadId.makeUnsafe("thread-1"),84 threadId: ThreadId.make("thread-1"),
85 cwd: sessionCwd,85 cwd: sessionCwd,
86 createdAt: now,86 createdAt: now,
87 updatedAt: now,87 updatedAt: now,
...@@ -130,7 +130,7 @@ async function waitForThread(...@@ -130,7 +130,7 @@ async function waitForThread(
130 activities: ReadonlyArray<{ kind: string }>;130 activities: ReadonlyArray<{ kind: string }>;
131 }> => {131 }> => {
132 const readModel = await Effect.runPromise(engine.getReadModel());132 const readModel = await Effect.runPromise(engine.getReadModel());
133 const thread = readModel.threads.find((entry) => entry.id === ThreadId.makeUnsafe("thread-1"));133 const thread = readModel.threads.find((entry) => entry.id === ThreadId.make("thread-1"));
134 if (thread && predicate(thread)) {134 if (thread && predicate(thread)) {
135 return thread;135 return thread;
136 }136 }
...@@ -310,7 +310,7 @@ describe("CheckpointReactor", () => {...@@ -310,7 +310,7 @@ describe("CheckpointReactor", () => {
310 await Effect.runPromise(310 await Effect.runPromise(
311 engine.dispatch({311 engine.dispatch({
312 type: "project.create",312 type: "project.create",
313 commandId: CommandId.makeUnsafe("cmd-project-create"),313 commandId: CommandId.make("cmd-project-create"),
314 projectId: asProjectId("project-1"),314 projectId: asProjectId("project-1"),
315 title: "Test Project",315 title: "Test Project",
316 workspaceRoot: options?.projectWorkspaceRoot ?? cwd,316 workspaceRoot: options?.projectWorkspaceRoot ?? cwd,
...@@ -324,8 +324,8 @@ describe("CheckpointReactor", () => {...@@ -324,8 +324,8 @@ describe("CheckpointReactor", () => {
324 await Effect.runPromise(324 await Effect.runPromise(
325 engine.dispatch({325 engine.dispatch({
326 type: "thread.create",326 type: "thread.create",
327 commandId: CommandId.makeUnsafe("cmd-thread-create"),327 commandId: CommandId.make("cmd-thread-create"),
328 threadId: ThreadId.makeUnsafe("thread-1"),328 threadId: ThreadId.make("thread-1"),
329 projectId: asProjectId("project-1"),329 projectId: asProjectId("project-1"),
330 title: "Thread",330 title: "Thread",
331 modelSelection: {331 modelSelection: {
...@@ -344,21 +344,21 @@ describe("CheckpointReactor", () => {...@@ -344,21 +344,21 @@ describe("CheckpointReactor", () => {
344 await runtime.runPromise(344 await runtime.runPromise(
345 checkpointStore.captureCheckpoint({345 checkpointStore.captureCheckpoint({
346 cwd,346 cwd,
347 checkpointRef: checkpointRefForThreadTurn(ThreadId.makeUnsafe("thread-1"), 0),347 checkpointRef: checkpointRefForThreadTurn(ThreadId.make("thread-1"), 0),
348 }),348 }),
349 );349 );
350 fs.writeFileSync(path.join(cwd, "README.md"), "v2\n", "utf8");350 fs.writeFileSync(path.join(cwd, "README.md"), "v2\n", "utf8");
351 await runtime.runPromise(351 await runtime.runPromise(
352 checkpointStore.captureCheckpoint({352 checkpointStore.captureCheckpoint({
353 cwd,353 cwd,
354 checkpointRef: checkpointRefForThreadTurn(ThreadId.makeUnsafe("thread-1"), 1),354 checkpointRef: checkpointRefForThreadTurn(ThreadId.make("thread-1"), 1),
355 }),355 }),
356 );356 );
357 fs.writeFileSync(path.join(cwd, "README.md"), "v3\n", "utf8");357 fs.writeFileSync(path.join(cwd, "README.md"), "v3\n", "utf8");
358 await runtime.runPromise(358 await runtime.runPromise(
359 checkpointStore.captureCheckpoint({359 checkpointStore.captureCheckpoint({
360 cwd,360 cwd,
361 checkpointRef: checkpointRefForThreadTurn(ThreadId.makeUnsafe("thread-1"), 2),361 checkpointRef: checkpointRefForThreadTurn(ThreadId.make("thread-1"), 2),
362 }),362 }),
363 );363 );
364 }364 }
...@@ -378,10 +378,10 @@ describe("CheckpointReactor", () => {...@@ -378,10 +378,10 @@ describe("CheckpointReactor", () => {
378 await Effect.runPromise(378 await Effect.runPromise(
379 harness.engine.dispatch({379 harness.engine.dispatch({
380 type: "thread.session.set",380 type: "thread.session.set",
381 commandId: CommandId.makeUnsafe("cmd-session-set-capture"),381 commandId: CommandId.make("cmd-session-set-capture"),
382 threadId: ThreadId.makeUnsafe("thread-1"),382 threadId: ThreadId.make("thread-1"),
383 session: {383 session: {
384 threadId: ThreadId.makeUnsafe("thread-1"),384 threadId: ThreadId.make("thread-1"),
385 status: "ready",385 status: "ready",
386 providerName: "codex",386 providerName: "codex",
387 runtimeMode: "approval-required",387 runtimeMode: "approval-required",
...@@ -395,26 +395,26 @@ describe("CheckpointReactor", () => {...@@ -395,26 +395,26 @@ describe("CheckpointReactor", () => {
395395
396 harness.provider.emit({396 harness.provider.emit({
397 type: "turn.started",397 type: "turn.started",
398 eventId: EventId.makeUnsafe("evt-turn-started-1"),398 eventId: EventId.make("evt-turn-started-1"),
399 provider: "codex",399 provider: "codex",
400400
401 createdAt: new Date().toISOString(),401 createdAt: new Date().toISOString(),
402 threadId: ThreadId.makeUnsafe("thread-1"),402 threadId: ThreadId.make("thread-1"),
403 turnId: asTurnId("turn-1"),403 turnId: asTurnId("turn-1"),
404 });404 });
405 await waitForGitRefExists(405 await waitForGitRefExists(
406 harness.cwd,406 harness.cwd,
407 checkpointRefForThreadTurn(ThreadId.makeUnsafe("thread-1"), 0),407 checkpointRefForThreadTurn(ThreadId.make("thread-1"), 0),
408 );408 );
409409
410 fs.writeFileSync(path.join(harness.cwd, "README.md"), "v2\n", "utf8");410 fs.writeFileSync(path.join(harness.cwd, "README.md"), "v2\n", "utf8");
411 harness.provider.emit({411 harness.provider.emit({
412 type: "turn.completed",412 type: "turn.completed",
413 eventId: EventId.makeUnsafe("evt-turn-completed-1"),413 eventId: EventId.make("evt-turn-completed-1"),
414 provider: "codex",414 provider: "codex",
415415
416 createdAt: new Date().toISOString(),416 createdAt: new Date().toISOString(),
417 threadId: ThreadId.makeUnsafe("thread-1"),417 threadId: ThreadId.make("thread-1"),
418 turnId: asTurnId("turn-1"),418 turnId: asTurnId("turn-1"),
419 payload: { state: "completed" },419 payload: { state: "completed" },
420 });420 });
...@@ -426,22 +426,22 @@ describe("CheckpointReactor", () => {...@@ -426,22 +426,22 @@ describe("CheckpointReactor", () => {
426 );426 );
427 expect(thread.checkpoints[0]?.checkpointTurnCount).toBe(1);427 expect(thread.checkpoints[0]?.checkpointTurnCount).toBe(1);
428 expect(428 expect(
429 gitRefExists(harness.cwd, checkpointRefForThreadTurn(ThreadId.makeUnsafe("thread-1"), 0)),429 gitRefExists(harness.cwd, checkpointRefForThreadTurn(ThreadId.make("thread-1"), 0)),
430 ).toBe(true);430 ).toBe(true);
431 expect(431 expect(
432 gitRefExists(harness.cwd, checkpointRefForThreadTurn(ThreadId.makeUnsafe("thread-1"), 1)),432 gitRefExists(harness.cwd, checkpointRefForThreadTurn(ThreadId.make("thread-1"), 1)),
433 ).toBe(true);433 ).toBe(true);
434 expect(434 expect(
435 gitShowFileAtRef(435 gitShowFileAtRef(
436 harness.cwd,436 harness.cwd,
437 checkpointRefForThreadTurn(ThreadId.makeUnsafe("thread-1"), 0),437 checkpointRefForThreadTurn(ThreadId.make("thread-1"), 0),
438 "README.md",438 "README.md",
439 ),439 ),
440 ).toBe("v1\n");440 ).toBe("v1\n");
441 expect(441 expect(
442 gitShowFileAtRef(442 gitShowFileAtRef(
443 harness.cwd,443 harness.cwd,
444 checkpointRefForThreadTurn(ThreadId.makeUnsafe("thread-1"), 1),444 checkpointRefForThreadTurn(ThreadId.make("thread-1"), 1),
445 "README.md",445 "README.md",
446 ),446 ),
447 ).toBe("v2\n");447 ).toBe("v2\n");
...@@ -456,10 +456,10 @@ describe("CheckpointReactor", () => {...@@ -456,10 +456,10 @@ describe("CheckpointReactor", () => {
456456
457 harness.provider.emit({457 harness.provider.emit({
458 type: "turn.completed",458 type: "turn.completed",
459 eventId: EventId.makeUnsafe("evt-turn-completed-refresh-local-status"),459 eventId: EventId.make("evt-turn-completed-refresh-local-status"),
460 provider: "codex",460 provider: "codex",
461 createdAt: new Date().toISOString(),461 createdAt: new Date().toISOString(),
462 threadId: ThreadId.makeUnsafe("thread-1"),462 threadId: ThreadId.make("thread-1"),
463 turnId: asTurnId("turn-refresh-local-status"),463 turnId: asTurnId("turn-refresh-local-status"),
464 payload: { state: "completed" },464 payload: { state: "completed" },
465 });465 });
...@@ -476,10 +476,10 @@ describe("CheckpointReactor", () => {...@@ -476,10 +476,10 @@ describe("CheckpointReactor", () => {
476 await Effect.runPromise(476 await Effect.runPromise(
477 harness.engine.dispatch({477 harness.engine.dispatch({
478 type: "thread.session.set",478 type: "thread.session.set",
479 commandId: CommandId.makeUnsafe("cmd-session-set-primary-running"),479 commandId: CommandId.make("cmd-session-set-primary-running"),
480 threadId: ThreadId.makeUnsafe("thread-1"),480 threadId: ThreadId.make("thread-1"),
481 session: {481 session: {
482 threadId: ThreadId.makeUnsafe("thread-1"),482 threadId: ThreadId.make("thread-1"),
483 status: "running",483 status: "running",
484 providerName: "codex",484 providerName: "codex",
485 runtimeMode: "approval-required",485 runtimeMode: "approval-required",
...@@ -493,45 +493,43 @@ describe("CheckpointReactor", () => {...@@ -493,45 +493,43 @@ describe("CheckpointReactor", () => {
493493
494 harness.provider.emit({494 harness.provider.emit({
495 type: "turn.started",495 type: "turn.started",
496 eventId: EventId.makeUnsafe("evt-turn-started-main"),496 eventId: EventId.make("evt-turn-started-main"),
497 provider: "codex",497 provider: "codex",
498498
499 createdAt: new Date().toISOString(),499 createdAt: new Date().toISOString(),
500 threadId: ThreadId.makeUnsafe("thread-1"),500 threadId: ThreadId.make("thread-1"),
501 turnId: asTurnId("turn-main"),501 turnId: asTurnId("turn-main"),
502 });502 });
503 await waitForGitRefExists(503 await waitForGitRefExists(
504 harness.cwd,504 harness.cwd,
505 checkpointRefForThreadTurn(ThreadId.makeUnsafe("thread-1"), 0),505 checkpointRefForThreadTurn(ThreadId.make("thread-1"), 0),
506 );506 );
507507
508 fs.writeFileSync(path.join(harness.cwd, "README.md"), "v2\n", "utf8");508 fs.writeFileSync(path.join(harness.cwd, "README.md"), "v2\n", "utf8");
509509
510 harness.provider.emit({510 harness.provider.emit({
511 type: "turn.completed",511 type: "turn.completed",
512 eventId: EventId.makeUnsafe("evt-turn-completed-aux"),512 eventId: EventId.make("evt-turn-completed-aux"),
513 provider: "codex",513 provider: "codex",
514514
515 createdAt: new Date().toISOString(),515 createdAt: new Date().toISOString(),
516 threadId: ThreadId.makeUnsafe("thread-1"),516 threadId: ThreadId.make("thread-1"),
517 turnId: asTurnId("turn-aux"),517 turnId: asTurnId("turn-aux"),
518 payload: { state: "completed" },518 payload: { state: "completed" },
519 });519 });
520520
521 await harness.drain();521 await harness.drain();
522 const midReadModel = await Effect.runPromise(harness.engine.getReadModel());522 const midReadModel = await Effect.runPromise(harness.engine.getReadModel());
523 const midThread = midReadModel.threads.find(523 const midThread = midReadModel.threads.find((entry) => entry.id === ThreadId.make("thread-1"));
524 (entry) => entry.id === ThreadId.makeUnsafe("thread-1"),
525 );
526 expect(midThread?.checkpoints).toHaveLength(0);524 expect(midThread?.checkpoints).toHaveLength(0);
527525
528 harness.provider.emit({526 harness.provider.emit({
529 type: "turn.completed",527 type: "turn.completed",
530 eventId: EventId.makeUnsafe("evt-turn-completed-main"),528 eventId: EventId.make("evt-turn-completed-main"),
531 provider: "codex",529 provider: "codex",
532530
533 createdAt: new Date().toISOString(),531 createdAt: new Date().toISOString(),
534 threadId: ThreadId.makeUnsafe("thread-1"),532 threadId: ThreadId.make("thread-1"),
535 turnId: asTurnId("turn-main"),533 turnId: asTurnId("turn-main"),
536 payload: { state: "completed" },534 payload: { state: "completed" },
537 });535 });
...@@ -553,10 +551,10 @@ describe("CheckpointReactor", () => {...@@ -553,10 +551,10 @@ describe("CheckpointReactor", () => {
553 await Effect.runPromise(551 await Effect.runPromise(
554 harness.engine.dispatch({552 harness.engine.dispatch({
555 type: "thread.session.set",553 type: "thread.session.set",
556 commandId: CommandId.makeUnsafe("cmd-session-set-capture-claude"),554 commandId: CommandId.make("cmd-session-set-capture-claude"),
557 threadId: ThreadId.makeUnsafe("thread-1"),555 threadId: ThreadId.make("thread-1"),
558 session: {556 session: {
559 threadId: ThreadId.makeUnsafe("thread-1"),557 threadId: ThreadId.make("thread-1"),
560 status: "ready",558 status: "ready",
561 providerName: "claudeAgent",559 providerName: "claudeAgent",
562 runtimeMode: "approval-required",560 runtimeMode: "approval-required",
...@@ -570,24 +568,24 @@ describe("CheckpointReactor", () => {...@@ -570,24 +568,24 @@ describe("CheckpointReactor", () => {
570568
571 harness.provider.emit({569 harness.provider.emit({
572 type: "turn.started",570 type: "turn.started",
573 eventId: EventId.makeUnsafe("evt-turn-started-claude-1"),571 eventId: EventId.make("evt-turn-started-claude-1"),
574 provider: "claudeAgent",572 provider: "claudeAgent",
575 createdAt: new Date().toISOString(),573 createdAt: new Date().toISOString(),
576 threadId: ThreadId.makeUnsafe("thread-1"),574 threadId: ThreadId.make("thread-1"),
577 turnId: asTurnId("turn-claude-1"),575 turnId: asTurnId("turn-claude-1"),
578 });576 });
579 await waitForGitRefExists(577 await waitForGitRefExists(
580 harness.cwd,578 harness.cwd,
581 checkpointRefForThreadTurn(ThreadId.makeUnsafe("thread-1"), 0),579 checkpointRefForThreadTurn(ThreadId.make("thread-1"), 0),
582 );580 );
583581
584 fs.writeFileSync(path.join(harness.cwd, "README.md"), "v2\n", "utf8");582 fs.writeFileSync(path.join(harness.cwd, "README.md"), "v2\n", "utf8");
585 harness.provider.emit({583 harness.provider.emit({
586 type: "turn.completed",584 type: "turn.completed",
587 eventId: EventId.makeUnsafe("evt-turn-completed-claude-1"),585 eventId: EventId.make("evt-turn-completed-claude-1"),
588 provider: "claudeAgent",586 provider: "claudeAgent",
589 createdAt: new Date().toISOString(),587 createdAt: new Date().toISOString(),
590 threadId: ThreadId.makeUnsafe("thread-1"),588 threadId: ThreadId.make("thread-1"),
591 turnId: asTurnId("turn-claude-1"),589 turnId: asTurnId("turn-claude-1"),
592 payload: { state: "completed" },590 payload: { state: "completed" },
593 });591 });
...@@ -600,7 +598,7 @@ describe("CheckpointReactor", () => {...@@ -600,7 +598,7 @@ describe("CheckpointReactor", () => {
600598
601 expect(thread.checkpoints[0]?.checkpointTurnCount).toBe(1);599 expect(thread.checkpoints[0]?.checkpointTurnCount).toBe(1);
602 expect(600 expect(
603 gitRefExists(harness.cwd, checkpointRefForThreadTurn(ThreadId.makeUnsafe("thread-1"), 1)),601 gitRefExists(harness.cwd, checkpointRefForThreadTurn(ThreadId.make("thread-1"), 1)),
604 ).toBe(true);602 ).toBe(true);
605 });603 });
606604
...@@ -611,10 +609,10 @@ describe("CheckpointReactor", () => {...@@ -611,10 +609,10 @@ describe("CheckpointReactor", () => {
611 await Effect.runPromise(609 await Effect.runPromise(
612 harness.engine.dispatch({610 harness.engine.dispatch({
613 type: "thread.session.set",611 type: "thread.session.set",
614 commandId: CommandId.makeUnsafe("cmd-session-set-missing-baseline-diff"),612 commandId: CommandId.make("cmd-session-set-missing-baseline-diff"),
615 threadId: ThreadId.makeUnsafe("thread-1"),613 threadId: ThreadId.make("thread-1"),
616 session: {614 session: {
617 threadId: ThreadId.makeUnsafe("thread-1"),615 threadId: ThreadId.make("thread-1"),
618 status: "ready",616 status: "ready",
619 providerName: "codex",617 providerName: "codex",
620 runtimeMode: "approval-required",618 runtimeMode: "approval-required",
...@@ -628,11 +626,11 @@ describe("CheckpointReactor", () => {...@@ -628,11 +626,11 @@ describe("CheckpointReactor", () => {
628626
629 harness.provider.emit({627 harness.provider.emit({
630 type: "turn.completed",628 type: "turn.completed",
631 eventId: EventId.makeUnsafe("evt-turn-completed-missing-baseline"),629 eventId: EventId.make("evt-turn-completed-missing-baseline"),
632 provider: "codex",630 provider: "codex",
633631
634 createdAt: new Date().toISOString(),632 createdAt: new Date().toISOString(),
635 threadId: ThreadId.makeUnsafe("thread-1"),633 threadId: ThreadId.make("thread-1"),
636 turnId: asTurnId("turn-missing-baseline"),634 turnId: asTurnId("turn-missing-baseline"),
637 payload: { state: "completed" },635 payload: { state: "completed" },
638 });636 });
...@@ -661,10 +659,10 @@ describe("CheckpointReactor", () => {...@@ -661,10 +659,10 @@ describe("CheckpointReactor", () => {
661 await Effect.runPromise(659 await Effect.runPromise(
662 harness.engine.dispatch({660 harness.engine.dispatch({
663 type: "thread.turn.start",661 type: "thread.turn.start",
664 commandId: CommandId.makeUnsafe("cmd-turn-start-for-baseline"),662 commandId: CommandId.make("cmd-turn-start-for-baseline"),
665 threadId: ThreadId.makeUnsafe("thread-1"),663 threadId: ThreadId.make("thread-1"),
666 message: {664 message: {
667 messageId: MessageId.makeUnsafe("message-user-1"),665 messageId: MessageId.make("message-user-1"),
668 role: "user",666 role: "user",
669 text: "start turn",667 text: "start turn",
670 attachments: [],668 attachments: [],
...@@ -677,12 +675,12 @@ describe("CheckpointReactor", () => {...@@ -677,12 +675,12 @@ describe("CheckpointReactor", () => {
677675
678 await waitForGitRefExists(676 await waitForGitRefExists(
679 harness.cwd,677 harness.cwd,
680 checkpointRefForThreadTurn(ThreadId.makeUnsafe("thread-1"), 0),678 checkpointRefForThreadTurn(ThreadId.make("thread-1"), 0),
681 );679 );
682 expect(680 expect(
683 gitShowFileAtRef(681 gitShowFileAtRef(
684 harness.cwd,682 harness.cwd,
685 checkpointRefForThreadTurn(ThreadId.makeUnsafe("thread-1"), 0),683 checkpointRefForThreadTurn(ThreadId.make("thread-1"), 0),
686 "README.md",684 "README.md",
687 ),685 ),
688 ).toBe("v1\n");686 ).toBe("v1\n");
...@@ -699,10 +697,10 @@ describe("CheckpointReactor", () => {...@@ -699,10 +697,10 @@ describe("CheckpointReactor", () => {
699 await Effect.runPromise(697 await Effect.runPromise(
700 harness.engine.dispatch({698 harness.engine.dispatch({
701 type: "thread.session.set",699 type: "thread.session.set",
702 commandId: CommandId.makeUnsafe("cmd-session-set-missing-provider-cwd"),700 commandId: CommandId.make("cmd-session-set-missing-provider-cwd"),
703 threadId: ThreadId.makeUnsafe("thread-1"),701 threadId: ThreadId.make("thread-1"),
704 session: {702 session: {
705 threadId: ThreadId.makeUnsafe("thread-1"),703 threadId: ThreadId.make("thread-1"),
706 status: "running",704 status: "running",
707 providerName: "codex",705 providerName: "codex",
708 runtimeMode: "approval-required",706 runtimeMode: "approval-required",
...@@ -717,23 +715,23 @@ describe("CheckpointReactor", () => {...@@ -717,23 +715,23 @@ describe("CheckpointReactor", () => {
717 fs.writeFileSync(path.join(harness.cwd, "README.md"), "v2\n", "utf8");715 fs.writeFileSync(path.join(harness.cwd, "README.md"), "v2\n", "utf8");
718 harness.provider.emit({716 harness.provider.emit({
719 type: "turn.completed",717 type: "turn.completed",
720 eventId: EventId.makeUnsafe("evt-turn-completed-missing-provider-cwd"),718 eventId: EventId.make("evt-turn-completed-missing-provider-cwd"),
721 provider: "codex",719 provider: "codex",
722720
723 createdAt: new Date().toISOString(),721 createdAt: new Date().toISOString(),
724 threadId: ThreadId.makeUnsafe("thread-1"),722 threadId: ThreadId.make("thread-1"),
725 turnId: asTurnId("turn-missing-cwd"),723 turnId: asTurnId("turn-missing-cwd"),
726 payload: { state: "completed" },724 payload: { state: "completed" },
727 });725 });
728726
729 await waitForEvent(harness.engine, (event) => event.type === "thread.turn-diff-completed");727 await waitForEvent(harness.engine, (event) => event.type === "thread.turn-diff-completed");
730 expect(728 expect(
731 gitRefExists(harness.cwd, checkpointRefForThreadTurn(ThreadId.makeUnsafe("thread-1"), 1)),729 gitRefExists(harness.cwd, checkpointRefForThreadTurn(ThreadId.make("thread-1"), 1)),
732 ).toBe(true);730 ).toBe(true);
733 expect(731 expect(
734 gitShowFileAtRef(732 gitShowFileAtRef(
735 harness.cwd,733 harness.cwd,
736 checkpointRefForThreadTurn(ThreadId.makeUnsafe("thread-1"), 1),734 checkpointRefForThreadTurn(ThreadId.make("thread-1"), 1),
737 "README.md",735 "README.md",
738 ),736 ),
739 ).toBe("v2\n");737 ).toBe("v2\n");
...@@ -746,10 +744,10 @@ describe("CheckpointReactor", () => {...@@ -746,10 +744,10 @@ describe("CheckpointReactor", () => {
746 await Effect.runPromise(744 await Effect.runPromise(
747 harness.engine.dispatch({745 harness.engine.dispatch({
748 type: "thread.session.set",746 type: "thread.session.set",
749 commandId: CommandId.makeUnsafe("cmd-session-set-checkpoint-captured"),747 commandId: CommandId.make("cmd-session-set-checkpoint-captured"),
750 threadId: ThreadId.makeUnsafe("thread-1"),748 threadId: ThreadId.make("thread-1"),
751 session: {749 session: {
752 threadId: ThreadId.makeUnsafe("thread-1"),750 threadId: ThreadId.make("thread-1"),
753 status: "ready",751 status: "ready",
754 providerName: "codex",752 providerName: "codex",
755 runtimeMode: "approval-required",753 runtimeMode: "approval-required",
...@@ -763,11 +761,11 @@ describe("CheckpointReactor", () => {...@@ -763,11 +761,11 @@ describe("CheckpointReactor", () => {
763761
764 harness.provider.emit({762 harness.provider.emit({
765 type: "checkpoint.captured",763 type: "checkpoint.captured",
766 eventId: EventId.makeUnsafe("evt-checkpoint-captured-3"),764 eventId: EventId.make("evt-checkpoint-captured-3"),
767 provider: "codex",765 provider: "codex",
768766
769 createdAt: new Date().toISOString(),767 createdAt: new Date().toISOString(),
770 threadId: ThreadId.makeUnsafe("thread-1"),768 threadId: ThreadId.make("thread-1"),
771 turnId: asTurnId("turn-3"),769 turnId: asTurnId("turn-3"),
772 turnCount: 3,770 turnCount: 3,
773 status: "completed",771 status: "completed",
...@@ -775,7 +773,7 @@ describe("CheckpointReactor", () => {...@@ -775,7 +773,7 @@ describe("CheckpointReactor", () => {
775773
776 await harness.drain();774 await harness.drain();
777 const readModel = await Effect.runPromise(harness.engine.getReadModel());775 const readModel = await Effect.runPromise(harness.engine.getReadModel());
778 const thread = readModel.threads.find((entry) => entry.id === ThreadId.makeUnsafe("thread-1"));776 const thread = readModel.threads.find((entry) => entry.id === ThreadId.make("thread-1"));
779 expect(thread?.checkpoints.some((checkpoint) => checkpoint.checkpointTurnCount === 3)).toBe(777 expect(thread?.checkpoints.some((checkpoint) => checkpoint.checkpointTurnCount === 3)).toBe(
780 false,778 false,
781 );779 );
...@@ -796,10 +794,10 @@ describe("CheckpointReactor", () => {...@@ -796,10 +794,10 @@ describe("CheckpointReactor", () => {
796 await Effect.runPromise(794 await Effect.runPromise(
797 harness.engine.dispatch({795 harness.engine.dispatch({
798 type: "thread.session.set",796 type: "thread.session.set",
799 commandId: CommandId.makeUnsafe("cmd-session-set-non-repo-runtime"),797 commandId: CommandId.make("cmd-session-set-non-repo-runtime"),
800 threadId: ThreadId.makeUnsafe("thread-1"),798 threadId: ThreadId.make("thread-1"),
801 session: {799 session: {
802 threadId: ThreadId.makeUnsafe("thread-1"),800 threadId: ThreadId.make("thread-1"),
803 status: "ready",801 status: "ready",
804 providerName: "codex",802 providerName: "codex",
805 runtimeMode: "approval-required",803 runtimeMode: "approval-required",
...@@ -813,31 +811,31 @@ describe("CheckpointReactor", () => {...@@ -813,31 +811,31 @@ describe("CheckpointReactor", () => {
813811
814 harness.provider.emit({812 harness.provider.emit({
815 type: "turn.completed",813 type: "turn.completed",
816 eventId: EventId.makeUnsafe("evt-runtime-capture-failure"),814 eventId: EventId.make("evt-runtime-capture-failure"),
817 provider: "codex",815 provider: "codex",
818816
819 createdAt: new Date().toISOString(),817 createdAt: new Date().toISOString(),
820 threadId: ThreadId.makeUnsafe("thread-1"),818 threadId: ThreadId.make("thread-1"),
821 turnId: asTurnId("turn-runtime-failure"),819 turnId: asTurnId("turn-runtime-failure"),
822 payload: { state: "completed" },820 payload: { state: "completed" },
823 });821 });
824822
825 harness.provider.emit({823 harness.provider.emit({
826 type: "turn.started",824 type: "turn.started",
827 eventId: EventId.makeUnsafe("evt-turn-started-after-runtime-failure"),825 eventId: EventId.make("evt-turn-started-after-runtime-failure"),
828 provider: "codex",826 provider: "codex",
829827
830 createdAt: new Date().toISOString(),828 createdAt: new Date().toISOString(),
831 threadId: ThreadId.makeUnsafe("thread-1"),829 threadId: ThreadId.make("thread-1"),
832 turnId: asTurnId("turn-after-runtime-failure"),830 turnId: asTurnId("turn-after-runtime-failure"),
833 });831 });
834832
835 await waitForGitRefExists(833 await waitForGitRefExists(
836 harness.cwd,834 harness.cwd,
837 checkpointRefForThreadTurn(ThreadId.makeUnsafe("thread-1"), 0),835 checkpointRefForThreadTurn(ThreadId.make("thread-1"), 0),
838 );836 );
839 expect(837 expect(
840 gitRefExists(harness.cwd, checkpointRefForThreadTurn(ThreadId.makeUnsafe("thread-1"), 0)),838 gitRefExists(harness.cwd, checkpointRefForThreadTurn(ThreadId.make("thread-1"), 0)),
841 ).toBe(true);839 ).toBe(true);
842 });840 });
843841
...@@ -848,10 +846,10 @@ describe("CheckpointReactor", () => {...@@ -848,10 +846,10 @@ describe("CheckpointReactor", () => {
848 await Effect.runPromise(846 await Effect.runPromise(
849 harness.engine.dispatch({847 harness.engine.dispatch({
850 type: "thread.session.set",848 type: "thread.session.set",
851 commandId: CommandId.makeUnsafe("cmd-session-set"),849 commandId: CommandId.make("cmd-session-set"),
852 threadId: ThreadId.makeUnsafe("thread-1"),850 threadId: ThreadId.make("thread-1"),
853 session: {851 session: {
854 threadId: ThreadId.makeUnsafe("thread-1"),852 threadId: ThreadId.make("thread-1"),
855 status: "ready",853 status: "ready",
856 providerName: "codex",854 providerName: "codex",
857 runtimeMode: "approval-required",855 runtimeMode: "approval-required",
...@@ -866,11 +864,11 @@ describe("CheckpointReactor", () => {...@@ -866,11 +864,11 @@ describe("CheckpointReactor", () => {
866 await Effect.runPromise(864 await Effect.runPromise(
867 harness.engine.dispatch({865 harness.engine.dispatch({
868 type: "thread.turn.diff.complete",866 type: "thread.turn.diff.complete",
869 commandId: CommandId.makeUnsafe("cmd-diff-1"),867 commandId: CommandId.make("cmd-diff-1"),
870 threadId: ThreadId.makeUnsafe("thread-1"),868 threadId: ThreadId.make("thread-1"),
871 turnId: asTurnId("turn-1"),869 turnId: asTurnId("turn-1"),
872 completedAt: createdAt,870 completedAt: createdAt,
873 checkpointRef: checkpointRefForThreadTurn(ThreadId.makeUnsafe("thread-1"), 1),871 checkpointRef: checkpointRefForThreadTurn(ThreadId.make("thread-1"), 1),
874 status: "ready",872 status: "ready",
875 files: [],873 files: [],
876 checkpointTurnCount: 1,874 checkpointTurnCount: 1,
...@@ -880,11 +878,11 @@ describe("CheckpointReactor", () => {...@@ -880,11 +878,11 @@ describe("CheckpointReactor", () => {
880 await Effect.runPromise(878 await Effect.runPromise(
881 harness.engine.dispatch({879 harness.engine.dispatch({
882 type: "thread.turn.diff.complete",880 type: "thread.turn.diff.complete",
883 commandId: CommandId.makeUnsafe("cmd-diff-2"),881 commandId: CommandId.make("cmd-diff-2"),
884 threadId: ThreadId.makeUnsafe("thread-1"),882 threadId: ThreadId.make("thread-1"),
885 turnId: asTurnId("turn-2"),883 turnId: asTurnId("turn-2"),
886 completedAt: createdAt,884 completedAt: createdAt,
887 checkpointRef: checkpointRefForThreadTurn(ThreadId.makeUnsafe("thread-1"), 2),885 checkpointRef: checkpointRefForThreadTurn(ThreadId.make("thread-1"), 2),
888 status: "ready",886 status: "ready",
889 files: [],887 files: [],
890 checkpointTurnCount: 2,888 checkpointTurnCount: 2,
...@@ -895,8 +893,8 @@ describe("CheckpointReactor", () => {...@@ -895,8 +893,8 @@ describe("CheckpointReactor", () => {
895 await Effect.runPromise(893 await Effect.runPromise(
896 harness.engine.dispatch({894 harness.engine.dispatch({
897 type: "thread.checkpoint.revert",895 type: "thread.checkpoint.revert",
898 commandId: CommandId.makeUnsafe("cmd-revert-request"),896 commandId: CommandId.make("cmd-revert-request"),
899 threadId: ThreadId.makeUnsafe("thread-1"),897 threadId: ThreadId.make("thread-1"),
900 turnCount: 1,898 turnCount: 1,
901 createdAt,899 createdAt,
902 }),900 }),
...@@ -910,12 +908,12 @@ describe("CheckpointReactor", () => {...@@ -910,12 +908,12 @@ describe("CheckpointReactor", () => {
910 expect(thread.checkpoints[0]?.checkpointTurnCount).toBe(1);908 expect(thread.checkpoints[0]?.checkpointTurnCount).toBe(1);
911 expect(harness.provider.rollbackConversation).toHaveBeenCalledTimes(1);909 expect(harness.provider.rollbackConversation).toHaveBeenCalledTimes(1);
912 expect(harness.provider.rollbackConversation).toHaveBeenCalledWith({910 expect(harness.provider.rollbackConversation).toHaveBeenCalledWith({
913 threadId: ThreadId.makeUnsafe("thread-1"),911 threadId: ThreadId.make("thread-1"),
914 numTurns: 1,912 numTurns: 1,
915 });913 });
916 expect(fs.readFileSync(path.join(harness.cwd, "README.md"), "utf8")).toBe("v2\n");914 expect(fs.readFileSync(path.join(harness.cwd, "README.md"), "utf8")).toBe("v2\n");
917 expect(915 expect(
918 gitRefExists(harness.cwd, checkpointRefForThreadTurn(ThreadId.makeUnsafe("thread-1"), 2)),916 gitRefExists(harness.cwd, checkpointRefForThreadTurn(ThreadId.make("thread-1"), 2)),
919 ).toBe(false);917 ).toBe(false);
920 });918 });
921919
...@@ -926,10 +924,10 @@ describe("CheckpointReactor", () => {...@@ -926,10 +924,10 @@ describe("CheckpointReactor", () => {
926 await Effect.runPromise(924 await Effect.runPromise(
927 harness.engine.dispatch({925 harness.engine.dispatch({
928 type: "thread.session.set",926 type: "thread.session.set",
929 commandId: CommandId.makeUnsafe("cmd-session-set-claude"),927 commandId: CommandId.make("cmd-session-set-claude"),
930 threadId: ThreadId.makeUnsafe("thread-1"),928 threadId: ThreadId.make("thread-1"),
931 session: {929 session: {
932 threadId: ThreadId.makeUnsafe("thread-1"),930 threadId: ThreadId.make("thread-1"),
933 status: "ready",931 status: "ready",
934 providerName: "claudeAgent",932 providerName: "claudeAgent",
935 runtimeMode: "approval-required",933 runtimeMode: "approval-required",
...@@ -944,11 +942,11 @@ describe("CheckpointReactor", () => {...@@ -944,11 +942,11 @@ describe("CheckpointReactor", () => {
944 await Effect.runPromise(942 await Effect.runPromise(
945 harness.engine.dispatch({943 harness.engine.dispatch({
946 type: "thread.turn.diff.complete",944 type: "thread.turn.diff.complete",
947 commandId: CommandId.makeUnsafe("cmd-diff-claude-1"),945 commandId: CommandId.make("cmd-diff-claude-1"),
948 threadId: ThreadId.makeUnsafe("thread-1"),946 threadId: ThreadId.make("thread-1"),
949 turnId: asTurnId("turn-claude-1"),947 turnId: asTurnId("turn-claude-1"),
950 completedAt: createdAt,948 completedAt: createdAt,
951 checkpointRef: checkpointRefForThreadTurn(ThreadId.makeUnsafe("thread-1"), 1),949 checkpointRef: checkpointRefForThreadTurn(ThreadId.make("thread-1"), 1),
952 status: "ready",950 status: "ready",
953 files: [],951 files: [],
954 checkpointTurnCount: 1,952 checkpointTurnCount: 1,
...@@ -958,11 +956,11 @@ describe("CheckpointReactor", () => {...@@ -958,11 +956,11 @@ describe("CheckpointReactor", () => {
958 await Effect.runPromise(956 await Effect.runPromise(
959 harness.engine.dispatch({957 harness.engine.dispatch({
960 type: "thread.turn.diff.complete",958 type: "thread.turn.diff.complete",
961 commandId: CommandId.makeUnsafe("cmd-diff-claude-2"),959 commandId: CommandId.make("cmd-diff-claude-2"),
962 threadId: ThreadId.makeUnsafe("thread-1"),960 threadId: ThreadId.make("thread-1"),
963 turnId: asTurnId("turn-claude-2"),961 turnId: asTurnId("turn-claude-2"),
964 completedAt: createdAt,962 completedAt: createdAt,
965 checkpointRef: checkpointRefForThreadTurn(ThreadId.makeUnsafe("thread-1"), 2),963 checkpointRef: checkpointRefForThreadTurn(ThreadId.make("thread-1"), 2),
966 status: "ready",964 status: "ready",
967 files: [],965 files: [],
968 checkpointTurnCount: 2,966 checkpointTurnCount: 2,
...@@ -973,8 +971,8 @@ describe("CheckpointReactor", () => {...@@ -973,8 +971,8 @@ describe("CheckpointReactor", () => {
973 await Effect.runPromise(971 await Effect.runPromise(
974 harness.engine.dispatch({972 harness.engine.dispatch({
975 type: "thread.checkpoint.revert",973 type: "thread.checkpoint.revert",
976 commandId: CommandId.makeUnsafe("cmd-revert-request-claude"),974 commandId: CommandId.make("cmd-revert-request-claude"),
977 threadId: ThreadId.makeUnsafe("thread-1"),975 threadId: ThreadId.make("thread-1"),
978 turnCount: 1,976 turnCount: 1,
979 createdAt,977 createdAt,
980 }),978 }),
...@@ -983,7 +981,7 @@ describe("CheckpointReactor", () => {...@@ -983,7 +981,7 @@ describe("CheckpointReactor", () => {
983 await waitForEvent(harness.engine, (event) => event.type === "thread.reverted");981 await waitForEvent(harness.engine, (event) => event.type === "thread.reverted");
984 expect(harness.provider.rollbackConversation).toHaveBeenCalledTimes(1);982 expect(harness.provider.rollbackConversation).toHaveBeenCalledTimes(1);
985 expect(harness.provider.rollbackConversation).toHaveBeenCalledWith({983 expect(harness.provider.rollbackConversation).toHaveBeenCalledWith({
986 threadId: ThreadId.makeUnsafe("thread-1"),984 threadId: ThreadId.make("thread-1"),
987 numTurns: 1,985 numTurns: 1,
988 });986 });
989 });987 });
...@@ -995,10 +993,10 @@ describe("CheckpointReactor", () => {...@@ -995,10 +993,10 @@ describe("CheckpointReactor", () => {
995 await Effect.runPromise(993 await Effect.runPromise(
996 harness.engine.dispatch({994 harness.engine.dispatch({
997 type: "thread.session.set",995 type: "thread.session.set",
998 commandId: CommandId.makeUnsafe("cmd-session-set-inline-revert"),996 commandId: CommandId.make("cmd-session-set-inline-revert"),
999 threadId: ThreadId.makeUnsafe("thread-1"),997 threadId: ThreadId.make("thread-1"),
1000 session: {998 session: {
1001 threadId: ThreadId.makeUnsafe("thread-1"),999 threadId: ThreadId.make("thread-1"),
1002 status: "ready",1000 status: "ready",
1003 providerName: "codex",1001 providerName: "codex",
1004 runtimeMode: "approval-required",1002 runtimeMode: "approval-required",
...@@ -1013,11 +1011,11 @@ describe("CheckpointReactor", () => {...@@ -1013,11 +1011,11 @@ describe("CheckpointReactor", () => {
1013 await Effect.runPromise(1011 await Effect.runPromise(
1014 harness.engine.dispatch({1012 harness.engine.dispatch({
1015 type: "thread.turn.diff.complete",1013 type: "thread.turn.diff.complete",
1016 commandId: CommandId.makeUnsafe("cmd-inline-revert-diff-1"),1014 commandId: CommandId.make("cmd-inline-revert-diff-1"),
1017 threadId: ThreadId.makeUnsafe("thread-1"),1015 threadId: ThreadId.make("thread-1"),
1018 turnId: asTurnId("turn-1"),1016 turnId: asTurnId("turn-1"),
1019 completedAt: createdAt,1017 completedAt: createdAt,
1020 checkpointRef: checkpointRefForThreadTurn(ThreadId.makeUnsafe("thread-1"), 1),1018 checkpointRef: checkpointRefForThreadTurn(ThreadId.make("thread-1"), 1),
1021 status: "ready",1019 status: "ready",
1022 files: [],1020 files: [],
1023 checkpointTurnCount: 1,1021 checkpointTurnCount: 1,
...@@ -1027,11 +1025,11 @@ describe("CheckpointReactor", () => {...@@ -1027,11 +1025,11 @@ describe("CheckpointReactor", () => {
1027 await Effect.runPromise(1025 await Effect.runPromise(
1028 harness.engine.dispatch({1026 harness.engine.dispatch({
1029 type: "thread.turn.diff.complete",1027 type: "thread.turn.diff.complete",
1030 commandId: CommandId.makeUnsafe("cmd-inline-revert-diff-2"),1028 commandId: CommandId.make("cmd-inline-revert-diff-2"),
1031 threadId: ThreadId.makeUnsafe("thread-1"),1029 threadId: ThreadId.make("thread-1"),
1032 turnId: asTurnId("turn-2"),1030 turnId: asTurnId("turn-2"),
1033 completedAt: createdAt,1031 completedAt: createdAt,
1034 checkpointRef: checkpointRefForThreadTurn(ThreadId.makeUnsafe("thread-1"), 2),1032 checkpointRef: checkpointRefForThreadTurn(ThreadId.make("thread-1"), 2),
1035 status: "ready",1033 status: "ready",
1036 files: [],1034 files: [],
1037 checkpointTurnCount: 2,1035 checkpointTurnCount: 2,
...@@ -1042,8 +1040,8 @@ describe("CheckpointReactor", () => {...@@ -1042,8 +1040,8 @@ describe("CheckpointReactor", () => {
1042 await Effect.runPromise(1040 await Effect.runPromise(
1043 harness.engine.dispatch({1041 harness.engine.dispatch({
1044 type: "thread.checkpoint.revert",1042 type: "thread.checkpoint.revert",
1045 commandId: CommandId.makeUnsafe("cmd-sequenced-revert-request-1"),1043 commandId: CommandId.make("cmd-sequenced-revert-request-1"),
1046 threadId: ThreadId.makeUnsafe("thread-1"),1044 threadId: ThreadId.make("thread-1"),
1047 turnCount: 1,1045 turnCount: 1,
1048 createdAt,1046 createdAt,
1049 }),1047 }),
...@@ -1051,8 +1049,8 @@ describe("CheckpointReactor", () => {...@@ -1051,8 +1049,8 @@ describe("CheckpointReactor", () => {
1051 await Effect.runPromise(1049 await Effect.runPromise(
1052 harness.engine.dispatch({1050 harness.engine.dispatch({
1053 type: "thread.checkpoint.revert",1051 type: "thread.checkpoint.revert",
1054 commandId: CommandId.makeUnsafe("cmd-sequenced-revert-request-0"),1052 commandId: CommandId.make("cmd-sequenced-revert-request-0"),
1055 threadId: ThreadId.makeUnsafe("thread-1"),1053 threadId: ThreadId.make("thread-1"),
1056 turnCount: 0,1054 turnCount: 0,
1057 createdAt,1055 createdAt,
1058 }),1056 }),
...@@ -1062,11 +1060,11 @@ describe("CheckpointReactor", () => {...@@ -1062,11 +1060,11 @@ describe("CheckpointReactor", () => {
10621060
1063 expect(harness.provider.rollbackConversation).toHaveBeenCalledTimes(2);1061 expect(harness.provider.rollbackConversation).toHaveBeenCalledTimes(2);
1064 expect(harness.provider.rollbackConversation.mock.calls[0]?.[0]).toEqual({1062 expect(harness.provider.rollbackConversation.mock.calls[0]?.[0]).toEqual({
1065 threadId: ThreadId.makeUnsafe("thread-1"),1063 threadId: ThreadId.make("thread-1"),
1066 numTurns: 1,1064 numTurns: 1,
1067 });1065 });
1068 expect(harness.provider.rollbackConversation.mock.calls[1]?.[0]).toEqual({1066 expect(harness.provider.rollbackConversation.mock.calls[1]?.[0]).toEqual({
1069 threadId: ThreadId.makeUnsafe("thread-1"),1067 threadId: ThreadId.make("thread-1"),
1070 numTurns: 1,1068 numTurns: 1,
1071 });1069 });
1072 });1070 });
...@@ -1078,8 +1076,8 @@ describe("CheckpointReactor", () => {...@@ -1078,8 +1076,8 @@ describe("CheckpointReactor", () => {
1078 await Effect.runPromise(1076 await Effect.runPromise(
1079 harness.engine.dispatch({1077 harness.engine.dispatch({
1080 type: "thread.checkpoint.revert",1078 type: "thread.checkpoint.revert",
1081 commandId: CommandId.makeUnsafe("cmd-revert-no-session"),1079 commandId: CommandId.make("cmd-revert-no-session"),
1082 threadId: ThreadId.makeUnsafe("thread-1"),1080 threadId: ThreadId.make("thread-1"),
1083 turnCount: 1,1081 turnCount: 1,
1084 createdAt,1082 createdAt,
1085 }),1083 }),
apps/server/src/orchestration/Layers/CheckpointReactor.ts+6-6
...@@ -38,7 +38,7 @@ type ReactorInput =...@@ -38,7 +38,7 @@ type ReactorInput =
38 };38 };
3939
40function toTurnId(value: string | undefined): TurnId | null {40function toTurnId(value: string | undefined): TurnId | null {
41 return value === undefined ? null : TurnId.makeUnsafe(String(value));41 return value === undefined ? null : TurnId.make(String(value));
42}42}
4343
44function sameId(left: string | null | undefined, right: string | null | undefined): boolean {44function sameId(left: string | null | undefined, right: string | null | undefined): boolean {
...@@ -62,7 +62,7 @@ function checkpointStatusFromRuntime(status: string | undefined): "ready" | "mis...@@ -62,7 +62,7 @@ function checkpointStatusFromRuntime(status: string | undefined): "ready" | "mis
62}62}
6363
64const serverCommandId = (tag: string): CommandId =>64const serverCommandId = (tag: string): CommandId =>
65 CommandId.makeUnsafe(`server:${tag}:${crypto.randomUUID()}`);65 CommandId.make(`server:${tag}:${crypto.randomUUID()}`);
6666
67const make = Effect.gen(function* () {67const make = Effect.gen(function* () {
68 const orchestrationEngine = yield* OrchestrationEngineService;68 const orchestrationEngine = yield* OrchestrationEngineService;
...@@ -83,7 +83,7 @@ const make = Effect.gen(function* () {...@@ -83,7 +83,7 @@ const make = Effect.gen(function* () {
83 commandId: serverCommandId("checkpoint-revert-failure"),83 commandId: serverCommandId("checkpoint-revert-failure"),
84 threadId: input.threadId,84 threadId: input.threadId,
85 activity: {85 activity: {
86 id: EventId.makeUnsafe(crypto.randomUUID()),86 id: EventId.make(crypto.randomUUID()),
87 tone: "error",87 tone: "error",
88 kind: "checkpoint.revert.failed",88 kind: "checkpoint.revert.failed",
89 summary: "Checkpoint revert failed",89 summary: "Checkpoint revert failed",
...@@ -108,7 +108,7 @@ const make = Effect.gen(function* () {...@@ -108,7 +108,7 @@ const make = Effect.gen(function* () {
108 commandId: serverCommandId("checkpoint-capture-failure"),108 commandId: serverCommandId("checkpoint-capture-failure"),
109 threadId: input.threadId,109 threadId: input.threadId,
110 activity: {110 activity: {
111 id: EventId.makeUnsafe(crypto.randomUUID()),111 id: EventId.make(crypto.randomUUID()),
112 tone: "error",112 tone: "error",
113 kind: "checkpoint.capture.failed",113 kind: "checkpoint.capture.failed",
114 summary: "Checkpoint capture failed",114 summary: "Checkpoint capture failed",
...@@ -270,7 +270,7 @@ const make = Effect.gen(function* () {...@@ -270,7 +270,7 @@ const make = Effect.gen(function* () {
270 input.thread.messages270 input.thread.messages
271 .toReversed()271 .toReversed()
272 .find((entry) => entry.role === "assistant" && entry.turnId === input.turnId)?.id ??272 .find((entry) => entry.role === "assistant" && entry.turnId === input.turnId)?.id ??
273 MessageId.makeUnsafe(`assistant:${input.turnId}`);273 MessageId.make(`assistant:${input.turnId}`);
274274
275 yield* orchestrationEngine.dispatch({275 yield* orchestrationEngine.dispatch({
276 type: "thread.turn.diff.complete",276 type: "thread.turn.diff.complete",
...@@ -307,7 +307,7 @@ const make = Effect.gen(function* () {...@@ -307,7 +307,7 @@ const make = Effect.gen(function* () {
307 commandId: serverCommandId("checkpoint-captured-activity"),307 commandId: serverCommandId("checkpoint-captured-activity"),
308 threadId: input.threadId,308 threadId: input.threadId,
309 activity: {309 activity: {
310 id: EventId.makeUnsafe(crypto.randomUUID()),310 id: EventId.make(crypto.randomUUID()),
311 tone: "info",311 tone: "info",
312 kind: "checkpoint.captured",312 kind: "checkpoint.captured",
313 summary: "Checkpoint captured",313 summary: "Checkpoint captured",
apps/server/src/orchestration/Layers/OrchestrationEngine.test.ts+62-62
...@@ -32,10 +32,10 @@ import { ProjectionSnapshotQuery } from "../Services/ProjectionSnapshotQuery.ts"...@@ -32,10 +32,10 @@ import { ProjectionSnapshotQuery } from "../Services/ProjectionSnapshotQuery.ts"
32import { ServerConfig } from "../../config.ts";32import { ServerConfig } from "../../config.ts";
33import * as NodeServices from "@effect/platform-node/NodeServices";33import * as NodeServices from "@effect/platform-node/NodeServices";
3434
35const asProjectId = (value: string): ProjectId => ProjectId.makeUnsafe(value);35const asProjectId = (value: string): ProjectId => ProjectId.make(value);
36const asMessageId = (value: string): MessageId => MessageId.makeUnsafe(value);36const asMessageId = (value: string): MessageId => MessageId.make(value);
37const asTurnId = (value: string): TurnId => TurnId.makeUnsafe(value);37const asTurnId = (value: string): TurnId => TurnId.make(value);
38const asCheckpointRef = (value: string): CheckpointRef => CheckpointRef.makeUnsafe(value);38const asCheckpointRef = (value: string): CheckpointRef => CheckpointRef.make(value);
3939
40async function createOrchestrationSystem() {40async function createOrchestrationSystem() {
41 const ServerConfigLayer = ServerConfig.layerTest(process.cwd(), {41 const ServerConfigLayer = ServerConfig.layerTest(process.cwd(), {
...@@ -115,7 +115,7 @@ describe("OrchestrationEngine", () => {...@@ -115,7 +115,7 @@ describe("OrchestrationEngine", () => {
115 ],115 ],
116 threads: [116 threads: [
117 {117 {
118 id: ThreadId.makeUnsafe("thread-bootstrap"),118 id: ThreadId.make("thread-bootstrap"),
119 projectId: asProjectId("project-bootstrap"),119 projectId: asProjectId("project-bootstrap"),
120 title: "Bootstrap Thread",120 title: "Bootstrap Thread",
121 modelSelection: {121 modelSelection: {
...@@ -183,7 +183,7 @@ describe("OrchestrationEngine", () => {...@@ -183,7 +183,7 @@ describe("OrchestrationEngine", () => {
183 await system.run(183 await system.run(
184 engine.dispatch({184 engine.dispatch({
185 type: "project.create",185 type: "project.create",
186 commandId: CommandId.makeUnsafe("cmd-project-1-create"),186 commandId: CommandId.make("cmd-project-1-create"),
187 projectId: asProjectId("project-1"),187 projectId: asProjectId("project-1"),
188 title: "Project 1",188 title: "Project 1",
189 workspaceRoot: "/tmp/project-1",189 workspaceRoot: "/tmp/project-1",
...@@ -197,8 +197,8 @@ describe("OrchestrationEngine", () => {...@@ -197,8 +197,8 @@ describe("OrchestrationEngine", () => {
197 await system.run(197 await system.run(
198 engine.dispatch({198 engine.dispatch({
199 type: "thread.create",199 type: "thread.create",
200 commandId: CommandId.makeUnsafe("cmd-thread-1-create"),200 commandId: CommandId.make("cmd-thread-1-create"),
201 threadId: ThreadId.makeUnsafe("thread-1"),201 threadId: ThreadId.make("thread-1"),
202 projectId: asProjectId("project-1"),202 projectId: asProjectId("project-1"),
203 title: "Thread",203 title: "Thread",
204 modelSelection: {204 modelSelection: {
...@@ -215,8 +215,8 @@ describe("OrchestrationEngine", () => {...@@ -215,8 +215,8 @@ describe("OrchestrationEngine", () => {
215 await system.run(215 await system.run(
216 engine.dispatch({216 engine.dispatch({
217 type: "thread.turn.start",217 type: "thread.turn.start",
218 commandId: CommandId.makeUnsafe("cmd-turn-start-1"),218 commandId: CommandId.make("cmd-turn-start-1"),
219 threadId: ThreadId.makeUnsafe("thread-1"),219 threadId: ThreadId.make("thread-1"),
220 message: {220 message: {
221 messageId: asMessageId("msg-1"),221 messageId: asMessageId("msg-1"),
222 role: "user",222 role: "user",
...@@ -243,7 +243,7 @@ describe("OrchestrationEngine", () => {...@@ -243,7 +243,7 @@ describe("OrchestrationEngine", () => {
243 await system.run(243 await system.run(
244 engine.dispatch({244 engine.dispatch({
245 type: "project.create",245 type: "project.create",
246 commandId: CommandId.makeUnsafe("cmd-project-archive-create"),246 commandId: CommandId.make("cmd-project-archive-create"),
247 projectId: asProjectId("project-archive"),247 projectId: asProjectId("project-archive"),
248 title: "Project Archive",248 title: "Project Archive",
249 workspaceRoot: "/tmp/project-archive",249 workspaceRoot: "/tmp/project-archive",
...@@ -257,8 +257,8 @@ describe("OrchestrationEngine", () => {...@@ -257,8 +257,8 @@ describe("OrchestrationEngine", () => {
257 await system.run(257 await system.run(
258 engine.dispatch({258 engine.dispatch({
259 type: "thread.create",259 type: "thread.create",
260 commandId: CommandId.makeUnsafe("cmd-thread-archive-create"),260 commandId: CommandId.make("cmd-thread-archive-create"),
261 threadId: ThreadId.makeUnsafe("thread-archive"),261 threadId: ThreadId.make("thread-archive"),
262 projectId: asProjectId("project-archive"),262 projectId: asProjectId("project-archive"),
263 title: "Archive me",263 title: "Archive me",
264 modelSelection: {264 modelSelection: {
...@@ -276,8 +276,8 @@ describe("OrchestrationEngine", () => {...@@ -276,8 +276,8 @@ describe("OrchestrationEngine", () => {
276 await system.run(276 await system.run(
277 engine.dispatch({277 engine.dispatch({
278 type: "thread.archive",278 type: "thread.archive",
279 commandId: CommandId.makeUnsafe("cmd-thread-archive"),279 commandId: CommandId.make("cmd-thread-archive"),
280 threadId: ThreadId.makeUnsafe("thread-archive"),280 threadId: ThreadId.make("thread-archive"),
281 }),281 }),
282 );282 );
283 expect(283 expect(
...@@ -289,8 +289,8 @@ describe("OrchestrationEngine", () => {...@@ -289,8 +289,8 @@ describe("OrchestrationEngine", () => {
289 await system.run(289 await system.run(
290 engine.dispatch({290 engine.dispatch({
291 type: "thread.unarchive",291 type: "thread.unarchive",
292 commandId: CommandId.makeUnsafe("cmd-thread-unarchive"),292 commandId: CommandId.make("cmd-thread-unarchive"),
293 threadId: ThreadId.makeUnsafe("thread-archive"),293 threadId: ThreadId.make("thread-archive"),
294 }),294 }),
295 );295 );
296 expect(296 expect(
...@@ -310,7 +310,7 @@ describe("OrchestrationEngine", () => {...@@ -310,7 +310,7 @@ describe("OrchestrationEngine", () => {
310 await system.run(310 await system.run(
311 engine.dispatch({311 engine.dispatch({
312 type: "project.create",312 type: "project.create",
313 commandId: CommandId.makeUnsafe("cmd-project-replay-create"),313 commandId: CommandId.make("cmd-project-replay-create"),
314 projectId: asProjectId("project-replay"),314 projectId: asProjectId("project-replay"),
315 title: "Replay Project",315 title: "Replay Project",
316 workspaceRoot: "/tmp/project-replay",316 workspaceRoot: "/tmp/project-replay",
...@@ -324,8 +324,8 @@ describe("OrchestrationEngine", () => {...@@ -324,8 +324,8 @@ describe("OrchestrationEngine", () => {
324 await system.run(324 await system.run(
325 engine.dispatch({325 engine.dispatch({
326 type: "thread.create",326 type: "thread.create",
327 commandId: CommandId.makeUnsafe("cmd-thread-replay-create"),327 commandId: CommandId.make("cmd-thread-replay-create"),
328 threadId: ThreadId.makeUnsafe("thread-replay"),328 threadId: ThreadId.make("thread-replay"),
329 projectId: asProjectId("project-replay"),329 projectId: asProjectId("project-replay"),
330 title: "replay",330 title: "replay",
331 modelSelection: {331 modelSelection: {
...@@ -342,8 +342,8 @@ describe("OrchestrationEngine", () => {...@@ -342,8 +342,8 @@ describe("OrchestrationEngine", () => {
342 await system.run(342 await system.run(
343 engine.dispatch({343 engine.dispatch({
344 type: "thread.delete",344 type: "thread.delete",
345 commandId: CommandId.makeUnsafe("cmd-thread-replay-delete"),345 commandId: CommandId.make("cmd-thread-replay-delete"),
346 threadId: ThreadId.makeUnsafe("thread-replay"),346 threadId: ThreadId.make("thread-replay"),
347 }),347 }),
348 );348 );
349349
...@@ -368,7 +368,7 @@ describe("OrchestrationEngine", () => {...@@ -368,7 +368,7 @@ describe("OrchestrationEngine", () => {
368 await system.run(368 await system.run(
369 engine.dispatch({369 engine.dispatch({
370 type: "project.create",370 type: "project.create",
371 commandId: CommandId.makeUnsafe("cmd-project-stream-create"),371 commandId: CommandId.make("cmd-project-stream-create"),
372 projectId: asProjectId("project-stream"),372 projectId: asProjectId("project-stream"),
373 title: "Stream Project",373 title: "Stream Project",
374 workspaceRoot: "/tmp/project-stream",374 workspaceRoot: "/tmp/project-stream",
...@@ -392,8 +392,8 @@ describe("OrchestrationEngine", () => {...@@ -392,8 +392,8 @@ describe("OrchestrationEngine", () => {
392 yield* Effect.sleep("10 millis");392 yield* Effect.sleep("10 millis");
393 yield* engine.dispatch({393 yield* engine.dispatch({
394 type: "thread.create",394 type: "thread.create",
395 commandId: CommandId.makeUnsafe("cmd-stream-thread-create"),395 commandId: CommandId.make("cmd-stream-thread-create"),
396 threadId: ThreadId.makeUnsafe("thread-stream"),396 threadId: ThreadId.make("thread-stream"),
397 projectId: asProjectId("project-stream"),397 projectId: asProjectId("project-stream"),
398 title: "domain-stream",398 title: "domain-stream",
399 modelSelection: {399 modelSelection: {
...@@ -408,8 +408,8 @@ describe("OrchestrationEngine", () => {...@@ -408,8 +408,8 @@ describe("OrchestrationEngine", () => {
408 });408 });
409 yield* engine.dispatch({409 yield* engine.dispatch({
410 type: "thread.meta.update",410 type: "thread.meta.update",
411 commandId: CommandId.makeUnsafe("cmd-stream-thread-update"),411 commandId: CommandId.make("cmd-stream-thread-update"),
412 threadId: ThreadId.makeUnsafe("thread-stream"),412 threadId: ThreadId.make("thread-stream"),
413 title: "domain-stream-updated",413 title: "domain-stream-updated",
414 });414 });
415 eventTypes.push((yield* Queue.take(eventQueue)).type);415 eventTypes.push((yield* Queue.take(eventQueue)).type);
...@@ -429,7 +429,7 @@ describe("OrchestrationEngine", () => {...@@ -429,7 +429,7 @@ describe("OrchestrationEngine", () => {
429 await system.run(429 await system.run(
430 engine.dispatch({430 engine.dispatch({
431 type: "project.create",431 type: "project.create",
432 commandId: CommandId.makeUnsafe("cmd-project-ack-create"),432 commandId: CommandId.make("cmd-project-ack-create"),
433 projectId: asProjectId("project-ack"),433 projectId: asProjectId("project-ack"),
434 title: "Ack Project",434 title: "Ack Project",
435 workspaceRoot: "/tmp/project-ack",435 workspaceRoot: "/tmp/project-ack",
...@@ -444,8 +444,8 @@ describe("OrchestrationEngine", () => {...@@ -444,8 +444,8 @@ describe("OrchestrationEngine", () => {
444 await system.run(444 await system.run(
445 engine.dispatch({445 engine.dispatch({
446 type: "thread.create",446 type: "thread.create",
447 commandId: CommandId.makeUnsafe("cmd-thread-ack-create"),447 commandId: CommandId.make("cmd-thread-ack-create"),
448 threadId: ThreadId.makeUnsafe("thread-ack"),448 threadId: ThreadId.make("thread-ack"),
449 projectId: asProjectId("project-ack"),449 projectId: asProjectId("project-ack"),
450 title: "Ack Thread",450 title: "Ack Thread",
451 modelSelection: {451 modelSelection: {
...@@ -481,8 +481,8 @@ describe("OrchestrationEngine", () => {...@@ -481,8 +481,8 @@ describe("OrchestrationEngine", () => {
481 system.run(481 system.run(
482 engine.dispatch({482 engine.dispatch({
483 type: "thread.create",483 type: "thread.create",
484 commandId: CommandId.makeUnsafe("cmd-thread-missing-project"),484 commandId: CommandId.make("cmd-thread-missing-project"),
485 threadId: ThreadId.makeUnsafe("thread-missing-project"),485 threadId: ThreadId.make("thread-missing-project"),
486 projectId: asProjectId("project-missing"),486 projectId: asProjectId("project-missing"),
487 title: "Missing Project Thread",487 title: "Missing Project Thread",
488 modelSelection: {488 modelSelection: {
...@@ -518,7 +518,7 @@ describe("OrchestrationEngine", () => {...@@ -518,7 +518,7 @@ describe("OrchestrationEngine", () => {
518 await system.run(518 await system.run(
519 engine.dispatch({519 engine.dispatch({
520 type: "project.create",520 type: "project.create",
521 commandId: CommandId.makeUnsafe("cmd-project-turn-diff-create"),521 commandId: CommandId.make("cmd-project-turn-diff-create"),
522 projectId: asProjectId("project-turn-diff"),522 projectId: asProjectId("project-turn-diff"),
523 title: "Turn Diff Project",523 title: "Turn Diff Project",
524 workspaceRoot: "/tmp/project-turn-diff",524 workspaceRoot: "/tmp/project-turn-diff",
...@@ -532,8 +532,8 @@ describe("OrchestrationEngine", () => {...@@ -532,8 +532,8 @@ describe("OrchestrationEngine", () => {
532 await system.run(532 await system.run(
533 engine.dispatch({533 engine.dispatch({
534 type: "thread.create",534 type: "thread.create",
535 commandId: CommandId.makeUnsafe("cmd-thread-turn-diff-create"),535 commandId: CommandId.make("cmd-thread-turn-diff-create"),
536 threadId: ThreadId.makeUnsafe("thread-turn-diff"),536 threadId: ThreadId.make("thread-turn-diff"),
537 projectId: asProjectId("project-turn-diff"),537 projectId: asProjectId("project-turn-diff"),
538 title: "Turn diff thread",538 title: "Turn diff thread",
539 modelSelection: {539 modelSelection: {
...@@ -550,8 +550,8 @@ describe("OrchestrationEngine", () => {...@@ -550,8 +550,8 @@ describe("OrchestrationEngine", () => {
550 await system.run(550 await system.run(
551 engine.dispatch({551 engine.dispatch({
552 type: "thread.turn.diff.complete",552 type: "thread.turn.diff.complete",
553 commandId: CommandId.makeUnsafe("cmd-turn-diff-complete"),553 commandId: CommandId.make("cmd-turn-diff-complete"),
554 threadId: ThreadId.makeUnsafe("thread-turn-diff"),554 threadId: ThreadId.make("thread-turn-diff"),
555 turnId: asTurnId("turn-1"),555 turnId: asTurnId("turn-1"),
556 completedAt: createdAt,556 completedAt: createdAt,
557 checkpointRef: asCheckpointRef("refs/t3/checkpoints/thread-turn-diff/turn/1"),557 checkpointRef: asCheckpointRef("refs/t3/checkpoints/thread-turn-diff/turn/1"),
...@@ -590,7 +590,7 @@ describe("OrchestrationEngine", () => {...@@ -590,7 +590,7 @@ describe("OrchestrationEngine", () => {
590590
591 const flakyStore: OrchestrationEventStoreShape = {591 const flakyStore: OrchestrationEventStoreShape = {
592 append(event) {592 append(event) {
593 if (shouldFailFirstAppend && event.commandId === CommandId.makeUnsafe("cmd-flaky-1")) {593 if (shouldFailFirstAppend && event.commandId === CommandId.make("cmd-flaky-1")) {
594 shouldFailFirstAppend = false;594 shouldFailFirstAppend = false;
595 return Effect.fail(595 return Effect.fail(
596 new PersistenceSqlError({596 new PersistenceSqlError({
...@@ -637,7 +637,7 @@ describe("OrchestrationEngine", () => {...@@ -637,7 +637,7 @@ describe("OrchestrationEngine", () => {
637 await runtime.runPromise(637 await runtime.runPromise(
638 engine.dispatch({638 engine.dispatch({
639 type: "project.create",639 type: "project.create",
640 commandId: CommandId.makeUnsafe("cmd-project-flaky-create"),640 commandId: CommandId.make("cmd-project-flaky-create"),
641 projectId: asProjectId("project-flaky"),641 projectId: asProjectId("project-flaky"),
642 title: "Flaky Project",642 title: "Flaky Project",
643 workspaceRoot: "/tmp/project-flaky",643 workspaceRoot: "/tmp/project-flaky",
...@@ -653,8 +653,8 @@ describe("OrchestrationEngine", () => {...@@ -653,8 +653,8 @@ describe("OrchestrationEngine", () => {
653 runtime.runPromise(653 runtime.runPromise(
654 engine.dispatch({654 engine.dispatch({
655 type: "thread.create",655 type: "thread.create",
656 commandId: CommandId.makeUnsafe("cmd-flaky-1"),656 commandId: CommandId.make("cmd-flaky-1"),
657 threadId: ThreadId.makeUnsafe("thread-flaky-fail"),657 threadId: ThreadId.make("thread-flaky-fail"),
658 projectId: asProjectId("project-flaky"),658 projectId: asProjectId("project-flaky"),
659 title: "flaky-fail",659 title: "flaky-fail",
660 modelSelection: {660 modelSelection: {
...@@ -673,8 +673,8 @@ describe("OrchestrationEngine", () => {...@@ -673,8 +673,8 @@ describe("OrchestrationEngine", () => {
673 const result = await runtime.runPromise(673 const result = await runtime.runPromise(
674 engine.dispatch({674 engine.dispatch({
675 type: "thread.create",675 type: "thread.create",
676 commandId: CommandId.makeUnsafe("cmd-flaky-2"),676 commandId: CommandId.make("cmd-flaky-2"),
677 threadId: ThreadId.makeUnsafe("thread-flaky-ok"),677 threadId: ThreadId.make("thread-flaky-ok"),
678 projectId: asProjectId("project-flaky"),678 projectId: asProjectId("project-flaky"),
679 title: "flaky-ok",679 title: "flaky-ok",
680 modelSelection: {680 modelSelection: {
...@@ -701,7 +701,7 @@ describe("OrchestrationEngine", () => {...@@ -701,7 +701,7 @@ describe("OrchestrationEngine", () => {
701 projectEvent: (event) => {701 projectEvent: (event) => {
702 if (702 if (
703 shouldFailRequestedProjection &&703 shouldFailRequestedProjection &&
704 event.commandId === CommandId.makeUnsafe("cmd-turn-start-atomic") &&704 event.commandId === CommandId.make("cmd-turn-start-atomic") &&
705 event.type === "thread.turn-start-requested"705 event.type === "thread.turn-start-requested"
706 ) {706 ) {
707 shouldFailRequestedProjection = false;707 shouldFailRequestedProjection = false;
...@@ -732,7 +732,7 @@ describe("OrchestrationEngine", () => {...@@ -732,7 +732,7 @@ describe("OrchestrationEngine", () => {
732 await runtime.runPromise(732 await runtime.runPromise(
733 engine.dispatch({733 engine.dispatch({
734 type: "project.create",734 type: "project.create",
735 commandId: CommandId.makeUnsafe("cmd-project-atomic-create"),735 commandId: CommandId.make("cmd-project-atomic-create"),
736 projectId: asProjectId("project-atomic"),736 projectId: asProjectId("project-atomic"),
737 title: "Atomic Project",737 title: "Atomic Project",
738 workspaceRoot: "/tmp/project-atomic",738 workspaceRoot: "/tmp/project-atomic",
...@@ -746,8 +746,8 @@ describe("OrchestrationEngine", () => {...@@ -746,8 +746,8 @@ describe("OrchestrationEngine", () => {
746 await runtime.runPromise(746 await runtime.runPromise(
747 engine.dispatch({747 engine.dispatch({
748 type: "thread.create",748 type: "thread.create",
749 commandId: CommandId.makeUnsafe("cmd-thread-atomic-create"),749 commandId: CommandId.make("cmd-thread-atomic-create"),
750 threadId: ThreadId.makeUnsafe("thread-atomic"),750 threadId: ThreadId.make("thread-atomic"),
751 projectId: asProjectId("project-atomic"),751 projectId: asProjectId("project-atomic"),
752 title: "atomic",752 title: "atomic",
753 modelSelection: {753 modelSelection: {
...@@ -764,8 +764,8 @@ describe("OrchestrationEngine", () => {...@@ -764,8 +764,8 @@ describe("OrchestrationEngine", () => {
764764
765 const turnStartCommand = {765 const turnStartCommand = {
766 type: "thread.turn.start" as const,766 type: "thread.turn.start" as const,
767 commandId: CommandId.makeUnsafe("cmd-turn-start-atomic"),767 commandId: CommandId.make("cmd-turn-start-atomic"),
768 threadId: ThreadId.makeUnsafe("thread-atomic"),768 threadId: ThreadId.make("thread-atomic"),
769 message: {769 message: {
770 messageId: asMessageId("msg-atomic-1"),770 messageId: asMessageId("msg-atomic-1"),
771 role: "user" as const,771 role: "user" as const,
...@@ -845,7 +845,7 @@ describe("OrchestrationEngine", () => {...@@ -845,7 +845,7 @@ describe("OrchestrationEngine", () => {
845 projectEvent: (event) => {845 projectEvent: (event) => {
846 if (846 if (
847 shouldFailProjection &&847 shouldFailProjection &&
848 event.commandId === CommandId.makeUnsafe("cmd-thread-meta-sync-fail")848 event.commandId === CommandId.make("cmd-thread-meta-sync-fail")
849 ) {849 ) {
850 shouldFailProjection = false;850 shouldFailProjection = false;
851 return Effect.fail(851 return Effect.fail(
...@@ -875,7 +875,7 @@ describe("OrchestrationEngine", () => {...@@ -875,7 +875,7 @@ describe("OrchestrationEngine", () => {
875 await runtime.runPromise(875 await runtime.runPromise(
876 engine.dispatch({876 engine.dispatch({
877 type: "project.create",877 type: "project.create",
878 commandId: CommandId.makeUnsafe("cmd-project-sync-create"),878 commandId: CommandId.make("cmd-project-sync-create"),
879 projectId: asProjectId("project-sync"),879 projectId: asProjectId("project-sync"),
880 title: "Sync Project",880 title: "Sync Project",
881 workspaceRoot: "/tmp/project-sync",881 workspaceRoot: "/tmp/project-sync",
...@@ -889,8 +889,8 @@ describe("OrchestrationEngine", () => {...@@ -889,8 +889,8 @@ describe("OrchestrationEngine", () => {
889 await runtime.runPromise(889 await runtime.runPromise(
890 engine.dispatch({890 engine.dispatch({
891 type: "thread.create",891 type: "thread.create",
892 commandId: CommandId.makeUnsafe("cmd-thread-sync-create"),892 commandId: CommandId.make("cmd-thread-sync-create"),
893 threadId: ThreadId.makeUnsafe("thread-sync"),893 threadId: ThreadId.make("thread-sync"),
894 projectId: asProjectId("project-sync"),894 projectId: asProjectId("project-sync"),
895 title: "sync-before",895 title: "sync-before",
896 modelSelection: {896 modelSelection: {
...@@ -909,8 +909,8 @@ describe("OrchestrationEngine", () => {...@@ -909,8 +909,8 @@ describe("OrchestrationEngine", () => {
909 runtime.runPromise(909 runtime.runPromise(
910 engine.dispatch({910 engine.dispatch({
911 type: "thread.meta.update",911 type: "thread.meta.update",
912 commandId: CommandId.makeUnsafe("cmd-thread-meta-sync-fail"),912 commandId: CommandId.make("cmd-thread-meta-sync-fail"),
913 threadId: ThreadId.makeUnsafe("thread-sync"),913 threadId: ThreadId.make("thread-sync"),
914 title: "sync-after-failed-projection",914 title: "sync-after-failed-projection",
915 }),915 }),
916 ),916 ),
...@@ -934,8 +934,8 @@ describe("OrchestrationEngine", () => {...@@ -934,8 +934,8 @@ describe("OrchestrationEngine", () => {
934 system.run(934 system.run(
935 engine.dispatch({935 engine.dispatch({
936 type: "thread.turn.start",936 type: "thread.turn.start",
937 commandId: CommandId.makeUnsafe("cmd-invariant-missing-thread"),937 commandId: CommandId.make("cmd-invariant-missing-thread"),
938 threadId: ThreadId.makeUnsafe("thread-missing"),938 threadId: ThreadId.make("thread-missing"),
939 message: {939 message: {
940 messageId: asMessageId("msg-missing"),940 messageId: asMessageId("msg-missing"),
941 role: "user",941 role: "user",
...@@ -960,7 +960,7 @@ describe("OrchestrationEngine", () => {...@@ -960,7 +960,7 @@ describe("OrchestrationEngine", () => {
960 await system.run(960 await system.run(
961 engine.dispatch({961 engine.dispatch({
962 type: "project.create",962 type: "project.create",
963 commandId: CommandId.makeUnsafe("cmd-project-duplicate-create"),963 commandId: CommandId.make("cmd-project-duplicate-create"),
964 projectId: asProjectId("project-duplicate"),964 projectId: asProjectId("project-duplicate"),
965 title: "Duplicate Project",965 title: "Duplicate Project",
966 workspaceRoot: "/tmp/project-duplicate",966 workspaceRoot: "/tmp/project-duplicate",
...@@ -975,8 +975,8 @@ describe("OrchestrationEngine", () => {...@@ -975,8 +975,8 @@ describe("OrchestrationEngine", () => {
975 await system.run(975 await system.run(
976 engine.dispatch({976 engine.dispatch({
977 type: "thread.create",977 type: "thread.create",
978 commandId: CommandId.makeUnsafe("cmd-thread-duplicate-1"),978 commandId: CommandId.make("cmd-thread-duplicate-1"),
979 threadId: ThreadId.makeUnsafe("thread-duplicate"),979 threadId: ThreadId.make("thread-duplicate"),
980 projectId: asProjectId("project-duplicate"),980 projectId: asProjectId("project-duplicate"),
981 title: "duplicate",981 title: "duplicate",
982 modelSelection: {982 modelSelection: {
...@@ -995,8 +995,8 @@ describe("OrchestrationEngine", () => {...@@ -995,8 +995,8 @@ describe("OrchestrationEngine", () => {
995 system.run(995 system.run(
996 engine.dispatch({996 engine.dispatch({
997 type: "thread.create",997 type: "thread.create",
998 commandId: CommandId.makeUnsafe("cmd-thread-duplicate-2"),998 commandId: CommandId.make("cmd-thread-duplicate-2"),
999 threadId: ThreadId.makeUnsafe("thread-duplicate"),999 threadId: ThreadId.make("thread-duplicate"),
1000 projectId: asProjectId("project-duplicate"),1000 projectId: asProjectId("project-duplicate"),
1001 title: "duplicate",1001 title: "duplicate",
1002 modelSelection: {1002 modelSelection: {
apps/server/src/orchestration/Layers/ProjectionPipeline.test.ts+302-302
...@@ -58,16 +58,16 @@ it.layer(BaseTestLayer)("OrchestrationProjectionPipeline", (it) => {...@@ -58,16 +58,16 @@ it.layer(BaseTestLayer)("OrchestrationProjectionPipeline", (it) => {
5858
59 yield* eventStore.append({59 yield* eventStore.append({
60 type: "project.created",60 type: "project.created",
61 eventId: EventId.makeUnsafe("evt-1"),61 eventId: EventId.make("evt-1"),
62 aggregateKind: "project",62 aggregateKind: "project",
63 aggregateId: ProjectId.makeUnsafe("project-1"),63 aggregateId: ProjectId.make("project-1"),
64 occurredAt: now,64 occurredAt: now,
65 commandId: CommandId.makeUnsafe("cmd-1"),65 commandId: CommandId.make("cmd-1"),
66 causationEventId: null,66 causationEventId: null,
67 correlationId: CommandId.makeUnsafe("cmd-1"),67 correlationId: CommandId.make("cmd-1"),
68 metadata: {},68 metadata: {},
69 payload: {69 payload: {
70 projectId: ProjectId.makeUnsafe("project-1"),70 projectId: ProjectId.make("project-1"),
71 title: "Project 1",71 title: "Project 1",
72 workspaceRoot: "/tmp/project-1",72 workspaceRoot: "/tmp/project-1",
73 defaultModelSelection: null,73 defaultModelSelection: null,
...@@ -79,17 +79,17 @@ it.layer(BaseTestLayer)("OrchestrationProjectionPipeline", (it) => {...@@ -79,17 +79,17 @@ it.layer(BaseTestLayer)("OrchestrationProjectionPipeline", (it) => {
7979
80 yield* eventStore.append({80 yield* eventStore.append({
81 type: "thread.created",81 type: "thread.created",
82 eventId: EventId.makeUnsafe("evt-2"),82 eventId: EventId.make("evt-2"),
83 aggregateKind: "thread",83 aggregateKind: "thread",
84 aggregateId: ThreadId.makeUnsafe("thread-1"),84 aggregateId: ThreadId.make("thread-1"),
85 occurredAt: now,85 occurredAt: now,
86 commandId: CommandId.makeUnsafe("cmd-2"),86 commandId: CommandId.make("cmd-2"),
87 causationEventId: null,87 causationEventId: null,
88 correlationId: CommandId.makeUnsafe("cmd-2"),88 correlationId: CommandId.make("cmd-2"),
89 metadata: {},89 metadata: {},
90 payload: {90 payload: {
91 threadId: ThreadId.makeUnsafe("thread-1"),91 threadId: ThreadId.make("thread-1"),
92 projectId: ProjectId.makeUnsafe("project-1"),92 projectId: ProjectId.make("project-1"),
93 title: "Thread 1",93 title: "Thread 1",
94 modelSelection: {94 modelSelection: {
95 provider: "codex",95 provider: "codex",
...@@ -105,17 +105,17 @@ it.layer(BaseTestLayer)("OrchestrationProjectionPipeline", (it) => {...@@ -105,17 +105,17 @@ it.layer(BaseTestLayer)("OrchestrationProjectionPipeline", (it) => {
105105
106 yield* eventStore.append({106 yield* eventStore.append({
107 type: "thread.message-sent",107 type: "thread.message-sent",
108 eventId: EventId.makeUnsafe("evt-3"),108 eventId: EventId.make("evt-3"),
109 aggregateKind: "thread",109 aggregateKind: "thread",
110 aggregateId: ThreadId.makeUnsafe("thread-1"),110 aggregateId: ThreadId.make("thread-1"),
111 occurredAt: now,111 occurredAt: now,
112 commandId: CommandId.makeUnsafe("cmd-3"),112 commandId: CommandId.make("cmd-3"),
113 causationEventId: null,113 causationEventId: null,
114 correlationId: CommandId.makeUnsafe("cmd-3"),114 correlationId: CommandId.make("cmd-3"),
115 metadata: {},115 metadata: {},
116 payload: {116 payload: {
117 threadId: ThreadId.makeUnsafe("thread-1"),117 threadId: ThreadId.make("thread-1"),
118 messageId: MessageId.makeUnsafe("message-1"),118 messageId: MessageId.make("message-1"),
119 role: "assistant",119 role: "assistant",
120 text: "hello",120 text: "hello",
121 turnId: null,121 turnId: null,
...@@ -183,17 +183,17 @@ it.layer(Layer.fresh(makeProjectionPipelinePrefixedTestLayer("t3-base-")))(...@@ -183,17 +183,17 @@ it.layer(Layer.fresh(makeProjectionPipelinePrefixedTestLayer("t3-base-")))(
183183
184 yield* eventStore.append({184 yield* eventStore.append({
185 type: "thread.message-sent",185 type: "thread.message-sent",
186 eventId: EventId.makeUnsafe("evt-attachments"),186 eventId: EventId.make("evt-attachments"),
187 aggregateKind: "thread",187 aggregateKind: "thread",
188 aggregateId: ThreadId.makeUnsafe("thread-attachments"),188 aggregateId: ThreadId.make("thread-attachments"),
189 occurredAt: now,189 occurredAt: now,
190 commandId: CommandId.makeUnsafe("cmd-attachments"),190 commandId: CommandId.make("cmd-attachments"),
191 causationEventId: null,191 causationEventId: null,
192 correlationId: CommandId.makeUnsafe("cmd-attachments"),192 correlationId: CommandId.make("cmd-attachments"),
193 metadata: {},193 metadata: {},
194 payload: {194 payload: {
195 threadId: ThreadId.makeUnsafe("thread-attachments"),195 threadId: ThreadId.make("thread-attachments"),
196 messageId: MessageId.makeUnsafe("message-attachments"),196 messageId: MessageId.make("message-attachments"),
197 role: "user",197 role: "user",
198 text: "Inspect this",198 text: "Inspect this",
199 attachments: [199 attachments: [
...@@ -249,17 +249,17 @@ it.layer(Layer.fresh(makeProjectionPipelinePrefixedTestLayer("t3-projection-atta...@@ -249,17 +249,17 @@ it.layer(Layer.fresh(makeProjectionPipelinePrefixedTestLayer("t3-projection-atta
249249
250 yield* eventStore.append({250 yield* eventStore.append({
251 type: "thread.message-sent",251 type: "thread.message-sent",
252 eventId: EventId.makeUnsafe("evt-attachments-safe"),252 eventId: EventId.make("evt-attachments-safe"),
253 aggregateKind: "thread",253 aggregateKind: "thread",
254 aggregateId: ThreadId.makeUnsafe("thread-attachments-safe"),254 aggregateId: ThreadId.make("thread-attachments-safe"),
255 occurredAt: now,255 occurredAt: now,
256 commandId: CommandId.makeUnsafe("cmd-attachments-safe"),256 commandId: CommandId.make("cmd-attachments-safe"),
257 causationEventId: null,257 causationEventId: null,
258 correlationId: CommandId.makeUnsafe("cmd-attachments-safe"),258 correlationId: CommandId.make("cmd-attachments-safe"),
259 metadata: {},259 metadata: {},
260 payload: {260 payload: {
261 threadId: ThreadId.makeUnsafe("thread-attachments-safe"),261 threadId: ThreadId.make("thread-attachments-safe"),
262 messageId: MessageId.makeUnsafe("message-attachments-safe"),262 messageId: MessageId.make("message-attachments-safe"),
263 role: "user",263 role: "user",
264 text: "Inspect this",264 text: "Inspect this",
265 attachments: [265 attachments: [
...@@ -330,16 +330,16 @@ it.layer(BaseTestLayer)("OrchestrationProjectionPipeline", (it) => {...@@ -330,16 +330,16 @@ it.layer(BaseTestLayer)("OrchestrationProjectionPipeline", (it) => {
330330
331 yield* eventStore.append({331 yield* eventStore.append({
332 type: "project.created",332 type: "project.created",
333 eventId: EventId.makeUnsafe("evt-clear-attachments-1"),333 eventId: EventId.make("evt-clear-attachments-1"),
334 aggregateKind: "project",334 aggregateKind: "project",
335 aggregateId: ProjectId.makeUnsafe("project-clear-attachments"),335 aggregateId: ProjectId.make("project-clear-attachments"),
336 occurredAt: now,336 occurredAt: now,
337 commandId: CommandId.makeUnsafe("cmd-clear-attachments-1"),337 commandId: CommandId.make("cmd-clear-attachments-1"),
338 causationEventId: null,338 causationEventId: null,
339 correlationId: CommandId.makeUnsafe("cmd-clear-attachments-1"),339 correlationId: CommandId.make("cmd-clear-attachments-1"),
340 metadata: {},340 metadata: {},
341 payload: {341 payload: {
342 projectId: ProjectId.makeUnsafe("project-clear-attachments"),342 projectId: ProjectId.make("project-clear-attachments"),
343 title: "Project Clear Attachments",343 title: "Project Clear Attachments",
344 workspaceRoot: "/tmp/project-clear-attachments",344 workspaceRoot: "/tmp/project-clear-attachments",
345 defaultModelSelection: null,345 defaultModelSelection: null,
...@@ -351,17 +351,17 @@ it.layer(BaseTestLayer)("OrchestrationProjectionPipeline", (it) => {...@@ -351,17 +351,17 @@ it.layer(BaseTestLayer)("OrchestrationProjectionPipeline", (it) => {
351351
352 yield* eventStore.append({352 yield* eventStore.append({
353 type: "thread.created",353 type: "thread.created",
354 eventId: EventId.makeUnsafe("evt-clear-attachments-2"),354 eventId: EventId.make("evt-clear-attachments-2"),
355 aggregateKind: "thread",355 aggregateKind: "thread",
356 aggregateId: ThreadId.makeUnsafe("thread-clear-attachments"),356 aggregateId: ThreadId.make("thread-clear-attachments"),
357 occurredAt: now,357 occurredAt: now,
358 commandId: CommandId.makeUnsafe("cmd-clear-attachments-2"),358 commandId: CommandId.make("cmd-clear-attachments-2"),
359 causationEventId: null,359 causationEventId: null,
360 correlationId: CommandId.makeUnsafe("cmd-clear-attachments-2"),360 correlationId: CommandId.make("cmd-clear-attachments-2"),
361 metadata: {},361 metadata: {},
362 payload: {362 payload: {
363 threadId: ThreadId.makeUnsafe("thread-clear-attachments"),363 threadId: ThreadId.make("thread-clear-attachments"),
364 projectId: ProjectId.makeUnsafe("project-clear-attachments"),364 projectId: ProjectId.make("project-clear-attachments"),
365 title: "Thread Clear Attachments",365 title: "Thread Clear Attachments",
366 modelSelection: {366 modelSelection: {
367 provider: "codex",367 provider: "codex",
...@@ -377,17 +377,17 @@ it.layer(BaseTestLayer)("OrchestrationProjectionPipeline", (it) => {...@@ -377,17 +377,17 @@ it.layer(BaseTestLayer)("OrchestrationProjectionPipeline", (it) => {
377377
378 yield* eventStore.append({378 yield* eventStore.append({
379 type: "thread.message-sent",379 type: "thread.message-sent",
380 eventId: EventId.makeUnsafe("evt-clear-attachments-3"),380 eventId: EventId.make("evt-clear-attachments-3"),
381 aggregateKind: "thread",381 aggregateKind: "thread",
382 aggregateId: ThreadId.makeUnsafe("thread-clear-attachments"),382 aggregateId: ThreadId.make("thread-clear-attachments"),
383 occurredAt: now,383 occurredAt: now,
384 commandId: CommandId.makeUnsafe("cmd-clear-attachments-3"),384 commandId: CommandId.make("cmd-clear-attachments-3"),
385 causationEventId: null,385 causationEventId: null,
386 correlationId: CommandId.makeUnsafe("cmd-clear-attachments-3"),386 correlationId: CommandId.make("cmd-clear-attachments-3"),
387 metadata: {},387 metadata: {},
388 payload: {388 payload: {
389 threadId: ThreadId.makeUnsafe("thread-clear-attachments"),389 threadId: ThreadId.make("thread-clear-attachments"),
390 messageId: MessageId.makeUnsafe("message-clear-attachments"),390 messageId: MessageId.make("message-clear-attachments"),
391 role: "user",391 role: "user",
392 text: "Has attachments",392 text: "Has attachments",
393 attachments: [393 attachments: [
...@@ -408,17 +408,17 @@ it.layer(BaseTestLayer)("OrchestrationProjectionPipeline", (it) => {...@@ -408,17 +408,17 @@ it.layer(BaseTestLayer)("OrchestrationProjectionPipeline", (it) => {
408408
409 yield* eventStore.append({409 yield* eventStore.append({
410 type: "thread.message-sent",410 type: "thread.message-sent",
411 eventId: EventId.makeUnsafe("evt-clear-attachments-4"),411 eventId: EventId.make("evt-clear-attachments-4"),
412 aggregateKind: "thread",412 aggregateKind: "thread",
413 aggregateId: ThreadId.makeUnsafe("thread-clear-attachments"),413 aggregateId: ThreadId.make("thread-clear-attachments"),
414 occurredAt: later,414 occurredAt: later,
415 commandId: CommandId.makeUnsafe("cmd-clear-attachments-4"),415 commandId: CommandId.make("cmd-clear-attachments-4"),
416 causationEventId: null,416 causationEventId: null,
417 correlationId: CommandId.makeUnsafe("cmd-clear-attachments-4"),417 correlationId: CommandId.make("cmd-clear-attachments-4"),
418 metadata: {},418 metadata: {},
419 payload: {419 payload: {
420 threadId: ThreadId.makeUnsafe("thread-clear-attachments"),420 threadId: ThreadId.make("thread-clear-attachments"),
421 messageId: MessageId.makeUnsafe("message-clear-attachments"),421 messageId: MessageId.make("message-clear-attachments"),
422 role: "user",422 role: "user",
423 text: "",423 text: "",
424 attachments: [],424 attachments: [],
...@@ -458,16 +458,16 @@ it.layer(...@@ -458,16 +458,16 @@ it.layer(
458458
459 yield* eventStore.append({459 yield* eventStore.append({
460 type: "project.created",460 type: "project.created",
461 eventId: EventId.makeUnsafe("evt-overwrite-1"),461 eventId: EventId.make("evt-overwrite-1"),
462 aggregateKind: "project",462 aggregateKind: "project",
463 aggregateId: ProjectId.makeUnsafe("project-overwrite"),463 aggregateId: ProjectId.make("project-overwrite"),
464 occurredAt: now,464 occurredAt: now,
465 commandId: CommandId.makeUnsafe("cmd-overwrite-1"),465 commandId: CommandId.make("cmd-overwrite-1"),
466 causationEventId: null,466 causationEventId: null,
467 correlationId: CommandId.makeUnsafe("cmd-overwrite-1"),467 correlationId: CommandId.make("cmd-overwrite-1"),
468 metadata: {},468 metadata: {},
469 payload: {469 payload: {
470 projectId: ProjectId.makeUnsafe("project-overwrite"),470 projectId: ProjectId.make("project-overwrite"),
471 title: "Project Overwrite",471 title: "Project Overwrite",
472 workspaceRoot: "/tmp/project-overwrite",472 workspaceRoot: "/tmp/project-overwrite",
473 defaultModelSelection: null,473 defaultModelSelection: null,
...@@ -479,17 +479,17 @@ it.layer(...@@ -479,17 +479,17 @@ it.layer(
479479
480 yield* eventStore.append({480 yield* eventStore.append({
481 type: "thread.created",481 type: "thread.created",
482 eventId: EventId.makeUnsafe("evt-overwrite-2"),482 eventId: EventId.make("evt-overwrite-2"),
483 aggregateKind: "thread",483 aggregateKind: "thread",
484 aggregateId: ThreadId.makeUnsafe("thread-overwrite"),484 aggregateId: ThreadId.make("thread-overwrite"),
485 occurredAt: now,485 occurredAt: now,
486 commandId: CommandId.makeUnsafe("cmd-overwrite-2"),486 commandId: CommandId.make("cmd-overwrite-2"),
487 causationEventId: null,487 causationEventId: null,
488 correlationId: CommandId.makeUnsafe("cmd-overwrite-2"),488 correlationId: CommandId.make("cmd-overwrite-2"),
489 metadata: {},489 metadata: {},
490 payload: {490 payload: {
491 threadId: ThreadId.makeUnsafe("thread-overwrite"),491 threadId: ThreadId.make("thread-overwrite"),
492 projectId: ProjectId.makeUnsafe("project-overwrite"),492 projectId: ProjectId.make("project-overwrite"),
493 title: "Thread Overwrite",493 title: "Thread Overwrite",
494 modelSelection: {494 modelSelection: {
495 provider: "codex",495 provider: "codex",
...@@ -505,17 +505,17 @@ it.layer(...@@ -505,17 +505,17 @@ it.layer(
505505
506 yield* eventStore.append({506 yield* eventStore.append({
507 type: "thread.message-sent",507 type: "thread.message-sent",
508 eventId: EventId.makeUnsafe("evt-overwrite-3"),508 eventId: EventId.make("evt-overwrite-3"),
509 aggregateKind: "thread",509 aggregateKind: "thread",
510 aggregateId: ThreadId.makeUnsafe("thread-overwrite"),510 aggregateId: ThreadId.make("thread-overwrite"),
511 occurredAt: now,511 occurredAt: now,
512 commandId: CommandId.makeUnsafe("cmd-overwrite-3"),512 commandId: CommandId.make("cmd-overwrite-3"),
513 causationEventId: null,513 causationEventId: null,
514 correlationId: CommandId.makeUnsafe("cmd-overwrite-3"),514 correlationId: CommandId.make("cmd-overwrite-3"),
515 metadata: {},515 metadata: {},
516 payload: {516 payload: {
517 threadId: ThreadId.makeUnsafe("thread-overwrite"),517 threadId: ThreadId.make("thread-overwrite"),
518 messageId: MessageId.makeUnsafe("message-overwrite"),518 messageId: MessageId.make("message-overwrite"),
519 role: "user",519 role: "user",
520 text: "first image",520 text: "first image",
521 attachments: [521 attachments: [
...@@ -536,17 +536,17 @@ it.layer(...@@ -536,17 +536,17 @@ it.layer(
536536
537 yield* eventStore.append({537 yield* eventStore.append({
538 type: "thread.message-sent",538 type: "thread.message-sent",
539 eventId: EventId.makeUnsafe("evt-overwrite-4"),539 eventId: EventId.make("evt-overwrite-4"),
540 aggregateKind: "thread",540 aggregateKind: "thread",
541 aggregateId: ThreadId.makeUnsafe("thread-overwrite"),541 aggregateId: ThreadId.make("thread-overwrite"),
542 occurredAt: later,542 occurredAt: later,
543 commandId: CommandId.makeUnsafe("cmd-overwrite-4"),543 commandId: CommandId.make("cmd-overwrite-4"),
544 causationEventId: null,544 causationEventId: null,
545 correlationId: CommandId.makeUnsafe("cmd-overwrite-4"),545 correlationId: CommandId.make("cmd-overwrite-4"),
546 metadata: {},546 metadata: {},
547 payload: {547 payload: {
548 threadId: ThreadId.makeUnsafe("thread-overwrite"),548 threadId: ThreadId.make("thread-overwrite"),
549 messageId: MessageId.makeUnsafe("message-overwrite"),549 messageId: MessageId.make("message-overwrite"),
550 role: "user",550 role: "user",
551 text: "",551 text: "",
552 attachments: [552 attachments: [
...@@ -606,16 +606,16 @@ it.layer(...@@ -606,16 +606,16 @@ it.layer(
606606
607 yield* appendAndProject({607 yield* appendAndProject({
608 type: "project.created",608 type: "project.created",
609 eventId: EventId.makeUnsafe("evt-rollback-1"),609 eventId: EventId.make("evt-rollback-1"),
610 aggregateKind: "project",610 aggregateKind: "project",
611 aggregateId: ProjectId.makeUnsafe("project-rollback"),611 aggregateId: ProjectId.make("project-rollback"),
612 occurredAt: now,612 occurredAt: now,
613 commandId: CommandId.makeUnsafe("cmd-rollback-1"),613 commandId: CommandId.make("cmd-rollback-1"),
614 causationEventId: null,614 causationEventId: null,
615 correlationId: CorrelationId.makeUnsafe("cmd-rollback-1"),615 correlationId: CorrelationId.make("cmd-rollback-1"),
616 metadata: {},616 metadata: {},
617 payload: {617 payload: {
618 projectId: ProjectId.makeUnsafe("project-rollback"),618 projectId: ProjectId.make("project-rollback"),
619 title: "Project Rollback",619 title: "Project Rollback",
620 workspaceRoot: "/tmp/project-rollback",620 workspaceRoot: "/tmp/project-rollback",
621 defaultModelSelection: null,621 defaultModelSelection: null,
...@@ -627,17 +627,17 @@ it.layer(...@@ -627,17 +627,17 @@ it.layer(
627627
628 yield* appendAndProject({628 yield* appendAndProject({
629 type: "thread.created",629 type: "thread.created",
630 eventId: EventId.makeUnsafe("evt-rollback-2"),630 eventId: EventId.make("evt-rollback-2"),
631 aggregateKind: "thread",631 aggregateKind: "thread",
632 aggregateId: ThreadId.makeUnsafe("thread-rollback"),632 aggregateId: ThreadId.make("thread-rollback"),
633 occurredAt: now,633 occurredAt: now,
634 commandId: CommandId.makeUnsafe("cmd-rollback-2"),634 commandId: CommandId.make("cmd-rollback-2"),
635 causationEventId: null,635 causationEventId: null,
636 correlationId: CorrelationId.makeUnsafe("cmd-rollback-2"),636 correlationId: CorrelationId.make("cmd-rollback-2"),
637 metadata: {},637 metadata: {},
638 payload: {638 payload: {
639 threadId: ThreadId.makeUnsafe("thread-rollback"),639 threadId: ThreadId.make("thread-rollback"),
640 projectId: ProjectId.makeUnsafe("project-rollback"),640 projectId: ProjectId.make("project-rollback"),
641 title: "Thread Rollback",641 title: "Thread Rollback",
642 modelSelection: {642 modelSelection: {
643 provider: "codex",643 provider: "codex",
...@@ -663,17 +663,17 @@ it.layer(...@@ -663,17 +663,17 @@ it.layer(
663 const result = yield* Effect.result(663 const result = yield* Effect.result(
664 appendAndProject({664 appendAndProject({
665 type: "thread.message-sent",665 type: "thread.message-sent",
666 eventId: EventId.makeUnsafe("evt-rollback-3"),666 eventId: EventId.make("evt-rollback-3"),
667 aggregateKind: "thread",667 aggregateKind: "thread",
668 aggregateId: ThreadId.makeUnsafe("thread-rollback"),668 aggregateId: ThreadId.make("thread-rollback"),
669 occurredAt: now,669 occurredAt: now,
670 commandId: CommandId.makeUnsafe("cmd-rollback-3"),670 commandId: CommandId.make("cmd-rollback-3"),
671 causationEventId: null,671 causationEventId: null,
672 correlationId: CorrelationId.makeUnsafe("cmd-rollback-3"),672 correlationId: CorrelationId.make("cmd-rollback-3"),
673 metadata: {},673 metadata: {},
674 payload: {674 payload: {
675 threadId: ThreadId.makeUnsafe("thread-rollback"),675 threadId: ThreadId.make("thread-rollback"),
676 messageId: MessageId.makeUnsafe("message-rollback"),676 messageId: MessageId.make("message-rollback"),
677 role: "user",677 role: "user",
678 text: "Rollback me",678 text: "Rollback me",
679 attachments: [679 attachments: [
...@@ -722,7 +722,7 @@ it.layer(...@@ -722,7 +722,7 @@ it.layer(
722 const eventStore = yield* OrchestrationEventStore;722 const eventStore = yield* OrchestrationEventStore;
723 const { attachmentsDir } = yield* ServerConfig;723 const { attachmentsDir } = yield* ServerConfig;
724 const now = new Date().toISOString();724 const now = new Date().toISOString();
725 const threadId = ThreadId.makeUnsafe("Thread Revert.Files");725 const threadId = ThreadId.make("Thread Revert.Files");
726 const keepAttachmentId = "thread-revert-files-00000000-0000-4000-8000-000000000001";726 const keepAttachmentId = "thread-revert-files-00000000-0000-4000-8000-000000000001";
727 const removeAttachmentId = "thread-revert-files-00000000-0000-4000-8000-000000000002";727 const removeAttachmentId = "thread-revert-files-00000000-0000-4000-8000-000000000002";
728 const otherThreadAttachmentId =728 const otherThreadAttachmentId =
...@@ -735,16 +735,16 @@ it.layer(...@@ -735,16 +735,16 @@ it.layer(
735735
736 yield* appendAndProject({736 yield* appendAndProject({
737 type: "project.created",737 type: "project.created",
738 eventId: EventId.makeUnsafe("evt-revert-files-1"),738 eventId: EventId.make("evt-revert-files-1"),
739 aggregateKind: "project",739 aggregateKind: "project",
740 aggregateId: ProjectId.makeUnsafe("project-revert-files"),740 aggregateId: ProjectId.make("project-revert-files"),
741 occurredAt: now,741 occurredAt: now,
742 commandId: CommandId.makeUnsafe("cmd-revert-files-1"),742 commandId: CommandId.make("cmd-revert-files-1"),
743 causationEventId: null,743 causationEventId: null,
744 correlationId: CorrelationId.makeUnsafe("cmd-revert-files-1"),744 correlationId: CorrelationId.make("cmd-revert-files-1"),
745 metadata: {},745 metadata: {},
746 payload: {746 payload: {
747 projectId: ProjectId.makeUnsafe("project-revert-files"),747 projectId: ProjectId.make("project-revert-files"),
748 title: "Project Revert Files",748 title: "Project Revert Files",
749 workspaceRoot: "/tmp/project-revert-files",749 workspaceRoot: "/tmp/project-revert-files",
750 defaultModelSelection: null,750 defaultModelSelection: null,
...@@ -756,17 +756,17 @@ it.layer(...@@ -756,17 +756,17 @@ it.layer(
756756
757 yield* appendAndProject({757 yield* appendAndProject({
758 type: "thread.created",758 type: "thread.created",
759 eventId: EventId.makeUnsafe("evt-revert-files-2"),759 eventId: EventId.make("evt-revert-files-2"),
760 aggregateKind: "thread",760 aggregateKind: "thread",
761 aggregateId: threadId,761 aggregateId: threadId,
762 occurredAt: now,762 occurredAt: now,
763 commandId: CommandId.makeUnsafe("cmd-revert-files-2"),763 commandId: CommandId.make("cmd-revert-files-2"),
764 causationEventId: null,764 causationEventId: null,
765 correlationId: CorrelationId.makeUnsafe("cmd-revert-files-2"),765 correlationId: CorrelationId.make("cmd-revert-files-2"),
766 metadata: {},766 metadata: {},
767 payload: {767 payload: {
768 threadId,768 threadId,
769 projectId: ProjectId.makeUnsafe("project-revert-files"),769 projectId: ProjectId.make("project-revert-files"),
770 title: "Thread Revert Files",770 title: "Thread Revert Files",
771 modelSelection: {771 modelSelection: {
772 provider: "codex",772 provider: "codex",
...@@ -782,39 +782,39 @@ it.layer(...@@ -782,39 +782,39 @@ it.layer(
782782
783 yield* appendAndProject({783 yield* appendAndProject({
784 type: "thread.turn-diff-completed",784 type: "thread.turn-diff-completed",
785 eventId: EventId.makeUnsafe("evt-revert-files-3"),785 eventId: EventId.make("evt-revert-files-3"),
786 aggregateKind: "thread",786 aggregateKind: "thread",
787 aggregateId: threadId,787 aggregateId: threadId,
788 occurredAt: now,788 occurredAt: now,
789 commandId: CommandId.makeUnsafe("cmd-revert-files-3"),789 commandId: CommandId.make("cmd-revert-files-3"),
790 causationEventId: null,790 causationEventId: null,
791 correlationId: CorrelationId.makeUnsafe("cmd-revert-files-3"),791 correlationId: CorrelationId.make("cmd-revert-files-3"),
792 metadata: {},792 metadata: {},
793 payload: {793 payload: {
794 threadId,794 threadId,
795 turnId: TurnId.makeUnsafe("turn-keep"),795 turnId: TurnId.make("turn-keep"),
796 checkpointTurnCount: 1,796 checkpointTurnCount: 1,
797 checkpointRef: CheckpointRef.makeUnsafe("refs/t3/checkpoints/thread-revert-files/turn/1"),797 checkpointRef: CheckpointRef.make("refs/t3/checkpoints/thread-revert-files/turn/1"),
798 status: "ready",798 status: "ready",
799 files: [],799 files: [],
800 assistantMessageId: MessageId.makeUnsafe("message-keep"),800 assistantMessageId: MessageId.make("message-keep"),
801 completedAt: now,801 completedAt: now,
802 },802 },
803 });803 });
804804
805 yield* appendAndProject({805 yield* appendAndProject({
806 type: "thread.message-sent",806 type: "thread.message-sent",
807 eventId: EventId.makeUnsafe("evt-revert-files-4"),807 eventId: EventId.make("evt-revert-files-4"),
808 aggregateKind: "thread",808 aggregateKind: "thread",
809 aggregateId: threadId,809 aggregateId: threadId,
810 occurredAt: now,810 occurredAt: now,
811 commandId: CommandId.makeUnsafe("cmd-revert-files-4"),811 commandId: CommandId.make("cmd-revert-files-4"),
812 causationEventId: null,812 causationEventId: null,
813 correlationId: CorrelationId.makeUnsafe("cmd-revert-files-4"),813 correlationId: CorrelationId.make("cmd-revert-files-4"),
814 metadata: {},814 metadata: {},
815 payload: {815 payload: {
816 threadId,816 threadId,
817 messageId: MessageId.makeUnsafe("message-keep"),817 messageId: MessageId.make("message-keep"),
818 role: "assistant",818 role: "assistant",
819 text: "Keep",819 text: "Keep",
820 attachments: [820 attachments: [
...@@ -826,7 +826,7 @@ it.layer(...@@ -826,7 +826,7 @@ it.layer(
826 sizeBytes: 5,826 sizeBytes: 5,
827 },827 },
828 ],828 ],
829 turnId: TurnId.makeUnsafe("turn-keep"),829 turnId: TurnId.make("turn-keep"),
830 streaming: false,830 streaming: false,
831 createdAt: now,831 createdAt: now,
832 updatedAt: now,832 updatedAt: now,
...@@ -835,39 +835,39 @@ it.layer(...@@ -835,39 +835,39 @@ it.layer(
835835
836 yield* appendAndProject({836 yield* appendAndProject({
837 type: "thread.turn-diff-completed",837 type: "thread.turn-diff-completed",
838 eventId: EventId.makeUnsafe("evt-revert-files-5"),838 eventId: EventId.make("evt-revert-files-5"),
839 aggregateKind: "thread",839 aggregateKind: "thread",
840 aggregateId: threadId,840 aggregateId: threadId,
841 occurredAt: now,841 occurredAt: now,
842 commandId: CommandId.makeUnsafe("cmd-revert-files-5"),842 commandId: CommandId.make("cmd-revert-files-5"),
843 causationEventId: null,843 causationEventId: null,
844 correlationId: CorrelationId.makeUnsafe("cmd-revert-files-5"),844 correlationId: CorrelationId.make("cmd-revert-files-5"),
845 metadata: {},845 metadata: {},
846 payload: {846 payload: {
847 threadId,847 threadId,
848 turnId: TurnId.makeUnsafe("turn-remove"),848 turnId: TurnId.make("turn-remove"),
849 checkpointTurnCount: 2,849 checkpointTurnCount: 2,
850 checkpointRef: CheckpointRef.makeUnsafe("refs/t3/checkpoints/thread-revert-files/turn/2"),850 checkpointRef: CheckpointRef.make("refs/t3/checkpoints/thread-revert-files/turn/2"),
851 status: "ready",851 status: "ready",
852 files: [],852 files: [],
853 assistantMessageId: MessageId.makeUnsafe("message-remove"),853 assistantMessageId: MessageId.make("message-remove"),
854 completedAt: now,854 completedAt: now,
855 },855 },
856 });856 });
857857
858 yield* appendAndProject({858 yield* appendAndProject({
859 type: "thread.message-sent",859 type: "thread.message-sent",
860 eventId: EventId.makeUnsafe("evt-revert-files-6"),860 eventId: EventId.make("evt-revert-files-6"),
861 aggregateKind: "thread",861 aggregateKind: "thread",
862 aggregateId: threadId,862 aggregateId: threadId,
863 occurredAt: now,863 occurredAt: now,
864 commandId: CommandId.makeUnsafe("cmd-revert-files-6"),864 commandId: CommandId.make("cmd-revert-files-6"),
865 causationEventId: null,865 causationEventId: null,
866 correlationId: CorrelationId.makeUnsafe("cmd-revert-files-6"),866 correlationId: CorrelationId.make("cmd-revert-files-6"),
867 metadata: {},867 metadata: {},
868 payload: {868 payload: {
869 threadId,869 threadId,
870 messageId: MessageId.makeUnsafe("message-remove"),870 messageId: MessageId.make("message-remove"),
871 role: "assistant",871 role: "assistant",
872 text: "Remove",872 text: "Remove",
873 attachments: [873 attachments: [
...@@ -879,7 +879,7 @@ it.layer(...@@ -879,7 +879,7 @@ it.layer(
879 sizeBytes: 5,879 sizeBytes: 5,
880 },880 },
881 ],881 ],
882 turnId: TurnId.makeUnsafe("turn-remove"),882 turnId: TurnId.make("turn-remove"),
883 streaming: false,883 streaming: false,
884 createdAt: now,884 createdAt: now,
885 updatedAt: now,885 updatedAt: now,
...@@ -899,13 +899,13 @@ it.layer(...@@ -899,13 +899,13 @@ it.layer(
899899
900 yield* appendAndProject({900 yield* appendAndProject({
901 type: "thread.reverted",901 type: "thread.reverted",
902 eventId: EventId.makeUnsafe("evt-revert-files-7"),902 eventId: EventId.make("evt-revert-files-7"),
903 aggregateKind: "thread",903 aggregateKind: "thread",
904 aggregateId: threadId,904 aggregateId: threadId,
905 occurredAt: now,905 occurredAt: now,
906 commandId: CommandId.makeUnsafe("cmd-revert-files-7"),906 commandId: CommandId.make("cmd-revert-files-7"),
907 causationEventId: null,907 causationEventId: null,
908 correlationId: CorrelationId.makeUnsafe("cmd-revert-files-7"),908 correlationId: CorrelationId.make("cmd-revert-files-7"),
909 metadata: {},909 metadata: {},
910 payload: {910 payload: {
911 threadId,911 threadId,
...@@ -931,7 +931,7 @@ it.layer(Layer.fresh(makeProjectionPipelinePrefixedTestLayer("t3-projection-atta...@@ -931,7 +931,7 @@ it.layer(Layer.fresh(makeProjectionPipelinePrefixedTestLayer("t3-projection-atta
931 const eventStore = yield* OrchestrationEventStore;931 const eventStore = yield* OrchestrationEventStore;
932 const { attachmentsDir } = yield* ServerConfig;932 const { attachmentsDir } = yield* ServerConfig;
933 const now = new Date().toISOString();933 const now = new Date().toISOString();
934 const threadId = ThreadId.makeUnsafe("Thread Delete.Files");934 const threadId = ThreadId.make("Thread Delete.Files");
935 const attachmentId = "thread-delete-files-00000000-0000-4000-8000-000000000001";935 const attachmentId = "thread-delete-files-00000000-0000-4000-8000-000000000001";
936 const otherThreadAttachmentId =936 const otherThreadAttachmentId =
937 "thread-delete-files-extra-00000000-0000-4000-8000-000000000002";937 "thread-delete-files-extra-00000000-0000-4000-8000-000000000002";
...@@ -943,16 +943,16 @@ it.layer(Layer.fresh(makeProjectionPipelinePrefixedTestLayer("t3-projection-atta...@@ -943,16 +943,16 @@ it.layer(Layer.fresh(makeProjectionPipelinePrefixedTestLayer("t3-projection-atta
943943
944 yield* appendAndProject({944 yield* appendAndProject({
945 type: "project.created",945 type: "project.created",
946 eventId: EventId.makeUnsafe("evt-delete-files-1"),946 eventId: EventId.make("evt-delete-files-1"),
947 aggregateKind: "project",947 aggregateKind: "project",
948 aggregateId: ProjectId.makeUnsafe("project-delete-files"),948 aggregateId: ProjectId.make("project-delete-files"),
949 occurredAt: now,949 occurredAt: now,
950 commandId: CommandId.makeUnsafe("cmd-delete-files-1"),950 commandId: CommandId.make("cmd-delete-files-1"),
951 causationEventId: null,951 causationEventId: null,
952 correlationId: CorrelationId.makeUnsafe("cmd-delete-files-1"),952 correlationId: CorrelationId.make("cmd-delete-files-1"),
953 metadata: {},953 metadata: {},
954 payload: {954 payload: {
955 projectId: ProjectId.makeUnsafe("project-delete-files"),955 projectId: ProjectId.make("project-delete-files"),
956 title: "Project Delete Files",956 title: "Project Delete Files",
957 workspaceRoot: "/tmp/project-delete-files",957 workspaceRoot: "/tmp/project-delete-files",
958 defaultModelSelection: null,958 defaultModelSelection: null,
...@@ -964,17 +964,17 @@ it.layer(Layer.fresh(makeProjectionPipelinePrefixedTestLayer("t3-projection-atta...@@ -964,17 +964,17 @@ it.layer(Layer.fresh(makeProjectionPipelinePrefixedTestLayer("t3-projection-atta
964964
965 yield* appendAndProject({965 yield* appendAndProject({
966 type: "thread.created",966 type: "thread.created",
967 eventId: EventId.makeUnsafe("evt-delete-files-2"),967 eventId: EventId.make("evt-delete-files-2"),
968 aggregateKind: "thread",968 aggregateKind: "thread",
969 aggregateId: threadId,969 aggregateId: threadId,
970 occurredAt: now,970 occurredAt: now,
971 commandId: CommandId.makeUnsafe("cmd-delete-files-2"),971 commandId: CommandId.make("cmd-delete-files-2"),
972 causationEventId: null,972 causationEventId: null,
973 correlationId: CorrelationId.makeUnsafe("cmd-delete-files-2"),973 correlationId: CorrelationId.make("cmd-delete-files-2"),
974 metadata: {},974 metadata: {},
975 payload: {975 payload: {
976 threadId,976 threadId,
977 projectId: ProjectId.makeUnsafe("project-delete-files"),977 projectId: ProjectId.make("project-delete-files"),
978 title: "Thread Delete Files",978 title: "Thread Delete Files",
979 modelSelection: {979 modelSelection: {
980 provider: "codex",980 provider: "codex",
...@@ -990,17 +990,17 @@ it.layer(Layer.fresh(makeProjectionPipelinePrefixedTestLayer("t3-projection-atta...@@ -990,17 +990,17 @@ it.layer(Layer.fresh(makeProjectionPipelinePrefixedTestLayer("t3-projection-atta
990990
991 yield* appendAndProject({991 yield* appendAndProject({
992 type: "thread.message-sent",992 type: "thread.message-sent",
993 eventId: EventId.makeUnsafe("evt-delete-files-3"),993 eventId: EventId.make("evt-delete-files-3"),
994 aggregateKind: "thread",994 aggregateKind: "thread",
995 aggregateId: threadId,995 aggregateId: threadId,
996 occurredAt: now,996 occurredAt: now,
997 commandId: CommandId.makeUnsafe("cmd-delete-files-3"),997 commandId: CommandId.make("cmd-delete-files-3"),
998 causationEventId: null,998 causationEventId: null,
999 correlationId: CorrelationId.makeUnsafe("cmd-delete-files-3"),999 correlationId: CorrelationId.make("cmd-delete-files-3"),
1000 metadata: {},1000 metadata: {},
1001 payload: {1001 payload: {
1002 threadId,1002 threadId,
1003 messageId: MessageId.makeUnsafe("message-delete-files"),1003 messageId: MessageId.make("message-delete-files"),
1004 role: "user",1004 role: "user",
1005 text: "Delete",1005 text: "Delete",
1006 attachments: [1006 attachments: [
...@@ -1032,13 +1032,13 @@ it.layer(Layer.fresh(makeProjectionPipelinePrefixedTestLayer("t3-projection-atta...@@ -1032,13 +1032,13 @@ it.layer(Layer.fresh(makeProjectionPipelinePrefixedTestLayer("t3-projection-atta
10321032
1033 yield* appendAndProject({1033 yield* appendAndProject({
1034 type: "thread.deleted",1034 type: "thread.deleted",
1035 eventId: EventId.makeUnsafe("evt-delete-files-4"),1035 eventId: EventId.make("evt-delete-files-4"),
1036 aggregateKind: "thread",1036 aggregateKind: "thread",
1037 aggregateId: threadId,1037 aggregateId: threadId,
1038 occurredAt: now,1038 occurredAt: now,
1039 commandId: CommandId.makeUnsafe("cmd-delete-files-4"),1039 commandId: CommandId.make("cmd-delete-files-4"),
1040 causationEventId: null,1040 causationEventId: null,
1041 correlationId: CorrelationId.makeUnsafe("cmd-delete-files-4"),1041 correlationId: CorrelationId.make("cmd-delete-files-4"),
1042 metadata: {},1042 metadata: {},
1043 payload: {1043 payload: {
1044 threadId,1044 threadId,
...@@ -1072,16 +1072,16 @@ it.layer(Layer.fresh(makeProjectionPipelinePrefixedTestLayer("t3-projection-atta...@@ -1072,16 +1072,16 @@ it.layer(Layer.fresh(makeProjectionPipelinePrefixedTestLayer("t3-projection-atta
10721072
1073 yield* eventStore.append({1073 yield* eventStore.append({
1074 type: "thread.deleted",1074 type: "thread.deleted",
1075 eventId: EventId.makeUnsafe("evt-unsafe-thread-delete"),1075 eventId: EventId.make("evt-unsafe-thread-delete"),
1076 aggregateKind: "thread",1076 aggregateKind: "thread",
1077 aggregateId: ThreadId.makeUnsafe(".."),1077 aggregateId: ThreadId.make(".."),
1078 occurredAt: now,1078 occurredAt: now,
1079 commandId: CommandId.makeUnsafe("cmd-unsafe-thread-delete"),1079 commandId: CommandId.make("cmd-unsafe-thread-delete"),
1080 causationEventId: null,1080 causationEventId: null,
1081 correlationId: CorrelationId.makeUnsafe("cmd-unsafe-thread-delete"),1081 correlationId: CorrelationId.make("cmd-unsafe-thread-delete"),
1082 metadata: {},1082 metadata: {},
1083 payload: {1083 payload: {
1084 threadId: ThreadId.makeUnsafe(".."),1084 threadId: ThreadId.make(".."),
1085 deletedAt: now,1085 deletedAt: now,
1086 },1086 },
1087 });1087 });
...@@ -1106,16 +1106,16 @@ it.layer(BaseTestLayer)("OrchestrationProjectionPipeline", (it) => {...@@ -1106,16 +1106,16 @@ it.layer(BaseTestLayer)("OrchestrationProjectionPipeline", (it) => {
11061106
1107 yield* eventStore.append({1107 yield* eventStore.append({
1108 type: "project.created",1108 type: "project.created",
1109 eventId: EventId.makeUnsafe("evt-a1"),1109 eventId: EventId.make("evt-a1"),
1110 aggregateKind: "project",1110 aggregateKind: "project",
1111 aggregateId: ProjectId.makeUnsafe("project-a"),1111 aggregateId: ProjectId.make("project-a"),
1112 occurredAt: now,1112 occurredAt: now,
1113 commandId: CommandId.makeUnsafe("cmd-a1"),1113 commandId: CommandId.make("cmd-a1"),
1114 causationEventId: null,1114 causationEventId: null,
1115 correlationId: CorrelationId.makeUnsafe("cmd-a1"),1115 correlationId: CorrelationId.make("cmd-a1"),
1116 metadata: {},1116 metadata: {},
1117 payload: {1117 payload: {
1118 projectId: ProjectId.makeUnsafe("project-a"),1118 projectId: ProjectId.make("project-a"),
1119 title: "Project A",1119 title: "Project A",
1120 workspaceRoot: "/tmp/project-a",1120 workspaceRoot: "/tmp/project-a",
1121 defaultModelSelection: null,1121 defaultModelSelection: null,
...@@ -1127,17 +1127,17 @@ it.layer(BaseTestLayer)("OrchestrationProjectionPipeline", (it) => {...@@ -1127,17 +1127,17 @@ it.layer(BaseTestLayer)("OrchestrationProjectionPipeline", (it) => {
11271127
1128 yield* eventStore.append({1128 yield* eventStore.append({
1129 type: "thread.created",1129 type: "thread.created",
1130 eventId: EventId.makeUnsafe("evt-a2"),1130 eventId: EventId.make("evt-a2"),
1131 aggregateKind: "thread",1131 aggregateKind: "thread",
1132 aggregateId: ThreadId.makeUnsafe("thread-a"),1132 aggregateId: ThreadId.make("thread-a"),
1133 occurredAt: now,1133 occurredAt: now,
1134 commandId: CommandId.makeUnsafe("cmd-a2"),1134 commandId: CommandId.make("cmd-a2"),
1135 causationEventId: null,1135 causationEventId: null,
1136 correlationId: CorrelationId.makeUnsafe("cmd-a2"),1136 correlationId: CorrelationId.make("cmd-a2"),
1137 metadata: {},1137 metadata: {},
1138 payload: {1138 payload: {
1139 threadId: ThreadId.makeUnsafe("thread-a"),1139 threadId: ThreadId.make("thread-a"),
1140 projectId: ProjectId.makeUnsafe("project-a"),1140 projectId: ProjectId.make("project-a"),
1141 title: "Thread A",1141 title: "Thread A",
1142 modelSelection: {1142 modelSelection: {
1143 provider: "codex",1143 provider: "codex",
...@@ -1153,17 +1153,17 @@ it.layer(BaseTestLayer)("OrchestrationProjectionPipeline", (it) => {...@@ -1153,17 +1153,17 @@ it.layer(BaseTestLayer)("OrchestrationProjectionPipeline", (it) => {
11531153
1154 yield* eventStore.append({1154 yield* eventStore.append({
1155 type: "thread.message-sent",1155 type: "thread.message-sent",
1156 eventId: EventId.makeUnsafe("evt-a3"),1156 eventId: EventId.make("evt-a3"),
1157 aggregateKind: "thread",1157 aggregateKind: "thread",
1158 aggregateId: ThreadId.makeUnsafe("thread-a"),1158 aggregateId: ThreadId.make("thread-a"),
1159 occurredAt: now,1159 occurredAt: now,
1160 commandId: CommandId.makeUnsafe("cmd-a3"),1160 commandId: CommandId.make("cmd-a3"),
1161 causationEventId: null,1161 causationEventId: null,
1162 correlationId: CorrelationId.makeUnsafe("cmd-a3"),1162 correlationId: CorrelationId.make("cmd-a3"),
1163 metadata: {},1163 metadata: {},
1164 payload: {1164 payload: {
1165 threadId: ThreadId.makeUnsafe("thread-a"),1165 threadId: ThreadId.make("thread-a"),
1166 messageId: MessageId.makeUnsafe("message-a"),1166 messageId: MessageId.make("message-a"),
1167 role: "assistant",1167 role: "assistant",
1168 text: "hello",1168 text: "hello",
1169 turnId: null,1169 turnId: null,
...@@ -1177,17 +1177,17 @@ it.layer(BaseTestLayer)("OrchestrationProjectionPipeline", (it) => {...@@ -1177,17 +1177,17 @@ it.layer(BaseTestLayer)("OrchestrationProjectionPipeline", (it) => {
11771177
1178 yield* eventStore.append({1178 yield* eventStore.append({
1179 type: "thread.message-sent",1179 type: "thread.message-sent",
1180 eventId: EventId.makeUnsafe("evt-a4"),1180 eventId: EventId.make("evt-a4"),
1181 aggregateKind: "thread",1181 aggregateKind: "thread",
1182 aggregateId: ThreadId.makeUnsafe("thread-a"),1182 aggregateId: ThreadId.make("thread-a"),
1183 occurredAt: now,1183 occurredAt: now,
1184 commandId: CommandId.makeUnsafe("cmd-a4"),1184 commandId: CommandId.make("cmd-a4"),
1185 causationEventId: null,1185 causationEventId: null,
1186 correlationId: CorrelationId.makeUnsafe("cmd-a4"),1186 correlationId: CorrelationId.make("cmd-a4"),
1187 metadata: {},1187 metadata: {},
1188 payload: {1188 payload: {
1189 threadId: ThreadId.makeUnsafe("thread-a"),1189 threadId: ThreadId.make("thread-a"),
1190 messageId: MessageId.makeUnsafe("message-a"),1190 messageId: MessageId.make("message-a"),
1191 role: "assistant",1191 role: "assistant",
1192 text: " world",1192 text: " world",
1193 turnId: null,1193 turnId: null,
...@@ -1233,16 +1233,16 @@ it.layer(BaseTestLayer)("OrchestrationProjectionPipeline", (it) => {...@@ -1233,16 +1233,16 @@ it.layer(BaseTestLayer)("OrchestrationProjectionPipeline", (it) => {
12331233
1234 yield* eventStore.append({1234 yield* eventStore.append({
1235 type: "project.created",1235 type: "project.created",
1236 eventId: EventId.makeUnsafe("evt-empty-1"),1236 eventId: EventId.make("evt-empty-1"),
1237 aggregateKind: "project",1237 aggregateKind: "project",
1238 aggregateId: ProjectId.makeUnsafe("project-empty"),1238 aggregateId: ProjectId.make("project-empty"),
1239 occurredAt: now,1239 occurredAt: now,
1240 commandId: CommandId.makeUnsafe("cmd-empty-1"),1240 commandId: CommandId.make("cmd-empty-1"),
1241 causationEventId: null,1241 causationEventId: null,
1242 correlationId: CorrelationId.makeUnsafe("cmd-empty-1"),1242 correlationId: CorrelationId.make("cmd-empty-1"),
1243 metadata: {},1243 metadata: {},
1244 payload: {1244 payload: {
1245 projectId: ProjectId.makeUnsafe("project-empty"),1245 projectId: ProjectId.make("project-empty"),
1246 title: "Project Empty",1246 title: "Project Empty",
1247 workspaceRoot: "/tmp/project-empty",1247 workspaceRoot: "/tmp/project-empty",
1248 defaultModelSelection: null,1248 defaultModelSelection: null,
...@@ -1254,17 +1254,17 @@ it.layer(BaseTestLayer)("OrchestrationProjectionPipeline", (it) => {...@@ -1254,17 +1254,17 @@ it.layer(BaseTestLayer)("OrchestrationProjectionPipeline", (it) => {
12541254
1255 yield* eventStore.append({1255 yield* eventStore.append({
1256 type: "thread.created",1256 type: "thread.created",
1257 eventId: EventId.makeUnsafe("evt-empty-2"),1257 eventId: EventId.make("evt-empty-2"),
1258 aggregateKind: "thread",1258 aggregateKind: "thread",
1259 aggregateId: ThreadId.makeUnsafe("thread-empty"),1259 aggregateId: ThreadId.make("thread-empty"),
1260 occurredAt: now,1260 occurredAt: now,
1261 commandId: CommandId.makeUnsafe("cmd-empty-2"),1261 commandId: CommandId.make("cmd-empty-2"),
1262 causationEventId: null,1262 causationEventId: null,
1263 correlationId: CorrelationId.makeUnsafe("cmd-empty-2"),1263 correlationId: CorrelationId.make("cmd-empty-2"),
1264 metadata: {},1264 metadata: {},
1265 payload: {1265 payload: {
1266 threadId: ThreadId.makeUnsafe("thread-empty"),1266 threadId: ThreadId.make("thread-empty"),
1267 projectId: ProjectId.makeUnsafe("project-empty"),1267 projectId: ProjectId.make("project-empty"),
1268 title: "Thread Empty",1268 title: "Thread Empty",
1269 modelSelection: {1269 modelSelection: {
1270 provider: "codex",1270 provider: "codex",
...@@ -1280,17 +1280,17 @@ it.layer(BaseTestLayer)("OrchestrationProjectionPipeline", (it) => {...@@ -1280,17 +1280,17 @@ it.layer(BaseTestLayer)("OrchestrationProjectionPipeline", (it) => {
12801280
1281 yield* eventStore.append({1281 yield* eventStore.append({
1282 type: "thread.message-sent",1282 type: "thread.message-sent",
1283 eventId: EventId.makeUnsafe("evt-empty-3"),1283 eventId: EventId.make("evt-empty-3"),
1284 aggregateKind: "thread",1284 aggregateKind: "thread",
1285 aggregateId: ThreadId.makeUnsafe("thread-empty"),1285 aggregateId: ThreadId.make("thread-empty"),
1286 occurredAt: now,1286 occurredAt: now,
1287 commandId: CommandId.makeUnsafe("cmd-empty-3"),1287 commandId: CommandId.make("cmd-empty-3"),
1288 causationEventId: null,1288 causationEventId: null,
1289 correlationId: CorrelationId.makeUnsafe("cmd-empty-3"),1289 correlationId: CorrelationId.make("cmd-empty-3"),
1290 metadata: {},1290 metadata: {},
1291 payload: {1291 payload: {
1292 threadId: ThreadId.makeUnsafe("thread-empty"),1292 threadId: ThreadId.make("thread-empty"),
1293 messageId: MessageId.makeUnsafe("assistant-empty"),1293 messageId: MessageId.make("assistant-empty"),
1294 role: "assistant",1294 role: "assistant",
1295 text: "Hello",1295 text: "Hello",
1296 turnId: null,1296 turnId: null,
...@@ -1302,17 +1302,17 @@ it.layer(BaseTestLayer)("OrchestrationProjectionPipeline", (it) => {...@@ -1302,17 +1302,17 @@ it.layer(BaseTestLayer)("OrchestrationProjectionPipeline", (it) => {
13021302
1303 yield* eventStore.append({1303 yield* eventStore.append({
1304 type: "thread.message-sent",1304 type: "thread.message-sent",
1305 eventId: EventId.makeUnsafe("evt-empty-4"),1305 eventId: EventId.make("evt-empty-4"),
1306 aggregateKind: "thread",1306 aggregateKind: "thread",
1307 aggregateId: ThreadId.makeUnsafe("thread-empty"),1307 aggregateId: ThreadId.make("thread-empty"),
1308 occurredAt: now,1308 occurredAt: now,
1309 commandId: CommandId.makeUnsafe("cmd-empty-4"),1309 commandId: CommandId.make("cmd-empty-4"),
1310 causationEventId: null,1310 causationEventId: null,
1311 correlationId: CorrelationId.makeUnsafe("cmd-empty-4"),1311 correlationId: CorrelationId.make("cmd-empty-4"),
1312 metadata: {},1312 metadata: {},
1313 payload: {1313 payload: {
1314 threadId: ThreadId.makeUnsafe("thread-empty"),1314 threadId: ThreadId.make("thread-empty"),
1315 messageId: MessageId.makeUnsafe("assistant-empty"),1315 messageId: MessageId.make("assistant-empty"),
1316 role: "assistant",1316 role: "assistant",
1317 text: " world",1317 text: " world",
1318 turnId: null,1318 turnId: null,
...@@ -1324,17 +1324,17 @@ it.layer(BaseTestLayer)("OrchestrationProjectionPipeline", (it) => {...@@ -1324,17 +1324,17 @@ it.layer(BaseTestLayer)("OrchestrationProjectionPipeline", (it) => {
13241324
1325 yield* eventStore.append({1325 yield* eventStore.append({
1326 type: "thread.message-sent",1326 type: "thread.message-sent",
1327 eventId: EventId.makeUnsafe("evt-empty-5"),1327 eventId: EventId.make("evt-empty-5"),
1328 aggregateKind: "thread",1328 aggregateKind: "thread",
1329 aggregateId: ThreadId.makeUnsafe("thread-empty"),1329 aggregateId: ThreadId.make("thread-empty"),
1330 occurredAt: now,1330 occurredAt: now,
1331 commandId: CommandId.makeUnsafe("cmd-empty-5"),1331 commandId: CommandId.make("cmd-empty-5"),
1332 causationEventId: null,1332 causationEventId: null,
1333 correlationId: CorrelationId.makeUnsafe("cmd-empty-5"),1333 correlationId: CorrelationId.make("cmd-empty-5"),
1334 metadata: {},1334 metadata: {},
1335 payload: {1335 payload: {
1336 threadId: ThreadId.makeUnsafe("thread-empty"),1336 threadId: ThreadId.make("thread-empty"),
1337 messageId: MessageId.makeUnsafe("assistant-empty"),1337 messageId: MessageId.make("assistant-empty"),
1338 role: "assistant",1338 role: "assistant",
1339 text: "",1339 text: "",
1340 turnId: null,1340 turnId: null,
...@@ -1373,16 +1373,16 @@ it.layer(BaseTestLayer)("OrchestrationProjectionPipeline", (it) => {...@@ -1373,16 +1373,16 @@ it.layer(BaseTestLayer)("OrchestrationProjectionPipeline", (it) => {
13731373
1374 yield* appendAndProject({1374 yield* appendAndProject({
1375 type: "project.created",1375 type: "project.created",
1376 eventId: EventId.makeUnsafe("evt-conflict-1"),1376 eventId: EventId.make("evt-conflict-1"),
1377 aggregateKind: "project",1377 aggregateKind: "project",
1378 aggregateId: ProjectId.makeUnsafe("project-conflict"),1378 aggregateId: ProjectId.make("project-conflict"),
1379 occurredAt: "2026-02-26T13:00:00.000Z",1379 occurredAt: "2026-02-26T13:00:00.000Z",
1380 commandId: CommandId.makeUnsafe("cmd-conflict-1"),1380 commandId: CommandId.make("cmd-conflict-1"),
1381 causationEventId: null,1381 causationEventId: null,
1382 correlationId: CorrelationId.makeUnsafe("cmd-conflict-1"),1382 correlationId: CorrelationId.make("cmd-conflict-1"),
1383 metadata: {},1383 metadata: {},
1384 payload: {1384 payload: {
1385 projectId: ProjectId.makeUnsafe("project-conflict"),1385 projectId: ProjectId.make("project-conflict"),
1386 title: "Project Conflict",1386 title: "Project Conflict",
1387 workspaceRoot: "/tmp/project-conflict",1387 workspaceRoot: "/tmp/project-conflict",
1388 defaultModelSelection: null,1388 defaultModelSelection: null,
...@@ -1394,17 +1394,17 @@ it.layer(BaseTestLayer)("OrchestrationProjectionPipeline", (it) => {...@@ -1394,17 +1394,17 @@ it.layer(BaseTestLayer)("OrchestrationProjectionPipeline", (it) => {
13941394
1395 yield* appendAndProject({1395 yield* appendAndProject({
1396 type: "thread.created",1396 type: "thread.created",
1397 eventId: EventId.makeUnsafe("evt-conflict-2"),1397 eventId: EventId.make("evt-conflict-2"),
1398 aggregateKind: "thread",1398 aggregateKind: "thread",
1399 aggregateId: ThreadId.makeUnsafe("thread-conflict"),1399 aggregateId: ThreadId.make("thread-conflict"),
1400 occurredAt: "2026-02-26T13:00:01.000Z",1400 occurredAt: "2026-02-26T13:00:01.000Z",
1401 commandId: CommandId.makeUnsafe("cmd-conflict-2"),1401 commandId: CommandId.make("cmd-conflict-2"),
1402 causationEventId: null,1402 causationEventId: null,
1403 correlationId: CorrelationId.makeUnsafe("cmd-conflict-2"),1403 correlationId: CorrelationId.make("cmd-conflict-2"),
1404 metadata: {},1404 metadata: {},
1405 payload: {1405 payload: {
1406 threadId: ThreadId.makeUnsafe("thread-conflict"),1406 threadId: ThreadId.make("thread-conflict"),
1407 projectId: ProjectId.makeUnsafe("project-conflict"),1407 projectId: ProjectId.make("project-conflict"),
1408 title: "Thread Conflict",1408 title: "Thread Conflict",
1409 modelSelection: {1409 modelSelection: {
1410 provider: "codex",1410 provider: "codex",
...@@ -1420,37 +1420,37 @@ it.layer(BaseTestLayer)("OrchestrationProjectionPipeline", (it) => {...@@ -1420,37 +1420,37 @@ it.layer(BaseTestLayer)("OrchestrationProjectionPipeline", (it) => {
14201420
1421 yield* appendAndProject({1421 yield* appendAndProject({
1422 type: "thread.turn-interrupt-requested",1422 type: "thread.turn-interrupt-requested",
1423 eventId: EventId.makeUnsafe("evt-conflict-3"),1423 eventId: EventId.make("evt-conflict-3"),
1424 aggregateKind: "thread",1424 aggregateKind: "thread",
1425 aggregateId: ThreadId.makeUnsafe("thread-conflict"),1425 aggregateId: ThreadId.make("thread-conflict"),
1426 occurredAt: "2026-02-26T13:00:02.000Z",1426 occurredAt: "2026-02-26T13:00:02.000Z",
1427 commandId: CommandId.makeUnsafe("cmd-conflict-3"),1427 commandId: CommandId.make("cmd-conflict-3"),
1428 causationEventId: null,1428 causationEventId: null,
1429 correlationId: CorrelationId.makeUnsafe("cmd-conflict-3"),1429 correlationId: CorrelationId.make("cmd-conflict-3"),
1430 metadata: {},1430 metadata: {},
1431 payload: {1431 payload: {
1432 threadId: ThreadId.makeUnsafe("thread-conflict"),1432 threadId: ThreadId.make("thread-conflict"),
1433 turnId: TurnId.makeUnsafe("turn-interrupted"),1433 turnId: TurnId.make("turn-interrupted"),
1434 createdAt: "2026-02-26T13:00:02.000Z",1434 createdAt: "2026-02-26T13:00:02.000Z",
1435 },1435 },
1436 });1436 });
14371437
1438 yield* appendAndProject({1438 yield* appendAndProject({
1439 type: "thread.message-sent",1439 type: "thread.message-sent",
1440 eventId: EventId.makeUnsafe("evt-conflict-4"),1440 eventId: EventId.make("evt-conflict-4"),
1441 aggregateKind: "thread",1441 aggregateKind: "thread",
1442 aggregateId: ThreadId.makeUnsafe("thread-conflict"),1442 aggregateId: ThreadId.make("thread-conflict"),
1443 occurredAt: "2026-02-26T13:00:03.000Z",1443 occurredAt: "2026-02-26T13:00:03.000Z",
1444 commandId: CommandId.makeUnsafe("cmd-conflict-4"),1444 commandId: CommandId.make("cmd-conflict-4"),
1445 causationEventId: null,1445 causationEventId: null,
1446 correlationId: CorrelationId.makeUnsafe("cmd-conflict-4"),1446 correlationId: CorrelationId.make("cmd-conflict-4"),
1447 metadata: {},1447 metadata: {},
1448 payload: {1448 payload: {
1449 threadId: ThreadId.makeUnsafe("thread-conflict"),1449 threadId: ThreadId.make("thread-conflict"),
1450 messageId: MessageId.makeUnsafe("assistant-conflict"),1450 messageId: MessageId.make("assistant-conflict"),
1451 role: "assistant",1451 role: "assistant",
1452 text: "done",1452 text: "done",
1453 turnId: TurnId.makeUnsafe("turn-completed"),1453 turnId: TurnId.make("turn-completed"),
1454 streaming: false,1454 streaming: false,
1455 createdAt: "2026-02-26T13:00:03.000Z",1455 createdAt: "2026-02-26T13:00:03.000Z",
1456 updatedAt: "2026-02-26T13:00:03.000Z",1456 updatedAt: "2026-02-26T13:00:03.000Z",
...@@ -1459,22 +1459,22 @@ it.layer(BaseTestLayer)("OrchestrationProjectionPipeline", (it) => {...@@ -1459,22 +1459,22 @@ it.layer(BaseTestLayer)("OrchestrationProjectionPipeline", (it) => {
14591459
1460 yield* appendAndProject({1460 yield* appendAndProject({
1461 type: "thread.turn-diff-completed",1461 type: "thread.turn-diff-completed",
1462 eventId: EventId.makeUnsafe("evt-conflict-5"),1462 eventId: EventId.make("evt-conflict-5"),
1463 aggregateKind: "thread",1463 aggregateKind: "thread",
1464 aggregateId: ThreadId.makeUnsafe("thread-conflict"),1464 aggregateId: ThreadId.make("thread-conflict"),
1465 occurredAt: "2026-02-26T13:00:04.000Z",1465 occurredAt: "2026-02-26T13:00:04.000Z",
1466 commandId: CommandId.makeUnsafe("cmd-conflict-5"),1466 commandId: CommandId.make("cmd-conflict-5"),
1467 causationEventId: null,1467 causationEventId: null,
1468 correlationId: CorrelationId.makeUnsafe("cmd-conflict-5"),1468 correlationId: CorrelationId.make("cmd-conflict-5"),
1469 metadata: {},1469 metadata: {},
1470 payload: {1470 payload: {
1471 threadId: ThreadId.makeUnsafe("thread-conflict"),1471 threadId: ThreadId.make("thread-conflict"),
1472 turnId: TurnId.makeUnsafe("turn-completed"),1472 turnId: TurnId.make("turn-completed"),
1473 checkpointTurnCount: 1,1473 checkpointTurnCount: 1,
1474 checkpointRef: CheckpointRef.makeUnsafe("refs/t3/checkpoints/thread-conflict/turn/1"),1474 checkpointRef: CheckpointRef.make("refs/t3/checkpoints/thread-conflict/turn/1"),
1475 status: "ready",1475 status: "ready",
1476 files: [],1476 files: [],
1477 assistantMessageId: MessageId.makeUnsafe("assistant-conflict"),1477 assistantMessageId: MessageId.make("assistant-conflict"),
1478 completedAt: "2026-02-26T13:00:04.000Z",1478 completedAt: "2026-02-26T13:00:04.000Z",
1479 },1479 },
1480 });1480 });
...@@ -1517,16 +1517,16 @@ it.layer(BaseTestLayer)("OrchestrationProjectionPipeline", (it) => {...@@ -1517,16 +1517,16 @@ it.layer(BaseTestLayer)("OrchestrationProjectionPipeline", (it) => {
15171517
1518 yield* appendAndProject({1518 yield* appendAndProject({
1519 type: "project.created",1519 type: "project.created",
1520 eventId: EventId.makeUnsafe("evt-revert-1"),1520 eventId: EventId.make("evt-revert-1"),
1521 aggregateKind: "project",1521 aggregateKind: "project",
1522 aggregateId: ProjectId.makeUnsafe("project-revert"),1522 aggregateId: ProjectId.make("project-revert"),
1523 occurredAt: "2026-02-26T12:00:00.000Z",1523 occurredAt: "2026-02-26T12:00:00.000Z",
1524 commandId: CommandId.makeUnsafe("cmd-revert-1"),1524 commandId: CommandId.make("cmd-revert-1"),
1525 causationEventId: null,1525 causationEventId: null,
1526 correlationId: CorrelationId.makeUnsafe("cmd-revert-1"),1526 correlationId: CorrelationId.make("cmd-revert-1"),
1527 metadata: {},1527 metadata: {},
1528 payload: {1528 payload: {
1529 projectId: ProjectId.makeUnsafe("project-revert"),1529 projectId: ProjectId.make("project-revert"),
1530 title: "Project Revert",1530 title: "Project Revert",
1531 workspaceRoot: "/tmp/project-revert",1531 workspaceRoot: "/tmp/project-revert",
1532 defaultModelSelection: null,1532 defaultModelSelection: null,
...@@ -1538,17 +1538,17 @@ it.layer(BaseTestLayer)("OrchestrationProjectionPipeline", (it) => {...@@ -1538,17 +1538,17 @@ it.layer(BaseTestLayer)("OrchestrationProjectionPipeline", (it) => {
15381538
1539 yield* appendAndProject({1539 yield* appendAndProject({
1540 type: "thread.created",1540 type: "thread.created",
1541 eventId: EventId.makeUnsafe("evt-revert-2"),1541 eventId: EventId.make("evt-revert-2"),
1542 aggregateKind: "thread",1542 aggregateKind: "thread",
1543 aggregateId: ThreadId.makeUnsafe("thread-revert"),1543 aggregateId: ThreadId.make("thread-revert"),
1544 occurredAt: "2026-02-26T12:00:01.000Z",1544 occurredAt: "2026-02-26T12:00:01.000Z",
1545 commandId: CommandId.makeUnsafe("cmd-revert-2"),1545 commandId: CommandId.make("cmd-revert-2"),
1546 causationEventId: null,1546 causationEventId: null,
1547 correlationId: CorrelationId.makeUnsafe("cmd-revert-2"),1547 correlationId: CorrelationId.make("cmd-revert-2"),
1548 metadata: {},1548 metadata: {},
1549 payload: {1549 payload: {
1550 threadId: ThreadId.makeUnsafe("thread-revert"),1550 threadId: ThreadId.make("thread-revert"),
1551 projectId: ProjectId.makeUnsafe("project-revert"),1551 projectId: ProjectId.make("project-revert"),
1552 title: "Thread Revert",1552 title: "Thread Revert",
1553 modelSelection: {1553 modelSelection: {
1554 provider: "codex",1554 provider: "codex",
...@@ -1564,42 +1564,42 @@ it.layer(BaseTestLayer)("OrchestrationProjectionPipeline", (it) => {...@@ -1564,42 +1564,42 @@ it.layer(BaseTestLayer)("OrchestrationProjectionPipeline", (it) => {
15641564
1565 yield* appendAndProject({1565 yield* appendAndProject({
1566 type: "thread.turn-diff-completed",1566 type: "thread.turn-diff-completed",
1567 eventId: EventId.makeUnsafe("evt-revert-3"),1567 eventId: EventId.make("evt-revert-3"),
1568 aggregateKind: "thread",1568 aggregateKind: "thread",
1569 aggregateId: ThreadId.makeUnsafe("thread-revert"),1569 aggregateId: ThreadId.make("thread-revert"),
1570 occurredAt: "2026-02-26T12:00:02.000Z",1570 occurredAt: "2026-02-26T12:00:02.000Z",
1571 commandId: CommandId.makeUnsafe("cmd-revert-3"),1571 commandId: CommandId.make("cmd-revert-3"),
1572 causationEventId: null,1572 causationEventId: null,
1573 correlationId: CorrelationId.makeUnsafe("cmd-revert-3"),1573 correlationId: CorrelationId.make("cmd-revert-3"),
1574 metadata: {},1574 metadata: {},
1575 payload: {1575 payload: {
1576 threadId: ThreadId.makeUnsafe("thread-revert"),1576 threadId: ThreadId.make("thread-revert"),
1577 turnId: TurnId.makeUnsafe("turn-1"),1577 turnId: TurnId.make("turn-1"),
1578 checkpointTurnCount: 1,1578 checkpointTurnCount: 1,
1579 checkpointRef: CheckpointRef.makeUnsafe("refs/t3/checkpoints/thread-revert/turn/1"),1579 checkpointRef: CheckpointRef.make("refs/t3/checkpoints/thread-revert/turn/1"),
1580 status: "ready",1580 status: "ready",
1581 files: [],1581 files: [],
1582 assistantMessageId: MessageId.makeUnsafe("assistant-keep"),1582 assistantMessageId: MessageId.make("assistant-keep"),
1583 completedAt: "2026-02-26T12:00:02.000Z",1583 completedAt: "2026-02-26T12:00:02.000Z",
1584 },1584 },
1585 });1585 });
15861586
1587 yield* appendAndProject({1587 yield* appendAndProject({
1588 type: "thread.message-sent",1588 type: "thread.message-sent",
1589 eventId: EventId.makeUnsafe("evt-revert-4"),1589 eventId: EventId.make("evt-revert-4"),
1590 aggregateKind: "thread",1590 aggregateKind: "thread",
1591 aggregateId: ThreadId.makeUnsafe("thread-revert"),1591 aggregateId: ThreadId.make("thread-revert"),
1592 occurredAt: "2026-02-26T12:00:02.100Z",1592 occurredAt: "2026-02-26T12:00:02.100Z",
1593 commandId: CommandId.makeUnsafe("cmd-revert-4"),1593 commandId: CommandId.make("cmd-revert-4"),
1594 causationEventId: null,1594 causationEventId: null,
1595 correlationId: CorrelationId.makeUnsafe("cmd-revert-4"),1595 correlationId: CorrelationId.make("cmd-revert-4"),
1596 metadata: {},1596 metadata: {},
1597 payload: {1597 payload: {
1598 threadId: ThreadId.makeUnsafe("thread-revert"),1598 threadId: ThreadId.make("thread-revert"),
1599 messageId: MessageId.makeUnsafe("assistant-keep"),1599 messageId: MessageId.make("assistant-keep"),
1600 role: "assistant",1600 role: "assistant",
1601 text: "kept",1601 text: "kept",
1602 turnId: TurnId.makeUnsafe("turn-1"),1602 turnId: TurnId.make("turn-1"),
1603 streaming: false,1603 streaming: false,
1604 createdAt: "2026-02-26T12:00:02.100Z",1604 createdAt: "2026-02-26T12:00:02.100Z",
1605 updatedAt: "2026-02-26T12:00:02.100Z",1605 updatedAt: "2026-02-26T12:00:02.100Z",
...@@ -1608,42 +1608,42 @@ it.layer(BaseTestLayer)("OrchestrationProjectionPipeline", (it) => {...@@ -1608,42 +1608,42 @@ it.layer(BaseTestLayer)("OrchestrationProjectionPipeline", (it) => {
16081608
1609 yield* appendAndProject({1609 yield* appendAndProject({
1610 type: "thread.turn-diff-completed",1610 type: "thread.turn-diff-completed",
1611 eventId: EventId.makeUnsafe("evt-revert-5"),1611 eventId: EventId.make("evt-revert-5"),
1612 aggregateKind: "thread",1612 aggregateKind: "thread",
1613 aggregateId: ThreadId.makeUnsafe("thread-revert"),1613 aggregateId: ThreadId.make("thread-revert"),
1614 occurredAt: "2026-02-26T12:00:03.000Z",1614 occurredAt: "2026-02-26T12:00:03.000Z",
1615 commandId: CommandId.makeUnsafe("cmd-revert-5"),1615 commandId: CommandId.make("cmd-revert-5"),
1616 causationEventId: null,1616 causationEventId: null,
1617 correlationId: CorrelationId.makeUnsafe("cmd-revert-5"),1617 correlationId: CorrelationId.make("cmd-revert-5"),
1618 metadata: {},1618 metadata: {},
1619 payload: {1619 payload: {
1620 threadId: ThreadId.makeUnsafe("thread-revert"),1620 threadId: ThreadId.make("thread-revert"),
1621 turnId: TurnId.makeUnsafe("turn-2"),1621 turnId: TurnId.make("turn-2"),
1622 checkpointTurnCount: 2,1622 checkpointTurnCount: 2,
1623 checkpointRef: CheckpointRef.makeUnsafe("refs/t3/checkpoints/thread-revert/turn/2"),1623 checkpointRef: CheckpointRef.make("refs/t3/checkpoints/thread-revert/turn/2"),
1624 status: "ready",1624 status: "ready",
1625 files: [],1625 files: [],
1626 assistantMessageId: MessageId.makeUnsafe("assistant-remove"),1626 assistantMessageId: MessageId.make("assistant-remove"),
1627 completedAt: "2026-02-26T12:00:03.000Z",1627 completedAt: "2026-02-26T12:00:03.000Z",
1628 },1628 },
1629 });1629 });
16301630
1631 yield* appendAndProject({1631 yield* appendAndProject({
1632 type: "thread.message-sent",1632 type: "thread.message-sent",
1633 eventId: EventId.makeUnsafe("evt-revert-6"),1633 eventId: EventId.make("evt-revert-6"),
1634 aggregateKind: "thread",1634 aggregateKind: "thread",
1635 aggregateId: ThreadId.makeUnsafe("thread-revert"),1635 aggregateId: ThreadId.make("thread-revert"),
1636 occurredAt: "2026-02-26T12:00:03.050Z",1636 occurredAt: "2026-02-26T12:00:03.050Z",
1637 commandId: CommandId.makeUnsafe("cmd-revert-6"),1637 commandId: CommandId.make("cmd-revert-6"),
1638 causationEventId: null,1638 causationEventId: null,
1639 correlationId: CorrelationId.makeUnsafe("cmd-revert-6"),1639 correlationId: CorrelationId.make("cmd-revert-6"),
1640 metadata: {},1640 metadata: {},
1641 payload: {1641 payload: {
1642 threadId: ThreadId.makeUnsafe("thread-revert"),1642 threadId: ThreadId.make("thread-revert"),
1643 messageId: MessageId.makeUnsafe("user-remove"),1643 messageId: MessageId.make("user-remove"),
1644 role: "user",1644 role: "user",
1645 text: "removed",1645 text: "removed",
1646 turnId: TurnId.makeUnsafe("turn-2"),1646 turnId: TurnId.make("turn-2"),
1647 streaming: false,1647 streaming: false,
1648 createdAt: "2026-02-26T12:00:03.050Z",1648 createdAt: "2026-02-26T12:00:03.050Z",
1649 updatedAt: "2026-02-26T12:00:03.050Z",1649 updatedAt: "2026-02-26T12:00:03.050Z",
...@@ -1652,20 +1652,20 @@ it.layer(BaseTestLayer)("OrchestrationProjectionPipeline", (it) => {...@@ -1652,20 +1652,20 @@ it.layer(BaseTestLayer)("OrchestrationProjectionPipeline", (it) => {
16521652
1653 yield* appendAndProject({1653 yield* appendAndProject({
1654 type: "thread.message-sent",1654 type: "thread.message-sent",
1655 eventId: EventId.makeUnsafe("evt-revert-7"),1655 eventId: EventId.make("evt-revert-7"),
1656 aggregateKind: "thread",1656 aggregateKind: "thread",
1657 aggregateId: ThreadId.makeUnsafe("thread-revert"),1657 aggregateId: ThreadId.make("thread-revert"),
1658 occurredAt: "2026-02-26T12:00:03.100Z",1658 occurredAt: "2026-02-26T12:00:03.100Z",
1659 commandId: CommandId.makeUnsafe("cmd-revert-7"),1659 commandId: CommandId.make("cmd-revert-7"),
1660 causationEventId: null,1660 causationEventId: null,
1661 correlationId: CorrelationId.makeUnsafe("cmd-revert-7"),1661 correlationId: CorrelationId.make("cmd-revert-7"),
1662 metadata: {},1662 metadata: {},
1663 payload: {1663 payload: {
1664 threadId: ThreadId.makeUnsafe("thread-revert"),1664 threadId: ThreadId.make("thread-revert"),
1665 messageId: MessageId.makeUnsafe("assistant-remove"),1665 messageId: MessageId.make("assistant-remove"),
1666 role: "assistant",1666 role: "assistant",
1667 text: "removed",1667 text: "removed",
1668 turnId: TurnId.makeUnsafe("turn-2"),1668 turnId: TurnId.make("turn-2"),
1669 streaming: false,1669 streaming: false,
1670 createdAt: "2026-02-26T12:00:03.100Z",1670 createdAt: "2026-02-26T12:00:03.100Z",
1671 updatedAt: "2026-02-26T12:00:03.100Z",1671 updatedAt: "2026-02-26T12:00:03.100Z",
...@@ -1674,16 +1674,16 @@ it.layer(BaseTestLayer)("OrchestrationProjectionPipeline", (it) => {...@@ -1674,16 +1674,16 @@ it.layer(BaseTestLayer)("OrchestrationProjectionPipeline", (it) => {
16741674
1675 yield* appendAndProject({1675 yield* appendAndProject({
1676 type: "thread.reverted",1676 type: "thread.reverted",
1677 eventId: EventId.makeUnsafe("evt-revert-8"),1677 eventId: EventId.make("evt-revert-8"),
1678 aggregateKind: "thread",1678 aggregateKind: "thread",
1679 aggregateId: ThreadId.makeUnsafe("thread-revert"),1679 aggregateId: ThreadId.make("thread-revert"),
1680 occurredAt: "2026-02-26T12:00:04.000Z",1680 occurredAt: "2026-02-26T12:00:04.000Z",
1681 commandId: CommandId.makeUnsafe("cmd-revert-8"),1681 commandId: CommandId.make("cmd-revert-8"),
1682 causationEventId: null,1682 causationEventId: null,
1683 correlationId: CorrelationId.makeUnsafe("cmd-revert-8"),1683 correlationId: CorrelationId.make("cmd-revert-8"),
1684 metadata: {},1684 metadata: {},
1685 payload: {1685 payload: {
1686 threadId: ThreadId.makeUnsafe("thread-revert"),1686 threadId: ThreadId.make("thread-revert"),
1687 turnCount: 1,1687 turnCount: 1,
1688 },1688 },
1689 });1689 });
...@@ -1725,10 +1725,10 @@ it.effect("restores pending turn-start metadata across projection pipeline resta...@@ -1725,10 +1725,10 @@ it.effect("restores pending turn-start metadata across projection pipeline resta
1725 Layer.provideMerge(persistenceLayer),1725 Layer.provideMerge(persistenceLayer),
1726 );1726 );
17271727
1728 const threadId = ThreadId.makeUnsafe("thread-restart");1728 const threadId = ThreadId.make("thread-restart");
1729 const turnId = TurnId.makeUnsafe("turn-restart");1729 const turnId = TurnId.make("turn-restart");
1730 const messageId = MessageId.makeUnsafe("message-restart");1730 const messageId = MessageId.make("message-restart");
1731 const sourcePlanThreadId = ThreadId.makeUnsafe("thread-plan-source");1731 const sourcePlanThreadId = ThreadId.make("thread-plan-source");
1732 const sourcePlanId = "plan-source";1732 const sourcePlanId = "plan-source";
1733 const turnStartedAt = "2026-02-26T14:00:00.000Z";1733 const turnStartedAt = "2026-02-26T14:00:00.000Z";
1734 const sessionSetAt = "2026-02-26T14:00:05.000Z";1734 const sessionSetAt = "2026-02-26T14:00:05.000Z";
...@@ -1739,13 +1739,13 @@ it.effect("restores pending turn-start metadata across projection pipeline resta...@@ -1739,13 +1739,13 @@ it.effect("restores pending turn-start metadata across projection pipeline resta
17391739
1740 yield* eventStore.append({1740 yield* eventStore.append({
1741 type: "thread.turn-start-requested",1741 type: "thread.turn-start-requested",
1742 eventId: EventId.makeUnsafe("evt-restart-1"),1742 eventId: EventId.make("evt-restart-1"),
1743 aggregateKind: "thread",1743 aggregateKind: "thread",
1744 aggregateId: threadId,1744 aggregateId: threadId,
1745 occurredAt: turnStartedAt,1745 occurredAt: turnStartedAt,
1746 commandId: CommandId.makeUnsafe("cmd-restart-1"),1746 commandId: CommandId.make("cmd-restart-1"),
1747 causationEventId: null,1747 causationEventId: null,
1748 correlationId: CorrelationId.makeUnsafe("cmd-restart-1"),1748 correlationId: CorrelationId.make("cmd-restart-1"),
1749 metadata: {},1749 metadata: {},
1750 payload: {1750 payload: {
1751 threadId,1751 threadId,
...@@ -1769,13 +1769,13 @@ it.effect("restores pending turn-start metadata across projection pipeline resta...@@ -1769,13 +1769,13 @@ it.effect("restores pending turn-start metadata across projection pipeline resta
17691769
1770 yield* eventStore.append({1770 yield* eventStore.append({
1771 type: "thread.session-set",1771 type: "thread.session-set",
1772 eventId: EventId.makeUnsafe("evt-restart-2"),1772 eventId: EventId.make("evt-restart-2"),
1773 aggregateKind: "thread",1773 aggregateKind: "thread",
1774 aggregateId: threadId,1774 aggregateId: threadId,
1775 occurredAt: sessionSetAt,1775 occurredAt: sessionSetAt,
1776 commandId: CommandId.makeUnsafe("cmd-restart-2"),1776 commandId: CommandId.make("cmd-restart-2"),
1777 causationEventId: null,1777 causationEventId: null,
1778 correlationId: CorrelationId.makeUnsafe("cmd-restart-2"),1778 correlationId: CorrelationId.make("cmd-restart-2"),
1779 metadata: {},1779 metadata: {},
1780 payload: {1780 payload: {
1781 threadId,1781 threadId,
...@@ -1867,8 +1867,8 @@ engineLayer("OrchestrationProjectionPipeline via engine dispatch", (it) => {...@@ -1867,8 +1867,8 @@ engineLayer("OrchestrationProjectionPipeline via engine dispatch", (it) => {
18671867
1868 yield* engine.dispatch({1868 yield* engine.dispatch({
1869 type: "project.create",1869 type: "project.create",
1870 commandId: CommandId.makeUnsafe("cmd-live-project"),1870 commandId: CommandId.make("cmd-live-project"),
1871 projectId: ProjectId.makeUnsafe("project-live"),1871 projectId: ProjectId.make("project-live"),
1872 title: "Live Project",1872 title: "Live Project",
1873 workspaceRoot: "/tmp/project-live",1873 workspaceRoot: "/tmp/project-live",
1874 defaultModelSelection: {1874 defaultModelSelection: {
...@@ -1905,8 +1905,8 @@ engineLayer("OrchestrationProjectionPipeline via engine dispatch", (it) => {...@@ -1905,8 +1905,8 @@ engineLayer("OrchestrationProjectionPipeline via engine dispatch", (it) => {
19051905
1906 yield* engine.dispatch({1906 yield* engine.dispatch({
1907 type: "project.create",1907 type: "project.create",
1908 commandId: CommandId.makeUnsafe("cmd-scripts-project-create"),1908 commandId: CommandId.make("cmd-scripts-project-create"),
1909 projectId: ProjectId.makeUnsafe("project-scripts"),1909 projectId: ProjectId.make("project-scripts"),
1910 title: "Scripts Project",1910 title: "Scripts Project",
1911 workspaceRoot: "/tmp/project-scripts",1911 workspaceRoot: "/tmp/project-scripts",
1912 defaultModelSelection: {1912 defaultModelSelection: {
...@@ -1918,8 +1918,8 @@ engineLayer("OrchestrationProjectionPipeline via engine dispatch", (it) => {...@@ -1918,8 +1918,8 @@ engineLayer("OrchestrationProjectionPipeline via engine dispatch", (it) => {
19181918
1919 yield* engine.dispatch({1919 yield* engine.dispatch({
1920 type: "project.meta.update",1920 type: "project.meta.update",
1921 commandId: CommandId.makeUnsafe("cmd-scripts-project-update"),1921 commandId: CommandId.make("cmd-scripts-project-update"),
1922 projectId: ProjectId.makeUnsafe("project-scripts"),1922 projectId: ProjectId.make("project-scripts"),
1923 scripts: [1923 scripts: [
1924 {1924 {
1925 id: "script-1",1925 id: "script-1",
apps/server/src/orchestration/Layers/ProjectionPipeline.ts+1-1
...@@ -86,7 +86,7 @@ function extractActivityRequestId(payload: unknown): ApprovalRequestId | null {...@@ -86,7 +86,7 @@ function extractActivityRequestId(payload: unknown): ApprovalRequestId | null {
86 return null;86 return null;
87 }87 }
88 const requestId = (payload as Record<string, unknown>).requestId;88 const requestId = (payload as Record<string, unknown>).requestId;
89 return typeof requestId === "string" ? ApprovalRequestId.makeUnsafe(requestId) : null;89 return typeof requestId === "string" ? ApprovalRequestId.make(requestId) : null;
90}90}
9191
92function retainProjectionMessagesAfterRevert(92function retainProjectionMessagesAfterRevert(
apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.test.ts+12-12
...@@ -9,11 +9,11 @@ import { ORCHESTRATION_PROJECTOR_NAMES } from "./ProjectionPipeline.ts";...@@ -9,11 +9,11 @@ import { ORCHESTRATION_PROJECTOR_NAMES } from "./ProjectionPipeline.ts";
9import { OrchestrationProjectionSnapshotQueryLive } from "./ProjectionSnapshotQuery.ts";9import { OrchestrationProjectionSnapshotQueryLive } from "./ProjectionSnapshotQuery.ts";
10import { ProjectionSnapshotQuery } from "../Services/ProjectionSnapshotQuery.ts";10import { ProjectionSnapshotQuery } from "../Services/ProjectionSnapshotQuery.ts";
1111
12const asProjectId = (value: string): ProjectId => ProjectId.makeUnsafe(value);12const asProjectId = (value: string): ProjectId => ProjectId.make(value);
13const asTurnId = (value: string): TurnId => TurnId.makeUnsafe(value);13const asTurnId = (value: string): TurnId => TurnId.make(value);
14const asMessageId = (value: string): MessageId => MessageId.makeUnsafe(value);14const asMessageId = (value: string): MessageId => MessageId.make(value);
15const asEventId = (value: string): EventId => EventId.makeUnsafe(value);15const asEventId = (value: string): EventId => EventId.make(value);
16const asCheckpointRef = (value: string): CheckpointRef => CheckpointRef.makeUnsafe(value);16const asCheckpointRef = (value: string): CheckpointRef => CheckpointRef.make(value);
1717
18const projectionSnapshotLayer = it.layer(18const projectionSnapshotLayer = it.layer(
19 OrchestrationProjectionSnapshotQueryLive.pipe(19 OrchestrationProjectionSnapshotQueryLive.pipe(
...@@ -259,7 +259,7 @@ projectionSnapshotLayer("ProjectionSnapshotQuery", (it) => {...@@ -259,7 +259,7 @@ projectionSnapshotLayer("ProjectionSnapshotQuery", (it) => {
259 ]);259 ]);
260 assert.deepEqual(snapshot.threads, [260 assert.deepEqual(snapshot.threads, [
261 {261 {
262 id: ThreadId.makeUnsafe("thread-1"),262 id: ThreadId.make("thread-1"),
263 projectId: asProjectId("project-1"),263 projectId: asProjectId("project-1"),
264 title: "Thread 1",264 title: "Thread 1",
265 modelSelection: {265 modelSelection: {
...@@ -278,7 +278,7 @@ projectionSnapshotLayer("ProjectionSnapshotQuery", (it) => {...@@ -278,7 +278,7 @@ projectionSnapshotLayer("ProjectionSnapshotQuery", (it) => {
278 completedAt: "2026-02-24T00:00:08.000Z",278 completedAt: "2026-02-24T00:00:08.000Z",
279 assistantMessageId: asMessageId("message-1"),279 assistantMessageId: asMessageId("message-1"),
280 sourceProposedPlan: {280 sourceProposedPlan: {
281 threadId: ThreadId.makeUnsafe("thread-1"),281 threadId: ThreadId.make("thread-1"),
282 planId: "plan-1",282 planId: "plan-1",
283 },283 },
284 },284 },
...@@ -303,7 +303,7 @@ projectionSnapshotLayer("ProjectionSnapshotQuery", (it) => {...@@ -303,7 +303,7 @@ projectionSnapshotLayer("ProjectionSnapshotQuery", (it) => {
303 turnId: asTurnId("turn-1"),303 turnId: asTurnId("turn-1"),
304 planMarkdown: "# Ship it",304 planMarkdown: "# Ship it",
305 implementedAt: "2026-02-24T00:00:05.500Z",305 implementedAt: "2026-02-24T00:00:05.500Z",
306 implementationThreadId: ThreadId.makeUnsafe("thread-2"),306 implementationThreadId: ThreadId.make("thread-2"),
307 createdAt: "2026-02-24T00:00:05.000Z",307 createdAt: "2026-02-24T00:00:05.000Z",
308 updatedAt: "2026-02-24T00:00:05.500Z",308 updatedAt: "2026-02-24T00:00:05.500Z",
309 },309 },
...@@ -331,7 +331,7 @@ projectionSnapshotLayer("ProjectionSnapshotQuery", (it) => {...@@ -331,7 +331,7 @@ projectionSnapshotLayer("ProjectionSnapshotQuery", (it) => {
331 },331 },
332 ],332 ],
333 session: {333 session: {
334 threadId: ThreadId.makeUnsafe("thread-1"),334 threadId: ThreadId.make("thread-1"),
335 status: "running",335 status: "running",
336 providerName: "codex",336 providerName: "codex",
337 runtimeMode: "approval-required",337 runtimeMode: "approval-required",
...@@ -473,7 +473,7 @@ projectionSnapshotLayer("ProjectionSnapshotQuery", (it) => {...@@ -473,7 +473,7 @@ projectionSnapshotLayer("ProjectionSnapshotQuery", (it) => {
473 );473 );
474 assert.equal(firstThreadId._tag, "Some");474 assert.equal(firstThreadId._tag, "Some");
475 if (firstThreadId._tag === "Some") {475 if (firstThreadId._tag === "Some") {
476 assert.equal(firstThreadId.value, ThreadId.makeUnsafe("thread-first"));476 assert.equal(firstThreadId.value, ThreadId.make("thread-first"));
477 }477 }
478 }),478 }),
479 );479 );
...@@ -596,12 +596,12 @@ projectionSnapshotLayer("ProjectionSnapshotQuery", (it) => {...@@ -596,12 +596,12 @@ projectionSnapshotLayer("ProjectionSnapshotQuery", (it) => {
596 `;596 `;
597597
598 const context = yield* snapshotQuery.getThreadCheckpointContext(598 const context = yield* snapshotQuery.getThreadCheckpointContext(
599 ThreadId.makeUnsafe("thread-context"),599 ThreadId.make("thread-context"),
600 );600 );
601 assert.equal(context._tag, "Some");601 assert.equal(context._tag, "Some");
602 if (context._tag === "Some") {602 if (context._tag === "Some") {
603 assert.deepEqual(context.value, {603 assert.deepEqual(context.value, {
604 threadId: ThreadId.makeUnsafe("thread-context"),604 threadId: ThreadId.make("thread-context"),
605 projectId: asProjectId("project-context"),605 projectId: asProjectId("project-context"),
606 workspaceRoot: "/tmp/context-workspace",606 workspaceRoot: "/tmp/context-workspace",
607 worktreePath: "/tmp/context-worktree",607 worktreePath: "/tmp/context-worktree",
apps/server/src/orchestration/Layers/ProviderCommandReactor.test.ts+138-151
...@@ -38,11 +38,10 @@ import { ProviderCommandReactor } from "../Services/ProviderCommandReactor.ts";...@@ -38,11 +38,10 @@ import { ProviderCommandReactor } from "../Services/ProviderCommandReactor.ts";
38import * as NodeServices from "@effect/platform-node/NodeServices";38import * as NodeServices from "@effect/platform-node/NodeServices";
39import { ServerSettingsService } from "../../serverSettings.ts";39import { ServerSettingsService } from "../../serverSettings.ts";
4040
41const asProjectId = (value: string): ProjectId => ProjectId.makeUnsafe(value);41const asProjectId = (value: string): ProjectId => ProjectId.make(value);
42const asApprovalRequestId = (value: string): ApprovalRequestId =>42const asApprovalRequestId = (value: string): ApprovalRequestId => ApprovalRequestId.make(value);
43 ApprovalRequestId.makeUnsafe(value);43const asMessageId = (value: string): MessageId => MessageId.make(value);
44const asMessageId = (value: string): MessageId => MessageId.makeUnsafe(value);44const asTurnId = (value: string): TurnId => TurnId.make(value);
45const asTurnId = (value: string): TurnId => TurnId.makeUnsafe(value);
4645
47const deriveServerPathsSync = (baseDir: string, devUrl: URL | undefined) =>46const deriveServerPathsSync = (baseDir: string, devUrl: URL | undefined) =>
48 Effect.runSync(deriveServerPaths(baseDir, devUrl).pipe(Effect.provide(NodeServices.layer)));47 Effect.runSync(deriveServerPaths(baseDir, devUrl).pipe(Effect.provide(NodeServices.layer)));
...@@ -122,8 +121,8 @@ describe("ProviderCommandReactor", () => {...@@ -122,8 +121,8 @@ describe("ProviderCommandReactor", () => {
122 input !== null &&121 input !== null &&
123 "threadId" in input &&122 "threadId" in input &&
124 typeof input.threadId === "string"123 typeof input.threadId === "string"
125 ? ThreadId.makeUnsafe(input.threadId)124 ? ThreadId.make(input.threadId)
126 : ThreadId.makeUnsafe(`thread-${sessionIndex}`);125 : ThreadId.make(`thread-${sessionIndex}`);
127 const session: ProviderSession = {126 const session: ProviderSession = {
128 provider: modelSelection.provider,127 provider: modelSelection.provider,
129 status: "ready" as const,128 status: "ready" as const,
...@@ -145,7 +144,7 @@ describe("ProviderCommandReactor", () => {...@@ -145,7 +144,7 @@ describe("ProviderCommandReactor", () => {
145 });144 });
146 const sendTurn = vi.fn((_: unknown) =>145 const sendTurn = vi.fn((_: unknown) =>
147 Effect.succeed({146 Effect.succeed({
148 threadId: ThreadId.makeUnsafe("thread-1"),147 threadId: ThreadId.make("thread-1"),
149 turnId: asTurnId("turn-1"),148 turnId: asTurnId("turn-1"),
150 }),149 }),
151 );150 );
...@@ -247,7 +246,7 @@ describe("ProviderCommandReactor", () => {...@@ -247,7 +246,7 @@ describe("ProviderCommandReactor", () => {
247 await Effect.runPromise(246 await Effect.runPromise(
248 engine.dispatch({247 engine.dispatch({
249 type: "project.create",248 type: "project.create",
250 commandId: CommandId.makeUnsafe("cmd-project-create"),249 commandId: CommandId.make("cmd-project-create"),
251 projectId: asProjectId("project-1"),250 projectId: asProjectId("project-1"),
252 title: "Provider Project",251 title: "Provider Project",
253 workspaceRoot: "/tmp/provider-project",252 workspaceRoot: "/tmp/provider-project",
...@@ -258,8 +257,8 @@ describe("ProviderCommandReactor", () => {...@@ -258,8 +257,8 @@ describe("ProviderCommandReactor", () => {
258 await Effect.runPromise(257 await Effect.runPromise(
259 engine.dispatch({258 engine.dispatch({
260 type: "thread.create",259 type: "thread.create",
261 commandId: CommandId.makeUnsafe("cmd-thread-create"),260 commandId: CommandId.make("cmd-thread-create"),
262 threadId: ThreadId.makeUnsafe("thread-1"),261 threadId: ThreadId.make("thread-1"),
263 projectId: asProjectId("project-1"),262 projectId: asProjectId("project-1"),
264 title: "Thread",263 title: "Thread",
265 modelSelection: modelSelection,264 modelSelection: modelSelection,
...@@ -294,8 +293,8 @@ describe("ProviderCommandReactor", () => {...@@ -294,8 +293,8 @@ describe("ProviderCommandReactor", () => {
294 await Effect.runPromise(293 await Effect.runPromise(
295 harness.engine.dispatch({294 harness.engine.dispatch({
296 type: "thread.turn.start",295 type: "thread.turn.start",
297 commandId: CommandId.makeUnsafe("cmd-turn-start-1"),296 commandId: CommandId.make("cmd-turn-start-1"),
298 threadId: ThreadId.makeUnsafe("thread-1"),297 threadId: ThreadId.make("thread-1"),
299 message: {298 message: {
300 messageId: asMessageId("user-message-1"),299 messageId: asMessageId("user-message-1"),
301 role: "user",300 role: "user",
...@@ -310,7 +309,7 @@ describe("ProviderCommandReactor", () => {...@@ -310,7 +309,7 @@ describe("ProviderCommandReactor", () => {
310309
311 await waitFor(() => harness.startSession.mock.calls.length === 1);310 await waitFor(() => harness.startSession.mock.calls.length === 1);
312 await waitFor(() => harness.sendTurn.mock.calls.length === 1);311 await waitFor(() => harness.sendTurn.mock.calls.length === 1);
313 expect(harness.startSession.mock.calls[0]?.[0]).toEqual(ThreadId.makeUnsafe("thread-1"));312 expect(harness.startSession.mock.calls[0]?.[0]).toEqual(ThreadId.make("thread-1"));
314 expect(harness.startSession.mock.calls[0]?.[1]).toMatchObject({313 expect(harness.startSession.mock.calls[0]?.[1]).toMatchObject({
315 cwd: "/tmp/provider-project",314 cwd: "/tmp/provider-project",
316 modelSelection: {315 modelSelection: {
...@@ -321,7 +320,7 @@ describe("ProviderCommandReactor", () => {...@@ -321,7 +320,7 @@ describe("ProviderCommandReactor", () => {
321 });320 });
322321
323 const readModel = await Effect.runPromise(harness.engine.getReadModel());322 const readModel = await Effect.runPromise(harness.engine.getReadModel());
324 const thread = readModel.threads.find((entry) => entry.id === ThreadId.makeUnsafe("thread-1"));323 const thread = readModel.threads.find((entry) => entry.id === ThreadId.make("thread-1"));
325 expect(thread?.session?.threadId).toBe("thread-1");324 expect(thread?.session?.threadId).toBe("thread-1");
326 expect(thread?.session?.runtimeMode).toBe("approval-required");325 expect(thread?.session?.runtimeMode).toBe("approval-required");
327 });326 });
...@@ -335,8 +334,8 @@ describe("ProviderCommandReactor", () => {...@@ -335,8 +334,8 @@ describe("ProviderCommandReactor", () => {
335 await Effect.runPromise(334 await Effect.runPromise(
336 harness.engine.dispatch({335 harness.engine.dispatch({
337 type: "thread.meta.update",336 type: "thread.meta.update",
338 commandId: CommandId.makeUnsafe("cmd-thread-title-seed"),337 commandId: CommandId.make("cmd-thread-title-seed"),
339 threadId: ThreadId.makeUnsafe("thread-1"),338 threadId: ThreadId.make("thread-1"),
340 title: seededTitle,339 title: seededTitle,
341 }),340 }),
342 );341 );
...@@ -344,8 +343,8 @@ describe("ProviderCommandReactor", () => {...@@ -344,8 +343,8 @@ describe("ProviderCommandReactor", () => {
344 await Effect.runPromise(343 await Effect.runPromise(
345 harness.engine.dispatch({344 harness.engine.dispatch({
346 type: "thread.turn.start",345 type: "thread.turn.start",
347 commandId: CommandId.makeUnsafe("cmd-turn-start-title"),346 commandId: CommandId.make("cmd-turn-start-title"),
348 threadId: ThreadId.makeUnsafe("thread-1"),347 threadId: ThreadId.make("thread-1"),
349 message: {348 message: {
350 messageId: asMessageId("user-message-title"),349 messageId: asMessageId("user-message-title"),
351 role: "user",350 role: "user",
...@@ -367,12 +366,12 @@ describe("ProviderCommandReactor", () => {...@@ -367,12 +366,12 @@ describe("ProviderCommandReactor", () => {
367 await waitFor(async () => {366 await waitFor(async () => {
368 const readModel = await Effect.runPromise(harness.engine.getReadModel());367 const readModel = await Effect.runPromise(harness.engine.getReadModel());
369 return (368 return (
370 readModel.threads.find((entry) => entry.id === ThreadId.makeUnsafe("thread-1"))?.title ===369 readModel.threads.find((entry) => entry.id === ThreadId.make("thread-1"))?.title ===
371 "Generated title"370 "Generated title"
372 );371 );
373 });372 });
374 const readModel = await Effect.runPromise(harness.engine.getReadModel());373 const readModel = await Effect.runPromise(harness.engine.getReadModel());
375 const thread = readModel.threads.find((entry) => entry.id === ThreadId.makeUnsafe("thread-1"));374 const thread = readModel.threads.find((entry) => entry.id === ThreadId.make("thread-1"));
376 expect(thread?.title).toBe("Generated title");375 expect(thread?.title).toBe("Generated title");
377 });376 });
378377
...@@ -384,8 +383,8 @@ describe("ProviderCommandReactor", () => {...@@ -384,8 +383,8 @@ describe("ProviderCommandReactor", () => {
384 await Effect.runPromise(383 await Effect.runPromise(
385 harness.engine.dispatch({384 harness.engine.dispatch({
386 type: "thread.meta.update",385 type: "thread.meta.update",
387 commandId: CommandId.makeUnsafe("cmd-thread-title-custom"),386 commandId: CommandId.make("cmd-thread-title-custom"),
388 threadId: ThreadId.makeUnsafe("thread-1"),387 threadId: ThreadId.make("thread-1"),
389 title: "Keep this custom title",388 title: "Keep this custom title",
390 }),389 }),
391 );390 );
...@@ -393,8 +392,8 @@ describe("ProviderCommandReactor", () => {...@@ -393,8 +392,8 @@ describe("ProviderCommandReactor", () => {
393 await Effect.runPromise(392 await Effect.runPromise(
394 harness.engine.dispatch({393 harness.engine.dispatch({
395 type: "thread.turn.start",394 type: "thread.turn.start",
396 commandId: CommandId.makeUnsafe("cmd-turn-start-title-preserve"),395 commandId: CommandId.make("cmd-turn-start-title-preserve"),
397 threadId: ThreadId.makeUnsafe("thread-1"),396 threadId: ThreadId.make("thread-1"),
398 message: {397 message: {
399 messageId: asMessageId("user-message-title-preserve"),398 messageId: asMessageId("user-message-title-preserve"),
400 role: "user",399 role: "user",
...@@ -412,7 +411,7 @@ describe("ProviderCommandReactor", () => {...@@ -412,7 +411,7 @@ describe("ProviderCommandReactor", () => {
412 expect(harness.generateThreadTitle).not.toHaveBeenCalled();411 expect(harness.generateThreadTitle).not.toHaveBeenCalled();
413412
414 const readModel = await Effect.runPromise(harness.engine.getReadModel());413 const readModel = await Effect.runPromise(harness.engine.getReadModel());
415 const thread = readModel.threads.find((entry) => entry.id === ThreadId.makeUnsafe("thread-1"));414 const thread = readModel.threads.find((entry) => entry.id === ThreadId.make("thread-1"));
416 expect(thread?.title).toBe("Keep this custom title");415 expect(thread?.title).toBe("Keep this custom title");
417 });416 });
418417
...@@ -429,8 +428,8 @@ describe("ProviderCommandReactor", () => {...@@ -429,8 +428,8 @@ describe("ProviderCommandReactor", () => {
429 await Effect.runPromise(428 await Effect.runPromise(
430 harness.engine.dispatch({429 harness.engine.dispatch({
431 type: "thread.meta.update",430 type: "thread.meta.update",
432 commandId: CommandId.makeUnsafe("cmd-thread-title-formatted-seed"),431 commandId: CommandId.make("cmd-thread-title-formatted-seed"),
433 threadId: ThreadId.makeUnsafe("thread-1"),432 threadId: ThreadId.make("thread-1"),
434 title: seededTitle,433 title: seededTitle,
435 }),434 }),
436 );435 );
...@@ -438,8 +437,8 @@ describe("ProviderCommandReactor", () => {...@@ -438,8 +437,8 @@ describe("ProviderCommandReactor", () => {
438 await Effect.runPromise(437 await Effect.runPromise(
439 harness.engine.dispatch({438 harness.engine.dispatch({
440 type: "thread.turn.start",439 type: "thread.turn.start",
441 commandId: CommandId.makeUnsafe("cmd-turn-start-title-formatted"),440 commandId: CommandId.make("cmd-turn-start-title-formatted"),
442 threadId: ThreadId.makeUnsafe("thread-1"),441 threadId: ThreadId.make("thread-1"),
443 message: {442 message: {
444 messageId: asMessageId("user-message-title-formatted"),443 messageId: asMessageId("user-message-title-formatted"),
445 role: "user",444 role: "user",
...@@ -457,13 +456,13 @@ describe("ProviderCommandReactor", () => {...@@ -457,13 +456,13 @@ describe("ProviderCommandReactor", () => {
457 await waitFor(async () => {456 await waitFor(async () => {
458 const readModel = await Effect.runPromise(harness.engine.getReadModel());457 const readModel = await Effect.runPromise(harness.engine.getReadModel());
459 return (458 return (
460 readModel.threads.find((entry) => entry.id === ThreadId.makeUnsafe("thread-1"))?.title ===459 readModel.threads.find((entry) => entry.id === ThreadId.make("thread-1"))?.title ===
461 "Reconnect spinner resume bug"460 "Reconnect spinner resume bug"
462 );461 );
463 });462 });
464463
465 const readModel = await Effect.runPromise(harness.engine.getReadModel());464 const readModel = await Effect.runPromise(harness.engine.getReadModel());
466 const thread = readModel.threads.find((entry) => entry.id === ThreadId.makeUnsafe("thread-1"));465 const thread = readModel.threads.find((entry) => entry.id === ThreadId.make("thread-1"));
467 expect(thread?.title).toBe("Reconnect spinner resume bug");466 expect(thread?.title).toBe("Reconnect spinner resume bug");
468 });467 });
469468
...@@ -474,8 +473,8 @@ describe("ProviderCommandReactor", () => {...@@ -474,8 +473,8 @@ describe("ProviderCommandReactor", () => {
474 await Effect.runPromise(473 await Effect.runPromise(
475 harness.engine.dispatch({474 harness.engine.dispatch({
476 type: "thread.meta.update",475 type: "thread.meta.update",
477 commandId: CommandId.makeUnsafe("cmd-thread-branch"),476 commandId: CommandId.make("cmd-thread-branch"),
478 threadId: ThreadId.makeUnsafe("thread-1"),477 threadId: ThreadId.make("thread-1"),
479 branch: "t3code/1234abcd",478 branch: "t3code/1234abcd",
480 worktreePath: "/tmp/provider-project-worktree",479 worktreePath: "/tmp/provider-project-worktree",
481 }),480 }),
...@@ -499,8 +498,8 @@ describe("ProviderCommandReactor", () => {...@@ -499,8 +498,8 @@ describe("ProviderCommandReactor", () => {
499 await Effect.runPromise(498 await Effect.runPromise(
500 harness.engine.dispatch({499 harness.engine.dispatch({
501 type: "thread.turn.start",500 type: "thread.turn.start",
502 commandId: CommandId.makeUnsafe("cmd-turn-start-branch-model"),501 commandId: CommandId.make("cmd-turn-start-branch-model"),
503 threadId: ThreadId.makeUnsafe("thread-1"),502 threadId: ThreadId.make("thread-1"),
504 message: {503 message: {
505 messageId: asMessageId("user-message-branch-model"),504 messageId: asMessageId("user-message-branch-model"),
506 role: "user",505 role: "user",
...@@ -526,8 +525,8 @@ describe("ProviderCommandReactor", () => {...@@ -526,8 +525,8 @@ describe("ProviderCommandReactor", () => {
526 await Effect.runPromise(525 await Effect.runPromise(
527 harness.engine.dispatch({526 harness.engine.dispatch({
528 type: "thread.turn.start",527 type: "thread.turn.start",
529 commandId: CommandId.makeUnsafe("cmd-turn-start-fast"),528 commandId: CommandId.make("cmd-turn-start-fast"),
530 threadId: ThreadId.makeUnsafe("thread-1"),529 threadId: ThreadId.make("thread-1"),
531 message: {530 message: {
532 messageId: asMessageId("user-message-fast"),531 messageId: asMessageId("user-message-fast"),
533 role: "user",532 role: "user",
...@@ -561,7 +560,7 @@ describe("ProviderCommandReactor", () => {...@@ -561,7 +560,7 @@ describe("ProviderCommandReactor", () => {
561 },560 },
562 });561 });
563 expect(harness.sendTurn.mock.calls[0]?.[0]).toMatchObject({562 expect(harness.sendTurn.mock.calls[0]?.[0]).toMatchObject({
564 threadId: ThreadId.makeUnsafe("thread-1"),563 threadId: ThreadId.make("thread-1"),
565 modelSelection: {564 modelSelection: {
566 provider: "codex",565 provider: "codex",
567 model: "gpt-5.3-codex",566 model: "gpt-5.3-codex",
...@@ -582,8 +581,8 @@ describe("ProviderCommandReactor", () => {...@@ -582,8 +581,8 @@ describe("ProviderCommandReactor", () => {
582 await Effect.runPromise(581 await Effect.runPromise(
583 harness.engine.dispatch({582 harness.engine.dispatch({
584 type: "thread.turn.start",583 type: "thread.turn.start",
585 commandId: CommandId.makeUnsafe("cmd-turn-start-claude-effort"),584 commandId: CommandId.make("cmd-turn-start-claude-effort"),
586 threadId: ThreadId.makeUnsafe("thread-1"),585 threadId: ThreadId.make("thread-1"),
587 message: {586 message: {
588 messageId: asMessageId("user-message-claude-effort"),587 messageId: asMessageId("user-message-claude-effort"),
589 role: "user",588 role: "user",
...@@ -615,7 +614,7 @@ describe("ProviderCommandReactor", () => {...@@ -615,7 +614,7 @@ describe("ProviderCommandReactor", () => {
615 },614 },
616 });615 });
617 expect(harness.sendTurn.mock.calls[0]?.[0]).toMatchObject({616 expect(harness.sendTurn.mock.calls[0]?.[0]).toMatchObject({
618 threadId: ThreadId.makeUnsafe("thread-1"),617 threadId: ThreadId.make("thread-1"),
619 modelSelection: {618 modelSelection: {
620 provider: "claudeAgent",619 provider: "claudeAgent",
621 model: "claude-sonnet-4-6",620 model: "claude-sonnet-4-6",
...@@ -635,8 +634,8 @@ describe("ProviderCommandReactor", () => {...@@ -635,8 +634,8 @@ describe("ProviderCommandReactor", () => {
635 await Effect.runPromise(634 await Effect.runPromise(
636 harness.engine.dispatch({635 harness.engine.dispatch({
637 type: "thread.turn.start",636 type: "thread.turn.start",
638 commandId: CommandId.makeUnsafe("cmd-turn-start-claude-fast-mode"),637 commandId: CommandId.make("cmd-turn-start-claude-fast-mode"),
639 threadId: ThreadId.makeUnsafe("thread-1"),638 threadId: ThreadId.make("thread-1"),
640 message: {639 message: {
641 messageId: asMessageId("user-message-claude-fast-mode"),640 messageId: asMessageId("user-message-claude-fast-mode"),
642 role: "user",641 role: "user",
...@@ -668,7 +667,7 @@ describe("ProviderCommandReactor", () => {...@@ -668,7 +667,7 @@ describe("ProviderCommandReactor", () => {
668 },667 },
669 });668 });
670 expect(harness.sendTurn.mock.calls[0]?.[0]).toMatchObject({669 expect(harness.sendTurn.mock.calls[0]?.[0]).toMatchObject({
671 threadId: ThreadId.makeUnsafe("thread-1"),670 threadId: ThreadId.make("thread-1"),
672 modelSelection: {671 modelSelection: {
673 provider: "claudeAgent",672 provider: "claudeAgent",
674 model: "claude-opus-4-6",673 model: "claude-opus-4-6",
...@@ -686,8 +685,8 @@ describe("ProviderCommandReactor", () => {...@@ -686,8 +685,8 @@ describe("ProviderCommandReactor", () => {
686 await Effect.runPromise(685 await Effect.runPromise(
687 harness.engine.dispatch({686 harness.engine.dispatch({
688 type: "thread.interaction-mode.set",687 type: "thread.interaction-mode.set",
689 commandId: CommandId.makeUnsafe("cmd-interaction-mode-set-plan"),688 commandId: CommandId.make("cmd-interaction-mode-set-plan"),
690 threadId: ThreadId.makeUnsafe("thread-1"),689 threadId: ThreadId.make("thread-1"),
691 interactionMode: "plan",690 interactionMode: "plan",
692 createdAt: now,691 createdAt: now,
693 }),692 }),
...@@ -696,8 +695,8 @@ describe("ProviderCommandReactor", () => {...@@ -696,8 +695,8 @@ describe("ProviderCommandReactor", () => {
696 await Effect.runPromise(695 await Effect.runPromise(
697 harness.engine.dispatch({696 harness.engine.dispatch({
698 type: "thread.turn.start",697 type: "thread.turn.start",
699 commandId: CommandId.makeUnsafe("cmd-turn-start-plan"),698 commandId: CommandId.make("cmd-turn-start-plan"),
700 threadId: ThreadId.makeUnsafe("thread-1"),699 threadId: ThreadId.make("thread-1"),
701 message: {700 message: {
702 messageId: asMessageId("user-message-plan"),701 messageId: asMessageId("user-message-plan"),
703 role: "user",702 role: "user",
...@@ -712,7 +711,7 @@ describe("ProviderCommandReactor", () => {...@@ -712,7 +711,7 @@ describe("ProviderCommandReactor", () => {
712711
713 await waitFor(() => harness.sendTurn.mock.calls.length === 1);712 await waitFor(() => harness.sendTurn.mock.calls.length === 1);
714 expect(harness.sendTurn.mock.calls[0]?.[0]).toMatchObject({713 expect(harness.sendTurn.mock.calls[0]?.[0]).toMatchObject({
715 threadId: ThreadId.makeUnsafe("thread-1"),714 threadId: ThreadId.make("thread-1"),
716 interactionMode: "plan",715 interactionMode: "plan",
717 });716 });
718 });717 });
...@@ -726,8 +725,8 @@ describe("ProviderCommandReactor", () => {...@@ -726,8 +725,8 @@ describe("ProviderCommandReactor", () => {
726 await Effect.runPromise(725 await Effect.runPromise(
727 harness.engine.dispatch({726 harness.engine.dispatch({
728 type: "thread.turn.start",727 type: "thread.turn.start",
729 commandId: CommandId.makeUnsafe("cmd-turn-start-provider-first"),728 commandId: CommandId.make("cmd-turn-start-provider-first"),
730 threadId: ThreadId.makeUnsafe("thread-1"),729 threadId: ThreadId.make("thread-1"),
731 message: {730 message: {
732 messageId: asMessageId("user-message-provider-first"),731 messageId: asMessageId("user-message-provider-first"),
733 role: "user",732 role: "user",
...@@ -746,9 +745,7 @@ describe("ProviderCommandReactor", () => {...@@ -746,9 +745,7 @@ describe("ProviderCommandReactor", () => {
746745
747 await waitFor(async () => {746 await waitFor(async () => {
748 const readModel = await Effect.runPromise(harness.engine.getReadModel());747 const readModel = await Effect.runPromise(harness.engine.getReadModel());
749 const thread = readModel.threads.find(748 const thread = readModel.threads.find((entry) => entry.id === ThreadId.make("thread-1"));
750 (entry) => entry.id === ThreadId.makeUnsafe("thread-1"),
751 );
752 return (749 return (
753 thread?.activities.some((activity) => activity.kind === "provider.turn.start.failed") ??750 thread?.activities.some((activity) => activity.kind === "provider.turn.start.failed") ??
754 false751 false
...@@ -759,7 +756,7 @@ describe("ProviderCommandReactor", () => {...@@ -759,7 +756,7 @@ describe("ProviderCommandReactor", () => {
759 expect(harness.sendTurn).not.toHaveBeenCalled();756 expect(harness.sendTurn).not.toHaveBeenCalled();
760757
761 const readModel = await Effect.runPromise(harness.engine.getReadModel());758 const readModel = await Effect.runPromise(harness.engine.getReadModel());
762 const thread = readModel.threads.find((entry) => entry.id === ThreadId.makeUnsafe("thread-1"));759 const thread = readModel.threads.find((entry) => entry.id === ThreadId.make("thread-1"));
763 expect(thread?.session).toBeNull();760 expect(thread?.session).toBeNull();
764 expect(761 expect(
765 thread?.activities.find((activity) => activity.kind === "provider.turn.start.failed"),762 thread?.activities.find((activity) => activity.kind === "provider.turn.start.failed"),
...@@ -778,8 +775,8 @@ describe("ProviderCommandReactor", () => {...@@ -778,8 +775,8 @@ describe("ProviderCommandReactor", () => {
778 await Effect.runPromise(775 await Effect.runPromise(
779 harness.engine.dispatch({776 harness.engine.dispatch({
780 type: "thread.turn.start",777 type: "thread.turn.start",
781 commandId: CommandId.makeUnsafe("cmd-turn-start-unsupported-1"),778 commandId: CommandId.make("cmd-turn-start-unsupported-1"),
782 threadId: ThreadId.makeUnsafe("thread-1"),779 threadId: ThreadId.make("thread-1"),
783 message: {780 message: {
784 messageId: asMessageId("user-message-unsupported-1"),781 messageId: asMessageId("user-message-unsupported-1"),
785 role: "user",782 role: "user",
...@@ -797,8 +794,8 @@ describe("ProviderCommandReactor", () => {...@@ -797,8 +794,8 @@ describe("ProviderCommandReactor", () => {
797 await Effect.runPromise(794 await Effect.runPromise(
798 harness.engine.dispatch({795 harness.engine.dispatch({
799 type: "thread.turn.start",796 type: "thread.turn.start",
800 commandId: CommandId.makeUnsafe("cmd-turn-start-unsupported-2"),797 commandId: CommandId.make("cmd-turn-start-unsupported-2"),
801 threadId: ThreadId.makeUnsafe("thread-1"),798 threadId: ThreadId.make("thread-1"),
802 message: {799 message: {
803 messageId: asMessageId("user-message-unsupported-2"),800 messageId: asMessageId("user-message-unsupported-2"),
804 role: "user",801 role: "user",
...@@ -814,7 +811,7 @@ describe("ProviderCommandReactor", () => {...@@ -814,7 +811,7 @@ describe("ProviderCommandReactor", () => {
814 await waitFor(() => harness.sendTurn.mock.calls.length === 2);811 await waitFor(() => harness.sendTurn.mock.calls.length === 2);
815812
816 expect(harness.sendTurn.mock.calls[1]?.[0]).toMatchObject({813 expect(harness.sendTurn.mock.calls[1]?.[0]).toMatchObject({
817 threadId: ThreadId.makeUnsafe("thread-1"),814 threadId: ThreadId.make("thread-1"),
818 modelSelection: {815 modelSelection: {
819 provider: "codex",816 provider: "codex",
820 model: "gpt-5-codex",817 model: "gpt-5-codex",
...@@ -829,8 +826,8 @@ describe("ProviderCommandReactor", () => {...@@ -829,8 +826,8 @@ describe("ProviderCommandReactor", () => {
829 await Effect.runPromise(826 await Effect.runPromise(
830 harness.engine.dispatch({827 harness.engine.dispatch({
831 type: "thread.turn.start",828 type: "thread.turn.start",
832 commandId: CommandId.makeUnsafe("cmd-turn-start-unchanged-1"),829 commandId: CommandId.make("cmd-turn-start-unchanged-1"),
833 threadId: ThreadId.makeUnsafe("thread-1"),830 threadId: ThreadId.make("thread-1"),
834 message: {831 message: {
835 messageId: asMessageId("user-message-unchanged-1"),832 messageId: asMessageId("user-message-unchanged-1"),
836 role: "user",833 role: "user",
...@@ -849,8 +846,8 @@ describe("ProviderCommandReactor", () => {...@@ -849,8 +846,8 @@ describe("ProviderCommandReactor", () => {
849 await Effect.runPromise(846 await Effect.runPromise(
850 harness.engine.dispatch({847 harness.engine.dispatch({
851 type: "thread.turn.start",848 type: "thread.turn.start",
852 commandId: CommandId.makeUnsafe("cmd-turn-start-unchanged-2"),849 commandId: CommandId.make("cmd-turn-start-unchanged-2"),
853 threadId: ThreadId.makeUnsafe("thread-1"),850 threadId: ThreadId.make("thread-1"),
854 message: {851 message: {
855 messageId: asMessageId("user-message-unchanged-2"),852 messageId: asMessageId("user-message-unchanged-2"),
856 role: "user",853 role: "user",
...@@ -877,8 +874,8 @@ describe("ProviderCommandReactor", () => {...@@ -877,8 +874,8 @@ describe("ProviderCommandReactor", () => {
877 await Effect.runPromise(874 await Effect.runPromise(
878 harness.engine.dispatch({875 harness.engine.dispatch({
879 type: "thread.turn.start",876 type: "thread.turn.start",
880 commandId: CommandId.makeUnsafe("cmd-turn-start-claude-effort-1"),877 commandId: CommandId.make("cmd-turn-start-claude-effort-1"),
881 threadId: ThreadId.makeUnsafe("thread-1"),878 threadId: ThreadId.make("thread-1"),
882 message: {879 message: {
883 messageId: asMessageId("user-message-claude-effort-1"),880 messageId: asMessageId("user-message-claude-effort-1"),
884 role: "user",881 role: "user",
...@@ -904,8 +901,8 @@ describe("ProviderCommandReactor", () => {...@@ -904,8 +901,8 @@ describe("ProviderCommandReactor", () => {
904 await Effect.runPromise(901 await Effect.runPromise(
905 harness.engine.dispatch({902 harness.engine.dispatch({
906 type: "thread.turn.start",903 type: "thread.turn.start",
907 commandId: CommandId.makeUnsafe("cmd-turn-start-claude-effort-2"),904 commandId: CommandId.make("cmd-turn-start-claude-effort-2"),
908 threadId: ThreadId.makeUnsafe("thread-1"),905 threadId: ThreadId.make("thread-1"),
909 message: {906 message: {
910 messageId: asMessageId("user-message-claude-effort-2"),907 messageId: asMessageId("user-message-claude-effort-2"),
911 role: "user",908 role: "user",
...@@ -946,8 +943,8 @@ describe("ProviderCommandReactor", () => {...@@ -946,8 +943,8 @@ describe("ProviderCommandReactor", () => {
946 await Effect.runPromise(943 await Effect.runPromise(
947 harness.engine.dispatch({944 harness.engine.dispatch({
948 type: "thread.runtime-mode.set",945 type: "thread.runtime-mode.set",
949 commandId: CommandId.makeUnsafe("cmd-runtime-mode-set-initial-full-access"),946 commandId: CommandId.make("cmd-runtime-mode-set-initial-full-access"),
950 threadId: ThreadId.makeUnsafe("thread-1"),947 threadId: ThreadId.make("thread-1"),
951 runtimeMode: "full-access",948 runtimeMode: "full-access",
952 createdAt: now,949 createdAt: now,
953 }),950 }),
...@@ -956,8 +953,8 @@ describe("ProviderCommandReactor", () => {...@@ -956,8 +953,8 @@ describe("ProviderCommandReactor", () => {
956 await Effect.runPromise(953 await Effect.runPromise(
957 harness.engine.dispatch({954 harness.engine.dispatch({
958 type: "thread.turn.start",955 type: "thread.turn.start",
959 commandId: CommandId.makeUnsafe("cmd-turn-start-runtime-mode-1"),956 commandId: CommandId.make("cmd-turn-start-runtime-mode-1"),
960 threadId: ThreadId.makeUnsafe("thread-1"),957 threadId: ThreadId.make("thread-1"),
961 message: {958 message: {
962 messageId: asMessageId("user-message-runtime-mode-1"),959 messageId: asMessageId("user-message-runtime-mode-1"),
963 role: "user",960 role: "user",
...@@ -976,8 +973,8 @@ describe("ProviderCommandReactor", () => {...@@ -976,8 +973,8 @@ describe("ProviderCommandReactor", () => {
976 await Effect.runPromise(973 await Effect.runPromise(
977 harness.engine.dispatch({974 harness.engine.dispatch({
978 type: "thread.runtime-mode.set",975 type: "thread.runtime-mode.set",
979 commandId: CommandId.makeUnsafe("cmd-runtime-mode-set-1"),976 commandId: CommandId.make("cmd-runtime-mode-set-1"),
980 threadId: ThreadId.makeUnsafe("thread-1"),977 threadId: ThreadId.make("thread-1"),
981 runtimeMode: "approval-required",978 runtimeMode: "approval-required",
982 createdAt: now,979 createdAt: now,
983 }),980 }),
...@@ -985,17 +982,15 @@ describe("ProviderCommandReactor", () => {...@@ -985,17 +982,15 @@ describe("ProviderCommandReactor", () => {
985982
986 await waitFor(async () => {983 await waitFor(async () => {
987 const readModel = await Effect.runPromise(harness.engine.getReadModel());984 const readModel = await Effect.runPromise(harness.engine.getReadModel());
988 const thread = readModel.threads.find(985 const thread = readModel.threads.find((entry) => entry.id === ThreadId.make("thread-1"));
989 (entry) => entry.id === ThreadId.makeUnsafe("thread-1"),
990 );
991 return thread?.runtimeMode === "approval-required";986 return thread?.runtimeMode === "approval-required";
992 });987 });
993 await waitFor(() => harness.startSession.mock.calls.length === 2);988 await waitFor(() => harness.startSession.mock.calls.length === 2);
994 await Effect.runPromise(989 await Effect.runPromise(
995 harness.engine.dispatch({990 harness.engine.dispatch({
996 type: "thread.turn.start",991 type: "thread.turn.start",
997 commandId: CommandId.makeUnsafe("cmd-turn-start-runtime-mode-2"),992 commandId: CommandId.make("cmd-turn-start-runtime-mode-2"),
998 threadId: ThreadId.makeUnsafe("thread-1"),993 threadId: ThreadId.make("thread-1"),
999 message: {994 message: {
1000 messageId: asMessageId("user-message-runtime-mode-2"),995 messageId: asMessageId("user-message-runtime-mode-2"),
1001 role: "user",996 role: "user",
...@@ -1012,16 +1007,16 @@ describe("ProviderCommandReactor", () => {...@@ -1012,16 +1007,16 @@ describe("ProviderCommandReactor", () => {
10121007
1013 expect(harness.stopSession.mock.calls.length).toBe(0);1008 expect(harness.stopSession.mock.calls.length).toBe(0);
1014 expect(harness.startSession.mock.calls[1]?.[1]).toMatchObject({1009 expect(harness.startSession.mock.calls[1]?.[1]).toMatchObject({
1015 threadId: ThreadId.makeUnsafe("thread-1"),1010 threadId: ThreadId.make("thread-1"),
1016 resumeCursor: { opaque: "resume-1" },1011 resumeCursor: { opaque: "resume-1" },
1017 runtimeMode: "approval-required",1012 runtimeMode: "approval-required",
1018 });1013 });
1019 expect(harness.sendTurn.mock.calls[1]?.[0]).toMatchObject({1014 expect(harness.sendTurn.mock.calls[1]?.[0]).toMatchObject({
1020 threadId: ThreadId.makeUnsafe("thread-1"),1015 threadId: ThreadId.make("thread-1"),
1021 });1016 });
10221017
1023 const readModel = await Effect.runPromise(harness.engine.getReadModel());1018 const readModel = await Effect.runPromise(harness.engine.getReadModel());
1024 const thread = readModel.threads.find((entry) => entry.id === ThreadId.makeUnsafe("thread-1"));1019 const thread = readModel.threads.find((entry) => entry.id === ThreadId.make("thread-1"));
1025 expect(thread?.session?.threadId).toBe("thread-1");1020 expect(thread?.session?.threadId).toBe("thread-1");
1026 expect(thread?.session?.runtimeMode).toBe("approval-required");1021 expect(thread?.session?.runtimeMode).toBe("approval-required");
1027 });1022 });
...@@ -1035,10 +1030,10 @@ describe("ProviderCommandReactor", () => {...@@ -1035,10 +1030,10 @@ describe("ProviderCommandReactor", () => {
1035 await Effect.runPromise(1030 await Effect.runPromise(
1036 harness.engine.dispatch({1031 harness.engine.dispatch({
1037 type: "thread.session.set",1032 type: "thread.session.set",
1038 commandId: CommandId.makeUnsafe("cmd-session-set-runtime-mode-claude"),1033 commandId: CommandId.make("cmd-session-set-runtime-mode-claude"),
1039 threadId: ThreadId.makeUnsafe("thread-1"),1034 threadId: ThreadId.make("thread-1"),
1040 session: {1035 session: {
1041 threadId: ThreadId.makeUnsafe("thread-1"),1036 threadId: ThreadId.make("thread-1"),
1042 status: "ready",1037 status: "ready",
1043 providerName: "claudeAgent",1038 providerName: "claudeAgent",
1044 runtimeMode: "full-access",1039 runtimeMode: "full-access",
...@@ -1053,8 +1048,8 @@ describe("ProviderCommandReactor", () => {...@@ -1053,8 +1048,8 @@ describe("ProviderCommandReactor", () => {
1053 await Effect.runPromise(1048 await Effect.runPromise(
1054 harness.engine.dispatch({1049 harness.engine.dispatch({
1055 type: "thread.runtime-mode.set",1050 type: "thread.runtime-mode.set",
1056 commandId: CommandId.makeUnsafe("cmd-runtime-mode-set-claude-no-options"),1051 commandId: CommandId.make("cmd-runtime-mode-set-claude-no-options"),
1057 threadId: ThreadId.makeUnsafe("thread-1"),1052 threadId: ThreadId.make("thread-1"),
1058 runtimeMode: "approval-required",1053 runtimeMode: "approval-required",
1059 createdAt: now,1054 createdAt: now,
1060 }),1055 }),
...@@ -1078,8 +1073,8 @@ describe("ProviderCommandReactor", () => {...@@ -1078,8 +1073,8 @@ describe("ProviderCommandReactor", () => {
1078 await Effect.runPromise(1073 await Effect.runPromise(
1079 harness.engine.dispatch({1074 harness.engine.dispatch({
1080 type: "thread.turn.start",1075 type: "thread.turn.start",
1081 commandId: CommandId.makeUnsafe("cmd-turn-start-provider-switch-1"),1076 commandId: CommandId.make("cmd-turn-start-provider-switch-1"),
1082 threadId: ThreadId.makeUnsafe("thread-1"),1077 threadId: ThreadId.make("thread-1"),
1083 message: {1078 message: {
1084 messageId: asMessageId("user-message-provider-switch-1"),1079 messageId: asMessageId("user-message-provider-switch-1"),
1085 role: "user",1080 role: "user",
...@@ -1098,8 +1093,8 @@ describe("ProviderCommandReactor", () => {...@@ -1098,8 +1093,8 @@ describe("ProviderCommandReactor", () => {
1098 await Effect.runPromise(1093 await Effect.runPromise(
1099 harness.engine.dispatch({1094 harness.engine.dispatch({
1100 type: "thread.turn.start",1095 type: "thread.turn.start",
1101 commandId: CommandId.makeUnsafe("cmd-turn-start-provider-switch-2"),1096 commandId: CommandId.make("cmd-turn-start-provider-switch-2"),
1102 threadId: ThreadId.makeUnsafe("thread-1"),1097 threadId: ThreadId.make("thread-1"),
1103 message: {1098 message: {
1104 messageId: asMessageId("user-message-provider-switch-2"),1099 messageId: asMessageId("user-message-provider-switch-2"),
1105 role: "user",1100 role: "user",
...@@ -1118,9 +1113,7 @@ describe("ProviderCommandReactor", () => {...@@ -1118,9 +1113,7 @@ describe("ProviderCommandReactor", () => {
11181113
1119 await waitFor(async () => {1114 await waitFor(async () => {
1120 const readModel = await Effect.runPromise(harness.engine.getReadModel());1115 const readModel = await Effect.runPromise(harness.engine.getReadModel());
1121 const thread = readModel.threads.find(1116 const thread = readModel.threads.find((entry) => entry.id === ThreadId.make("thread-1"));
1122 (entry) => entry.id === ThreadId.makeUnsafe("thread-1"),
1123 );
1124 return (1117 return (
1125 thread?.activities.some((activity) => activity.kind === "provider.turn.start.failed") ??1118 thread?.activities.some((activity) => activity.kind === "provider.turn.start.failed") ??
1126 false1119 false
...@@ -1132,7 +1125,7 @@ describe("ProviderCommandReactor", () => {...@@ -1132,7 +1125,7 @@ describe("ProviderCommandReactor", () => {
1132 expect(harness.stopSession.mock.calls.length).toBe(0);1125 expect(harness.stopSession.mock.calls.length).toBe(0);
11331126
1134 const readModel = await Effect.runPromise(harness.engine.getReadModel());1127 const readModel = await Effect.runPromise(harness.engine.getReadModel());
1135 const thread = readModel.threads.find((entry) => entry.id === ThreadId.makeUnsafe("thread-1"));1128 const thread = readModel.threads.find((entry) => entry.id === ThreadId.make("thread-1"));
1136 expect(thread?.session?.threadId).toBe("thread-1");1129 expect(thread?.session?.threadId).toBe("thread-1");
1137 expect(thread?.session?.providerName).toBe("codex");1130 expect(thread?.session?.providerName).toBe("codex");
1138 expect(thread?.session?.runtimeMode).toBe("approval-required");1131 expect(thread?.session?.runtimeMode).toBe("approval-required");
...@@ -1152,8 +1145,8 @@ describe("ProviderCommandReactor", () => {...@@ -1152,8 +1145,8 @@ describe("ProviderCommandReactor", () => {
1152 await Effect.runPromise(1145 await Effect.runPromise(
1153 harness.engine.dispatch({1146 harness.engine.dispatch({
1154 type: "thread.runtime-mode.set",1147 type: "thread.runtime-mode.set",
1155 commandId: CommandId.makeUnsafe("cmd-runtime-mode-set-initial-full-access-2"),1148 commandId: CommandId.make("cmd-runtime-mode-set-initial-full-access-2"),
1156 threadId: ThreadId.makeUnsafe("thread-1"),1149 threadId: ThreadId.make("thread-1"),
1157 runtimeMode: "full-access",1150 runtimeMode: "full-access",
1158 createdAt: now,1151 createdAt: now,
1159 }),1152 }),
...@@ -1162,8 +1155,8 @@ describe("ProviderCommandReactor", () => {...@@ -1162,8 +1155,8 @@ describe("ProviderCommandReactor", () => {
1162 await Effect.runPromise(1155 await Effect.runPromise(
1163 harness.engine.dispatch({1156 harness.engine.dispatch({
1164 type: "thread.turn.start",1157 type: "thread.turn.start",
1165 commandId: CommandId.makeUnsafe("cmd-turn-start-restart-failure-1"),1158 commandId: CommandId.make("cmd-turn-start-restart-failure-1"),
1166 threadId: ThreadId.makeUnsafe("thread-1"),1159 threadId: ThreadId.make("thread-1"),
1167 message: {1160 message: {
1168 messageId: asMessageId("user-message-restart-failure-1"),1161 messageId: asMessageId("user-message-restart-failure-1"),
1169 role: "user",1162 role: "user",
...@@ -1186,8 +1179,8 @@ describe("ProviderCommandReactor", () => {...@@ -1186,8 +1179,8 @@ describe("ProviderCommandReactor", () => {
1186 await Effect.runPromise(1179 await Effect.runPromise(
1187 harness.engine.dispatch({1180 harness.engine.dispatch({
1188 type: "thread.runtime-mode.set",1181 type: "thread.runtime-mode.set",
1189 commandId: CommandId.makeUnsafe("cmd-runtime-mode-set-restart-failure"),1182 commandId: CommandId.make("cmd-runtime-mode-set-restart-failure"),
1190 threadId: ThreadId.makeUnsafe("thread-1"),1183 threadId: ThreadId.make("thread-1"),
1191 runtimeMode: "approval-required",1184 runtimeMode: "approval-required",
1192 createdAt: now,1185 createdAt: now,
1193 }),1186 }),
...@@ -1195,9 +1188,7 @@ describe("ProviderCommandReactor", () => {...@@ -1195,9 +1188,7 @@ describe("ProviderCommandReactor", () => {
11951188
1196 await waitFor(async () => {1189 await waitFor(async () => {
1197 const readModel = await Effect.runPromise(harness.engine.getReadModel());1190 const readModel = await Effect.runPromise(harness.engine.getReadModel());
1198 const thread = readModel.threads.find(1191 const thread = readModel.threads.find((entry) => entry.id === ThreadId.make("thread-1"));
1199 (entry) => entry.id === ThreadId.makeUnsafe("thread-1"),
1200 );
1201 return thread?.runtimeMode === "approval-required";1192 return thread?.runtimeMode === "approval-required";
1202 });1193 });
1203 await waitFor(() => harness.startSession.mock.calls.length === 2);1194 await waitFor(() => harness.startSession.mock.calls.length === 2);
...@@ -1207,7 +1198,7 @@ describe("ProviderCommandReactor", () => {...@@ -1207,7 +1198,7 @@ describe("ProviderCommandReactor", () => {
1207 expect(harness.sendTurn.mock.calls.length).toBe(1);1198 expect(harness.sendTurn.mock.calls.length).toBe(1);
12081199
1209 const readModel = await Effect.runPromise(harness.engine.getReadModel());1200 const readModel = await Effect.runPromise(harness.engine.getReadModel());
1210 const thread = readModel.threads.find((entry) => entry.id === ThreadId.makeUnsafe("thread-1"));1201 const thread = readModel.threads.find((entry) => entry.id === ThreadId.make("thread-1"));
1211 expect(thread?.session?.threadId).toBe("thread-1");1202 expect(thread?.session?.threadId).toBe("thread-1");
1212 expect(thread?.session?.runtimeMode).toBe("full-access");1203 expect(thread?.session?.runtimeMode).toBe("full-access");
1213 });1204 });
...@@ -1219,10 +1210,10 @@ describe("ProviderCommandReactor", () => {...@@ -1219,10 +1210,10 @@ describe("ProviderCommandReactor", () => {
1219 await Effect.runPromise(1210 await Effect.runPromise(
1220 harness.engine.dispatch({1211 harness.engine.dispatch({
1221 type: "thread.session.set",1212 type: "thread.session.set",
1222 commandId: CommandId.makeUnsafe("cmd-session-set"),1213 commandId: CommandId.make("cmd-session-set"),
1223 threadId: ThreadId.makeUnsafe("thread-1"),1214 threadId: ThreadId.make("thread-1"),
1224 session: {1215 session: {
1225 threadId: ThreadId.makeUnsafe("thread-1"),1216 threadId: ThreadId.make("thread-1"),
1226 status: "running",1217 status: "running",
1227 providerName: "codex",1218 providerName: "codex",
1228 runtimeMode: "approval-required",1219 runtimeMode: "approval-required",
...@@ -1237,8 +1228,8 @@ describe("ProviderCommandReactor", () => {...@@ -1237,8 +1228,8 @@ describe("ProviderCommandReactor", () => {
1237 await Effect.runPromise(1228 await Effect.runPromise(
1238 harness.engine.dispatch({1229 harness.engine.dispatch({
1239 type: "thread.turn.interrupt",1230 type: "thread.turn.interrupt",
1240 commandId: CommandId.makeUnsafe("cmd-turn-interrupt"),1231 commandId: CommandId.make("cmd-turn-interrupt"),
1241 threadId: ThreadId.makeUnsafe("thread-1"),1232 threadId: ThreadId.make("thread-1"),
1242 turnId: asTurnId("turn-1"),1233 turnId: asTurnId("turn-1"),
1243 createdAt: now,1234 createdAt: now,
1244 }),1235 }),
...@@ -1257,10 +1248,10 @@ describe("ProviderCommandReactor", () => {...@@ -1257,10 +1248,10 @@ describe("ProviderCommandReactor", () => {
1257 await Effect.runPromise(1248 await Effect.runPromise(
1258 harness.engine.dispatch({1249 harness.engine.dispatch({
1259 type: "thread.session.set",1250 type: "thread.session.set",
1260 commandId: CommandId.makeUnsafe("cmd-session-set-for-approval"),1251 commandId: CommandId.make("cmd-session-set-for-approval"),
1261 threadId: ThreadId.makeUnsafe("thread-1"),1252 threadId: ThreadId.make("thread-1"),
1262 session: {1253 session: {
1263 threadId: ThreadId.makeUnsafe("thread-1"),1254 threadId: ThreadId.make("thread-1"),
1264 status: "running",1255 status: "running",
1265 providerName: "codex",1256 providerName: "codex",
1266 runtimeMode: "approval-required",1257 runtimeMode: "approval-required",
...@@ -1275,8 +1266,8 @@ describe("ProviderCommandReactor", () => {...@@ -1275,8 +1266,8 @@ describe("ProviderCommandReactor", () => {
1275 await Effect.runPromise(1266 await Effect.runPromise(
1276 harness.engine.dispatch({1267 harness.engine.dispatch({
1277 type: "thread.approval.respond",1268 type: "thread.approval.respond",
1278 commandId: CommandId.makeUnsafe("cmd-approval-respond"),1269 commandId: CommandId.make("cmd-approval-respond"),
1279 threadId: ThreadId.makeUnsafe("thread-1"),1270 threadId: ThreadId.make("thread-1"),
1280 requestId: asApprovalRequestId("approval-request-1"),1271 requestId: asApprovalRequestId("approval-request-1"),
1281 decision: "accept",1272 decision: "accept",
1282 createdAt: now,1273 createdAt: now,
...@@ -1298,10 +1289,10 @@ describe("ProviderCommandReactor", () => {...@@ -1298,10 +1289,10 @@ describe("ProviderCommandReactor", () => {
1298 await Effect.runPromise(1289 await Effect.runPromise(
1299 harness.engine.dispatch({1290 harness.engine.dispatch({
1300 type: "thread.session.set",1291 type: "thread.session.set",
1301 commandId: CommandId.makeUnsafe("cmd-session-set-for-user-input"),1292 commandId: CommandId.make("cmd-session-set-for-user-input"),
1302 threadId: ThreadId.makeUnsafe("thread-1"),1293 threadId: ThreadId.make("thread-1"),
1303 session: {1294 session: {
1304 threadId: ThreadId.makeUnsafe("thread-1"),1295 threadId: ThreadId.make("thread-1"),
1305 status: "running",1296 status: "running",
1306 providerName: "codex",1297 providerName: "codex",
1307 runtimeMode: "approval-required",1298 runtimeMode: "approval-required",
...@@ -1316,8 +1307,8 @@ describe("ProviderCommandReactor", () => {...@@ -1316,8 +1307,8 @@ describe("ProviderCommandReactor", () => {
1316 await Effect.runPromise(1307 await Effect.runPromise(
1317 harness.engine.dispatch({1308 harness.engine.dispatch({
1318 type: "thread.user-input.respond",1309 type: "thread.user-input.respond",
1319 commandId: CommandId.makeUnsafe("cmd-user-input-respond"),1310 commandId: CommandId.make("cmd-user-input-respond"),
1320 threadId: ThreadId.makeUnsafe("thread-1"),1311 threadId: ThreadId.make("thread-1"),
1321 requestId: asApprovalRequestId("user-input-request-1"),1312 requestId: asApprovalRequestId("user-input-request-1"),
1322 answers: {1313 answers: {
1323 sandbox_mode: "workspace-write",1314 sandbox_mode: "workspace-write",
...@@ -1352,10 +1343,10 @@ describe("ProviderCommandReactor", () => {...@@ -1352,10 +1343,10 @@ describe("ProviderCommandReactor", () => {
1352 await Effect.runPromise(1343 await Effect.runPromise(
1353 harness.engine.dispatch({1344 harness.engine.dispatch({
1354 type: "thread.session.set",1345 type: "thread.session.set",
1355 commandId: CommandId.makeUnsafe("cmd-session-set-for-approval-error"),1346 commandId: CommandId.make("cmd-session-set-for-approval-error"),
1356 threadId: ThreadId.makeUnsafe("thread-1"),1347 threadId: ThreadId.make("thread-1"),
1357 session: {1348 session: {
1358 threadId: ThreadId.makeUnsafe("thread-1"),1349 threadId: ThreadId.make("thread-1"),
1359 status: "running",1350 status: "running",
1360 providerName: "codex",1351 providerName: "codex",
1361 runtimeMode: "approval-required",1352 runtimeMode: "approval-required",
...@@ -1370,10 +1361,10 @@ describe("ProviderCommandReactor", () => {...@@ -1370,10 +1361,10 @@ describe("ProviderCommandReactor", () => {
1370 await Effect.runPromise(1361 await Effect.runPromise(
1371 harness.engine.dispatch({1362 harness.engine.dispatch({
1372 type: "thread.activity.append",1363 type: "thread.activity.append",
1373 commandId: CommandId.makeUnsafe("cmd-approval-requested"),1364 commandId: CommandId.make("cmd-approval-requested"),
1374 threadId: ThreadId.makeUnsafe("thread-1"),1365 threadId: ThreadId.make("thread-1"),
1375 activity: {1366 activity: {
1376 id: EventId.makeUnsafe("activity-approval-requested"),1367 id: EventId.make("activity-approval-requested"),
1377 tone: "approval",1368 tone: "approval",
1378 kind: "approval.requested",1369 kind: "approval.requested",
1379 summary: "Command approval requested",1370 summary: "Command approval requested",
...@@ -1391,8 +1382,8 @@ describe("ProviderCommandReactor", () => {...@@ -1391,8 +1382,8 @@ describe("ProviderCommandReactor", () => {
1391 await Effect.runPromise(1382 await Effect.runPromise(
1392 harness.engine.dispatch({1383 harness.engine.dispatch({
1393 type: "thread.approval.respond",1384 type: "thread.approval.respond",
1394 commandId: CommandId.makeUnsafe("cmd-approval-respond-stale"),1385 commandId: CommandId.make("cmd-approval-respond-stale"),
1395 threadId: ThreadId.makeUnsafe("thread-1"),1386 threadId: ThreadId.make("thread-1"),
1396 requestId: asApprovalRequestId("approval-request-1"),1387 requestId: asApprovalRequestId("approval-request-1"),
1397 decision: "acceptForSession",1388 decision: "acceptForSession",
1398 createdAt: now,1389 createdAt: now,
...@@ -1401,9 +1392,7 @@ describe("ProviderCommandReactor", () => {...@@ -1401,9 +1392,7 @@ describe("ProviderCommandReactor", () => {
14011392
1402 await waitFor(async () => {1393 await waitFor(async () => {
1403 const readModel = await Effect.runPromise(harness.engine.getReadModel());1394 const readModel = await Effect.runPromise(harness.engine.getReadModel());
1404 const thread = readModel.threads.find(1395 const thread = readModel.threads.find((entry) => entry.id === ThreadId.make("thread-1"));
1405 (entry) => entry.id === ThreadId.makeUnsafe("thread-1"),
1406 );
1407 if (!thread) return false;1396 if (!thread) return false;
1408 return thread.activities.some(1397 return thread.activities.some(
1409 (activity) => activity.kind === "provider.approval.respond.failed",1398 (activity) => activity.kind === "provider.approval.respond.failed",
...@@ -1411,7 +1400,7 @@ describe("ProviderCommandReactor", () => {...@@ -1411,7 +1400,7 @@ describe("ProviderCommandReactor", () => {
1411 });1400 });
14121401
1413 const readModel = await Effect.runPromise(harness.engine.getReadModel());1402 const readModel = await Effect.runPromise(harness.engine.getReadModel());
1414 const thread = readModel.threads.find((entry) => entry.id === ThreadId.makeUnsafe("thread-1"));1403 const thread = readModel.threads.find((entry) => entry.id === ThreadId.make("thread-1"));
1415 expect(thread).toBeDefined();1404 expect(thread).toBeDefined();
14161405
1417 const failureActivity = thread?.activities.find(1406 const failureActivity = thread?.activities.find(
...@@ -1449,10 +1438,10 @@ describe("ProviderCommandReactor", () => {...@@ -1449,10 +1438,10 @@ describe("ProviderCommandReactor", () => {
1449 await Effect.runPromise(1438 await Effect.runPromise(
1450 harness.engine.dispatch({1439 harness.engine.dispatch({
1451 type: "thread.session.set",1440 type: "thread.session.set",
1452 commandId: CommandId.makeUnsafe("cmd-session-set-for-user-input-error"),1441 commandId: CommandId.make("cmd-session-set-for-user-input-error"),
1453 threadId: ThreadId.makeUnsafe("thread-1"),1442 threadId: ThreadId.make("thread-1"),
1454 session: {1443 session: {
1455 threadId: ThreadId.makeUnsafe("thread-1"),1444 threadId: ThreadId.make("thread-1"),
1456 status: "running",1445 status: "running",
1457 providerName: "claudeAgent",1446 providerName: "claudeAgent",
1458 runtimeMode: "approval-required",1447 runtimeMode: "approval-required",
...@@ -1467,10 +1456,10 @@ describe("ProviderCommandReactor", () => {...@@ -1467,10 +1456,10 @@ describe("ProviderCommandReactor", () => {
1467 await Effect.runPromise(1456 await Effect.runPromise(
1468 harness.engine.dispatch({1457 harness.engine.dispatch({
1469 type: "thread.activity.append",1458 type: "thread.activity.append",
1470 commandId: CommandId.makeUnsafe("cmd-user-input-requested"),1459 commandId: CommandId.make("cmd-user-input-requested"),
1471 threadId: ThreadId.makeUnsafe("thread-1"),1460 threadId: ThreadId.make("thread-1"),
1472 activity: {1461 activity: {
1473 id: EventId.makeUnsafe("activity-user-input-requested"),1462 id: EventId.make("activity-user-input-requested"),
1474 tone: "info",1463 tone: "info",
1475 kind: "user-input.requested",1464 kind: "user-input.requested",
1476 summary: "User input requested",1465 summary: "User input requested",
...@@ -1500,8 +1489,8 @@ describe("ProviderCommandReactor", () => {...@@ -1500,8 +1489,8 @@ describe("ProviderCommandReactor", () => {
1500 await Effect.runPromise(1489 await Effect.runPromise(
1501 harness.engine.dispatch({1490 harness.engine.dispatch({
1502 type: "thread.user-input.respond",1491 type: "thread.user-input.respond",
1503 commandId: CommandId.makeUnsafe("cmd-user-input-respond-stale"),1492 commandId: CommandId.make("cmd-user-input-respond-stale"),
1504 threadId: ThreadId.makeUnsafe("thread-1"),1493 threadId: ThreadId.make("thread-1"),
1505 requestId: asApprovalRequestId("user-input-request-1"),1494 requestId: asApprovalRequestId("user-input-request-1"),
1506 answers: {1495 answers: {
1507 sandbox_mode: "workspace-write",1496 sandbox_mode: "workspace-write",
...@@ -1512,9 +1501,7 @@ describe("ProviderCommandReactor", () => {...@@ -1512,9 +1501,7 @@ describe("ProviderCommandReactor", () => {
15121501
1513 await waitFor(async () => {1502 await waitFor(async () => {
1514 const readModel = await Effect.runPromise(harness.engine.getReadModel());1503 const readModel = await Effect.runPromise(harness.engine.getReadModel());
1515 const thread = readModel.threads.find(1504 const thread = readModel.threads.find((entry) => entry.id === ThreadId.make("thread-1"));
1516 (entry) => entry.id === ThreadId.makeUnsafe("thread-1"),
1517 );
1518 if (!thread) return false;1505 if (!thread) return false;
1519 return thread.activities.some(1506 return thread.activities.some(
1520 (activity) => activity.kind === "provider.user-input.respond.failed",1507 (activity) => activity.kind === "provider.user-input.respond.failed",
...@@ -1522,7 +1509,7 @@ describe("ProviderCommandReactor", () => {...@@ -1522,7 +1509,7 @@ describe("ProviderCommandReactor", () => {
1522 });1509 });
15231510
1524 const readModel = await Effect.runPromise(harness.engine.getReadModel());1511 const readModel = await Effect.runPromise(harness.engine.getReadModel());
1525 const thread = readModel.threads.find((entry) => entry.id === ThreadId.makeUnsafe("thread-1"));1512 const thread = readModel.threads.find((entry) => entry.id === ThreadId.make("thread-1"));
1526 expect(thread).toBeDefined();1513 expect(thread).toBeDefined();
15271514
1528 const failureActivity = thread?.activities.find(1515 const failureActivity = thread?.activities.find(
...@@ -1551,10 +1538,10 @@ describe("ProviderCommandReactor", () => {...@@ -1551,10 +1538,10 @@ describe("ProviderCommandReactor", () => {
1551 await Effect.runPromise(1538 await Effect.runPromise(
1552 harness.engine.dispatch({1539 harness.engine.dispatch({
1553 type: "thread.session.set",1540 type: "thread.session.set",
1554 commandId: CommandId.makeUnsafe("cmd-session-set-for-stop"),1541 commandId: CommandId.make("cmd-session-set-for-stop"),
1555 threadId: ThreadId.makeUnsafe("thread-1"),1542 threadId: ThreadId.make("thread-1"),
1556 session: {1543 session: {
1557 threadId: ThreadId.makeUnsafe("thread-1"),1544 threadId: ThreadId.make("thread-1"),
1558 status: "ready",1545 status: "ready",
1559 providerName: "codex",1546 providerName: "codex",
1560 runtimeMode: "approval-required",1547 runtimeMode: "approval-required",
...@@ -1569,15 +1556,15 @@ describe("ProviderCommandReactor", () => {...@@ -1569,15 +1556,15 @@ describe("ProviderCommandReactor", () => {
1569 await Effect.runPromise(1556 await Effect.runPromise(
1570 harness.engine.dispatch({1557 harness.engine.dispatch({
1571 type: "thread.session.stop",1558 type: "thread.session.stop",
1572 commandId: CommandId.makeUnsafe("cmd-session-stop"),1559 commandId: CommandId.make("cmd-session-stop"),
1573 threadId: ThreadId.makeUnsafe("thread-1"),1560 threadId: ThreadId.make("thread-1"),
1574 createdAt: now,1561 createdAt: now,
1575 }),1562 }),
1576 );1563 );
15771564
1578 await waitFor(() => harness.stopSession.mock.calls.length === 1);1565 await waitFor(() => harness.stopSession.mock.calls.length === 1);
1579 const readModel = await Effect.runPromise(harness.engine.getReadModel());1566 const readModel = await Effect.runPromise(harness.engine.getReadModel());
1580 const thread = readModel.threads.find((entry) => entry.id === ThreadId.makeUnsafe("thread-1"));1567 const thread = readModel.threads.find((entry) => entry.id === ThreadId.make("thread-1"));
1581 expect(thread?.session).not.toBeNull();1568 expect(thread?.session).not.toBeNull();
1582 expect(thread?.session?.status).toBe("stopped");1569 expect(thread?.session?.status).toBe("stopped");
1583 expect(thread?.session?.threadId).toBe("thread-1");1570 expect(thread?.session?.threadId).toBe("thread-1");
apps/server/src/orchestration/Layers/ProviderCommandReactor.ts+2-2
...@@ -67,7 +67,7 @@ const turnStartKeyForEvent = (event: ProviderIntentEvent): string =>...@@ -67,7 +67,7 @@ const turnStartKeyForEvent = (event: ProviderIntentEvent): string =>
67 event.commandId !== null ? `command:${event.commandId}` : `event:${event.eventId}`;67 event.commandId !== null ? `command:${event.commandId}` : `event:${event.eventId}`;
6868
69const serverCommandId = (tag: string): CommandId =>69const serverCommandId = (tag: string): CommandId =>
70 CommandId.makeUnsafe(`server:${tag}:${crypto.randomUUID()}`);70 CommandId.make(`server:${tag}:${crypto.randomUUID()}`);
7171
72const HANDLED_TURN_START_KEY_MAX = 10_000;72const HANDLED_TURN_START_KEY_MAX = 10_000;
73const HANDLED_TURN_START_KEY_TTL = Duration.minutes(30);73const HANDLED_TURN_START_KEY_TTL = Duration.minutes(30);
...@@ -186,7 +186,7 @@ const make = Effect.gen(function* () {...@@ -186,7 +186,7 @@ const make = Effect.gen(function* () {
186 commandId: serverCommandId("provider-failure-activity"),186 commandId: serverCommandId("provider-failure-activity"),
187 threadId: input.threadId,187 threadId: input.threadId,
188 activity: {188 activity: {
189 id: EventId.makeUnsafe(crypto.randomUUID()),189 id: EventId.make(crypto.randomUUID()),
190 tone: "error",190 tone: "error",
191 kind: input.kind,191 kind: input.kind,
192 summary: input.summary,192 summary: input.summary,
apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.test.ts+40-48
...@@ -47,12 +47,12 @@ function makeTestServerSettingsLayer(overrides: Partial<ServerSettings> = {}) {...@@ -47,12 +47,12 @@ function makeTestServerSettingsLayer(overrides: Partial<ServerSettings> = {}) {
47 return ServerSettingsService.layerTest(overrides);47 return ServerSettingsService.layerTest(overrides);
48}48}
4949
50const asProjectId = (value: string): ProjectId => ProjectId.makeUnsafe(value);50const asProjectId = (value: string): ProjectId => ProjectId.make(value);
51const asItemId = (value: string): ProviderItemId => ProviderItemId.makeUnsafe(value);51const asItemId = (value: string): ProviderItemId => ProviderItemId.make(value);
52const asEventId = (value: string): EventId => EventId.makeUnsafe(value);52const asEventId = (value: string): EventId => EventId.make(value);
53const asMessageId = (value: string): MessageId => MessageId.makeUnsafe(value);53const asMessageId = (value: string): MessageId => MessageId.make(value);
54const asThreadId = (value: string): ThreadId => ThreadId.makeUnsafe(value);54const asThreadId = (value: string): ThreadId => ThreadId.make(value);
55const asTurnId = (value: string): TurnId => TurnId.makeUnsafe(value);55const asTurnId = (value: string): TurnId => TurnId.make(value);
5656
57type LegacyProviderRuntimeEvent = {57type LegacyProviderRuntimeEvent = {
58 readonly type: string;58 readonly type: string;
...@@ -227,7 +227,7 @@ describe("ProviderRuntimeIngestion", () => {...@@ -227,7 +227,7 @@ describe("ProviderRuntimeIngestion", () => {
227 await Effect.runPromise(227 await Effect.runPromise(
228 engine.dispatch({228 engine.dispatch({
229 type: "project.create",229 type: "project.create",
230 commandId: CommandId.makeUnsafe("cmd-provider-project-create"),230 commandId: CommandId.make("cmd-provider-project-create"),
231 projectId: asProjectId("project-1"),231 projectId: asProjectId("project-1"),
232 title: "Provider Project",232 title: "Provider Project",
233 workspaceRoot,233 workspaceRoot,
...@@ -241,8 +241,8 @@ describe("ProviderRuntimeIngestion", () => {...@@ -241,8 +241,8 @@ describe("ProviderRuntimeIngestion", () => {
241 await Effect.runPromise(241 await Effect.runPromise(
242 engine.dispatch({242 engine.dispatch({
243 type: "thread.create",243 type: "thread.create",
244 commandId: CommandId.makeUnsafe("cmd-thread-create"),244 commandId: CommandId.make("cmd-thread-create"),
245 threadId: ThreadId.makeUnsafe("thread-1"),245 threadId: ThreadId.make("thread-1"),
246 projectId: asProjectId("project-1"),246 projectId: asProjectId("project-1"),
247 title: "Thread",247 title: "Thread",
248 modelSelection: {248 modelSelection: {
...@@ -259,10 +259,10 @@ describe("ProviderRuntimeIngestion", () => {...@@ -259,10 +259,10 @@ describe("ProviderRuntimeIngestion", () => {
259 await Effect.runPromise(259 await Effect.runPromise(
260 engine.dispatch({260 engine.dispatch({
261 type: "thread.session.set",261 type: "thread.session.set",
262 commandId: CommandId.makeUnsafe("cmd-session-seed"),262 commandId: CommandId.make("cmd-session-seed"),
263 threadId: ThreadId.makeUnsafe("thread-1"),263 threadId: ThreadId.make("thread-1"),
264 session: {264 session: {
265 threadId: ThreadId.makeUnsafe("thread-1"),265 threadId: ThreadId.make("thread-1"),
266 status: "ready",266 status: "ready",
267 providerName: "codex",267 providerName: "codex",
268 runtimeMode: "approval-required",268 runtimeMode: "approval-required",
...@@ -277,7 +277,7 @@ describe("ProviderRuntimeIngestion", () => {...@@ -277,7 +277,7 @@ describe("ProviderRuntimeIngestion", () => {
277 provider: "codex",277 provider: "codex",
278 status: "ready",278 status: "ready",
279 runtimeMode: "approval-required",279 runtimeMode: "approval-required",
280 threadId: ThreadId.makeUnsafe("thread-1"),280 threadId: ThreadId.make("thread-1"),
281 createdAt,281 createdAt,
282 updatedAt: createdAt,282 updatedAt: createdAt,
283 });283 });
...@@ -457,9 +457,7 @@ describe("ProviderRuntimeIngestion", () => {...@@ -457,9 +457,7 @@ describe("ProviderRuntimeIngestion", () => {
457457
458 await harness.drain();458 await harness.drain();
459 const midReadModel = await Effect.runPromise(harness.engine.getReadModel());459 const midReadModel = await Effect.runPromise(harness.engine.getReadModel());
460 const midThread = midReadModel.threads.find(460 const midThread = midReadModel.threads.find((entry) => entry.id === ThreadId.make("thread-1"));
461 (entry) => entry.id === ThreadId.makeUnsafe("thread-1"),
462 );
463 expect(midThread?.session?.status).toBe("running");461 expect(midThread?.session?.status).toBe("running");
464 expect(midThread?.session?.activeTurnId).toBe("turn-midturn-lifecycle");462 expect(midThread?.session?.activeTurnId).toBe("turn-midturn-lifecycle");
465463
...@@ -486,10 +484,10 @@ describe("ProviderRuntimeIngestion", () => {...@@ -486,10 +484,10 @@ describe("ProviderRuntimeIngestion", () => {
486 await Effect.runPromise(484 await Effect.runPromise(
487 harness.engine.dispatch({485 harness.engine.dispatch({
488 type: "thread.session.set",486 type: "thread.session.set",
489 commandId: CommandId.makeUnsafe("cmd-session-seed-claude-placeholder"),487 commandId: CommandId.make("cmd-session-seed-claude-placeholder"),
490 threadId: ThreadId.makeUnsafe("thread-1"),488 threadId: ThreadId.make("thread-1"),
491 session: {489 session: {
492 threadId: ThreadId.makeUnsafe("thread-1"),490 threadId: ThreadId.make("thread-1"),
493 status: "ready",491 status: "ready",
494 providerName: "claudeAgent",492 providerName: "claudeAgent",
495 runtimeMode: "approval-required",493 runtimeMode: "approval-required",
...@@ -564,9 +562,7 @@ describe("ProviderRuntimeIngestion", () => {...@@ -564,9 +562,7 @@ describe("ProviderRuntimeIngestion", () => {
564562
565 await harness.drain();563 await harness.drain();
566 const midReadModel = await Effect.runPromise(harness.engine.getReadModel());564 const midReadModel = await Effect.runPromise(harness.engine.getReadModel());
567 const midThread = midReadModel.threads.find(565 const midThread = midReadModel.threads.find((entry) => entry.id === ThreadId.make("thread-1"));
568 (entry) => entry.id === ThreadId.makeUnsafe("thread-1"),
569 );
570 expect(midThread?.session?.status).toBe("running");566 expect(midThread?.session?.status).toBe("running");
571 expect(midThread?.session?.activeTurnId).toBe("turn-primary");567 expect(midThread?.session?.activeTurnId).toBe("turn-primary");
572568
...@@ -618,9 +614,7 @@ describe("ProviderRuntimeIngestion", () => {...@@ -618,9 +614,7 @@ describe("ProviderRuntimeIngestion", () => {
618614
619 await harness.drain();615 await harness.drain();
620 const midReadModel = await Effect.runPromise(harness.engine.getReadModel());616 const midReadModel = await Effect.runPromise(harness.engine.getReadModel());
621 const midThread = midReadModel.threads.find(617 const midThread = midReadModel.threads.find((entry) => entry.id === ThreadId.make("thread-1"));
622 (entry) => entry.id === ThreadId.makeUnsafe("thread-1"),
623 );
624 expect(midThread?.session?.status).toBe("running");618 expect(midThread?.session?.status).toBe("running");
625 expect(midThread?.session?.activeTurnId).toBe("turn-guarded-main");619 expect(midThread?.session?.activeTurnId).toBe("turn-guarded-main");
626620
...@@ -770,7 +764,7 @@ describe("ProviderRuntimeIngestion", () => {...@@ -770,7 +764,7 @@ describe("ProviderRuntimeIngestion", () => {
770 await Effect.runPromise(764 await Effect.runPromise(
771 harness.engine.dispatch({765 harness.engine.dispatch({
772 type: "thread.create",766 type: "thread.create",
773 commandId: CommandId.makeUnsafe("cmd-thread-create-plan-source"),767 commandId: CommandId.make("cmd-thread-create-plan-source"),
774 threadId: sourceThreadId,768 threadId: sourceThreadId,
775 projectId: asProjectId("project-1"),769 projectId: asProjectId("project-1"),
776 title: "Plan Source",770 title: "Plan Source",
...@@ -788,7 +782,7 @@ describe("ProviderRuntimeIngestion", () => {...@@ -788,7 +782,7 @@ describe("ProviderRuntimeIngestion", () => {
788 await Effect.runPromise(782 await Effect.runPromise(
789 harness.engine.dispatch({783 harness.engine.dispatch({
790 type: "thread.session.set",784 type: "thread.session.set",
791 commandId: CommandId.makeUnsafe("cmd-session-set-plan-source"),785 commandId: CommandId.make("cmd-session-set-plan-source"),
792 threadId: sourceThreadId,786 threadId: sourceThreadId,
793 session: {787 session: {
794 threadId: sourceThreadId,788 threadId: sourceThreadId,
...@@ -805,7 +799,7 @@ describe("ProviderRuntimeIngestion", () => {...@@ -805,7 +799,7 @@ describe("ProviderRuntimeIngestion", () => {
805 await Effect.runPromise(799 await Effect.runPromise(
806 harness.engine.dispatch({800 harness.engine.dispatch({
807 type: "thread.create",801 type: "thread.create",
808 commandId: CommandId.makeUnsafe("cmd-thread-create-plan-target"),802 commandId: CommandId.make("cmd-thread-create-plan-target"),
809 threadId: targetThreadId,803 threadId: targetThreadId,
810 projectId: asProjectId("project-1"),804 projectId: asProjectId("project-1"),
811 title: "Plan Target",805 title: "Plan Target",
...@@ -823,7 +817,7 @@ describe("ProviderRuntimeIngestion", () => {...@@ -823,7 +817,7 @@ describe("ProviderRuntimeIngestion", () => {
823 await Effect.runPromise(817 await Effect.runPromise(
824 harness.engine.dispatch({818 harness.engine.dispatch({
825 type: "thread.session.set",819 type: "thread.session.set",
826 commandId: CommandId.makeUnsafe("cmd-session-set-plan-target"),820 commandId: CommandId.make("cmd-session-set-plan-target"),
827 threadId: targetThreadId,821 threadId: targetThreadId,
828 session: {822 session: {
829 threadId: targetThreadId,823 threadId: targetThreadId,
...@@ -882,7 +876,7 @@ describe("ProviderRuntimeIngestion", () => {...@@ -882,7 +876,7 @@ describe("ProviderRuntimeIngestion", () => {
882 await Effect.runPromise(876 await Effect.runPromise(
883 harness.engine.dispatch({877 harness.engine.dispatch({
884 type: "thread.turn.start",878 type: "thread.turn.start",
885 commandId: CommandId.makeUnsafe("cmd-turn-start-plan-target"),879 commandId: CommandId.make("cmd-turn-start-plan-target"),
886 threadId: targetThreadId,880 threadId: targetThreadId,
887 message: {881 message: {
888 messageId: asMessageId("msg-plan-target"),882 messageId: asMessageId("msg-plan-target"),
...@@ -957,7 +951,7 @@ describe("ProviderRuntimeIngestion", () => {...@@ -957,7 +951,7 @@ describe("ProviderRuntimeIngestion", () => {
957 await Effect.runPromise(951 await Effect.runPromise(
958 harness.engine.dispatch({952 harness.engine.dispatch({
959 type: "thread.create",953 type: "thread.create",
960 commandId: CommandId.makeUnsafe("cmd-thread-create-plan-source-guarded"),954 commandId: CommandId.make("cmd-thread-create-plan-source-guarded"),
961 threadId: sourceThreadId,955 threadId: sourceThreadId,
962 projectId: asProjectId("project-1"),956 projectId: asProjectId("project-1"),
963 title: "Plan Source",957 title: "Plan Source",
...@@ -975,7 +969,7 @@ describe("ProviderRuntimeIngestion", () => {...@@ -975,7 +969,7 @@ describe("ProviderRuntimeIngestion", () => {
975 await Effect.runPromise(969 await Effect.runPromise(
976 harness.engine.dispatch({970 harness.engine.dispatch({
977 type: "thread.session.set",971 type: "thread.session.set",
978 commandId: CommandId.makeUnsafe("cmd-session-set-plan-source-guarded"),972 commandId: CommandId.make("cmd-session-set-plan-source-guarded"),
979 threadId: sourceThreadId,973 threadId: sourceThreadId,
980 session: {974 session: {
981 threadId: sourceThreadId,975 threadId: sourceThreadId,
...@@ -1051,7 +1045,7 @@ describe("ProviderRuntimeIngestion", () => {...@@ -1051,7 +1045,7 @@ describe("ProviderRuntimeIngestion", () => {
1051 await Effect.runPromise(1045 await Effect.runPromise(
1052 harness.engine.dispatch({1046 harness.engine.dispatch({
1053 type: "thread.turn.start",1047 type: "thread.turn.start",
1054 commandId: CommandId.makeUnsafe("cmd-turn-start-plan-target-guarded"),1048 commandId: CommandId.make("cmd-turn-start-plan-target-guarded"),
1055 threadId: targetThreadId,1049 threadId: targetThreadId,
1056 message: {1050 message: {
1057 messageId: asMessageId("msg-plan-target-guarded"),1051 messageId: asMessageId("msg-plan-target-guarded"),
...@@ -1110,7 +1104,7 @@ describe("ProviderRuntimeIngestion", () => {...@@ -1110,7 +1104,7 @@ describe("ProviderRuntimeIngestion", () => {
1110 await Effect.runPromise(1104 await Effect.runPromise(
1111 harness.engine.dispatch({1105 harness.engine.dispatch({
1112 type: "thread.create",1106 type: "thread.create",
1113 commandId: CommandId.makeUnsafe("cmd-thread-create-plan-source-unrelated"),1107 commandId: CommandId.make("cmd-thread-create-plan-source-unrelated"),
1114 threadId: sourceThreadId,1108 threadId: sourceThreadId,
1115 projectId: asProjectId("project-1"),1109 projectId: asProjectId("project-1"),
1116 title: "Plan Source",1110 title: "Plan Source",
...@@ -1128,7 +1122,7 @@ describe("ProviderRuntimeIngestion", () => {...@@ -1128,7 +1122,7 @@ describe("ProviderRuntimeIngestion", () => {
1128 await Effect.runPromise(1122 await Effect.runPromise(
1129 harness.engine.dispatch({1123 harness.engine.dispatch({
1130 type: "thread.session.set",1124 type: "thread.session.set",
1131 commandId: CommandId.makeUnsafe("cmd-session-set-plan-source-unrelated"),1125 commandId: CommandId.make("cmd-session-set-plan-source-unrelated"),
1132 threadId: sourceThreadId,1126 threadId: sourceThreadId,
1133 session: {1127 session: {
1134 threadId: sourceThreadId,1128 threadId: sourceThreadId,
...@@ -1145,7 +1139,7 @@ describe("ProviderRuntimeIngestion", () => {...@@ -1145,7 +1139,7 @@ describe("ProviderRuntimeIngestion", () => {
1145 await Effect.runPromise(1139 await Effect.runPromise(
1146 harness.engine.dispatch({1140 harness.engine.dispatch({
1147 type: "thread.create",1141 type: "thread.create",
1148 commandId: CommandId.makeUnsafe("cmd-thread-create-plan-target-unrelated"),1142 commandId: CommandId.make("cmd-thread-create-plan-target-unrelated"),
1149 threadId: targetThreadId,1143 threadId: targetThreadId,
1150 projectId: asProjectId("project-1"),1144 projectId: asProjectId("project-1"),
1151 title: "Plan Target",1145 title: "Plan Target",
...@@ -1163,7 +1157,7 @@ describe("ProviderRuntimeIngestion", () => {...@@ -1163,7 +1157,7 @@ describe("ProviderRuntimeIngestion", () => {
1163 await Effect.runPromise(1157 await Effect.runPromise(
1164 harness.engine.dispatch({1158 harness.engine.dispatch({
1165 type: "thread.session.set",1159 type: "thread.session.set",
1166 commandId: CommandId.makeUnsafe("cmd-session-set-plan-target-unrelated"),1160 commandId: CommandId.make("cmd-session-set-plan-target-unrelated"),
1167 threadId: targetThreadId,1161 threadId: targetThreadId,
1168 session: {1162 session: {
1169 threadId: targetThreadId,1163 threadId: targetThreadId,
...@@ -1213,7 +1207,7 @@ describe("ProviderRuntimeIngestion", () => {...@@ -1213,7 +1207,7 @@ describe("ProviderRuntimeIngestion", () => {
1213 await Effect.runPromise(1207 await Effect.runPromise(
1214 harness.engine.dispatch({1208 harness.engine.dispatch({
1215 type: "thread.turn.start",1209 type: "thread.turn.start",
1216 commandId: CommandId.makeUnsafe("cmd-turn-start-plan-target-unrelated"),1210 commandId: CommandId.make("cmd-turn-start-plan-target-unrelated"),
1217 threadId: targetThreadId,1211 threadId: targetThreadId,
1218 message: {1212 message: {
1219 messageId: asMessageId("msg-plan-target-unrelated"),1213 messageId: asMessageId("msg-plan-target-unrelated"),
...@@ -1364,9 +1358,7 @@ describe("ProviderRuntimeIngestion", () => {...@@ -1364,9 +1358,7 @@ describe("ProviderRuntimeIngestion", () => {
13641358
1365 await harness.drain();1359 await harness.drain();
1366 const midReadModel = await Effect.runPromise(harness.engine.getReadModel());1360 const midReadModel = await Effect.runPromise(harness.engine.getReadModel());
1367 const midThread = midReadModel.threads.find(1361 const midThread = midReadModel.threads.find((entry) => entry.id === ThreadId.make("thread-1"));
1368 (entry) => entry.id === ThreadId.makeUnsafe("thread-1"),
1369 );
1370 expect(1362 expect(
1371 midThread?.messages.some(1363 midThread?.messages.some(
1372 (message: ProviderRuntimeTestMessage) => message.id === "assistant:item-buffered",1364 (message: ProviderRuntimeTestMessage) => message.id === "assistant:item-buffered",
...@@ -1407,8 +1399,8 @@ describe("ProviderRuntimeIngestion", () => {...@@ -1407,8 +1399,8 @@ describe("ProviderRuntimeIngestion", () => {
1407 await Effect.runPromise(1399 await Effect.runPromise(
1408 harness.engine.dispatch({1400 harness.engine.dispatch({
1409 type: "thread.turn.start",1401 type: "thread.turn.start",
1410 commandId: CommandId.makeUnsafe("cmd-turn-start-streaming-mode"),1402 commandId: CommandId.make("cmd-turn-start-streaming-mode"),
1411 threadId: ThreadId.makeUnsafe("thread-1"),1403 threadId: ThreadId.make("thread-1"),
1412 message: {1404 message: {
1413 messageId: asMessageId("message-streaming-mode"),1405 messageId: asMessageId("message-streaming-mode"),
1414 role: "user",1406 role: "user",
...@@ -1649,7 +1641,7 @@ describe("ProviderRuntimeIngestion", () => {...@@ -1649,7 +1641,7 @@ describe("ProviderRuntimeIngestion", () => {
1649 provider: "codex",1641 provider: "codex",
1650 createdAt: now,1642 createdAt: now,
1651 threadId: asThreadId("thread-1"),1643 threadId: asThreadId("thread-1"),
1652 requestId: ApprovalRequestId.makeUnsafe("req-open"),1644 requestId: ApprovalRequestId.make("req-open"),
1653 payload: {1645 payload: {
1654 requestType: "command_execution_approval",1646 requestType: "command_execution_approval",
1655 detail: "pwd",1647 detail: "pwd",
...@@ -1662,7 +1654,7 @@ describe("ProviderRuntimeIngestion", () => {...@@ -1662,7 +1654,7 @@ describe("ProviderRuntimeIngestion", () => {
1662 provider: "codex",1654 provider: "codex",
1663 createdAt: now,1655 createdAt: now,
1664 threadId: asThreadId("thread-1"),1656 threadId: asThreadId("thread-1"),
1665 requestId: ApprovalRequestId.makeUnsafe("req-open"),1657 requestId: ApprovalRequestId.make("req-open"),
1666 payload: {1658 payload: {
1667 requestType: "command_execution_approval",1659 requestType: "command_execution_approval",
1668 decision: "accept",1660 decision: "accept",
...@@ -1681,7 +1673,7 @@ describe("ProviderRuntimeIngestion", () => {...@@ -1681,7 +1673,7 @@ describe("ProviderRuntimeIngestion", () => {
1681 );1673 );
16821674
1683 const readModel = await Effect.runPromise(harness.engine.getReadModel());1675 const readModel = await Effect.runPromise(harness.engine.getReadModel());
1684 const thread = readModel.threads.find((entry) => entry.id === ThreadId.makeUnsafe("thread-1"));1676 const thread = readModel.threads.find((entry) => entry.id === ThreadId.make("thread-1"));
1685 expect(thread).toBeDefined();1677 expect(thread).toBeDefined();
16861678
1687 const requested = thread?.activities.find(1679 const requested = thread?.activities.find(
...@@ -2286,7 +2278,7 @@ describe("ProviderRuntimeIngestion", () => {...@@ -2286,7 +2278,7 @@ describe("ProviderRuntimeIngestion", () => {
2286 createdAt: now,2278 createdAt: now,
2287 threadId: asThreadId("thread-1"),2279 threadId: asThreadId("thread-1"),
2288 turnId: asTurnId("turn-user-input"),2280 turnId: asTurnId("turn-user-input"),
2289 requestId: ApprovalRequestId.makeUnsafe("req-user-input-1"),2281 requestId: ApprovalRequestId.make("req-user-input-1"),
2290 payload: {2282 payload: {
2291 questions: [2283 questions: [
2292 {2284 {
...@@ -2311,7 +2303,7 @@ describe("ProviderRuntimeIngestion", () => {...@@ -2311,7 +2303,7 @@ describe("ProviderRuntimeIngestion", () => {
2311 createdAt: new Date().toISOString(),2303 createdAt: new Date().toISOString(),
2312 threadId: asThreadId("thread-1"),2304 threadId: asThreadId("thread-1"),
2313 turnId: asTurnId("turn-user-input"),2305 turnId: asTurnId("turn-user-input"),
2314 requestId: ApprovalRequestId.makeUnsafe("req-user-input-1"),2306 requestId: ApprovalRequestId.make("req-user-input-1"),
2315 payload: {2307 payload: {
2316 answers: {2308 answers: {
2317 sandbox_mode: "workspace-write",2309 sandbox_mode: "workspace-write",
apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts+8-10
...@@ -30,7 +30,7 @@ import { ServerSettingsService } from "../../serverSettings.ts";...@@ -30,7 +30,7 @@ import { ServerSettingsService } from "../../serverSettings.ts";
3030
31const providerTurnKey = (threadId: ThreadId, turnId: TurnId) => `${threadId}:${turnId}`;31const providerTurnKey = (threadId: ThreadId, turnId: TurnId) => `${threadId}:${turnId}`;
32const providerCommandId = (event: ProviderRuntimeEvent, tag: string): CommandId =>32const providerCommandId = (event: ProviderRuntimeEvent, tag: string): CommandId =>
33 CommandId.makeUnsafe(`provider:${event.eventId}:${tag}:${crypto.randomUUID()}`);33 CommandId.make(`provider:${event.eventId}:${tag}:${crypto.randomUUID()}`);
3434
35const TURN_MESSAGE_IDS_BY_TURN_CACHE_CAPACITY = 10_000;35const TURN_MESSAGE_IDS_BY_TURN_CACHE_CAPACITY = 10_000;
36const TURN_MESSAGE_IDS_BY_TURN_TTL = Duration.minutes(120);36const TURN_MESSAGE_IDS_BY_TURN_TTL = Duration.minutes(120);
...@@ -57,11 +57,11 @@ type RuntimeIngestionInput =...@@ -57,11 +57,11 @@ type RuntimeIngestionInput =
57 };57 };
5858
59function toTurnId(value: TurnId | string | undefined): TurnId | undefined {59function toTurnId(value: TurnId | string | undefined): TurnId | undefined {
60 return value === undefined ? undefined : TurnId.makeUnsafe(String(value));60 return value === undefined ? undefined : TurnId.make(String(value));
61}61}
6262
63function toApprovalRequestId(value: string | undefined): ApprovalRequestId | undefined {63function toApprovalRequestId(value: string | undefined): ApprovalRequestId | undefined {
64 return value === undefined ? undefined : ApprovalRequestId.makeUnsafe(value);64 return value === undefined ? undefined : ApprovalRequestId.make(value);
65}65}
6666
67function sameId(left: string | null | undefined, right: string | null | undefined): boolean {67function sameId(left: string | null | undefined, right: string | null | undefined): boolean {
...@@ -857,7 +857,7 @@ const make = Effect.fn("make")(function* () {...@@ -857,7 +857,7 @@ const make = Effect.fn("make")(function* () {
857857
858 yield* orchestrationEngine.dispatch({858 yield* orchestrationEngine.dispatch({
859 type: "thread.proposed-plan.upsert",859 type: "thread.proposed-plan.upsert",
860 commandId: CommandId.makeUnsafe(860 commandId: CommandId.make(
861 `provider:source-proposed-plan-implemented:${implementationThreadId}:${crypto.randomUUID()}`,861 `provider:source-proposed-plan-implemented:${implementationThreadId}:${crypto.randomUUID()}`,
862 ),862 ),
863 threadId: sourceThread.id,863 threadId: sourceThread.id,
...@@ -1003,7 +1003,7 @@ const make = Effect.fn("make")(function* () {...@@ -1003,7 +1003,7 @@ const make = Effect.fn("make")(function* () {
1003 event.type === "turn.proposed.delta" ? event.payload.delta : undefined;1003 event.type === "turn.proposed.delta" ? event.payload.delta : undefined;
10041004
1005 if (assistantDelta && assistantDelta.length > 0) {1005 if (assistantDelta && assistantDelta.length > 0) {
1006 const assistantMessageId = MessageId.makeUnsafe(1006 const assistantMessageId = MessageId.make(
1007 `assistant:${event.itemId ?? event.turnId ?? event.eventId}`,1007 `assistant:${event.itemId ?? event.turnId ?? event.eventId}`,
1008 );1008 );
1009 const turnId = toTurnId(event.turnId);1009 const turnId = toTurnId(event.turnId);
...@@ -1049,9 +1049,7 @@ const make = Effect.fn("make")(function* () {...@@ -1049,9 +1049,7 @@ const make = Effect.fn("make")(function* () {
1049 const assistantCompletion =1049 const assistantCompletion =
1050 event.type === "item.completed" && event.payload.itemType === "assistant_message"1050 event.type === "item.completed" && event.payload.itemType === "assistant_message"
1051 ? {1051 ? {
1052 messageId: MessageId.makeUnsafe(1052 messageId: MessageId.make(`assistant:${event.itemId ?? event.turnId ?? event.eventId}`),
1053 `assistant:${event.itemId ?? event.turnId ?? event.eventId}`,
1054 ),
1055 fallbackText: event.payload.detail,1053 fallbackText: event.payload.detail,
1056 }1054 }
1057 : undefined;1055 : undefined;
...@@ -1186,7 +1184,7 @@ const make = Effect.fn("make")(function* () {...@@ -1186,7 +1184,7 @@ const make = Effect.fn("make")(function* () {
1186 if (thread.checkpoints.some((c) => c.turnId === turnId)) {1184 if (thread.checkpoints.some((c) => c.turnId === turnId)) {
1187 // Already tracked; no-op.1185 // Already tracked; no-op.
1188 } else {1186 } else {
1189 const assistantMessageId = MessageId.makeUnsafe(1187 const assistantMessageId = MessageId.make(
1190 `assistant:${event.itemId ?? event.turnId ?? event.eventId}`,1188 `assistant:${event.itemId ?? event.turnId ?? event.eventId}`,
1191 );1189 );
1192 const maxTurnCount = thread.checkpoints.reduce(1190 const maxTurnCount = thread.checkpoints.reduce(
...@@ -1199,7 +1197,7 @@ const make = Effect.fn("make")(function* () {...@@ -1199,7 +1197,7 @@ const make = Effect.fn("make")(function* () {
1199 threadId: thread.id,1197 threadId: thread.id,
1200 turnId,1198 turnId,
1201 completedAt: now,1199 completedAt: now,
1202 checkpointRef: CheckpointRef.makeUnsafe(`provider-diff:${event.eventId}`),1200 checkpointRef: CheckpointRef.make(`provider-diff:${event.eventId}`),
1203 status: "missing",1201 status: "missing",
1204 files: [],1202 files: [],
1205 assistantMessageId,1203 assistantMessageId,
apps/server/src/orchestration/Services/CheckpointReactor.ts+4-5
...@@ -6,7 +6,7 @@...@@ -6,7 +6,7 @@
6 *6 *
7 * @module CheckpointReactor7 * @module CheckpointReactor
8 */8 */
9import { ServiceMap } from "effect";9import { Context } from "effect";
10import type { Effect, Scope } from "effect";10import type { Effect, Scope } from "effect";
1111
12/**12/**
...@@ -34,7 +34,6 @@ export interface CheckpointReactorShape {...@@ -34,7 +34,6 @@ export interface CheckpointReactorShape {
34/**34/**
35 * CheckpointReactor - Service tag for checkpoint reactor workers.35 * CheckpointReactor - Service tag for checkpoint reactor workers.
36 */36 */
37export class CheckpointReactor extends ServiceMap.Service<37export class CheckpointReactor extends Context.Service<CheckpointReactor, CheckpointReactorShape>()(
38 CheckpointReactor,38 "t3/orchestration/Services/CheckpointReactor",
39 CheckpointReactorShape39) {}
40>()("t3/orchestration/Services/CheckpointReactor") {}
apps/server/src/orchestration/Services/OrchestrationEngine.ts+3-3
...@@ -5,7 +5,7 @@...@@ -5,7 +5,7 @@
5 * `OrchestrationEventStore` persistence. It does not own provider process5 * `OrchestrationEventStore` persistence. It does not own provider process
6 * management or transport concerns (e.g. websocket request parsing).6 * management or transport concerns (e.g. websocket request parsing).
7 *7 *
8 * Uses Effect `ServiceMap.Service` for dependency injection. Command dispatch,8 * Uses Effect `Context.Service` for dependency injection. Command dispatch,
9 * replay, and unknown-input decoding all return typed domain errors.9 * replay, and unknown-input decoding all return typed domain errors.
10 *10 *
11 * @module OrchestrationEngineService11 * @module OrchestrationEngineService
...@@ -15,7 +15,7 @@ import type {...@@ -15,7 +15,7 @@ import type {
15 OrchestrationEvent,15 OrchestrationEvent,
16 OrchestrationReadModel,16 OrchestrationReadModel,
17} from "@t3tools/contracts";17} from "@t3tools/contracts";
18import { ServiceMap } from "effect";18import { Context } from "effect";
19import type { Effect, Stream } from "effect";19import type { Effect, Stream } from "effect";
2020
21import type { OrchestrationDispatchError } from "../Errors.ts";21import type { OrchestrationDispatchError } from "../Errors.ts";
...@@ -74,7 +74,7 @@ export interface OrchestrationEngineShape {...@@ -74,7 +74,7 @@ export interface OrchestrationEngineShape {
74 * })74 * })
75 * ```75 * ```
76 */76 */
77export class OrchestrationEngineService extends ServiceMap.Service<77export class OrchestrationEngineService extends Context.Service<
78 OrchestrationEngineService,78 OrchestrationEngineService,
79 OrchestrationEngineShape79 OrchestrationEngineShape
80>()("t3/orchestration/Services/OrchestrationEngine/OrchestrationEngineService") {}80>()("t3/orchestration/Services/OrchestrationEngine/OrchestrationEngineService") {}
apps/server/src/orchestration/Services/OrchestrationReactor.ts+2-2
...@@ -6,7 +6,7 @@...@@ -6,7 +6,7 @@
6 *6 *
7 * @module OrchestrationReactor7 * @module OrchestrationReactor
8 */8 */
9import { ServiceMap } from "effect";9import { Context } from "effect";
10import type { Effect, Scope } from "effect";10import type { Effect, Scope } from "effect";
1111
12/**12/**
...@@ -25,7 +25,7 @@ export interface OrchestrationReactorShape {...@@ -25,7 +25,7 @@ export interface OrchestrationReactorShape {
25/**25/**
26 * OrchestrationReactor - Service tag for orchestration reactor coordination.26 * OrchestrationReactor - Service tag for orchestration reactor coordination.
27 */27 */
28export class OrchestrationReactor extends ServiceMap.Service<28export class OrchestrationReactor extends Context.Service<
29 OrchestrationReactor,29 OrchestrationReactor,
30 OrchestrationReactorShape30 OrchestrationReactorShape
31>()("t3/orchestration/Services/OrchestrationReactor") {}31>()("t3/orchestration/Services/OrchestrationReactor") {}
apps/server/src/orchestration/Services/ProjectionPipeline.ts+2-2
...@@ -7,7 +7,7 @@...@@ -7,7 +7,7 @@
7 * @module OrchestrationProjectionPipeline7 * @module OrchestrationProjectionPipeline
8 */8 */
9import type { OrchestrationEvent } from "@t3tools/contracts";9import type { OrchestrationEvent } from "@t3tools/contracts";
10import { ServiceMap } from "effect";10import { Context } from "effect";
11import type { Effect } from "effect";11import type { Effect } from "effect";
1212
13import type { ProjectionRepositoryError } from "../../persistence/Errors.ts";13import type { ProjectionRepositoryError } from "../../persistence/Errors.ts";
...@@ -36,7 +36,7 @@ export interface OrchestrationProjectionPipelineShape {...@@ -36,7 +36,7 @@ export interface OrchestrationProjectionPipelineShape {
36/**36/**
37 * OrchestrationProjectionPipeline - Service tag for orchestration projections.37 * OrchestrationProjectionPipeline - Service tag for orchestration projections.
38 */38 */
39export class OrchestrationProjectionPipeline extends ServiceMap.Service<39export class OrchestrationProjectionPipeline extends Context.Service<
40 OrchestrationProjectionPipeline,40 OrchestrationProjectionPipeline,
41 OrchestrationProjectionPipelineShape41 OrchestrationProjectionPipelineShape
42>()("t3/orchestration/Services/ProjectionPipeline/OrchestrationProjectionPipeline") {}42>()("t3/orchestration/Services/ProjectionPipeline/OrchestrationProjectionPipeline") {}
apps/server/src/orchestration/Services/ProjectionSnapshotQuery.ts+2-2
...@@ -13,7 +13,7 @@ import type {...@@ -13,7 +13,7 @@ import type {
13 ProjectId,13 ProjectId,
14 ThreadId,14 ThreadId,
15} from "@t3tools/contracts";15} from "@t3tools/contracts";
16import { ServiceMap } from "effect";16import { Context } from "effect";
17import type { Option } from "effect";17import type { Option } from "effect";
18import type { Effect } from "effect";18import type { Effect } from "effect";
1919
...@@ -74,7 +74,7 @@ export interface ProjectionSnapshotQueryShape {...@@ -74,7 +74,7 @@ export interface ProjectionSnapshotQueryShape {
74/**74/**
75 * ProjectionSnapshotQuery - Service tag for projection snapshot queries.75 * ProjectionSnapshotQuery - Service tag for projection snapshot queries.
76 */76 */
77export class ProjectionSnapshotQuery extends ServiceMap.Service<77export class ProjectionSnapshotQuery extends Context.Service<
78 ProjectionSnapshotQuery,78 ProjectionSnapshotQuery,
79 ProjectionSnapshotQueryShape79 ProjectionSnapshotQueryShape
80>()("t3/orchestration/Services/ProjectionSnapshotQuery") {}80>()("t3/orchestration/Services/ProjectionSnapshotQuery") {}
apps/server/src/orchestration/Services/ProviderCommandReactor.ts+2-2
...@@ -6,7 +6,7 @@...@@ -6,7 +6,7 @@
6 *6 *
7 * @module ProviderCommandReactor7 * @module ProviderCommandReactor
8 */8 */
9import { ServiceMap } from "effect";9import { Context } from "effect";
10import type { Effect, Scope } from "effect";10import type { Effect, Scope } from "effect";
1111
12/**12/**
...@@ -34,7 +34,7 @@ export interface ProviderCommandReactorShape {...@@ -34,7 +34,7 @@ export interface ProviderCommandReactorShape {
34/**34/**
35 * ProviderCommandReactor - Service tag for provider command reaction workers.35 * ProviderCommandReactor - Service tag for provider command reaction workers.
36 */36 */
37export class ProviderCommandReactor extends ServiceMap.Service<37export class ProviderCommandReactor extends Context.Service<
38 ProviderCommandReactor,38 ProviderCommandReactor,
39 ProviderCommandReactorShape39 ProviderCommandReactorShape
40>()("t3/orchestration/Services/ProviderCommandReactor") {}40>()("t3/orchestration/Services/ProviderCommandReactor") {}
apps/server/src/orchestration/Services/ProviderRuntimeIngestion.ts+2-2
...@@ -6,7 +6,7 @@...@@ -6,7 +6,7 @@
6 *6 *
7 * @module ProviderRuntimeIngestionService7 * @module ProviderRuntimeIngestionService
8 */8 */
9import { ServiceMap } from "effect";9import { Context } from "effect";
10import type { Effect, Scope } from "effect";10import type { Effect, Scope } from "effect";
1111
12/**12/**
...@@ -34,7 +34,7 @@ export interface ProviderRuntimeIngestionShape {...@@ -34,7 +34,7 @@ export interface ProviderRuntimeIngestionShape {
34/**34/**
35 * ProviderRuntimeIngestionService - Service tag for runtime ingestion workers.35 * ProviderRuntimeIngestionService - Service tag for runtime ingestion workers.
36 */36 */
37export class ProviderRuntimeIngestionService extends ServiceMap.Service<37export class ProviderRuntimeIngestionService extends Context.Service<
38 ProviderRuntimeIngestionService,38 ProviderRuntimeIngestionService,
39 ProviderRuntimeIngestionShape39 ProviderRuntimeIngestionShape
40>()("t3/orchestration/Services/ProviderRuntimeIngestion/ProviderRuntimeIngestionService") {}40>()("t3/orchestration/Services/ProviderRuntimeIngestion/ProviderRuntimeIngestionService") {}
apps/server/src/orchestration/Services/RuntimeReceiptBus.ts+4-5
...@@ -15,7 +15,7 @@...@@ -15,7 +15,7 @@
15 * @module RuntimeReceiptBus15 * @module RuntimeReceiptBus
16 */16 */
17import { CheckpointRef, IsoDateTime, NonNegativeInt, ThreadId, TurnId } from "@t3tools/contracts";17import { CheckpointRef, IsoDateTime, NonNegativeInt, ThreadId, TurnId } from "@t3tools/contracts";
18import { Schema, ServiceMap } from "effect";18import { Schema, Context } from "effect";
19import type { Effect, Stream } from "effect";19import type { Effect, Stream } from "effect";
2020
21export const CheckpointBaselineCapturedReceipt = Schema.Struct({21export const CheckpointBaselineCapturedReceipt = Schema.Struct({
...@@ -59,7 +59,6 @@ export interface RuntimeReceiptBusShape {...@@ -59,7 +59,6 @@ export interface RuntimeReceiptBusShape {
59 readonly streamEventsForTest: Stream.Stream<OrchestrationRuntimeReceipt>;59 readonly streamEventsForTest: Stream.Stream<OrchestrationRuntimeReceipt>;
60}60}
6161
62export class RuntimeReceiptBus extends ServiceMap.Service<62export class RuntimeReceiptBus extends Context.Service<RuntimeReceiptBus, RuntimeReceiptBusShape>()(
63 RuntimeReceiptBus,63 "t3/orchestration/Services/RuntimeReceiptBus",
64 RuntimeReceiptBusShape64) {}
65>()("t3/orchestration/Services/RuntimeReceiptBus") {}
apps/server/src/orchestration/commandInvariants.test.ts+24-26
...@@ -25,7 +25,7 @@ const readModel: OrchestrationReadModel = {...@@ -25,7 +25,7 @@ const readModel: OrchestrationReadModel = {
25 updatedAt: now,25 updatedAt: now,
26 projects: [26 projects: [
27 {27 {
28 id: ProjectId.makeUnsafe("project-a"),28 id: ProjectId.make("project-a"),
29 title: "Project A",29 title: "Project A",
30 workspaceRoot: "/tmp/project-a",30 workspaceRoot: "/tmp/project-a",
31 defaultModelSelection: {31 defaultModelSelection: {
...@@ -38,7 +38,7 @@ const readModel: OrchestrationReadModel = {...@@ -38,7 +38,7 @@ const readModel: OrchestrationReadModel = {
38 deletedAt: null,38 deletedAt: null,
39 },39 },
40 {40 {
41 id: ProjectId.makeUnsafe("project-b"),41 id: ProjectId.make("project-b"),
42 title: "Project B",42 title: "Project B",
43 workspaceRoot: "/tmp/project-b",43 workspaceRoot: "/tmp/project-b",
44 defaultModelSelection: {44 defaultModelSelection: {
...@@ -53,8 +53,8 @@ const readModel: OrchestrationReadModel = {...@@ -53,8 +53,8 @@ const readModel: OrchestrationReadModel = {
53 ],53 ],
54 threads: [54 threads: [
55 {55 {
56 id: ThreadId.makeUnsafe("thread-1"),56 id: ThreadId.make("thread-1"),
57 projectId: ProjectId.makeUnsafe("project-a"),57 projectId: ProjectId.make("project-a"),
58 title: "Thread A",58 title: "Thread A",
59 modelSelection: {59 modelSelection: {
60 provider: "codex",60 provider: "codex",
...@@ -76,8 +76,8 @@ const readModel: OrchestrationReadModel = {...@@ -76,8 +76,8 @@ const readModel: OrchestrationReadModel = {
76 deletedAt: null,76 deletedAt: null,
77 },77 },
78 {78 {
79 id: ThreadId.makeUnsafe("thread-2"),79 id: ThreadId.make("thread-2"),
80 projectId: ProjectId.makeUnsafe("project-b"),80 projectId: ProjectId.make("project-b"),
81 title: "Thread B",81 title: "Thread B",
82 modelSelection: {82 modelSelection: {
83 provider: "codex",83 provider: "codex",
...@@ -103,10 +103,10 @@ const readModel: OrchestrationReadModel = {...@@ -103,10 +103,10 @@ const readModel: OrchestrationReadModel = {
103103
104const messageSendCommand: OrchestrationCommand = {104const messageSendCommand: OrchestrationCommand = {
105 type: "thread.turn.start",105 type: "thread.turn.start",
106 commandId: CommandId.makeUnsafe("cmd-1"),106 commandId: CommandId.make("cmd-1"),
107 threadId: ThreadId.makeUnsafe("thread-1"),107 threadId: ThreadId.make("thread-1"),
108 message: {108 message: {
109 messageId: MessageId.makeUnsafe("msg-1"),109 messageId: MessageId.make("msg-1"),
110 role: "user",110 role: "user",
111 text: "hello",111 text: "hello",
112 attachments: [],112 attachments: [],
...@@ -118,13 +118,11 @@ const messageSendCommand: OrchestrationCommand = {...@@ -118,13 +118,11 @@ const messageSendCommand: OrchestrationCommand = {
118118
119describe("commandInvariants", () => {119describe("commandInvariants", () => {
120 it("finds threads by id and project", () => {120 it("finds threads by id and project", () => {
121 expect(findThreadById(readModel, ThreadId.makeUnsafe("thread-1"))?.projectId).toBe("project-a");121 expect(findThreadById(readModel, ThreadId.make("thread-1"))?.projectId).toBe("project-a");
122 expect(findThreadById(readModel, ThreadId.makeUnsafe("missing"))).toBeUndefined();122 expect(findThreadById(readModel, ThreadId.make("missing"))).toBeUndefined();
123 expect(123 expect(
124 listThreadsByProjectId(readModel, ProjectId.makeUnsafe("project-b")).map(124 listThreadsByProjectId(readModel, ProjectId.make("project-b")).map((thread) => thread.id),
125 (thread) => thread.id,125 ).toEqual([ThreadId.make("thread-2")]);
126 ),
127 ).toEqual([ThreadId.makeUnsafe("thread-2")]);
128 });126 });
129127
130 it("requires existing thread", async () => {128 it("requires existing thread", async () => {
...@@ -132,17 +130,17 @@ describe("commandInvariants", () => {...@@ -132,17 +130,17 @@ describe("commandInvariants", () => {
132 requireThread({130 requireThread({
133 readModel,131 readModel,
134 command: messageSendCommand,132 command: messageSendCommand,
135 threadId: ThreadId.makeUnsafe("thread-1"),133 threadId: ThreadId.make("thread-1"),
136 }),134 }),
137 );135 );
138 expect(thread.id).toBe(ThreadId.makeUnsafe("thread-1"));136 expect(thread.id).toBe(ThreadId.make("thread-1"));
139137
140 await expect(138 await expect(
141 Effect.runPromise(139 Effect.runPromise(
142 requireThread({140 requireThread({
143 readModel,141 readModel,
144 command: messageSendCommand,142 command: messageSendCommand,
145 threadId: ThreadId.makeUnsafe("missing"),143 threadId: ThreadId.make("missing"),
146 }),144 }),
147 ),145 ),
148 ).rejects.toThrow("does not exist");146 ).rejects.toThrow("does not exist");
...@@ -154,9 +152,9 @@ describe("commandInvariants", () => {...@@ -154,9 +152,9 @@ describe("commandInvariants", () => {
154 readModel,152 readModel,
155 command: {153 command: {
156 type: "thread.create",154 type: "thread.create",
157 commandId: CommandId.makeUnsafe("cmd-2"),155 commandId: CommandId.make("cmd-2"),
158 threadId: ThreadId.makeUnsafe("thread-3"),156 threadId: ThreadId.make("thread-3"),
159 projectId: ProjectId.makeUnsafe("project-a"),157 projectId: ProjectId.make("project-a"),
160 title: "new",158 title: "new",
161 modelSelection: {159 modelSelection: {
162 provider: "codex",160 provider: "codex",
...@@ -168,7 +166,7 @@ describe("commandInvariants", () => {...@@ -168,7 +166,7 @@ describe("commandInvariants", () => {
168 worktreePath: null,166 worktreePath: null,
169 createdAt: now,167 createdAt: now,
170 },168 },
171 threadId: ThreadId.makeUnsafe("thread-3"),169 threadId: ThreadId.make("thread-3"),
172 }),170 }),
173 );171 );
174172
...@@ -178,9 +176,9 @@ describe("commandInvariants", () => {...@@ -178,9 +176,9 @@ describe("commandInvariants", () => {
178 readModel,176 readModel,
179 command: {177 command: {
180 type: "thread.create",178 type: "thread.create",
181 commandId: CommandId.makeUnsafe("cmd-3"),179 commandId: CommandId.make("cmd-3"),
182 threadId: ThreadId.makeUnsafe("thread-1"),180 threadId: ThreadId.make("thread-1"),
183 projectId: ProjectId.makeUnsafe("project-a"),181 projectId: ProjectId.make("project-a"),
184 title: "dup",182 title: "dup",
185 modelSelection: {183 modelSelection: {
186 provider: "codex",184 provider: "codex",
...@@ -192,7 +190,7 @@ describe("commandInvariants", () => {...@@ -192,7 +190,7 @@ describe("commandInvariants", () => {
192 worktreePath: null,190 worktreePath: null,
193 createdAt: now,191 createdAt: now,
194 },192 },
195 threadId: ThreadId.makeUnsafe("thread-1"),193 threadId: ThreadId.make("thread-1"),
196 }),194 }),
197 ),195 ),
198 ).rejects.toThrow("already exists");196 ).rejects.toThrow("already exists");
apps/server/src/orchestration/decider.projectScripts.test.ts+34-34
...@@ -12,9 +12,9 @@ import { Effect } from "effect";...@@ -12,9 +12,9 @@ import { Effect } from "effect";
12import { decideOrchestrationCommand } from "./decider.ts";12import { decideOrchestrationCommand } from "./decider.ts";
13import { createEmptyReadModel, projectEvent } from "./projector.ts";13import { createEmptyReadModel, projectEvent } from "./projector.ts";
1414
15const asEventId = (value: string): EventId => EventId.makeUnsafe(value);15const asEventId = (value: string): EventId => EventId.make(value);
16const asProjectId = (value: string): ProjectId => ProjectId.makeUnsafe(value);16const asProjectId = (value: string): ProjectId => ProjectId.make(value);
17const asMessageId = (value: string): MessageId => MessageId.makeUnsafe(value);17const asMessageId = (value: string): MessageId => MessageId.make(value);
1818
19describe("decider project scripts", () => {19describe("decider project scripts", () => {
20 it("emits empty scripts on project.create", async () => {20 it("emits empty scripts on project.create", async () => {
...@@ -25,7 +25,7 @@ describe("decider project scripts", () => {...@@ -25,7 +25,7 @@ describe("decider project scripts", () => {
25 decideOrchestrationCommand({25 decideOrchestrationCommand({
26 command: {26 command: {
27 type: "project.create",27 type: "project.create",
28 commandId: CommandId.makeUnsafe("cmd-project-create-scripts"),28 commandId: CommandId.make("cmd-project-create-scripts"),
29 projectId: asProjectId("project-scripts"),29 projectId: asProjectId("project-scripts"),
30 title: "Scripts",30 title: "Scripts",
31 workspaceRoot: "/tmp/scripts",31 workspaceRoot: "/tmp/scripts",
...@@ -51,9 +51,9 @@ describe("decider project scripts", () => {...@@ -51,9 +51,9 @@ describe("decider project scripts", () => {
51 aggregateId: asProjectId("project-scripts"),51 aggregateId: asProjectId("project-scripts"),
52 type: "project.created",52 type: "project.created",
53 occurredAt: now,53 occurredAt: now,
54 commandId: CommandId.makeUnsafe("cmd-project-create-scripts"),54 commandId: CommandId.make("cmd-project-create-scripts"),
55 causationEventId: null,55 causationEventId: null,
56 correlationId: CommandId.makeUnsafe("cmd-project-create-scripts"),56 correlationId: CommandId.make("cmd-project-create-scripts"),
57 metadata: {},57 metadata: {},
58 payload: {58 payload: {
59 projectId: asProjectId("project-scripts"),59 projectId: asProjectId("project-scripts"),
...@@ -81,7 +81,7 @@ describe("decider project scripts", () => {...@@ -81,7 +81,7 @@ describe("decider project scripts", () => {
81 decideOrchestrationCommand({81 decideOrchestrationCommand({
82 command: {82 command: {
83 type: "project.meta.update",83 type: "project.meta.update",
84 commandId: CommandId.makeUnsafe("cmd-project-update-scripts"),84 commandId: CommandId.make("cmd-project-update-scripts"),
85 projectId: asProjectId("project-scripts"),85 projectId: asProjectId("project-scripts"),
86 scripts: Array.from(scripts),86 scripts: Array.from(scripts),
87 },87 },
...@@ -105,9 +105,9 @@ describe("decider project scripts", () => {...@@ -105,9 +105,9 @@ describe("decider project scripts", () => {
105 aggregateId: asProjectId("project-1"),105 aggregateId: asProjectId("project-1"),
106 type: "project.created",106 type: "project.created",
107 occurredAt: now,107 occurredAt: now,
108 commandId: CommandId.makeUnsafe("cmd-project-create"),108 commandId: CommandId.make("cmd-project-create"),
109 causationEventId: null,109 causationEventId: null,
110 correlationId: CommandId.makeUnsafe("cmd-project-create"),110 correlationId: CommandId.make("cmd-project-create"),
111 metadata: {},111 metadata: {},
112 payload: {112 payload: {
113 projectId: asProjectId("project-1"),113 projectId: asProjectId("project-1"),
...@@ -125,15 +125,15 @@ describe("decider project scripts", () => {...@@ -125,15 +125,15 @@ describe("decider project scripts", () => {
125 sequence: 2,125 sequence: 2,
126 eventId: asEventId("evt-thread-create"),126 eventId: asEventId("evt-thread-create"),
127 aggregateKind: "thread",127 aggregateKind: "thread",
128 aggregateId: ThreadId.makeUnsafe("thread-1"),128 aggregateId: ThreadId.make("thread-1"),
129 type: "thread.created",129 type: "thread.created",
130 occurredAt: now,130 occurredAt: now,
131 commandId: CommandId.makeUnsafe("cmd-thread-create"),131 commandId: CommandId.make("cmd-thread-create"),
132 causationEventId: null,132 causationEventId: null,
133 correlationId: CommandId.makeUnsafe("cmd-thread-create"),133 correlationId: CommandId.make("cmd-thread-create"),
134 metadata: {},134 metadata: {},
135 payload: {135 payload: {
136 threadId: ThreadId.makeUnsafe("thread-1"),136 threadId: ThreadId.make("thread-1"),
137 projectId: asProjectId("project-1"),137 projectId: asProjectId("project-1"),
138 title: "Thread",138 title: "Thread",
139 modelSelection: {139 modelSelection: {
...@@ -154,8 +154,8 @@ describe("decider project scripts", () => {...@@ -154,8 +154,8 @@ describe("decider project scripts", () => {
154 decideOrchestrationCommand({154 decideOrchestrationCommand({
155 command: {155 command: {
156 type: "thread.turn.start",156 type: "thread.turn.start",
157 commandId: CommandId.makeUnsafe("cmd-turn-start"),157 commandId: CommandId.make("cmd-turn-start"),
158 threadId: ThreadId.makeUnsafe("thread-1"),158 threadId: ThreadId.make("thread-1"),
159 message: {159 message: {
160 messageId: asMessageId("message-user-1"),160 messageId: asMessageId("message-user-1"),
161 role: "user",161 role: "user",
...@@ -189,7 +189,7 @@ describe("decider project scripts", () => {...@@ -189,7 +189,7 @@ describe("decider project scripts", () => {
189 return;189 return;
190 }190 }
191 expect(turnStartEvent.payload).toMatchObject({191 expect(turnStartEvent.payload).toMatchObject({
192 threadId: ThreadId.makeUnsafe("thread-1"),192 threadId: ThreadId.make("thread-1"),
193 messageId: asMessageId("message-user-1"),193 messageId: asMessageId("message-user-1"),
194 modelSelection: {194 modelSelection: {
195 provider: "codex",195 provider: "codex",
...@@ -214,9 +214,9 @@ describe("decider project scripts", () => {...@@ -214,9 +214,9 @@ describe("decider project scripts", () => {
214 aggregateId: asProjectId("project-1"),214 aggregateId: asProjectId("project-1"),
215 type: "project.created",215 type: "project.created",
216 occurredAt: now,216 occurredAt: now,
217 commandId: CommandId.makeUnsafe("cmd-project-create"),217 commandId: CommandId.make("cmd-project-create"),
218 causationEventId: null,218 causationEventId: null,
219 correlationId: CommandId.makeUnsafe("cmd-project-create"),219 correlationId: CommandId.make("cmd-project-create"),
220 metadata: {},220 metadata: {},
221 payload: {221 payload: {
222 projectId: asProjectId("project-1"),222 projectId: asProjectId("project-1"),
...@@ -234,15 +234,15 @@ describe("decider project scripts", () => {...@@ -234,15 +234,15 @@ describe("decider project scripts", () => {
234 sequence: 2,234 sequence: 2,
235 eventId: asEventId("evt-thread-create"),235 eventId: asEventId("evt-thread-create"),
236 aggregateKind: "thread",236 aggregateKind: "thread",
237 aggregateId: ThreadId.makeUnsafe("thread-1"),237 aggregateId: ThreadId.make("thread-1"),
238 type: "thread.created",238 type: "thread.created",
239 occurredAt: now,239 occurredAt: now,
240 commandId: CommandId.makeUnsafe("cmd-thread-create"),240 commandId: CommandId.make("cmd-thread-create"),
241 causationEventId: null,241 causationEventId: null,
242 correlationId: CommandId.makeUnsafe("cmd-thread-create"),242 correlationId: CommandId.make("cmd-thread-create"),
243 metadata: {},243 metadata: {},
244 payload: {244 payload: {
245 threadId: ThreadId.makeUnsafe("thread-1"),245 threadId: ThreadId.make("thread-1"),
246 projectId: asProjectId("project-1"),246 projectId: asProjectId("project-1"),
247 title: "Thread",247 title: "Thread",
248 modelSelection: {248 modelSelection: {
...@@ -263,8 +263,8 @@ describe("decider project scripts", () => {...@@ -263,8 +263,8 @@ describe("decider project scripts", () => {
263 decideOrchestrationCommand({263 decideOrchestrationCommand({
264 command: {264 command: {
265 type: "thread.runtime-mode.set",265 type: "thread.runtime-mode.set",
266 commandId: CommandId.makeUnsafe("cmd-runtime-mode-set"),266 commandId: CommandId.make("cmd-runtime-mode-set"),
267 threadId: ThreadId.makeUnsafe("thread-1"),267 threadId: ThreadId.make("thread-1"),
268 runtimeMode: "approval-required",268 runtimeMode: "approval-required",
269 createdAt: now,269 createdAt: now,
270 },270 },
...@@ -279,7 +279,7 @@ describe("decider project scripts", () => {...@@ -279,7 +279,7 @@ describe("decider project scripts", () => {
279 expect(singleResult).toMatchObject({279 expect(singleResult).toMatchObject({
280 type: "thread.runtime-mode-set",280 type: "thread.runtime-mode-set",
281 payload: {281 payload: {
282 threadId: ThreadId.makeUnsafe("thread-1"),282 threadId: ThreadId.make("thread-1"),
283 runtimeMode: "approval-required",283 runtimeMode: "approval-required",
284 },284 },
285 });285 });
...@@ -296,9 +296,9 @@ describe("decider project scripts", () => {...@@ -296,9 +296,9 @@ describe("decider project scripts", () => {
296 aggregateId: asProjectId("project-1"),296 aggregateId: asProjectId("project-1"),
297 type: "project.created",297 type: "project.created",
298 occurredAt: now,298 occurredAt: now,
299 commandId: CommandId.makeUnsafe("cmd-project-create"),299 commandId: CommandId.make("cmd-project-create"),
300 causationEventId: null,300 causationEventId: null,
301 correlationId: CommandId.makeUnsafe("cmd-project-create"),301 correlationId: CommandId.make("cmd-project-create"),
302 metadata: {},302 metadata: {},
303 payload: {303 payload: {
304 projectId: asProjectId("project-1"),304 projectId: asProjectId("project-1"),
...@@ -316,15 +316,15 @@ describe("decider project scripts", () => {...@@ -316,15 +316,15 @@ describe("decider project scripts", () => {
316 sequence: 2,316 sequence: 2,
317 eventId: asEventId("evt-thread-create"),317 eventId: asEventId("evt-thread-create"),
318 aggregateKind: "thread",318 aggregateKind: "thread",
319 aggregateId: ThreadId.makeUnsafe("thread-1"),319 aggregateId: ThreadId.make("thread-1"),
320 type: "thread.created",320 type: "thread.created",
321 occurredAt: now,321 occurredAt: now,
322 commandId: CommandId.makeUnsafe("cmd-thread-create"),322 commandId: CommandId.make("cmd-thread-create"),
323 causationEventId: null,323 causationEventId: null,
324 correlationId: CommandId.makeUnsafe("cmd-thread-create"),324 correlationId: CommandId.make("cmd-thread-create"),
325 metadata: {},325 metadata: {},
326 payload: {326 payload: {
327 threadId: ThreadId.makeUnsafe("thread-1"),327 threadId: ThreadId.make("thread-1"),
328 projectId: asProjectId("project-1"),328 projectId: asProjectId("project-1"),
329 title: "Thread",329 title: "Thread",
330 modelSelection: {330 modelSelection: {
...@@ -345,8 +345,8 @@ describe("decider project scripts", () => {...@@ -345,8 +345,8 @@ describe("decider project scripts", () => {
345 decideOrchestrationCommand({345 decideOrchestrationCommand({
346 command: {346 command: {
347 type: "thread.interaction-mode.set",347 type: "thread.interaction-mode.set",
348 commandId: CommandId.makeUnsafe("cmd-interaction-mode-set"),348 commandId: CommandId.make("cmd-interaction-mode-set"),
349 threadId: ThreadId.makeUnsafe("thread-1"),349 threadId: ThreadId.make("thread-1"),
350 interactionMode: "plan",350 interactionMode: "plan",
351 createdAt: now,351 createdAt: now,
352 },352 },
...@@ -361,7 +361,7 @@ describe("decider project scripts", () => {...@@ -361,7 +361,7 @@ describe("decider project scripts", () => {
361 expect(singleResult).toMatchObject({361 expect(singleResult).toMatchObject({
362 type: "thread.interaction-mode-set",362 type: "thread.interaction-mode-set",
363 payload: {363 payload: {
364 threadId: ThreadId.makeUnsafe("thread-1"),364 threadId: ThreadId.make("thread-1"),
365 interactionMode: "plan",365 interactionMode: "plan",
366 },366 },
367 });367 });
apps/server/src/orchestration/projector.test.ts+4-4
...@@ -21,15 +21,15 @@ function makeEvent(input: {...@@ -21,15 +21,15 @@ function makeEvent(input: {
21}): OrchestrationEvent {21}): OrchestrationEvent {
22 return {22 return {
23 sequence: input.sequence,23 sequence: input.sequence,
24 eventId: EventId.makeUnsafe(`event-${input.sequence}`),24 eventId: EventId.make(`event-${input.sequence}`),
25 type: input.type,25 type: input.type,
26 aggregateKind: input.aggregateKind,26 aggregateKind: input.aggregateKind,
27 aggregateId:27 aggregateId:
28 input.aggregateKind === "project"28 input.aggregateKind === "project"
29 ? ProjectId.makeUnsafe(input.aggregateId)29 ? ProjectId.make(input.aggregateId)
30 : ThreadId.makeUnsafe(input.aggregateId),30 : ThreadId.make(input.aggregateId),
31 occurredAt: input.occurredAt,31 occurredAt: input.occurredAt,
32 commandId: input.commandId === null ? null : CommandId.makeUnsafe(input.commandId),32 commandId: input.commandId === null ? null : CommandId.make(input.commandId),
33 causationEventId: null,33 causationEventId: null,
34 correlationId: null,34 correlationId: null,
35 metadata: {},35 metadata: {},
apps/server/src/persistence/Layers/OrchestrationEventStore.test.ts+8-8
...@@ -21,18 +21,18 @@ layer("OrchestrationEventStore", (it) => {...@@ -21,18 +21,18 @@ layer("OrchestrationEventStore", (it) => {
2121
22 const appended = yield* eventStore.append({22 const appended = yield* eventStore.append({
23 type: "project.created",23 type: "project.created",
24 eventId: EventId.makeUnsafe("evt-store-roundtrip"),24 eventId: EventId.make("evt-store-roundtrip"),
25 aggregateKind: "project",25 aggregateKind: "project",
26 aggregateId: ProjectId.makeUnsafe("project-roundtrip"),26 aggregateId: ProjectId.make("project-roundtrip"),
27 occurredAt: now,27 occurredAt: now,
28 commandId: CommandId.makeUnsafe("cmd-store-roundtrip"),28 commandId: CommandId.make("cmd-store-roundtrip"),
29 causationEventId: null,29 causationEventId: null,
30 correlationId: CommandId.makeUnsafe("cmd-store-roundtrip"),30 correlationId: CommandId.make("cmd-store-roundtrip"),
31 metadata: {31 metadata: {
32 adapterKey: "codex",32 adapterKey: "codex",
33 },33 },
34 payload: {34 payload: {
35 projectId: ProjectId.makeUnsafe("project-roundtrip"),35 projectId: ProjectId.make("project-roundtrip"),
36 title: "Roundtrip Project",36 title: "Roundtrip Project",
37 workspaceRoot: "/tmp/project-roundtrip",37 workspaceRoot: "/tmp/project-roundtrip",
38 defaultModelSelection: null,38 defaultModelSelection: null,
...@@ -87,13 +87,13 @@ layer("OrchestrationEventStore", (it) => {...@@ -87,13 +87,13 @@ layer("OrchestrationEventStore", (it) => {
87 metadata_json87 metadata_json
88 )88 )
89 VALUES (89 VALUES (
90 ${EventId.makeUnsafe("evt-store-invalid-json")},90 ${EventId.make("evt-store-invalid-json")},
91 ${"project"},91 ${"project"},
92 ${ProjectId.makeUnsafe("project-invalid-json")},92 ${ProjectId.make("project-invalid-json")},
93 ${0},93 ${0},
94 ${"project.created"},94 ${"project.created"},
95 ${now},95 ${now},
96 ${CommandId.makeUnsafe("cmd-store-invalid-json")},96 ${CommandId.make("cmd-store-invalid-json")},
97 ${null},97 ${null},
98 ${null},98 ${null},
99 ${"server"},99 ${"server"},
apps/server/src/persistence/Layers/ProjectionRepositories.test.ts+5-5
...@@ -24,7 +24,7 @@ projectionRepositoriesLayer("Projection repositories", (it) => {...@@ -24,7 +24,7 @@ projectionRepositoriesLayer("Projection repositories", (it) => {
24 const sql = yield* SqlClient.SqlClient;24 const sql = yield* SqlClient.SqlClient;
2525
26 yield* projects.upsert({26 yield* projects.upsert({
27 projectId: ProjectId.makeUnsafe("project-null-options"),27 projectId: ProjectId.make("project-null-options"),
28 title: "Null options project",28 title: "Null options project",
29 workspaceRoot: "/tmp/project-null-options",29 workspaceRoot: "/tmp/project-null-options",
30 defaultModelSelection: {30 defaultModelSelection: {
...@@ -58,7 +58,7 @@ projectionRepositoriesLayer("Projection repositories", (it) => {...@@ -58,7 +58,7 @@ projectionRepositoriesLayer("Projection repositories", (it) => {
58 );58 );
5959
60 const persisted = yield* projects.getById({60 const persisted = yield* projects.getById({
61 projectId: ProjectId.makeUnsafe("project-null-options"),61 projectId: ProjectId.make("project-null-options"),
62 });62 });
63 assert.deepStrictEqual(Option.getOrNull(persisted)?.defaultModelSelection, {63 assert.deepStrictEqual(Option.getOrNull(persisted)?.defaultModelSelection, {
64 provider: "codex",64 provider: "codex",
...@@ -73,8 +73,8 @@ projectionRepositoriesLayer("Projection repositories", (it) => {...@@ -73,8 +73,8 @@ projectionRepositoriesLayer("Projection repositories", (it) => {
73 const sql = yield* SqlClient.SqlClient;73 const sql = yield* SqlClient.SqlClient;
7474
75 yield* threads.upsert({75 yield* threads.upsert({
76 threadId: ThreadId.makeUnsafe("thread-null-options"),76 threadId: ThreadId.make("thread-null-options"),
77 projectId: ProjectId.makeUnsafe("project-null-options"),77 projectId: ProjectId.make("project-null-options"),
78 title: "Null options thread",78 title: "Null options thread",
79 modelSelection: {79 modelSelection: {
80 provider: "claudeAgent",80 provider: "claudeAgent",
...@@ -112,7 +112,7 @@ projectionRepositoriesLayer("Projection repositories", (it) => {...@@ -112,7 +112,7 @@ projectionRepositoriesLayer("Projection repositories", (it) => {
112 );112 );
113113
114 const persisted = yield* threads.getById({114 const persisted = yield* threads.getById({
115 threadId: ThreadId.makeUnsafe("thread-null-options"),115 threadId: ThreadId.make("thread-null-options"),
116 });116 });
117 assert.deepStrictEqual(Option.getOrNull(persisted)?.modelSelection, {117 assert.deepStrictEqual(Option.getOrNull(persisted)?.modelSelection, {
118 provider: "claudeAgent",118 provider: "claudeAgent",
apps/server/src/persistence/Layers/ProjectionThreadMessages.test.ts+4-4
...@@ -14,8 +14,8 @@ layer("ProjectionThreadMessageRepository", (it) => {...@@ -14,8 +14,8 @@ layer("ProjectionThreadMessageRepository", (it) => {
14 it.effect("preserves existing attachments when upsert omits attachments", () =>14 it.effect("preserves existing attachments when upsert omits attachments", () =>
15 Effect.gen(function* () {15 Effect.gen(function* () {
16 const repository = yield* ProjectionThreadMessageRepository;16 const repository = yield* ProjectionThreadMessageRepository;
17 const threadId = ThreadId.makeUnsafe("thread-preserve-attachments");17 const threadId = ThreadId.make("thread-preserve-attachments");
18 const messageId = MessageId.makeUnsafe("message-preserve-attachments");18 const messageId = MessageId.make("message-preserve-attachments");
19 const createdAt = "2026-02-28T19:00:00.000Z";19 const createdAt = "2026-02-28T19:00:00.000Z";
20 const updatedAt = "2026-02-28T19:00:01.000Z";20 const updatedAt = "2026-02-28T19:00:01.000Z";
21 const persistedAttachments = [21 const persistedAttachments = [
...@@ -68,8 +68,8 @@ layer("ProjectionThreadMessageRepository", (it) => {...@@ -68,8 +68,8 @@ layer("ProjectionThreadMessageRepository", (it) => {
68 it.effect("allows explicit attachment clearing with an empty array", () =>68 it.effect("allows explicit attachment clearing with an empty array", () =>
69 Effect.gen(function* () {69 Effect.gen(function* () {
70 const repository = yield* ProjectionThreadMessageRepository;70 const repository = yield* ProjectionThreadMessageRepository;
71 const threadId = ThreadId.makeUnsafe("thread-clear-attachments");71 const threadId = ThreadId.make("thread-clear-attachments");
72 const messageId = MessageId.makeUnsafe("message-clear-attachments");72 const messageId = MessageId.make("message-clear-attachments");
73 const createdAt = "2026-02-28T19:10:00.000Z";73 const createdAt = "2026-02-28T19:10:00.000Z";
7474
75 yield* repository.upsert({75 yield* repository.upsert({
apps/server/src/persistence/NodeSqliteClient.ts+10-10
...@@ -15,7 +15,7 @@ import { identity } from "effect/Function";...@@ -15,7 +15,7 @@ import { identity } from "effect/Function";
15import * as Layer from "effect/Layer";15import * as Layer from "effect/Layer";
16import * as Scope from "effect/Scope";16import * as Scope from "effect/Scope";
17import * as Semaphore from "effect/Semaphore";17import * as Semaphore from "effect/Semaphore";
18import * as ServiceMap from "effect/ServiceMap";18import * as Context from "effect/Context";
19import * as Stream from "effect/Stream";19import * as Stream from "effect/Stream";
20import * as Reactivity from "effect/unstable/reactivity/Reactivity";20import * as Reactivity from "effect/unstable/reactivity/Reactivity";
21import * as Client from "effect/unstable/sql/SqlClient";21import * as Client from "effect/unstable/sql/SqlClient";
...@@ -32,7 +32,7 @@ export type TypeId = "~local/sqlite-node/SqliteClient";...@@ -32,7 +32,7 @@ export type TypeId = "~local/sqlite-node/SqliteClient";
32/**32/**
33 * SqliteClient - Effect service tag for the sqlite SQL client.33 * SqliteClient - Effect service tag for the sqlite SQL client.
34 */34 */
35export const SqliteClient = ServiceMap.Service<Client.SqlClient>("t3/persistence/NodeSqliteClient");35export const SqliteClient = Context.Service<Client.SqlClient>("t3/persistence/NodeSqliteClient");
3636
37export interface SqliteClientConfig {37export interface SqliteClientConfig {
38 readonly filename: string;38 readonly filename: string;
...@@ -120,7 +120,7 @@ const makeWithDatabase = Effect.fn("makeWithDatabase")(function* (...@@ -120,7 +120,7 @@ const makeWithDatabase = Effect.fn("makeWithDatabase")(function* (
120120
121 const runStatement = (statement: StatementSync, params: ReadonlyArray<unknown>, raw: boolean) =>121 const runStatement = (statement: StatementSync, params: ReadonlyArray<unknown>, raw: boolean) =>
122 Effect.withFiber<ReadonlyArray<any>, SqlError>((fiber) => {122 Effect.withFiber<ReadonlyArray<any>, SqlError>((fiber) => {
123 statement.setReadBigInts(Boolean(ServiceMap.get(fiber.services, Client.SafeIntegers)));123 statement.setReadBigInts(Boolean(Context.get(fiber.context, Client.SafeIntegers)));
124 try {124 try {
125 if (hasRows(statement)) {125 if (hasRows(statement)) {
126 return Effect.succeed(statement.all(...(params as any)));126 return Effect.succeed(statement.all(...(params as any)));
...@@ -200,7 +200,7 @@ const makeWithDatabase = Effect.fn("makeWithDatabase")(function* (...@@ -200,7 +200,7 @@ const makeWithDatabase = Effect.fn("makeWithDatabase")(function* (
200 const acquirer = semaphore.withPermits(1)(Effect.succeed(connection));200 const acquirer = semaphore.withPermits(1)(Effect.succeed(connection));
201 const transactionAcquirer = Effect.uninterruptibleMask((restore) => {201 const transactionAcquirer = Effect.uninterruptibleMask((restore) => {
202 const fiber = Fiber.getCurrent()!;202 const fiber = Fiber.getCurrent()!;
203 const scope = ServiceMap.getUnsafe(fiber.services, Scope.Scope);203 const scope = Context.getUnsafe(fiber.context, Scope.Scope);
204 return Effect.as(204 return Effect.as(
205 Effect.tap(restore(semaphore.take(1)), () => Scope.addFinalizer(scope, semaphore.release(1))),205 Effect.tap(restore(semaphore.take(1)), () => Scope.addFinalizer(scope, semaphore.release(1))),
206 connection,206 connection,
...@@ -251,27 +251,27 @@ const makeMemory = (...@@ -251,27 +251,27 @@ const makeMemory = (
251export const layerConfig = (251export const layerConfig = (
252 config: Config.Wrap<SqliteClientConfig>,252 config: Config.Wrap<SqliteClientConfig>,
253): Layer.Layer<Client.SqlClient, Config.ConfigError> =>253): Layer.Layer<Client.SqlClient, Config.ConfigError> =>
254 Layer.effectServices(254 Layer.effectContext(
255 Config.unwrap(config)255 Config.unwrap(config)
256 .asEffect()256 .asEffect()
257 .pipe(257 .pipe(
258 Effect.flatMap(make),258 Effect.flatMap(make),
259 Effect.map((client) =>259 Effect.map((client) =>
260 ServiceMap.make(SqliteClient, client).pipe(ServiceMap.add(Client.SqlClient, client)),260 Context.make(SqliteClient, client).pipe(Context.add(Client.SqlClient, client)),
261 ),261 ),
262 ),262 ),
263 ).pipe(Layer.provide(Reactivity.layer));263 ).pipe(Layer.provide(Reactivity.layer));
264264
265export const layer = (config: SqliteClientConfig): Layer.Layer<Client.SqlClient> =>265export const layer = (config: SqliteClientConfig): Layer.Layer<Client.SqlClient> =>
266 Layer.effectServices(266 Layer.effectContext(
267 Effect.map(make(config), (client) =>267 Effect.map(make(config), (client) =>
268 ServiceMap.make(SqliteClient, client).pipe(ServiceMap.add(Client.SqlClient, client)),268 Context.make(SqliteClient, client).pipe(Context.add(Client.SqlClient, client)),
269 ),269 ),
270 ).pipe(Layer.provide(Reactivity.layer));270 ).pipe(Layer.provide(Reactivity.layer));
271271
272export const layerMemory = (config: SqliteMemoryClientConfig = {}): Layer.Layer<Client.SqlClient> =>272export const layerMemory = (config: SqliteMemoryClientConfig = {}): Layer.Layer<Client.SqlClient> =>
273 Layer.effectServices(273 Layer.effectContext(
274 Effect.map(makeMemory(config), (client) =>274 Effect.map(makeMemory(config), (client) =>
275 ServiceMap.make(SqliteClient, client).pipe(ServiceMap.add(Client.SqlClient, client)),275 Context.make(SqliteClient, client).pipe(Context.add(Client.SqlClient, client)),
276 ),276 ),
277 ).pipe(Layer.provide(Reactivity.layer));277 ).pipe(Layer.provide(Reactivity.layer));
apps/server/src/persistence/Services/AuthPairingLinks.ts+2-2
...@@ -1,4 +1,4 @@...@@ -1,4 +1,4 @@
1import { Option, Schema, ServiceMap } from "effect";1import { Option, Schema, Context } from "effect";
2import type { Effect } from "effect";2import type { Effect } from "effect";
33
4import type { AuthPairingLinkRepositoryError } from "../Errors.ts";4import type { AuthPairingLinkRepositoryError } from "../Errors.ts";
...@@ -70,7 +70,7 @@ export interface AuthPairingLinkRepositoryShape {...@@ -70,7 +70,7 @@ export interface AuthPairingLinkRepositoryShape {
70 ) => Effect.Effect<Option.Option<AuthPairingLinkRecord>, AuthPairingLinkRepositoryError>;70 ) => Effect.Effect<Option.Option<AuthPairingLinkRecord>, AuthPairingLinkRepositoryError>;
71}71}
7272
73export class AuthPairingLinkRepository extends ServiceMap.Service<73export class AuthPairingLinkRepository extends Context.Service<
74 AuthPairingLinkRepository,74 AuthPairingLinkRepository,
75 AuthPairingLinkRepositoryShape75 AuthPairingLinkRepositoryShape
76>()("t3/persistence/Services/AuthPairingLinks/AuthPairingLinkRepository") {}76>()("t3/persistence/Services/AuthPairingLinks/AuthPairingLinkRepository") {}
apps/server/src/persistence/Services/AuthSessions.ts+2-2
...@@ -1,5 +1,5 @@...@@ -1,5 +1,5 @@
1import { AuthClientMetadataDeviceType, AuthSessionId } from "@t3tools/contracts";1import { AuthClientMetadataDeviceType, AuthSessionId } from "@t3tools/contracts";
2import { Option, Schema, ServiceMap } from "effect";2import { Option, Schema, Context } from "effect";
3import type { Effect } from "effect";3import type { Effect } from "effect";
44
5import type { AuthSessionRepositoryError } from "../Errors.ts";5import type { AuthSessionRepositoryError } from "../Errors.ts";
...@@ -87,7 +87,7 @@ export interface AuthSessionRepositoryShape {...@@ -87,7 +87,7 @@ export interface AuthSessionRepositoryShape {
87 ) => Effect.Effect<void, AuthSessionRepositoryError>;87 ) => Effect.Effect<void, AuthSessionRepositoryError>;
88}88}
8989
90export class AuthSessionRepository extends ServiceMap.Service<90export class AuthSessionRepository extends Context.Service<
91 AuthSessionRepository,91 AuthSessionRepository,
92 AuthSessionRepositoryShape92 AuthSessionRepositoryShape
93>()("t3/persistence/Services/AuthSessions/AuthSessionRepository") {}93>()("t3/persistence/Services/AuthSessions/AuthSessionRepository") {}
apps/server/src/persistence/Services/OrchestrationCommandReceipts.ts+2-2
...@@ -15,7 +15,7 @@ import {...@@ -15,7 +15,7 @@ import {
15 ProjectId,15 ProjectId,
16 ThreadId,16 ThreadId,
17} from "@t3tools/contracts";17} from "@t3tools/contracts";
18import { Option, Schema, ServiceMap } from "effect";18import { Option, Schema, Context } from "effect";
19import type { Effect } from "effect";19import type { Effect } from "effect";
2020
21import type { OrchestrationCommandReceiptRepositoryError } from "../Errors.ts";21import type { OrchestrationCommandReceiptRepositoryError } from "../Errors.ts";
...@@ -63,7 +63,7 @@ export interface OrchestrationCommandReceiptRepositoryShape {...@@ -63,7 +63,7 @@ export interface OrchestrationCommandReceiptRepositoryShape {
63/**63/**
64 * OrchestrationCommandReceiptRepository - Service tag for command receipt persistence.64 * OrchestrationCommandReceiptRepository - Service tag for command receipt persistence.
65 */65 */
66export class OrchestrationCommandReceiptRepository extends ServiceMap.Service<66export class OrchestrationCommandReceiptRepository extends Context.Service<
67 OrchestrationCommandReceiptRepository,67 OrchestrationCommandReceiptRepository,
68 OrchestrationCommandReceiptRepositoryShape68 OrchestrationCommandReceiptRepositoryShape
69>()("t3/persistence/Services/OrchestrationCommandReceipts/OrchestrationCommandReceiptRepository") {}69>()("t3/persistence/Services/OrchestrationCommandReceipts/OrchestrationCommandReceiptRepository") {}
apps/server/src/persistence/Services/OrchestrationEventStore.ts+3-3
...@@ -4,13 +4,13 @@...@@ -4,13 +4,13 @@
4 * Owns durable append/replay access to the orchestration event stream. It does4 * Owns durable append/replay access to the orchestration event stream. It does
5 * not reduce events into read models or apply command validation rules.5 * not reduce events into read models or apply command validation rules.
6 *6 *
7 * Uses Effect `ServiceMap.Service` for dependency injection and exposes typed7 * Uses Effect `Context.Service` for dependency injection and exposes typed
8 * persistence/decode errors for event append and replay operations.8 * persistence/decode errors for event append and replay operations.
9 *9 *
10 * @module OrchestrationEventStore10 * @module OrchestrationEventStore
11 */11 */
12import { OrchestrationEvent } from "@t3tools/contracts";12import { OrchestrationEvent } from "@t3tools/contracts";
13import { ServiceMap } from "effect";13import { Context } from "effect";
14import type { Effect, Stream } from "effect";14import type { Effect, Stream } from "effect";
1515
16import type { OrchestrationEventStoreError } from "../Errors.ts";16import type { OrchestrationEventStoreError } from "../Errors.ts";
...@@ -64,7 +64,7 @@ export interface OrchestrationEventStoreShape {...@@ -64,7 +64,7 @@ export interface OrchestrationEventStoreShape {
64 * })64 * })
65 * ```65 * ```
66 */66 */
67export class OrchestrationEventStore extends ServiceMap.Service<67export class OrchestrationEventStore extends Context.Service<
68 OrchestrationEventStore,68 OrchestrationEventStore,
69 OrchestrationEventStoreShape69 OrchestrationEventStoreShape
70>()("t3/persistence/Services/OrchestrationEventStore") {}70>()("t3/persistence/Services/OrchestrationEventStore") {}
apps/server/src/persistence/Services/ProjectionCheckpoints.ts+2-2
...@@ -16,7 +16,7 @@ import {...@@ -16,7 +16,7 @@ import {
16 ThreadId,16 ThreadId,
17 TurnId,17 TurnId,
18} from "@t3tools/contracts";18} from "@t3tools/contracts";
19import { Option, ServiceMap, Schema } from "effect";19import { Option, Context, Schema } from "effect";
20import type { Effect } from "effect";20import type { Effect } from "effect";
2121
22import type { ProjectionRepositoryError } from "../Errors.ts";22import type { ProjectionRepositoryError } from "../Errors.ts";
...@@ -87,7 +87,7 @@ export interface ProjectionCheckpointRepositoryShape {...@@ -87,7 +87,7 @@ export interface ProjectionCheckpointRepositoryShape {
87/**87/**
88 * ProjectionCheckpointRepository - Service tag for checkpoint projection persistence.88 * ProjectionCheckpointRepository - Service tag for checkpoint projection persistence.
89 */89 */
90export class ProjectionCheckpointRepository extends ServiceMap.Service<90export class ProjectionCheckpointRepository extends Context.Service<
91 ProjectionCheckpointRepository,91 ProjectionCheckpointRepository,
92 ProjectionCheckpointRepositoryShape92 ProjectionCheckpointRepositoryShape
93>()("t3/persistence/Services/ProjectionCheckpoints/ProjectionCheckpointRepository") {}93>()("t3/persistence/Services/ProjectionCheckpoints/ProjectionCheckpointRepository") {}
apps/server/src/persistence/Services/ProjectionPendingApprovals.ts+2-2
...@@ -14,7 +14,7 @@ import {...@@ -14,7 +14,7 @@ import {
14 ThreadId,14 ThreadId,
15 TurnId,15 TurnId,
16} from "@t3tools/contracts";16} from "@t3tools/contracts";
17import { Option, Schema, ServiceMap } from "effect";17import { Option, Schema, Context } from "effect";
18import type { Effect } from "effect";18import type { Effect } from "effect";
1919
20import type { ProjectionRepositoryError } from "../Errors.ts";20import type { ProjectionRepositoryError } from "../Errors.ts";
...@@ -85,7 +85,7 @@ export interface ProjectionPendingApprovalRepositoryShape {...@@ -85,7 +85,7 @@ export interface ProjectionPendingApprovalRepositoryShape {
85/**85/**
86 * ProjectionPendingApprovalRepository - Service tag for pending approval persistence.86 * ProjectionPendingApprovalRepository - Service tag for pending approval persistence.
87 */87 */
88export class ProjectionPendingApprovalRepository extends ServiceMap.Service<88export class ProjectionPendingApprovalRepository extends Context.Service<
89 ProjectionPendingApprovalRepository,89 ProjectionPendingApprovalRepository,
90 ProjectionPendingApprovalRepositoryShape90 ProjectionPendingApprovalRepositoryShape
91>()("t3/persistence/Services/ProjectionPendingApprovals/ProjectionPendingApprovalRepository") {}91>()("t3/persistence/Services/ProjectionPendingApprovals/ProjectionPendingApprovalRepository") {}
apps/server/src/persistence/Services/ProjectionProjects.ts+2-2
...@@ -7,7 +7,7 @@...@@ -7,7 +7,7 @@
7 * @module ProjectionProjectRepository7 * @module ProjectionProjectRepository
8 */8 */
9import { IsoDateTime, ModelSelection, ProjectId, ProjectScript } from "@t3tools/contracts";9import { IsoDateTime, ModelSelection, ProjectId, ProjectScript } from "@t3tools/contracts";
10import { Option, Schema, ServiceMap } from "effect";10import { Option, Schema, Context } from "effect";
11import type { Effect } from "effect";11import type { Effect } from "effect";
1212
13import type { ProjectionRepositoryError } from "../Errors.ts";13import type { ProjectionRepositoryError } from "../Errors.ts";
...@@ -73,7 +73,7 @@ export interface ProjectionProjectRepositoryShape {...@@ -73,7 +73,7 @@ export interface ProjectionProjectRepositoryShape {
73/**73/**
74 * ProjectionProjectRepository - Service tag for project projection persistence.74 * ProjectionProjectRepository - Service tag for project projection persistence.
75 */75 */
76export class ProjectionProjectRepository extends ServiceMap.Service<76export class ProjectionProjectRepository extends Context.Service<
77 ProjectionProjectRepository,77 ProjectionProjectRepository,
78 ProjectionProjectRepositoryShape78 ProjectionProjectRepositoryShape
79>()("t3/persistence/Services/ProjectionProjects/ProjectionProjectRepository") {}79>()("t3/persistence/Services/ProjectionProjects/ProjectionProjectRepository") {}
apps/server/src/persistence/Services/ProjectionState.ts+2-2
...@@ -7,7 +7,7 @@...@@ -7,7 +7,7 @@
7 * @module ProjectionStateRepository7 * @module ProjectionStateRepository
8 */8 */
9import { IsoDateTime, NonNegativeInt } from "@t3tools/contracts";9import { IsoDateTime, NonNegativeInt } from "@t3tools/contracts";
10import { Option, Schema, ServiceMap } from "effect";10import { Option, Schema, Context } from "effect";
11import type { Effect } from "effect";11import type { Effect } from "effect";
1212
13import type { ProjectionRepositoryError } from "../Errors.ts";13import type { ProjectionRepositoryError } from "../Errors.ts";
...@@ -58,7 +58,7 @@ export interface ProjectionStateRepositoryShape {...@@ -58,7 +58,7 @@ export interface ProjectionStateRepositoryShape {
58/**58/**
59 * ProjectionStateRepository - Service tag for projection cursor persistence.59 * ProjectionStateRepository - Service tag for projection cursor persistence.
60 */60 */
61export class ProjectionStateRepository extends ServiceMap.Service<61export class ProjectionStateRepository extends Context.Service<
62 ProjectionStateRepository,62 ProjectionStateRepository,
63 ProjectionStateRepositoryShape63 ProjectionStateRepositoryShape
64>()("t3/persistence/Services/ProjectionState/ProjectionStateRepository") {}64>()("t3/persistence/Services/ProjectionState/ProjectionStateRepository") {}
apps/server/src/persistence/Services/ProjectionThreadActivities.ts+2-2
...@@ -14,7 +14,7 @@ import {...@@ -14,7 +14,7 @@ import {
14 ThreadId,14 ThreadId,
15 TurnId,15 TurnId,
16} from "@t3tools/contracts";16} from "@t3tools/contracts";
17import { Schema, ServiceMap } from "effect";17import { Schema, Context } from "effect";
18import type { Effect } from "effect";18import type { Effect } from "effect";
1919
20import type { ProjectionRepositoryError } from "../Errors.ts";20import type { ProjectionRepositoryError } from "../Errors.ts";
...@@ -77,7 +77,7 @@ export interface ProjectionThreadActivityRepositoryShape {...@@ -77,7 +77,7 @@ export interface ProjectionThreadActivityRepositoryShape {
77/**77/**
78 * ProjectionThreadActivityRepository - Service tag for thread activity persistence.78 * ProjectionThreadActivityRepository - Service tag for thread activity persistence.
79 */79 */
80export class ProjectionThreadActivityRepository extends ServiceMap.Service<80export class ProjectionThreadActivityRepository extends Context.Service<
81 ProjectionThreadActivityRepository,81 ProjectionThreadActivityRepository,
82 ProjectionThreadActivityRepositoryShape82 ProjectionThreadActivityRepositoryShape
83>()("t3/persistence/Services/ProjectionThreadActivities/ProjectionThreadActivityRepository") {}83>()("t3/persistence/Services/ProjectionThreadActivities/ProjectionThreadActivityRepository") {}
apps/server/src/persistence/Services/ProjectionThreadMessages.ts+2-2
...@@ -14,7 +14,7 @@ import {...@@ -14,7 +14,7 @@ import {
14 TurnId,14 TurnId,
15 IsoDateTime,15 IsoDateTime,
16} from "@t3tools/contracts";16} from "@t3tools/contracts";
17import { Schema, ServiceMap } from "effect";17import { Schema, Context } from "effect";
18import type { Option } from "effect";18import type { Option } from "effect";
19import type { Effect } from "effect";19import type { Effect } from "effect";
2020
...@@ -88,7 +88,7 @@ export interface ProjectionThreadMessageRepositoryShape {...@@ -88,7 +88,7 @@ export interface ProjectionThreadMessageRepositoryShape {
88/**88/**
89 * ProjectionThreadMessageRepository - Service tag for message projection persistence.89 * ProjectionThreadMessageRepository - Service tag for message projection persistence.
90 */90 */
91export class ProjectionThreadMessageRepository extends ServiceMap.Service<91export class ProjectionThreadMessageRepository extends Context.Service<
92 ProjectionThreadMessageRepository,92 ProjectionThreadMessageRepository,
93 ProjectionThreadMessageRepositoryShape93 ProjectionThreadMessageRepositoryShape
94>()("t3/persistence/Services/ProjectionThreadMessages/ProjectionThreadMessageRepository") {}94>()("t3/persistence/Services/ProjectionThreadMessages/ProjectionThreadMessageRepository") {}
apps/server/src/persistence/Services/ProjectionThreadProposedPlans.ts+2-2
...@@ -5,7 +5,7 @@ import {...@@ -5,7 +5,7 @@ import {
5 TrimmedNonEmptyString,5 TrimmedNonEmptyString,
6 TurnId,6 TurnId,
7} from "@t3tools/contracts";7} from "@t3tools/contracts";
8import { Schema, ServiceMap } from "effect";8import { Schema, Context } from "effect";
9import type { Effect } from "effect";9import type { Effect } from "effect";
1010
11import type { ProjectionRepositoryError } from "../Errors.ts";11import type { ProjectionRepositoryError } from "../Errors.ts";
...@@ -46,7 +46,7 @@ export interface ProjectionThreadProposedPlanRepositoryShape {...@@ -46,7 +46,7 @@ export interface ProjectionThreadProposedPlanRepositoryShape {
46 ) => Effect.Effect<void, ProjectionRepositoryError>;46 ) => Effect.Effect<void, ProjectionRepositoryError>;
47}47}
4848
49export class ProjectionThreadProposedPlanRepository extends ServiceMap.Service<49export class ProjectionThreadProposedPlanRepository extends Context.Service<
50 ProjectionThreadProposedPlanRepository,50 ProjectionThreadProposedPlanRepository,
51 ProjectionThreadProposedPlanRepositoryShape51 ProjectionThreadProposedPlanRepositoryShape
52>()(52>()(
apps/server/src/persistence/Services/ProjectionThreadSessions.ts+2-2
...@@ -13,7 +13,7 @@ import {...@@ -13,7 +13,7 @@ import {
13 ThreadId,13 ThreadId,
14 TurnId,14 TurnId,
15} from "@t3tools/contracts";15} from "@t3tools/contracts";
16import { Option, Schema, ServiceMap } from "effect";16import { Option, Schema, Context } from "effect";
17import type { Effect } from "effect";17import type { Effect } from "effect";
1818
19import type { ProjectionRepositoryError } from "../Errors.ts";19import type { ProjectionRepositoryError } from "../Errors.ts";
...@@ -68,7 +68,7 @@ export interface ProjectionThreadSessionRepositoryShape {...@@ -68,7 +68,7 @@ export interface ProjectionThreadSessionRepositoryShape {
68/**68/**
69 * ProjectionThreadSessionRepository - Service tag for thread-session persistence.69 * ProjectionThreadSessionRepository - Service tag for thread-session persistence.
70 */70 */
71export class ProjectionThreadSessionRepository extends ServiceMap.Service<71export class ProjectionThreadSessionRepository extends Context.Service<
72 ProjectionThreadSessionRepository,72 ProjectionThreadSessionRepository,
73 ProjectionThreadSessionRepositoryShape73 ProjectionThreadSessionRepositoryShape
74>()("t3/persistence/Services/ProjectionThreadSessions/ProjectionThreadSessionRepository") {}74>()("t3/persistence/Services/ProjectionThreadSessions/ProjectionThreadSessionRepository") {}
apps/server/src/persistence/Services/ProjectionThreads.ts+2-2
...@@ -15,7 +15,7 @@ import {...@@ -15,7 +15,7 @@ import {
15 ThreadId,15 ThreadId,
16 TurnId,16 TurnId,
17} from "@t3tools/contracts";17} from "@t3tools/contracts";
18import { Option, Schema, ServiceMap } from "effect";18import { Option, Schema, Context } from "effect";
19import type { Effect } from "effect";19import type { Effect } from "effect";
2020
21import type { ProjectionRepositoryError } from "../Errors.ts";21import type { ProjectionRepositoryError } from "../Errors.ts";
...@@ -90,7 +90,7 @@ export interface ProjectionThreadRepositoryShape {...@@ -90,7 +90,7 @@ export interface ProjectionThreadRepositoryShape {
90/**90/**
91 * ProjectionThreadRepository - Service tag for thread projection persistence.91 * ProjectionThreadRepository - Service tag for thread projection persistence.
92 */92 */
93export class ProjectionThreadRepository extends ServiceMap.Service<93export class ProjectionThreadRepository extends Context.Service<
94 ProjectionThreadRepository,94 ProjectionThreadRepository,
95 ProjectionThreadRepositoryShape95 ProjectionThreadRepositoryShape
96>()("t3/persistence/Services/ProjectionThreads/ProjectionThreadRepository") {}96>()("t3/persistence/Services/ProjectionThreads/ProjectionThreadRepository") {}
apps/server/src/persistence/Services/ProjectionTurns.ts+2-2
...@@ -17,7 +17,7 @@ import {...@@ -17,7 +17,7 @@ import {
17 ThreadId,17 ThreadId,
18 TurnId,18 TurnId,
19} from "@t3tools/contracts";19} from "@t3tools/contracts";
20import { Option, Schema, ServiceMap } from "effect";20import { Option, Schema, Context } from "effect";
21import type { Effect } from "effect";21import type { Effect } from "effect";
2222
23import type { ProjectionRepositoryError } from "../Errors.ts";23import type { ProjectionRepositoryError } from "../Errors.ts";
...@@ -162,7 +162,7 @@ export interface ProjectionTurnRepositoryShape {...@@ -162,7 +162,7 @@ export interface ProjectionTurnRepositoryShape {
162 ) => Effect.Effect<void, ProjectionRepositoryError>;162 ) => Effect.Effect<void, ProjectionRepositoryError>;
163}163}
164164
165export class ProjectionTurnRepository extends ServiceMap.Service<165export class ProjectionTurnRepository extends Context.Service<
166 ProjectionTurnRepository,166 ProjectionTurnRepository,
167 ProjectionTurnRepositoryShape167 ProjectionTurnRepositoryShape
168>()("t3/persistence/Services/ProjectionTurns/ProjectionTurnRepository") {}168>()("t3/persistence/Services/ProjectionTurns/ProjectionTurnRepository") {}
apps/server/src/persistence/Services/ProviderSessionRuntime.ts+2-2
...@@ -11,7 +11,7 @@ import {...@@ -11,7 +11,7 @@ import {
11 RuntimeMode,11 RuntimeMode,
12 ThreadId,12 ThreadId,
13} from "@t3tools/contracts";13} from "@t3tools/contracts";
14import { Option, Schema, ServiceMap } from "effect";14import { Option, Schema, Context } from "effect";
15import type { Effect } from "effect";15import type { Effect } from "effect";
1616
17import type { ProviderSessionRuntimeRepositoryError } from "../Errors.ts";17import type { ProviderSessionRuntimeRepositoryError } from "../Errors.ts";
...@@ -75,7 +75,7 @@ export interface ProviderSessionRuntimeRepositoryShape {...@@ -75,7 +75,7 @@ export interface ProviderSessionRuntimeRepositoryShape {
75/**75/**
76 * ProviderSessionRuntimeRepository - Service tag for provider runtime persistence.76 * ProviderSessionRuntimeRepository - Service tag for provider runtime persistence.
77 */77 */
78export class ProviderSessionRuntimeRepository extends ServiceMap.Service<78export class ProviderSessionRuntimeRepository extends Context.Service<
79 ProviderSessionRuntimeRepository,79 ProviderSessionRuntimeRepository,
80 ProviderSessionRuntimeRepositoryShape80 ProviderSessionRuntimeRepositoryShape
81>()("t3/persistence/Services/ProviderSessionRuntime/ProviderSessionRuntimeRepository") {}81>()("t3/persistence/Services/ProviderSessionRuntime/ProviderSessionRuntimeRepository") {}
apps/server/src/project/Layers/RepositoryIdentityResolver.ts+13-11
...@@ -116,17 +116,19 @@ async function resolveRepositoryIdentityFromCacheKey(...@@ -116,17 +116,19 @@ async function resolveRepositoryIdentityFromCacheKey(
116116
117export const makeRepositoryIdentityResolver = Effect.fn("makeRepositoryIdentityResolver")(117export const makeRepositoryIdentityResolver = Effect.fn("makeRepositoryIdentityResolver")(
118 function* (options: RepositoryIdentityResolverOptions = {}) {118 function* (options: RepositoryIdentityResolverOptions = {}) {
119 const repositoryIdentityCache = yield* Cache.makeWith<string, RepositoryIdentity | null>({119 const repositoryIdentityCache = yield* Cache.makeWith<string, RepositoryIdentity | null>(
120 capacity: options.cacheCapacity ?? DEFAULT_REPOSITORY_IDENTITY_CACHE_CAPACITY,120 (cacheKey) => Effect.promise(() => resolveRepositoryIdentityFromCacheKey(cacheKey)),
121 lookup: (cacheKey) => Effect.promise(() => resolveRepositoryIdentityFromCacheKey(cacheKey)),121 {
122 timeToLive: Exit.match({122 capacity: options.cacheCapacity ?? DEFAULT_REPOSITORY_IDENTITY_CACHE_CAPACITY,
123 onSuccess: (value) =>123 timeToLive: Exit.match({
124 value === null124 onSuccess: (value) =>
125 ? (options.negativeCacheTtl ?? DEFAULT_NEGATIVE_CACHE_TTL)125 value === null
126 : (options.positiveCacheTtl ?? DEFAULT_POSITIVE_CACHE_TTL),126 ? (options.negativeCacheTtl ?? DEFAULT_NEGATIVE_CACHE_TTL)
127 onFailure: () => Duration.zero,127 : (options.positiveCacheTtl ?? DEFAULT_POSITIVE_CACHE_TTL),
128 }),128 onFailure: () => Duration.zero,
129 });129 }),
130 },
131 );
130132
131 const resolve: RepositoryIdentityResolverShape["resolve"] = Effect.fn(133 const resolve: RepositoryIdentityResolverShape["resolve"] = Effect.fn(
132 "RepositoryIdentityResolver.resolve",134 "RepositoryIdentityResolver.resolve",
apps/server/src/project/Services/ProjectFaviconResolver.ts+2-2
...@@ -6,7 +6,7 @@...@@ -6,7 +6,7 @@
6 *6 *
7 * @module ProjectFaviconResolver7 * @module ProjectFaviconResolver
8 */8 */
9import { ServiceMap } from "effect";9import { Context } from "effect";
10import type { Effect } from "effect";10import type { Effect } from "effect";
1111
12/**12/**
...@@ -24,7 +24,7 @@ export interface ProjectFaviconResolverShape {...@@ -24,7 +24,7 @@ export interface ProjectFaviconResolverShape {
24/**24/**
25 * ProjectFaviconResolver - Service tag for project favicon resolution.25 * ProjectFaviconResolver - Service tag for project favicon resolution.
26 */26 */
27export class ProjectFaviconResolver extends ServiceMap.Service<27export class ProjectFaviconResolver extends Context.Service<
28 ProjectFaviconResolver,28 ProjectFaviconResolver,
29 ProjectFaviconResolverShape29 ProjectFaviconResolverShape
30>()("t3/project/Services/ProjectFaviconResolver") {}30>()("t3/project/Services/ProjectFaviconResolver") {}
apps/server/src/project/Services/ProjectSetupScriptRunner.ts+2-2
...@@ -1,4 +1,4 @@...@@ -1,4 +1,4 @@
1import { ServiceMap } from "effect";1import { Context } from "effect";
2import type { Effect } from "effect";2import type { Effect } from "effect";
33
4export interface ProjectSetupScriptRunnerResultNoScript {4export interface ProjectSetupScriptRunnerResultNoScript {
...@@ -31,7 +31,7 @@ export interface ProjectSetupScriptRunnerShape {...@@ -31,7 +31,7 @@ export interface ProjectSetupScriptRunnerShape {
31 ) => Effect.Effect<ProjectSetupScriptRunnerResult, Error>;31 ) => Effect.Effect<ProjectSetupScriptRunnerResult, Error>;
32}32}
3333
34export class ProjectSetupScriptRunner extends ServiceMap.Service<34export class ProjectSetupScriptRunner extends Context.Service<
35 ProjectSetupScriptRunner,35 ProjectSetupScriptRunner,
36 ProjectSetupScriptRunnerShape36 ProjectSetupScriptRunnerShape
37>()("t3/project/ProjectSetupScriptRunner") {}37>()("t3/project/ProjectSetupScriptRunner") {}
apps/server/src/project/Services/RepositoryIdentityResolver.ts+2-2
...@@ -1,12 +1,12 @@...@@ -1,12 +1,12 @@
1import type { RepositoryIdentity } from "@t3tools/contracts";1import type { RepositoryIdentity } from "@t3tools/contracts";
2import { ServiceMap } from "effect";2import { Context } from "effect";
3import type { Effect } from "effect";3import type { Effect } from "effect";
44
5export interface RepositoryIdentityResolverShape {5export interface RepositoryIdentityResolverShape {
6 readonly resolve: (cwd: string) => Effect.Effect<RepositoryIdentity | null>;6 readonly resolve: (cwd: string) => Effect.Effect<RepositoryIdentity | null>;
7}7}
88
9export class RepositoryIdentityResolver extends ServiceMap.Service<9export class RepositoryIdentityResolver extends Context.Service<
10 RepositoryIdentityResolver,10 RepositoryIdentityResolver,
11 RepositoryIdentityResolverShape11 RepositoryIdentityResolverShape
12>()("t3/project/Services/RepositoryIdentityResolver") {}12>()("t3/project/Services/RepositoryIdentityResolver") {}
apps/server/src/provider/Layers/ClaudeAdapter.test.ts+21-25
...@@ -237,8 +237,8 @@ async function readFirstPromptMessage(...@@ -237,8 +237,8 @@ async function readFirstPromptMessage(
237 return next.value;237 return next.value;
238}238}
239239
240const THREAD_ID = ThreadId.makeUnsafe("thread-claude-1");240const THREAD_ID = ThreadId.make("thread-claude-1");
241const RESUME_THREAD_ID = ThreadId.makeUnsafe("thread-claude-resume");241const RESUME_THREAD_ID = ThreadId.make("thread-claude-resume");
242242
243describe("ClaudeAdapterLive", () => {243describe("ClaudeAdapterLive", () => {
244 it.effect("returns validation error for non-claude provider on startSession", () => {244 it.effect("returns validation error for non-claude provider on startSession", () => {
...@@ -1104,8 +1104,8 @@ describe("ClaudeAdapterLive", () => {...@@ -1104,8 +1104,8 @@ describe("ClaudeAdapterLive", () => {
1104 it.effect("closes the session when the Claude stream aborts after a turn starts", () => {1104 it.effect("closes the session when the Claude stream aborts after a turn starts", () => {
1105 const harness = makeHarness();1105 const harness = makeHarness();
1106 return Effect.gen(function* () {1106 return Effect.gen(function* () {
1107 const services = yield* Effect.services();1107 const context = yield* Effect.context<never>();
1108 const runFork = Effect.runForkWith(services);1108 const runFork = Effect.runForkWith(context);
11091109
1110 const adapter = yield* ClaudeAdapter;1110 const adapter = yield* ClaudeAdapter;
1111 const runtimeEvents: Array<ProviderRuntimeEvent> = [];1111 const runtimeEvents: Array<ProviderRuntimeEvent> = [];
...@@ -1204,8 +1204,8 @@ describe("ClaudeAdapterLive", () => {...@@ -1204,8 +1204,8 @@ describe("ClaudeAdapterLive", () => {
1204 );1204 );
12051205
1206 return Effect.gen(function* () {1206 return Effect.gen(function* () {
1207 const services = yield* Effect.services();1207 const context = yield* Effect.context<never>();
1208 const runFork = Effect.runForkWith(services);1208 const runFork = Effect.runForkWith(context);
12091209
1210 const adapter = yield* ClaudeAdapter;1210 const adapter = yield* ClaudeAdapter;
12111211
...@@ -2108,7 +2108,7 @@ describe("ClaudeAdapterLive", () => {...@@ -2108,7 +2108,7 @@ describe("ClaudeAdapterLive", () => {
2108 return;2108 return;
2109 }2109 }
2110 assert.deepEqual(requested.value.providerRefs, {2110 assert.deepEqual(requested.value.providerRefs, {
2111 providerItemId: ProviderItemId.makeUnsafe("tool-use-1"),2111 providerItemId: ProviderItemId.make("tool-use-1"),
2112 });2112 });
2113 const runtimeRequestId = requested.value.requestId;2113 const runtimeRequestId = requested.value.requestId;
2114 assert.equal(typeof runtimeRequestId, "string");2114 assert.equal(typeof runtimeRequestId, "string");
...@@ -2118,7 +2118,7 @@ describe("ClaudeAdapterLive", () => {...@@ -2118,7 +2118,7 @@ describe("ClaudeAdapterLive", () => {
21182118
2119 yield* adapter.respondToRequest(2119 yield* adapter.respondToRequest(
2120 session.threadId,2120 session.threadId,
2121 ApprovalRequestId.makeUnsafe(runtimeRequestId),2121 ApprovalRequestId.make(runtimeRequestId),
2122 "accept",2122 "accept",
2123 );2123 );
21242124
...@@ -2134,7 +2134,7 @@ describe("ClaudeAdapterLive", () => {...@@ -2134,7 +2134,7 @@ describe("ClaudeAdapterLive", () => {
2134 assert.equal(resolved.value.requestId, requested.value.requestId);2134 assert.equal(resolved.value.requestId, requested.value.requestId);
2135 assert.equal(resolved.value.payload.decision, "accept");2135 assert.equal(resolved.value.payload.decision, "accept");
2136 assert.deepEqual(resolved.value.providerRefs, {2136 assert.deepEqual(resolved.value.providerRefs, {
2137 providerItemId: ProviderItemId.makeUnsafe("tool-use-1"),2137 providerItemId: ProviderItemId.make("tool-use-1"),
2138 });2138 });
21392139
2140 const permissionResult = yield* Effect.promise(() => permissionPromise);2140 const permissionResult = yield* Effect.promise(() => permissionPromise);
...@@ -2183,7 +2183,7 @@ describe("ClaudeAdapterLive", () => {...@@ -2183,7 +2183,7 @@ describe("ClaudeAdapterLive", () => {
21832183
2184 yield* adapter.respondToRequest(2184 yield* adapter.respondToRequest(
2185 session.threadId,2185 session.threadId,
2186 ApprovalRequestId.makeUnsafe(String(agentRequested.value.requestId)),2186 ApprovalRequestId.make(String(agentRequested.value.requestId)),
2187 "accept",2187 "accept",
2188 );2188 );
2189 yield* Stream.runHead(adapter.streamEvents);2189 yield* Stream.runHead(adapter.streamEvents);
...@@ -2207,7 +2207,7 @@ describe("ClaudeAdapterLive", () => {...@@ -2207,7 +2207,7 @@ describe("ClaudeAdapterLive", () => {
22072207
2208 yield* adapter.respondToRequest(2208 yield* adapter.respondToRequest(
2209 session.threadId,2209 session.threadId,
2210 ApprovalRequestId.makeUnsafe(String(grepRequested.value.requestId)),2210 ApprovalRequestId.make(String(grepRequested.value.requestId)),
2211 "accept",2211 "accept",
2212 );2212 );
2213 yield* Stream.runHead(adapter.streamEvents);2213 yield* Stream.runHead(adapter.streamEvents);
...@@ -2629,7 +2629,7 @@ describe("ClaudeAdapterLive", () => {...@@ -2629,7 +2629,7 @@ describe("ClaudeAdapterLive", () => {
2629 }2629 }
2630 assert.equal(proposedEvent.value.payload.planMarkdown, "# Ship it\n\n- one\n- two");2630 assert.equal(proposedEvent.value.payload.planMarkdown, "# Ship it\n\n- one\n- two");
2631 assert.deepEqual(proposedEvent.value.providerRefs, {2631 assert.deepEqual(proposedEvent.value.providerRefs, {
2632 providerItemId: ProviderItemId.makeUnsafe("tool-exit-1"),2632 providerItemId: ProviderItemId.make("tool-exit-1"),
2633 });2633 });
26342634
2635 const permissionResult = yield* Effect.promise(() => permissionPromise);2635 const permissionResult = yield* Effect.promise(() => permissionPromise);
...@@ -2707,7 +2707,7 @@ describe("ClaudeAdapterLive", () => {...@@ -2707,7 +2707,7 @@ describe("ClaudeAdapterLive", () => {
2707 }2707 }
2708 assert.equal(proposedEvent.value.payload.planMarkdown, "# Final plan\n\n- capture it");2708 assert.equal(proposedEvent.value.payload.planMarkdown, "# Final plan\n\n- capture it");
2709 assert.deepEqual(proposedEvent.value.providerRefs, {2709 assert.deepEqual(proposedEvent.value.providerRefs, {
2710 providerItemId: ProviderItemId.makeUnsafe("tool-exit-2"),2710 providerItemId: ProviderItemId.make("tool-exit-2"),
2711 });2711 });
2712 }).pipe(2712 }).pipe(
2713 Effect.provideService(Random.Random, makeDeterministicRandomService()),2713 Effect.provideService(Random.Random, makeDeterministicRandomService()),
...@@ -2798,15 +2798,13 @@ describe("ClaudeAdapterLive", () => {...@@ -2798,15 +2798,13 @@ describe("ClaudeAdapterLive", () => {
2798 assert.equal(requestedEvent.value.payload.questions.length, 1);2798 assert.equal(requestedEvent.value.payload.questions.length, 1);
2799 assert.equal(requestedEvent.value.payload.questions[0]?.question, "Which framework?");2799 assert.equal(requestedEvent.value.payload.questions[0]?.question, "Which framework?");
2800 assert.deepEqual(requestedEvent.value.providerRefs, {2800 assert.deepEqual(requestedEvent.value.providerRefs, {
2801 providerItemId: ProviderItemId.makeUnsafe("tool-ask-1"),2801 providerItemId: ProviderItemId.make("tool-ask-1"),
2802 });2802 });
28032803
2804 // Respond with the user's answers.2804 // Respond with the user's answers.
2805 yield* adapter.respondToUserInput(2805 yield* adapter.respondToUserInput(session.threadId, ApprovalRequestId.make(requestId!), {
2806 session.threadId,2806 "Which framework?": "React",
2807 ApprovalRequestId.makeUnsafe(requestId!),2807 });
2808 { "Which framework?": "React" },
2809 );
28102808
2811 // The adapter should emit a user-input.resolved event.2809 // The adapter should emit a user-input.resolved event.
2812 const resolvedEvent = yield* Stream.runHead(adapter.streamEvents);2810 const resolvedEvent = yield* Stream.runHead(adapter.streamEvents);
...@@ -2822,7 +2820,7 @@ describe("ClaudeAdapterLive", () => {...@@ -2822,7 +2820,7 @@ describe("ClaudeAdapterLive", () => {
2822 "Which framework?": "React",2820 "Which framework?": "React",
2823 });2821 });
2824 assert.deepEqual(resolvedEvent.value.providerRefs, {2822 assert.deepEqual(resolvedEvent.value.providerRefs, {
2825 providerItemId: ProviderItemId.makeUnsafe("tool-ask-1"),2823 providerItemId: ProviderItemId.make("tool-ask-1"),
2826 });2824 });
28272825
2828 // The canUseTool promise should resolve with the answers in SDK format.2826 // The canUseTool promise should resolve with the answers in SDK format.
...@@ -2889,11 +2887,9 @@ describe("ClaudeAdapterLive", () => {...@@ -2889,11 +2887,9 @@ describe("ClaudeAdapterLive", () => {
2889 }2887 }
2890 const requestId = requestedEvent.value.requestId;2888 const requestId = requestedEvent.value.requestId;
28912889
2892 yield* adapter.respondToUserInput(2890 yield* adapter.respondToUserInput(session.threadId, ApprovalRequestId.make(requestId!), {
2893 session.threadId,2891 "Deploy to which env?": "Staging",
2894 ApprovalRequestId.makeUnsafe(requestId!),2892 });
2895 { "Deploy to which env?": "Staging" },
2896 );
28972893
2898 // Drain the resolved event.2894 // Drain the resolved event.
2899 yield* Stream.runHead(adapter.streamEvents);2895 yield* Stream.runHead(adapter.streamEvents);
apps/server/src/provider/Layers/ClaudeAdapter.ts+16-16
...@@ -269,7 +269,7 @@ function isInterruptedResult(result: SDKResultMessage): boolean {...@@ -269,7 +269,7 @@ function isInterruptedResult(result: SDKResultMessage): boolean {
269}269}
270270
271function asRuntimeItemId(value: string): RuntimeItemId {271function asRuntimeItemId(value: string): RuntimeItemId {
272 return RuntimeItemId.makeUnsafe(value);272 return RuntimeItemId.make(value);
273}273}
274274
275function maxClaudeContextWindowFromModelUsage(modelUsage: unknown): number | undefined {275function maxClaudeContextWindowFromModelUsage(modelUsage: unknown): number | undefined {
...@@ -353,7 +353,7 @@ function asCanonicalTurnId(value: TurnId): TurnId {...@@ -353,7 +353,7 @@ function asCanonicalTurnId(value: TurnId): TurnId {
353}353}
354354
355function asRuntimeRequestId(value: ApprovalRequestId): RuntimeRequestId {355function asRuntimeRequestId(value: ApprovalRequestId): RuntimeRequestId {
356 return RuntimeRequestId.makeUnsafe(value);356 return RuntimeRequestId.make(value);
357}357}
358358
359function readClaudeResumeState(resumeCursor: unknown): ClaudeResumeState | undefined {359function readClaudeResumeState(resumeCursor: unknown): ClaudeResumeState | undefined {
...@@ -371,7 +371,7 @@ function readClaudeResumeState(resumeCursor: unknown): ClaudeResumeState | undef...@@ -371,7 +371,7 @@ function readClaudeResumeState(resumeCursor: unknown): ClaudeResumeState | undef
371 const threadIdCandidate = typeof cursor.threadId === "string" ? cursor.threadId : undefined;371 const threadIdCandidate = typeof cursor.threadId === "string" ? cursor.threadId : undefined;
372 const threadId =372 const threadId =
373 threadIdCandidate && !isSyntheticClaudeThreadId(threadIdCandidate)373 threadIdCandidate && !isSyntheticClaudeThreadId(threadIdCandidate)
374 ? ThreadId.makeUnsafe(threadIdCandidate)374 ? ThreadId.make(threadIdCandidate)
375 : undefined;375 : undefined;
376 const resumeCandidate =376 const resumeCandidate =
377 typeof cursor.resume === "string"377 typeof cursor.resume === "string"
...@@ -641,7 +641,7 @@ function nativeProviderRefs(...@@ -641,7 +641,7 @@ function nativeProviderRefs(
641): NonNullable<ProviderRuntimeEvent["providerRefs"]> {641): NonNullable<ProviderRuntimeEvent["providerRefs"]> {
642 if (options?.providerItemId) {642 if (options?.providerItemId) {
643 return {643 return {
644 providerItemId: ProviderItemId.makeUnsafe(options.providerItemId),644 providerItemId: ProviderItemId.make(options.providerItemId),
645 };645 };
646 }646 }
647 return {};647 return {};
...@@ -934,7 +934,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (...@@ -934,7 +934,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (
934 const serverSettingsService = yield* ServerSettingsService;934 const serverSettingsService = yield* ServerSettingsService;
935935
936 const nowIso = Effect.map(DateTime.now, DateTime.formatIso);936 const nowIso = Effect.map(DateTime.now, DateTime.formatIso);
937 const nextEventId = Effect.map(Random.nextUUIDv4, (id) => EventId.makeUnsafe(id));937 const nextEventId = Effect.map(Random.nextUUIDv4, (id) => EventId.make(id));
938 const makeEventStamp = () => Effect.all({ eventId: nextEventId, createdAt: nowIso });938 const makeEventStamp = () => Effect.all({ eventId: nextEventId, createdAt: nowIso });
939939
940 const offerRuntimeEvent = (event: ProviderRuntimeEvent): Effect.Effect<void> =>940 const offerRuntimeEvent = (event: ProviderRuntimeEvent): Effect.Effect<void> =>
...@@ -967,7 +967,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (...@@ -967,7 +967,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (
967 ? { providerThreadId: message.session_id }967 ? { providerThreadId: message.session_id }
968 : {}),968 : {}),
969 ...(context.turnState ? { turnId: asCanonicalTurnId(context.turnState.turnId) } : {}),969 ...(context.turnState ? { turnId: asCanonicalTurnId(context.turnState.turnId) } : {}),
970 ...(itemId ? { itemId: ProviderItemId.makeUnsafe(itemId) } : {}),970 ...(itemId ? { itemId: ProviderItemId.make(itemId) } : {}),
971 payload: message,971 payload: message,
972 },972 },
973 },973 },
...@@ -1822,7 +1822,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (...@@ -1822,7 +1822,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (
1822 // Auto-start a synthetic turn for assistant messages that arrive without1822 // Auto-start a synthetic turn for assistant messages that arrive without
1823 // an active turn (e.g., background agent/subagent responses between user prompts).1823 // an active turn (e.g., background agent/subagent responses between user prompts).
1824 if (!context.turnState) {1824 if (!context.turnState) {
1825 const turnId = TurnId.makeUnsafe(yield* Random.nextUUIDv4);1825 const turnId = TurnId.make(yield* Random.nextUUIDv4);
1826 const startedAt = yield* nowIso;1826 const startedAt = yield* nowIso;
1827 context.turnState = {1827 context.turnState = {
1828 turnId,1828 turnId,
...@@ -2013,7 +2013,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (...@@ -2013,7 +2013,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (
2013 ...base,2013 ...base,
2014 type: "task.started",2014 type: "task.started",
2015 payload: {2015 payload: {
2016 taskId: RuntimeTaskId.makeUnsafe(message.task_id),2016 taskId: RuntimeTaskId.make(message.task_id),
2017 description: message.description,2017 description: message.description,
2018 ...(message.task_type ? { taskType: message.task_type } : {}),2018 ...(message.task_type ? { taskType: message.task_type } : {}),
2019 },2019 },
...@@ -2043,7 +2043,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (...@@ -2043,7 +2043,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (
2043 ...base,2043 ...base,
2044 type: "task.progress",2044 type: "task.progress",
2045 payload: {2045 payload: {
2046 taskId: RuntimeTaskId.makeUnsafe(message.task_id),2046 taskId: RuntimeTaskId.make(message.task_id),
2047 description: message.description,2047 description: message.description,
2048 ...(message.summary ? { summary: message.summary } : {}),2048 ...(message.summary ? { summary: message.summary } : {}),
2049 ...(message.usage ? { usage: message.usage } : {}),2049 ...(message.usage ? { usage: message.usage } : {}),
...@@ -2075,7 +2075,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (...@@ -2075,7 +2075,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (
2075 ...base,2075 ...base,
2076 type: "task.completed",2076 type: "task.completed",
2077 payload: {2077 payload: {
2078 taskId: RuntimeTaskId.makeUnsafe(message.task_id),2078 taskId: RuntimeTaskId.make(message.task_id),
2079 status: message.status,2079 status: message.status,
2080 ...(message.summary ? { summary: message.summary } : {}),2080 ...(message.summary ? { summary: message.summary } : {}),
2081 ...(message.usage ? { usage: message.usage } : {}),2081 ...(message.usage ? { usage: message.usage } : {}),
...@@ -2380,9 +2380,9 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (...@@ -2380,9 +2380,9 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (
2380 existingResumeSessionId === undefined ? yield* Random.nextUUIDv4 : undefined;2380 existingResumeSessionId === undefined ? yield* Random.nextUUIDv4 : undefined;
2381 const sessionId = existingResumeSessionId ?? newSessionId;2381 const sessionId = existingResumeSessionId ?? newSessionId;
23822382
2383 const services = yield* Effect.services();2383 const runtimeContext = yield* Effect.context<never>();
2384 const runFork = Effect.runForkWith(services);2384 const runFork = Effect.runForkWith(runtimeContext);
2385 const runPromise = Effect.runPromiseWith(services);2385 const runPromise = Effect.runPromiseWith(runtimeContext);
23862386
2387 const promptQueue = yield* Queue.unbounded<PromptQueueItem>();2387 const promptQueue = yield* Queue.unbounded<PromptQueueItem>();
2388 const prompt = Stream.fromQueue(promptQueue).pipe(2388 const prompt = Stream.fromQueue(promptQueue).pipe(
...@@ -2409,7 +2409,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (...@@ -2409,7 +2409,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (
2409 toolInput: Record<string, unknown>,2409 toolInput: Record<string, unknown>,
2410 callbackOptions: { readonly signal: AbortSignal; readonly toolUseID?: string },2410 callbackOptions: { readonly signal: AbortSignal; readonly toolUseID?: string },
2411 ) {2411 ) {
2412 const requestId = ApprovalRequestId.makeUnsafe(yield* Random.nextUUIDv4);2412 const requestId = ApprovalRequestId.make(yield* Random.nextUUIDv4);
24132413
2414 // Parse questions from the SDK's AskUserQuestion input.2414 // Parse questions from the SDK's AskUserQuestion input.
2415 const rawQuestions = Array.isArray(toolInput.questions) ? toolInput.questions : [];2415 const rawQuestions = Array.isArray(toolInput.questions) ? toolInput.questions : [];
...@@ -2562,7 +2562,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (...@@ -2562,7 +2562,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (
2562 } satisfies PermissionResult;2562 } satisfies PermissionResult;
2563 }2563 }
25642564
2565 const requestId = ApprovalRequestId.makeUnsafe(yield* Random.nextUUIDv4);2565 const requestId = ApprovalRequestId.make(yield* Random.nextUUIDv4);
2566 const requestType = classifyRequestType(toolName);2566 const requestType = classifyRequestType(toolName);
2567 const detail = summarizeToolRequest(toolName, toolInput);2567 const detail = summarizeToolRequest(toolName, toolInput);
2568 const decisionDeferred = yield* Deferred.make<ProviderApprovalDecision>();2568 const decisionDeferred = yield* Deferred.make<ProviderApprovalDecision>();
...@@ -2890,7 +2890,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (...@@ -2890,7 +2890,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (
2890 });2890 });
2891 }2891 }
28922892
2893 const turnId = TurnId.makeUnsafe(yield* Random.nextUUIDv4);2893 const turnId = TurnId.make(yield* Random.nextUUIDv4);
2894 const turnState: ClaudeTurnState = {2894 const turnState: ClaudeTurnState = {
2895 turnId,2895 turnId,
2896 startedAt: yield* nowIso,2896 startedAt: yield* nowIso,
apps/server/src/provider/Layers/CodexAdapter.test.ts+8-8
...@@ -28,10 +28,10 @@ import { CodexAdapter } from "../Services/CodexAdapter.ts";...@@ -28,10 +28,10 @@ import { CodexAdapter } from "../Services/CodexAdapter.ts";
28import { ProviderSessionDirectory } from "../Services/ProviderSessionDirectory.ts";28import { ProviderSessionDirectory } from "../Services/ProviderSessionDirectory.ts";
29import { makeCodexAdapterLive } from "./CodexAdapter.ts";29import { makeCodexAdapterLive } from "./CodexAdapter.ts";
3030
31const asThreadId = (value: string): ThreadId => ThreadId.makeUnsafe(value);31const asThreadId = (value: string): ThreadId => ThreadId.make(value);
32const asTurnId = (value: string): TurnId => TurnId.makeUnsafe(value);32const asTurnId = (value: string): TurnId => TurnId.make(value);
33const asEventId = (value: string): EventId => EventId.makeUnsafe(value);33const asEventId = (value: string): EventId => EventId.make(value);
34const asItemId = (value: string): ProviderItemId => ProviderItemId.makeUnsafe(value);34const asItemId = (value: string): ProviderItemId => ProviderItemId.make(value);
3535
36class FakeCodexManager extends CodexAppServerManager {36class FakeCodexManager extends CodexAppServerManager {
37 public startSessionImpl = vi.fn(37 public startSessionImpl = vi.fn(
...@@ -556,7 +556,7 @@ lifecycleLayer("CodexAdapterLive lifecycle", (it) => {...@@ -556,7 +556,7 @@ lifecycleLayer("CodexAdapterLive lifecycle", (it) => {
556 threadId: asThreadId("thread-1"),556 threadId: asThreadId("thread-1"),
557 createdAt: new Date().toISOString(),557 createdAt: new Date().toISOString(),
558 method: "serverRequest/resolved",558 method: "serverRequest/resolved",
559 requestId: ApprovalRequestId.makeUnsafe("req-1"),559 requestId: ApprovalRequestId.make("req-1"),
560 payload: {560 payload: {
561 request: {561 request: {
562 method: "item/commandExecution/requestApproval",562 method: "item/commandExecution/requestApproval",
...@@ -592,7 +592,7 @@ lifecycleLayer("CodexAdapterLive lifecycle", (it) => {...@@ -592,7 +592,7 @@ lifecycleLayer("CodexAdapterLive lifecycle", (it) => {
592 threadId: asThreadId("thread-1"),592 threadId: asThreadId("thread-1"),
593 createdAt: new Date().toISOString(),593 createdAt: new Date().toISOString(),
594 method: "serverRequest/resolved",594 method: "serverRequest/resolved",
595 requestId: ApprovalRequestId.makeUnsafe("req-file-read-1"),595 requestId: ApprovalRequestId.make("req-file-read-1"),
596 payload: {596 payload: {
597 request: {597 request: {
598 method: "item/fileRead/requestApproval",598 method: "item/fileRead/requestApproval",
...@@ -710,7 +710,7 @@ lifecycleLayer("CodexAdapterLive lifecycle", (it) => {...@@ -710,7 +710,7 @@ lifecycleLayer("CodexAdapterLive lifecycle", (it) => {
710 threadId: asThreadId("thread-1"),710 threadId: asThreadId("thread-1"),
711 createdAt: new Date().toISOString(),711 createdAt: new Date().toISOString(),
712 method: "item/tool/requestUserInput",712 method: "item/tool/requestUserInput",
713 requestId: ApprovalRequestId.makeUnsafe("req-user-input-1"),713 requestId: ApprovalRequestId.make("req-user-input-1"),
714 payload: {714 payload: {
715 questions: [715 questions: [
716 {716 {
...@@ -735,7 +735,7 @@ lifecycleLayer("CodexAdapterLive lifecycle", (it) => {...@@ -735,7 +735,7 @@ lifecycleLayer("CodexAdapterLive lifecycle", (it) => {
735 threadId: asThreadId("thread-1"),735 threadId: asThreadId("thread-1"),
736 createdAt: new Date().toISOString(),736 createdAt: new Date().toISOString(),
737 method: "item/tool/requestUserInput/answered",737 method: "item/tool/requestUserInput/answered",
738 requestId: ApprovalRequestId.makeUnsafe("req-user-input-1"),738 requestId: ApprovalRequestId.make("req-user-input-1"),
739 payload: {739 payload: {
740 answers: {740 answers: {
741 sandbox_mode: {741 sandbox_mode: {
apps/server/src/provider/Layers/CodexAdapter.ts+9-9
...@@ -22,7 +22,7 @@ import {...@@ -22,7 +22,7 @@ import {
22 TurnId,22 TurnId,
23 ProviderSendTurnInput,23 ProviderSendTurnInput,
24} from "@t3tools/contracts";24} from "@t3tools/contracts";
25import { Effect, FileSystem, Layer, Queue, Schema, ServiceMap, Stream } from "effect";25import { Effect, FileSystem, Layer, Queue, Schema, Context, Stream } from "effect";
2626
27import {27import {
28 ProviderAdapterProcessError,28 ProviderAdapterProcessError,
...@@ -46,7 +46,7 @@ const PROVIDER = "codex" as const;...@@ -46,7 +46,7 @@ const PROVIDER = "codex" as const;
4646
47export interface CodexAdapterLiveOptions {47export interface CodexAdapterLiveOptions {
48 readonly manager?: CodexAppServerManager;48 readonly manager?: CodexAppServerManager;
49 readonly makeManager?: (services?: ServiceMap.ServiceMap<never>) => CodexAppServerManager;49 readonly makeManager?: (services?: Context.Context<never>) => CodexAppServerManager;
50 readonly nativeEventLogPath?: string;50 readonly nativeEventLogPath?: string;
51 readonly nativeEventLogger?: EventNdjsonLogger;51 readonly nativeEventLogger?: EventNdjsonLogger;
52}52}
...@@ -162,11 +162,11 @@ function normalizeCodexTokenUsage(value: unknown): ThreadTokenUsageSnapshot | un...@@ -162,11 +162,11 @@ function normalizeCodexTokenUsage(value: unknown): ThreadTokenUsageSnapshot | un
162}162}
163163
164function toTurnId(value: string | undefined): TurnId | undefined {164function toTurnId(value: string | undefined): TurnId | undefined {
165 return value?.trim() ? TurnId.makeUnsafe(value) : undefined;165 return value?.trim() ? TurnId.make(value) : undefined;
166}166}
167167
168function toProviderItemId(value: string | undefined): ProviderItemId | undefined {168function toProviderItemId(value: string | undefined): ProviderItemId | undefined {
169 return value?.trim() ? ProviderItemId.makeUnsafe(value) : undefined;169 return value?.trim() ? ProviderItemId.make(value) : undefined;
170}170}
171171
172function toTurnStatus(value: unknown): "completed" | "failed" | "cancelled" | "interrupted" {172function toTurnStatus(value: unknown): "completed" | "failed" | "cancelled" | "interrupted" {
...@@ -454,15 +454,15 @@ function extractProposedPlanMarkdown(text: string | undefined): string | undefin...@@ -454,15 +454,15 @@ function extractProposedPlanMarkdown(text: string | undefined): string | undefin
454}454}
455455
456function asRuntimeItemId(itemId: ProviderItemId): RuntimeItemId {456function asRuntimeItemId(itemId: ProviderItemId): RuntimeItemId {
457 return RuntimeItemId.makeUnsafe(itemId);457 return RuntimeItemId.make(itemId);
458}458}
459459
460function asRuntimeRequestId(requestId: string): RuntimeRequestId {460function asRuntimeRequestId(requestId: string): RuntimeRequestId {
461 return RuntimeRequestId.makeUnsafe(requestId);461 return RuntimeRequestId.make(requestId);
462}462}
463463
464function asRuntimeTaskId(taskId: string): RuntimeTaskId {464function asRuntimeTaskId(taskId: string): RuntimeTaskId {
465 return RuntimeTaskId.makeUnsafe(taskId);465 return RuntimeTaskId.make(taskId);
466}466}
467467
468function codexEventMessage(468function codexEventMessage(
...@@ -1366,7 +1366,7 @@ const makeCodexAdapter = Effect.fn("makeCodexAdapter")(function* (...@@ -1366,7 +1366,7 @@ const makeCodexAdapter = Effect.fn("makeCodexAdapter")(function* (
1366 if (options?.manager) {1366 if (options?.manager) {
1367 return options.manager;1367 return options.manager;
1368 }1368 }
1369 const services = yield* Effect.services<never>();1369 const services = yield* Effect.context<never>();
1370 return options?.makeManager?.(services) ?? new CodexAppServerManager(services);1370 return options?.makeManager?.(services) ?? new CodexAppServerManager(services);
1371 });1371 });
13721372
...@@ -1585,7 +1585,7 @@ const makeCodexAdapter = Effect.fn("makeCodexAdapter")(function* (...@@ -1585,7 +1585,7 @@ const makeCodexAdapter = Effect.fn("makeCodexAdapter")(function* (
1585 });1585 });
15861586
1587 const registerListener = Effect.fn("registerListener")(function* () {1587 const registerListener = Effect.fn("registerListener")(function* () {
1588 const services = yield* Effect.services<never>();1588 const services = yield* Effect.context<never>();
1589 const listenerEffect = Effect.fn("listener")(function* (event: ProviderEvent) {1589 const listenerEffect = Effect.fn("listener")(function* (event: ProviderEvent) {
1590 yield* writeNativeEvent(event);1590 yield* writeNativeEvent(event);
1591 const runtimeEvents = mapToRuntimeEvents(event, event.threadId);1591 const runtimeEvents = mapToRuntimeEvents(event, event.threadId);
apps/server/src/provider/Layers/EventNdjsonLogger.test.ts+3-3
...@@ -42,11 +42,11 @@ describe("EventNdjsonLogger", () => {...@@ -42,11 +42,11 @@ describe("EventNdjsonLogger", () => {
4242
43 yield* logger.write(43 yield* logger.write(
44 { threadId: "provider-thread-1", id: "evt-1" },44 { threadId: "provider-thread-1", id: "evt-1" },
45 ThreadId.makeUnsafe("thread-1"),45 ThreadId.make("thread-1"),
46 );46 );
47 yield* logger.write(47 yield* logger.write(
48 { type: "turn.completed", threadId: "provider-thread-2", id: "evt-2" },48 { type: "turn.completed", threadId: "provider-thread-2", id: "evt-2" },
49 ThreadId.makeUnsafe("thread-2"),49 ThreadId.make("thread-2"),
50 );50 );
51 yield* logger.close();51 yield* logger.close();
5252
...@@ -178,7 +178,7 @@ describe("EventNdjsonLogger", () => {...@@ -178,7 +178,7 @@ describe("EventNdjsonLogger", () => {
178 id: `evt-${index}`,178 id: `evt-${index}`,
179 payload: "x".repeat(40),179 payload: "x".repeat(40),
180 },180 },
181 ThreadId.makeUnsafe("thread-rotate"),181 ThreadId.make("thread-rotate"),
182 );182 );
183 }183 }
184 yield* logger.close();184 yield* logger.close();
apps/server/src/provider/Layers/ProviderRegistry.test.ts+1
...@@ -44,6 +44,7 @@ function mockHandle(result: { stdout: string; stderr: string; code: number }) {...@@ -44,6 +44,7 @@ function mockHandle(result: { stdout: string; stderr: string; code: number }) {
44 exitCode: Effect.succeed(ChildProcessSpawner.ExitCode(result.code)),44 exitCode: Effect.succeed(ChildProcessSpawner.ExitCode(result.code)),
45 isRunning: Effect.succeed(false),45 isRunning: Effect.succeed(false),
46 kill: () => Effect.void,46 kill: () => Effect.void,
47 unref: Effect.succeed(Effect.void),
47 stdin: Sink.drain,48 stdin: Sink.drain,
48 stdout: Stream.make(encoder.encode(result.stdout)),49 stdout: Stream.make(encoder.encode(result.stdout)),
49 stderr: Stream.make(encoder.encode(result.stderr)),50 stderr: Stream.make(encoder.encode(result.stderr)),
apps/server/src/provider/Layers/ProviderService.test.ts+6-6
...@@ -47,10 +47,10 @@ import { AnalyticsService } from "../../telemetry/Services/AnalyticsService.ts";...@@ -47,10 +47,10 @@ import { AnalyticsService } from "../../telemetry/Services/AnalyticsService.ts";
4747
48const defaultServerSettingsLayer = ServerSettingsService.layerTest();48const defaultServerSettingsLayer = ServerSettingsService.layerTest();
4949
50const asRequestId = (value: string): ApprovalRequestId => ApprovalRequestId.makeUnsafe(value);50const asRequestId = (value: string): ApprovalRequestId => ApprovalRequestId.make(value);
51const asEventId = (value: string): EventId => EventId.makeUnsafe(value);51const asEventId = (value: string): EventId => EventId.make(value);
52const asThreadId = (value: string): ThreadId => ThreadId.makeUnsafe(value);52const asThreadId = (value: string): ThreadId => ThreadId.make(value);
53const asTurnId = (value: string): TurnId => TurnId.makeUnsafe(value);53const asTurnId = (value: string): TurnId => TurnId.make(value);
5454
55type LegacyProviderRuntimeEvent = {55type LegacyProviderRuntimeEvent = {
56 readonly type: string;56 readonly type: string;
...@@ -102,7 +102,7 @@ function makeFakeCodexAdapter(provider: ProviderKind = "codex") {...@@ -102,7 +102,7 @@ function makeFakeCodexAdapter(provider: ProviderKind = "codex") {
102102
103 return Effect.succeed({103 return Effect.succeed({
104 threadId: input.threadId,104 threadId: input.threadId,
105 turnId: TurnId.makeUnsafe(`turn-${String(input.threadId)}`),105 turnId: TurnId.make(`turn-${String(input.threadId)}`),
106 });106 });
107 },107 },
108 );108 );
...@@ -358,7 +358,7 @@ it.effect("ProviderServiceLive keeps persisted resumable sessions on startup", (...@@ -358,7 +358,7 @@ it.effect("ProviderServiceLive keeps persisted resumable sessions on startup", (
358 const directory = yield* ProviderSessionDirectory;358 const directory = yield* ProviderSessionDirectory;
359 yield* directory.upsert({359 yield* directory.upsert({
360 provider: "codex",360 provider: "codex",
361 threadId: ThreadId.makeUnsafe("thread-stale"),361 threadId: ThreadId.make("thread-stale"),
362 });362 });
363 }).pipe(Effect.provide(directoryLayer));363 }).pipe(Effect.provide(directoryLayer));
364364
apps/server/src/provider/Layers/ProviderSessionDirectory.test.ts+5-5
...@@ -36,7 +36,7 @@ it.layer(makeDirectoryLayer(SqlitePersistenceMemory))("ProviderSessionDirectoryL...@@ -36,7 +36,7 @@ it.layer(makeDirectoryLayer(SqlitePersistenceMemory))("ProviderSessionDirectoryL
36 const directory = yield* ProviderSessionDirectory;36 const directory = yield* ProviderSessionDirectory;
37 const runtimeRepository = yield* ProviderSessionRuntimeRepository;37 const runtimeRepository = yield* ProviderSessionRuntimeRepository;
3838
39 const initialThreadId = ThreadId.makeUnsafe("thread-1");39 const initialThreadId = ThreadId.make("thread-1");
4040
41 yield* directory.upsert({41 yield* directory.upsert({
42 provider: "codex",42 provider: "codex",
...@@ -54,7 +54,7 @@ it.layer(makeDirectoryLayer(SqlitePersistenceMemory))("ProviderSessionDirectoryL...@@ -54,7 +54,7 @@ it.layer(makeDirectoryLayer(SqlitePersistenceMemory))("ProviderSessionDirectoryL
54 assert.equal(resolvedBinding.value.threadId, initialThreadId);54 assert.equal(resolvedBinding.value.threadId, initialThreadId);
55 }55 }
5656
57 const nextThreadId = ThreadId.makeUnsafe("thread-2");57 const nextThreadId = ThreadId.make("thread-2");
5858
59 yield* directory.upsert({59 yield* directory.upsert({
60 provider: "codex",60 provider: "codex",
...@@ -93,7 +93,7 @@ it.layer(makeDirectoryLayer(SqlitePersistenceMemory))("ProviderSessionDirectoryL...@@ -93,7 +93,7 @@ it.layer(makeDirectoryLayer(SqlitePersistenceMemory))("ProviderSessionDirectoryL
93 const directory = yield* ProviderSessionDirectory;93 const directory = yield* ProviderSessionDirectory;
94 const runtimeRepository = yield* ProviderSessionRuntimeRepository;94 const runtimeRepository = yield* ProviderSessionRuntimeRepository;
9595
96 const threadId = ThreadId.makeUnsafe("thread-runtime");96 const threadId = ThreadId.make("thread-runtime");
9797
98 yield* directory.upsert({98 yield* directory.upsert({
99 provider: "codex",99 provider: "codex",
...@@ -137,7 +137,7 @@ it.layer(makeDirectoryLayer(SqlitePersistenceMemory))("ProviderSessionDirectoryL...@@ -137,7 +137,7 @@ it.layer(makeDirectoryLayer(SqlitePersistenceMemory))("ProviderSessionDirectoryL
137 Effect.gen(function* () {137 Effect.gen(function* () {
138 const directory = yield* ProviderSessionDirectory;138 const directory = yield* ProviderSessionDirectory;
139 const runtimeRepository = yield* ProviderSessionRuntimeRepository;139 const runtimeRepository = yield* ProviderSessionRuntimeRepository;
140 const threadId = ThreadId.makeUnsafe("thread-provider-change");140 const threadId = ThreadId.make("thread-provider-change");
141141
142 yield* runtimeRepository.upsert({142 yield* runtimeRepository.upsert({
143 threadId,143 threadId,
...@@ -169,7 +169,7 @@ it.layer(makeDirectoryLayer(SqlitePersistenceMemory))("ProviderSessionDirectoryL...@@ -169,7 +169,7 @@ it.layer(makeDirectoryLayer(SqlitePersistenceMemory))("ProviderSessionDirectoryL
169 const dbPath = path.join(tempDir, "orchestration.sqlite");169 const dbPath = path.join(tempDir, "orchestration.sqlite");
170 const directoryLayer = makeDirectoryLayer(makeSqlitePersistenceLive(dbPath));170 const directoryLayer = makeDirectoryLayer(makeSqlitePersistenceLive(dbPath));
171171
172 const threadId = ThreadId.makeUnsafe("thread-restart");172 const threadId = ThreadId.make("thread-restart");
173173
174 yield* Effect.gen(function* () {174 yield* Effect.gen(function* () {
175 const directory = yield* ProviderSessionDirectory;175 const directory = yield* ProviderSessionDirectory;
apps/server/src/provider/Services/ClaudeAdapter.ts+3-3
...@@ -5,12 +5,12 @@...@@ -5,12 +5,12 @@
5 * provider runtime events. It does not perform cross-provider routing, shared5 * provider runtime events. It does not perform cross-provider routing, shared
6 * event fan-out, or checkpoint orchestration.6 * event fan-out, or checkpoint orchestration.
7 *7 *
8 * Uses Effect `ServiceMap.Service` for dependency injection and returns the8 * Uses Effect `Context.Service` for dependency injection and returns the
9 * shared provider-adapter error channel with `provider: "claudeAgent"` context.9 * shared provider-adapter error channel with `provider: "claudeAgent"` context.
10 *10 *
11 * @module ClaudeAdapter11 * @module ClaudeAdapter
12 */12 */
13import { ServiceMap } from "effect";13import { Context } from "effect";
1414
15import type { ProviderAdapterError } from "../Errors.ts";15import type { ProviderAdapterError } from "../Errors.ts";
16import type { ProviderAdapterShape } from "./ProviderAdapter.ts";16import type { ProviderAdapterShape } from "./ProviderAdapter.ts";
...@@ -25,6 +25,6 @@ export interface ClaudeAdapterShape extends ProviderAdapterShape<ProviderAdapter...@@ -25,6 +25,6 @@ export interface ClaudeAdapterShape extends ProviderAdapterShape<ProviderAdapter
25/**25/**
26 * ClaudeAdapter - Service tag for Claude Agent provider adapter operations.26 * ClaudeAdapter - Service tag for Claude Agent provider adapter operations.
27 */27 */
28export class ClaudeAdapter extends ServiceMap.Service<ClaudeAdapter, ClaudeAdapterShape>()(28export class ClaudeAdapter extends Context.Service<ClaudeAdapter, ClaudeAdapterShape>()(
29 "t3/provider/Services/ClaudeAdapter",29 "t3/provider/Services/ClaudeAdapter",
30) {}30) {}
apps/server/src/provider/Services/ClaudeProvider.ts+2-2
...@@ -1,9 +1,9 @@...@@ -1,9 +1,9 @@
1import { ServiceMap } from "effect";1import { Context } from "effect";
22
3import type { ServerProviderShape } from "./ServerProvider";3import type { ServerProviderShape } from "./ServerProvider";
44
5export interface ClaudeProviderShape extends ServerProviderShape {}5export interface ClaudeProviderShape extends ServerProviderShape {}
66
7export class ClaudeProvider extends ServiceMap.Service<ClaudeProvider, ClaudeProviderShape>()(7export class ClaudeProvider extends Context.Service<ClaudeProvider, ClaudeProviderShape>()(
8 "t3/provider/Services/ClaudeProvider",8 "t3/provider/Services/ClaudeProvider",
9) {}9) {}
apps/server/src/provider/Services/CodexAdapter.ts+3-3
...@@ -5,12 +5,12 @@...@@ -5,12 +5,12 @@
5 * Codex provider events. It does not perform cross-provider routing, shared5 * Codex provider events. It does not perform cross-provider routing, shared
6 * event fan-out, or checkpoint orchestration.6 * event fan-out, or checkpoint orchestration.
7 *7 *
8 * Uses Effect `ServiceMap.Service` for dependency injection and returns the8 * Uses Effect `Context.Service` for dependency injection and returns the
9 * shared provider-adapter error channel with `provider: "codex"` context.9 * shared provider-adapter error channel with `provider: "codex"` context.
10 *10 *
11 * @module CodexAdapter11 * @module CodexAdapter
12 */12 */
13import { ServiceMap } from "effect";13import { Context } from "effect";
1414
15import type { ProviderAdapterError } from "../Errors.ts";15import type { ProviderAdapterError } from "../Errors.ts";
16import type { ProviderAdapterShape } from "./ProviderAdapter.ts";16import type { ProviderAdapterShape } from "./ProviderAdapter.ts";
...@@ -25,6 +25,6 @@ export interface CodexAdapterShape extends ProviderAdapterShape<ProviderAdapterE...@@ -25,6 +25,6 @@ export interface CodexAdapterShape extends ProviderAdapterShape<ProviderAdapterE
25/**25/**
26 * CodexAdapter - Service tag for Codex provider adapter operations.26 * CodexAdapter - Service tag for Codex provider adapter operations.
27 */27 */
28export class CodexAdapter extends ServiceMap.Service<CodexAdapter, CodexAdapterShape>()(28export class CodexAdapter extends Context.Service<CodexAdapter, CodexAdapterShape>()(
29 "t3/provider/Services/CodexAdapter",29 "t3/provider/Services/CodexAdapter",
30) {}30) {}
apps/server/src/provider/Services/CodexProvider.ts+2-2
...@@ -1,9 +1,9 @@...@@ -1,9 +1,9 @@
1import { ServiceMap } from "effect";1import { Context } from "effect";
22
3import type { ServerProviderShape } from "./ServerProvider";3import type { ServerProviderShape } from "./ServerProvider";
44
5export interface CodexProviderShape extends ServerProviderShape {}5export interface CodexProviderShape extends ServerProviderShape {}
66
7export class CodexProvider extends ServiceMap.Service<CodexProvider, CodexProviderShape>()(7export class CodexProvider extends Context.Service<CodexProvider, CodexProviderShape>()(
8 "t3/provider/Services/CodexProvider",8 "t3/provider/Services/CodexProvider",
9) {}9) {}
apps/server/src/provider/Services/ProviderAdapterRegistry.ts+2-2
...@@ -8,7 +8,7 @@...@@ -8,7 +8,7 @@
8 * @module ProviderAdapterRegistry8 * @module ProviderAdapterRegistry
9 */9 */
10import type { ProviderKind } from "@t3tools/contracts";10import type { ProviderKind } from "@t3tools/contracts";
11import { ServiceMap } from "effect";11import { Context } from "effect";
12import type { Effect } from "effect";12import type { Effect } from "effect";
1313
14import type { ProviderAdapterError, ProviderUnsupportedError } from "../Errors.ts";14import type { ProviderAdapterError, ProviderUnsupportedError } from "../Errors.ts";
...@@ -34,7 +34,7 @@ export interface ProviderAdapterRegistryShape {...@@ -34,7 +34,7 @@ export interface ProviderAdapterRegistryShape {
34/**34/**
35 * ProviderAdapterRegistry - Service tag for provider adapter lookup.35 * ProviderAdapterRegistry - Service tag for provider adapter lookup.
36 */36 */
37export class ProviderAdapterRegistry extends ServiceMap.Service<37export class ProviderAdapterRegistry extends Context.Service<
38 ProviderAdapterRegistry,38 ProviderAdapterRegistry,
39 ProviderAdapterRegistryShape39 ProviderAdapterRegistryShape
40>()("t3/provider/Services/ProviderAdapterRegistry") {}40>()("t3/provider/Services/ProviderAdapterRegistry") {}
apps/server/src/provider/Services/ProviderRegistry.ts+2-2
...@@ -7,7 +7,7 @@...@@ -7,7 +7,7 @@
7 * @module ProviderRegistry7 * @module ProviderRegistry
8 */8 */
9import type { ProviderKind, ServerProvider } from "@t3tools/contracts";9import type { ProviderKind, ServerProvider } from "@t3tools/contracts";
10import { ServiceMap } from "effect";10import { Context } from "effect";
11import type { Effect, Stream } from "effect";11import type { Effect, Stream } from "effect";
1212
13export interface ProviderRegistryShape {13export interface ProviderRegistryShape {
...@@ -27,6 +27,6 @@ export interface ProviderRegistryShape {...@@ -27,6 +27,6 @@ export interface ProviderRegistryShape {
27 readonly streamChanges: Stream.Stream<ReadonlyArray<ServerProvider>>;27 readonly streamChanges: Stream.Stream<ReadonlyArray<ServerProvider>>;
28}28}
2929
30export class ProviderRegistry extends ServiceMap.Service<ProviderRegistry, ProviderRegistryShape>()(30export class ProviderRegistry extends Context.Service<ProviderRegistry, ProviderRegistryShape>()(
31 "t3/provider/Services/ProviderRegistry",31 "t3/provider/Services/ProviderRegistry",
32) {}32) {}
apps/server/src/provider/Services/ProviderService.ts+3-3
...@@ -6,7 +6,7 @@...@@ -6,7 +6,7 @@
6 * session-scoped calls via `ProviderSessionDirectory`, and exposes one unified6 * session-scoped calls via `ProviderSessionDirectory`, and exposes one unified
7 * provider event stream to callers.7 * provider event stream to callers.
8 *8 *
9 * Uses Effect `ServiceMap.Service` for dependency injection and returns typed9 * Uses Effect `Context.Service` for dependency injection and returns typed
10 * domain errors for validation, session, codex, and checkpoint workflows.10 * domain errors for validation, session, codex, and checkpoint workflows.
11 *11 *
12 * @module ProviderService12 * @module ProviderService
...@@ -24,7 +24,7 @@ import type {...@@ -24,7 +24,7 @@ import type {
24 ThreadId,24 ThreadId,
25 ProviderTurnStartResult,25 ProviderTurnStartResult,
26} from "@t3tools/contracts";26} from "@t3tools/contracts";
27import { ServiceMap } from "effect";27import { Context } from "effect";
28import type { Effect, Stream } from "effect";28import type { Effect, Stream } from "effect";
2929
30import type { ProviderServiceError } from "../Errors.ts";30import type { ProviderServiceError } from "../Errors.ts";
...@@ -110,6 +110,6 @@ export interface ProviderServiceShape {...@@ -110,6 +110,6 @@ export interface ProviderServiceShape {
110/**110/**
111 * ProviderService - Service tag for provider orchestration.111 * ProviderService - Service tag for provider orchestration.
112 */112 */
113export class ProviderService extends ServiceMap.Service<ProviderService, ProviderServiceShape>()(113export class ProviderService extends Context.Service<ProviderService, ProviderServiceShape>()(
114 "t3/provider/Services/ProviderService",114 "t3/provider/Services/ProviderService",
115) {}115) {}
apps/server/src/provider/Services/ProviderSessionDirectory.ts+2-2
...@@ -4,7 +4,7 @@ import type {...@@ -4,7 +4,7 @@ import type {
4 RuntimeMode,4 RuntimeMode,
5 ThreadId,5 ThreadId,
6} from "@t3tools/contracts";6} from "@t3tools/contracts";
7import { Option, ServiceMap } from "effect";7import { Option, Context } from "effect";
8import type { Effect } from "effect";8import type { Effect } from "effect";
99
10import type {10import type {
...@@ -51,7 +51,7 @@ export interface ProviderSessionDirectoryShape {...@@ -51,7 +51,7 @@ export interface ProviderSessionDirectoryShape {
51 >;51 >;
52}52}
5353
54export class ProviderSessionDirectory extends ServiceMap.Service<54export class ProviderSessionDirectory extends Context.Service<
55 ProviderSessionDirectory,55 ProviderSessionDirectory,
56 ProviderSessionDirectoryShape56 ProviderSessionDirectoryShape
57>()("t3/provider/Services/ProviderSessionDirectory") {}57>()("t3/provider/Services/ProviderSessionDirectory") {}
apps/server/src/server.test.ts+32-32
...@@ -101,15 +101,15 @@ import { WorkspacePathsLive } from "./workspace/Layers/WorkspacePaths.ts";...@@ -101,15 +101,15 @@ import { WorkspacePathsLive } from "./workspace/Layers/WorkspacePaths.ts";
101import { ServerSecretStoreLive } from "./auth/Layers/ServerSecretStore.ts";101import { ServerSecretStoreLive } from "./auth/Layers/ServerSecretStore.ts";
102import { ServerAuthLive } from "./auth/Layers/ServerAuth.ts";102import { ServerAuthLive } from "./auth/Layers/ServerAuth.ts";
103103
104const defaultProjectId = ProjectId.makeUnsafe("project-default");104const defaultProjectId = ProjectId.make("project-default");
105const defaultThreadId = ThreadId.makeUnsafe("thread-default");105const defaultThreadId = ThreadId.make("thread-default");
106const defaultDesktopBootstrapToken = "test-desktop-bootstrap-token";106const defaultDesktopBootstrapToken = "test-desktop-bootstrap-token";
107const defaultModelSelection = {107const defaultModelSelection = {
108 provider: "codex",108 provider: "codex",
109 model: "gpt-5-codex",109 model: "gpt-5-codex",
110} as const;110} as const;
111const testEnvironmentDescriptor = {111const testEnvironmentDescriptor = {
112 environmentId: EnvironmentId.makeUnsafe("environment-test"),112 environmentId: EnvironmentId.make("environment-test"),
113 label: "Test environment",113 label: "Test environment",
114 platform: {114 platform: {
115 os: "darwin" as const,115 os: "darwin" as const,
...@@ -1307,7 +1307,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => {...@@ -1307,7 +1307,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => {
1307 );1307 );
13081308
1309 assert.equal(error._tag, "RpcClientError");1309 assert.equal(error._tag, "RpcClientError");
1310 assertInclude(String(error), "401");1310 assertInclude(String(error), "SocketOpenError");
1311 }).pipe(Effect.provide(NodeHttpServer.layerTest)),1311 }).pipe(Effect.provide(NodeHttpServer.layerTest)),
1312 );1312 );
13131313
...@@ -2685,7 +2685,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => {...@@ -2685,7 +2685,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => {
2685 updatedAt: now,2685 updatedAt: now,
2686 projects: [2686 projects: [
2687 {2687 {
2688 id: ProjectId.makeUnsafe("project-a"),2688 id: ProjectId.make("project-a"),
2689 title: "Project A",2689 title: "Project A",
2690 workspaceRoot: "/tmp/project-a",2690 workspaceRoot: "/tmp/project-a",
2691 defaultModelSelection,2691 defaultModelSelection,
...@@ -2697,8 +2697,8 @@ it.layer(NodeServices.layer)("server router seam", (it) => {...@@ -2697,8 +2697,8 @@ it.layer(NodeServices.layer)("server router seam", (it) => {
2697 ],2697 ],
2698 threads: [2698 threads: [
2699 {2699 {
2700 id: ThreadId.makeUnsafe("thread-1"),2700 id: ThreadId.make("thread-1"),
2701 projectId: ProjectId.makeUnsafe("project-a"),2701 projectId: ProjectId.make("project-a"),
2702 title: "Thread A",2702 title: "Thread A",
2703 modelSelection: defaultModelSelection,2703 modelSelection: defaultModelSelection,
2704 interactionMode: "default" as const,2704 interactionMode: "default" as const,
...@@ -2731,14 +2731,14 @@ it.layer(NodeServices.layer)("server router seam", (it) => {...@@ -2731,14 +2731,14 @@ it.layer(NodeServices.layer)("server router seam", (it) => {
2731 checkpointDiffQuery: {2731 checkpointDiffQuery: {
2732 getTurnDiff: () =>2732 getTurnDiff: () =>
2733 Effect.succeed({2733 Effect.succeed({
2734 threadId: ThreadId.makeUnsafe("thread-1"),2734 threadId: ThreadId.make("thread-1"),
2735 fromTurnCount: 0,2735 fromTurnCount: 0,
2736 toTurnCount: 1,2736 toTurnCount: 1,
2737 diff: "turn-diff",2737 diff: "turn-diff",
2738 }),2738 }),
2739 getFullThreadDiff: () =>2739 getFullThreadDiff: () =>
2740 Effect.succeed({2740 Effect.succeed({
2741 threadId: ThreadId.makeUnsafe("thread-1"),2741 threadId: ThreadId.make("thread-1"),
2742 fromTurnCount: 0,2742 fromTurnCount: 0,
2743 toTurnCount: 1,2743 toTurnCount: 1,
2744 diff: "full-diff",2744 diff: "full-diff",
...@@ -2757,8 +2757,8 @@ it.layer(NodeServices.layer)("server router seam", (it) => {...@@ -2757,8 +2757,8 @@ it.layer(NodeServices.layer)("server router seam", (it) => {
2757 withWsRpcClient(wsUrl, (client) =>2757 withWsRpcClient(wsUrl, (client) =>
2758 client[ORCHESTRATION_WS_METHODS.dispatchCommand]({2758 client[ORCHESTRATION_WS_METHODS.dispatchCommand]({
2759 type: "thread.session.stop",2759 type: "thread.session.stop",
2760 commandId: CommandId.makeUnsafe("cmd-1"),2760 commandId: CommandId.make("cmd-1"),
2761 threadId: ThreadId.makeUnsafe("thread-1"),2761 threadId: ThreadId.make("thread-1"),
2762 createdAt: now,2762 createdAt: now,
2763 }),2763 }),
2764 ),2764 ),
...@@ -2768,7 +2768,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => {...@@ -2768,7 +2768,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => {
2768 const turnDiffResult = yield* Effect.scoped(2768 const turnDiffResult = yield* Effect.scoped(
2769 withWsRpcClient(wsUrl, (client) =>2769 withWsRpcClient(wsUrl, (client) =>
2770 client[ORCHESTRATION_WS_METHODS.getTurnDiff]({2770 client[ORCHESTRATION_WS_METHODS.getTurnDiff]({
2771 threadId: ThreadId.makeUnsafe("thread-1"),2771 threadId: ThreadId.make("thread-1"),
2772 fromTurnCount: 0,2772 fromTurnCount: 0,
2773 toTurnCount: 1,2773 toTurnCount: 1,
2774 }),2774 }),
...@@ -2779,7 +2779,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => {...@@ -2779,7 +2779,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => {
2779 const fullDiffResult = yield* Effect.scoped(2779 const fullDiffResult = yield* Effect.scoped(
2780 withWsRpcClient(wsUrl, (client) =>2780 withWsRpcClient(wsUrl, (client) =>
2781 client[ORCHESTRATION_WS_METHODS.getFullThreadDiff]({2781 client[ORCHESTRATION_WS_METHODS.getFullThreadDiff]({
2782 threadId: ThreadId.makeUnsafe("thread-1"),2782 threadId: ThreadId.make("thread-1"),
2783 toTurnCount: 1,2783 toTurnCount: 1,
2784 }),2784 }),
2785 ),2785 ),
...@@ -2818,7 +2818,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => {...@@ -2818,7 +2818,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => {
2818 readEvents: (_fromSequenceExclusive) =>2818 readEvents: (_fromSequenceExclusive) =>
2819 Stream.make({2819 Stream.make({
2820 sequence: 1,2820 sequence: 1,
2821 eventId: EventId.makeUnsafe("event-1"),2821 eventId: EventId.make("event-1"),
2822 aggregateKind: "project",2822 aggregateKind: "project",
2823 aggregateId: defaultProjectId,2823 aggregateId: defaultProjectId,
2824 occurredAt: "2026-04-05T00:00:00.000Z",2824 occurredAt: "2026-04-05T00:00:00.000Z",
...@@ -2866,7 +2866,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => {...@@ -2866,7 +2866,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => {
28662866
2867 it.effect("closes thread terminals after a successful archive command", () =>2867 it.effect("closes thread terminals after a successful archive command", () =>
2868 Effect.gen(function* () {2868 Effect.gen(function* () {
2869 const threadId = ThreadId.makeUnsafe("thread-archive");2869 const threadId = ThreadId.make("thread-archive");
2870 const closeInputs: Array<Parameters<TerminalManagerShape["close"]>[0]> = [];2870 const closeInputs: Array<Parameters<TerminalManagerShape["close"]>[0]> = [];
28712871
2872 yield* buildAppUnderTest({2872 yield* buildAppUnderTest({
...@@ -2888,7 +2888,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => {...@@ -2888,7 +2888,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => {
2888 withWsRpcClient(wsUrl, (client) =>2888 withWsRpcClient(wsUrl, (client) =>
2889 client[ORCHESTRATION_WS_METHODS.dispatchCommand]({2889 client[ORCHESTRATION_WS_METHODS.dispatchCommand]({
2890 type: "thread.archive",2890 type: "thread.archive",
2891 commandId: CommandId.makeUnsafe("cmd-thread-archive"),2891 commandId: CommandId.make("cmd-thread-archive"),
2892 threadId,2892 threadId,
2893 }),2893 }),
2894 ),2894 ),
...@@ -2948,10 +2948,10 @@ it.layer(NodeServices.layer)("server router seam", (it) => {...@@ -2948,10 +2948,10 @@ it.layer(NodeServices.layer)("server router seam", (it) => {
2948 withWsRpcClient(wsUrl, (client) =>2948 withWsRpcClient(wsUrl, (client) =>
2949 client[ORCHESTRATION_WS_METHODS.dispatchCommand]({2949 client[ORCHESTRATION_WS_METHODS.dispatchCommand]({
2950 type: "thread.turn.start",2950 type: "thread.turn.start",
2951 commandId: CommandId.makeUnsafe("cmd-bootstrap-turn-start"),2951 commandId: CommandId.make("cmd-bootstrap-turn-start"),
2952 threadId: ThreadId.makeUnsafe("thread-bootstrap"),2952 threadId: ThreadId.make("thread-bootstrap"),
2953 message: {2953 message: {
2954 messageId: MessageId.makeUnsafe("msg-bootstrap"),2954 messageId: MessageId.make("msg-bootstrap"),
2955 role: "user",2955 role: "user",
2956 text: "hello",2956 text: "hello",
2957 attachments: [],2957 attachments: [],
...@@ -3000,7 +3000,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => {...@@ -3000,7 +3000,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => {
3000 path: null,3000 path: null,
3001 });3001 });
3002 assert.deepEqual(runForThread.mock.calls[0]?.[0], {3002 assert.deepEqual(runForThread.mock.calls[0]?.[0], {
3003 threadId: ThreadId.makeUnsafe("thread-bootstrap"),3003 threadId: ThreadId.make("thread-bootstrap"),
3004 projectId: defaultProjectId,3004 projectId: defaultProjectId,
3005 projectCwd: "/tmp/project",3005 projectCwd: "/tmp/project",
3006 worktreePath: "/tmp/bootstrap-worktree",3006 worktreePath: "/tmp/bootstrap-worktree",
...@@ -3063,10 +3063,10 @@ it.layer(NodeServices.layer)("server router seam", (it) => {...@@ -3063,10 +3063,10 @@ it.layer(NodeServices.layer)("server router seam", (it) => {
3063 withWsRpcClient(wsUrl, (client) =>3063 withWsRpcClient(wsUrl, (client) =>
3064 client[ORCHESTRATION_WS_METHODS.dispatchCommand]({3064 client[ORCHESTRATION_WS_METHODS.dispatchCommand]({
3065 type: "thread.turn.start",3065 type: "thread.turn.start",
3066 commandId: CommandId.makeUnsafe("cmd-bootstrap-turn-start-setup-failure"),3066 commandId: CommandId.make("cmd-bootstrap-turn-start-setup-failure"),
3067 threadId: ThreadId.makeUnsafe("thread-bootstrap-setup-failure"),3067 threadId: ThreadId.make("thread-bootstrap-setup-failure"),
3068 message: {3068 message: {
3069 messageId: MessageId.makeUnsafe("msg-bootstrap-setup-failure"),3069 messageId: MessageId.make("msg-bootstrap-setup-failure"),
3070 role: "user",3070 role: "user",
3071 text: "hello",3071 text: "hello",
3072 attachments: [],3072 attachments: [],
...@@ -3179,10 +3179,10 @@ it.layer(NodeServices.layer)("server router seam", (it) => {...@@ -3179,10 +3179,10 @@ it.layer(NodeServices.layer)("server router seam", (it) => {
3179 withWsRpcClient(wsUrl, (client) =>3179 withWsRpcClient(wsUrl, (client) =>
3180 client[ORCHESTRATION_WS_METHODS.dispatchCommand]({3180 client[ORCHESTRATION_WS_METHODS.dispatchCommand]({
3181 type: "thread.turn.start",3181 type: "thread.turn.start",
3182 commandId: CommandId.makeUnsafe("cmd-bootstrap-turn-start-setup-activity-failure"),3182 commandId: CommandId.make("cmd-bootstrap-turn-start-setup-activity-failure"),
3183 threadId: ThreadId.makeUnsafe("thread-bootstrap-setup-activity-failure"),3183 threadId: ThreadId.make("thread-bootstrap-setup-activity-failure"),
3184 message: {3184 message: {
3185 messageId: MessageId.makeUnsafe("msg-bootstrap-setup-activity-failure"),3185 messageId: MessageId.make("msg-bootstrap-setup-activity-failure"),
3186 role: "user",3186 role: "user",
3187 text: "hello",3187 text: "hello",
3188 attachments: [],3188 attachments: [],
...@@ -3262,10 +3262,10 @@ it.layer(NodeServices.layer)("server router seam", (it) => {...@@ -3262,10 +3262,10 @@ it.layer(NodeServices.layer)("server router seam", (it) => {
3262 withWsRpcClient(wsUrl, (client) =>3262 withWsRpcClient(wsUrl, (client) =>
3263 client[ORCHESTRATION_WS_METHODS.dispatchCommand]({3263 client[ORCHESTRATION_WS_METHODS.dispatchCommand]({
3264 type: "thread.turn.start",3264 type: "thread.turn.start",
3265 commandId: CommandId.makeUnsafe("cmd-bootstrap-turn-start-defect"),3265 commandId: CommandId.make("cmd-bootstrap-turn-start-defect"),
3266 threadId: ThreadId.makeUnsafe("thread-bootstrap-defect"),3266 threadId: ThreadId.make("thread-bootstrap-defect"),
3267 message: {3267 message: {
3268 messageId: MessageId.makeUnsafe("msg-bootstrap-defect"),3268 messageId: MessageId.make("msg-bootstrap-defect"),
3269 role: "user",3269 role: "user",
3270 text: "hello",3270 text: "hello",
3271 attachments: [],3271 attachments: [],
...@@ -3311,7 +3311,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => {...@@ -3311,7 +3311,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => {
3311 () =>3311 () =>
3312 Effect.gen(function* () {3312 Effect.gen(function* () {
3313 const now = new Date().toISOString();3313 const now = new Date().toISOString();
3314 const threadId = ThreadId.makeUnsafe("thread-1");3314 const threadId = ThreadId.make("thread-1");
3315 let replayCursor: number | null = null;3315 let replayCursor: number | null = null;
3316 const makeEvent = (sequence: number): OrchestrationEvent =>3316 const makeEvent = (sequence: number): OrchestrationEvent =>
3317 ({3317 ({
...@@ -3393,7 +3393,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => {...@@ -3393,7 +3393,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => {
3393 readEvents: () =>3393 readEvents: () =>
3394 Stream.make({3394 Stream.make({
3395 sequence: 1,3395 sequence: 1,
3396 eventId: EventId.makeUnsafe("event-1"),3396 eventId: EventId.make("event-1"),
3397 aggregateKind: "project",3397 aggregateKind: "project",
3398 aggregateId: defaultProjectId,3398 aggregateId: defaultProjectId,
3399 occurredAt: "2026-04-06T00:00:00.000Z",3399 occurredAt: "2026-04-06T00:00:00.000Z",
...@@ -3464,7 +3464,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => {...@@ -3464,7 +3464,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => {
3464 }),3464 }),
3465 streamDomainEvents: Stream.make({3465 streamDomainEvents: Stream.make({
3466 sequence: 1,3466 sequence: 1,
3467 eventId: EventId.makeUnsafe("event-1"),3467 eventId: EventId.make("event-1"),
3468 aggregateKind: "project",3468 aggregateKind: "project",
3469 aggregateId: defaultProjectId,3469 aggregateId: defaultProjectId,
3470 occurredAt: "2026-04-05T00:00:00.000Z",3470 occurredAt: "2026-04-05T00:00:00.000Z",
apps/server/src/serverLifecycleEvents.test.ts+1-1
...@@ -11,7 +11,7 @@ it.effect(...@@ -11,7 +11,7 @@ it.effect(
11 Effect.gen(function* () {11 Effect.gen(function* () {
12 const lifecycleEvents = yield* ServerLifecycleEvents;12 const lifecycleEvents = yield* ServerLifecycleEvents;
13 const environment = {13 const environment = {
14 environmentId: EnvironmentId.makeUnsafe("environment-test"),14 environmentId: EnvironmentId.make("environment-test"),
15 label: "Test environment",15 label: "Test environment",
16 platform: { os: "darwin" as const, arch: "arm64" as const },16 platform: { os: "darwin" as const, arch: "arm64" as const },
17 serverVersion: "0.0.0-test",17 serverVersion: "0.0.0-test",
apps/server/src/serverLifecycleEvents.ts+2-2
...@@ -1,5 +1,5 @@...@@ -1,5 +1,5 @@
1import type { ServerLifecycleStreamEvent } from "@t3tools/contracts";1import type { ServerLifecycleStreamEvent } from "@t3tools/contracts";
2import { Effect, Layer, PubSub, Ref, ServiceMap, Stream } from "effect";2import { Effect, Layer, PubSub, Ref, Context, Stream } from "effect";
33
4type LifecycleEventInput =4type LifecycleEventInput =
5 | Omit<Extract<ServerLifecycleStreamEvent, { type: "welcome" }>, "sequence">5 | Omit<Extract<ServerLifecycleStreamEvent, { type: "welcome" }>, "sequence">
...@@ -16,7 +16,7 @@ export interface ServerLifecycleEventsShape {...@@ -16,7 +16,7 @@ export interface ServerLifecycleEventsShape {
16 readonly stream: Stream.Stream<ServerLifecycleStreamEvent>;16 readonly stream: Stream.Stream<ServerLifecycleStreamEvent>;
17}17}
1818
19export class ServerLifecycleEvents extends ServiceMap.Service<19export class ServerLifecycleEvents extends Context.Service<
20 ServerLifecycleEvents,20 ServerLifecycleEvents,
21 ServerLifecycleEventsShape21 ServerLifecycleEventsShape
22>()("t3/serverLifecycleEvents") {}22>()("t3/serverLifecycleEvents") {}
apps/server/src/serverRuntimeStartup.ts+6-6
...@@ -17,7 +17,7 @@ import {...@@ -17,7 +17,7 @@ import {
17 Queue,17 Queue,
18 Ref,18 Ref,
19 Scope,19 Scope,
20 ServiceMap,20 Context,
21 Console,21 Console,
22} from "effect";22} from "effect";
2323
...@@ -52,7 +52,7 @@ export interface ServerRuntimeStartupShape {...@@ -52,7 +52,7 @@ export interface ServerRuntimeStartupShape {
52 ) => Effect.Effect<A, E | ServerRuntimeStartupError>;52 ) => Effect.Effect<A, E | ServerRuntimeStartupError>;
53}53}
5454
55export class ServerRuntimeStartup extends ServiceMap.Service<55export class ServerRuntimeStartup extends Context.Service<
56 ServerRuntimeStartup,56 ServerRuntimeStartup,
57 ServerRuntimeStartupShape57 ServerRuntimeStartupShape
58>()("t3/serverRuntimeStartup") {}58>()("t3/serverRuntimeStartup") {}
...@@ -176,12 +176,12 @@ const autoBootstrapWelcome = Effect.gen(function* () {...@@ -176,12 +176,12 @@ const autoBootstrapWelcome = Effect.gen(function* () {
176176
177 if (Option.isNone(existingProject)) {177 if (Option.isNone(existingProject)) {
178 const createdAt = new Date().toISOString();178 const createdAt = new Date().toISOString();
179 nextProjectId = ProjectId.makeUnsafe(crypto.randomUUID());179 nextProjectId = ProjectId.make(crypto.randomUUID());
180 const bootstrapProjectTitle = path.basename(serverConfig.cwd) || "project";180 const bootstrapProjectTitle = path.basename(serverConfig.cwd) || "project";
181 nextProjectDefaultModelSelection = getAutoBootstrapDefaultModelSelection();181 nextProjectDefaultModelSelection = getAutoBootstrapDefaultModelSelection();
182 yield* orchestrationEngine.dispatch({182 yield* orchestrationEngine.dispatch({
183 type: "project.create",183 type: "project.create",
184 commandId: CommandId.makeUnsafe(crypto.randomUUID()),184 commandId: CommandId.make(crypto.randomUUID()),
185 projectId: nextProjectId,185 projectId: nextProjectId,
186 title: bootstrapProjectTitle,186 title: bootstrapProjectTitle,
187 workspaceRoot: serverConfig.cwd,187 workspaceRoot: serverConfig.cwd,
...@@ -198,10 +198,10 @@ const autoBootstrapWelcome = Effect.gen(function* () {...@@ -198,10 +198,10 @@ const autoBootstrapWelcome = Effect.gen(function* () {
198 yield* projectionReadModelQuery.getFirstActiveThreadIdByProjectId(nextProjectId);198 yield* projectionReadModelQuery.getFirstActiveThreadIdByProjectId(nextProjectId);
199 if (Option.isNone(existingThreadId)) {199 if (Option.isNone(existingThreadId)) {
200 const createdAt = new Date().toISOString();200 const createdAt = new Date().toISOString();
201 const createdThreadId = ThreadId.makeUnsafe(crypto.randomUUID());201 const createdThreadId = ThreadId.make(crypto.randomUUID());
202 yield* orchestrationEngine.dispatch({202 yield* orchestrationEngine.dispatch({
203 type: "thread.create",203 type: "thread.create",
204 commandId: CommandId.makeUnsafe(crypto.randomUUID()),204 commandId: CommandId.make(crypto.randomUUID()),
205 threadId: createdThreadId,205 threadId: createdThreadId,
206 projectId: nextProjectId,206 projectId: nextProjectId,
207 title: "New thread",207 title: "New thread",
apps/server/src/serverSettings.ts+2-2
...@@ -34,7 +34,7 @@ import {...@@ -34,7 +34,7 @@ import {
34 Schema,34 Schema,
35 SchemaIssue,35 SchemaIssue,
36 Scope,36 Scope,
37 ServiceMap,37 Context,
38 Stream,38 Stream,
39 Cause,39 Cause,
40} from "effect";40} from "effect";
...@@ -62,7 +62,7 @@ export interface ServerSettingsShape {...@@ -62,7 +62,7 @@ export interface ServerSettingsShape {
62 readonly streamChanges: Stream.Stream<ServerSettings>;62 readonly streamChanges: Stream.Stream<ServerSettings>;
63}63}
6464
65export class ServerSettingsService extends ServiceMap.Service<65export class ServerSettingsService extends Context.Service<
66 ServerSettingsService,66 ServerSettingsService,
67 ServerSettingsShape67 ServerSettingsShape
68>()("t3/serverSettings/ServerSettingsService") {68>()("t3/serverSettings/ServerSettingsService") {
apps/server/src/telemetry/Services/AnalyticsService.ts+2-2
...@@ -6,7 +6,7 @@...@@ -6,7 +6,7 @@
6 *6 *
7 * @module AnalyticsService7 * @module AnalyticsService
8 */8 */
9import { Effect, Layer, ServiceMap } from "effect";9import { Effect, Layer, Context } from "effect";
1010
11export interface AnalyticsServiceShape {11export interface AnalyticsServiceShape {
12 /**12 /**
...@@ -23,7 +23,7 @@ export interface AnalyticsServiceShape {...@@ -23,7 +23,7 @@ export interface AnalyticsServiceShape {
23 readonly flush: Effect.Effect<void, never>;23 readonly flush: Effect.Effect<void, never>;
24}24}
2525
26export class AnalyticsService extends ServiceMap.Service<AnalyticsService, AnalyticsServiceShape>()(26export class AnalyticsService extends Context.Service<AnalyticsService, AnalyticsServiceShape>()(
27 "t3/telemetry/Services/AnalyticsService",27 "t3/telemetry/Services/AnalyticsService",
28) {28) {
29 static readonly layerTest = Layer.succeed(AnalyticsService, {29 static readonly layerTest = Layer.succeed(AnalyticsService, {
apps/server/src/terminal/Layers/Manager.ts+2-2
...@@ -664,8 +664,8 @@ const makeTerminalManager = Effect.fn("makeTerminalManager")(function* () {...@@ -664,8 +664,8 @@ const makeTerminalManager = Effect.fn("makeTerminalManager")(function* () {
664export const makeTerminalManagerWithOptions = Effect.fn("makeTerminalManagerWithOptions")(664export const makeTerminalManagerWithOptions = Effect.fn("makeTerminalManagerWithOptions")(
665 function* (options: TerminalManagerOptions) {665 function* (options: TerminalManagerOptions) {
666 const fileSystem = yield* FileSystem.FileSystem;666 const fileSystem = yield* FileSystem.FileSystem;
667 const services = yield* Effect.services();667 const context = yield* Effect.context<never>();
668 const runFork = Effect.runForkWith(services);668 const runFork = Effect.runForkWith(context);
669669
670 const logsDir = options.logsDir;670 const logsDir = options.logsDir;
671 const historyLineLimit = options.historyLineLimit ?? DEFAULT_HISTORY_LINE_LIMIT;671 const historyLineLimit = options.historyLineLimit ?? DEFAULT_HISTORY_LINE_LIMIT;
apps/server/src/terminal/Services/Manager.ts+2-2
...@@ -23,7 +23,7 @@ import {...@@ -23,7 +23,7 @@ import {
23 TerminalWriteInput,23 TerminalWriteInput,
24} from "@t3tools/contracts";24} from "@t3tools/contracts";
25import { PtyProcess } from "./PTY";25import { PtyProcess } from "./PTY";
26import { Effect, ServiceMap } from "effect";26import { Effect, Context } from "effect";
2727
28export {28export {
29 TerminalCwdError,29 TerminalCwdError,
...@@ -122,6 +122,6 @@ export interface TerminalManagerShape {...@@ -122,6 +122,6 @@ export interface TerminalManagerShape {
122/**122/**
123 * TerminalManager - Service tag for terminal session orchestration.123 * TerminalManager - Service tag for terminal session orchestration.
124 */124 */
125export class TerminalManager extends ServiceMap.Service<TerminalManager, TerminalManagerShape>()(125export class TerminalManager extends Context.Service<TerminalManager, TerminalManagerShape>()(
126 "t3/terminal/Services/Manager/TerminalManager",126 "t3/terminal/Services/Manager/TerminalManager",
127) {}127) {}
apps/server/src/terminal/Services/PTY.ts+2-2
...@@ -6,7 +6,7 @@...@@ -6,7 +6,7 @@
6 *6 *
7 * @module PtyAdapter7 * @module PtyAdapter
8 */8 */
9import { Effect, Schema, ServiceMap } from "effect";9import { Effect, Schema, Context } from "effect";
1010
11/**11/**
12 * PtyError - Error type for PTY adapter operations.12 * PtyError - Error type for PTY adapter operations.
...@@ -53,6 +53,6 @@ export interface PtyAdapterShape {...@@ -53,6 +53,6 @@ export interface PtyAdapterShape {
53/**53/**
54 * PtyAdapter - Service tag for PTY process integration.54 * PtyAdapter - Service tag for PTY process integration.
55 */55 */
56export class PtyAdapter extends ServiceMap.Service<PtyAdapter, PtyAdapterShape>()(56export class PtyAdapter extends Context.Service<PtyAdapter, PtyAdapterShape>()(
57 "t3/terminal/Services/PTY/PtyAdapter",57 "t3/terminal/Services/PTY/PtyAdapter",
58) {}58) {}
apps/server/src/workspace/Layers/WorkspaceEntries.ts+8-6
...@@ -430,12 +430,14 @@ export const makeWorkspaceEntries = Effect.gen(function* () {...@@ -430,12 +430,14 @@ export const makeWorkspaceEntries = Effect.gen(function* () {
430 return yield* buildWorkspaceIndexFromFilesystem(cwd);430 return yield* buildWorkspaceIndexFromFilesystem(cwd);
431 });431 });
432432
433 const workspaceIndexCache = yield* Cache.makeWith<string, WorkspaceIndex, WorkspaceEntriesError>({433 const workspaceIndexCache = yield* Cache.makeWith<string, WorkspaceIndex, WorkspaceEntriesError>(
434 capacity: WORKSPACE_CACHE_MAX_KEYS,434 buildWorkspaceIndex,
435 lookup: buildWorkspaceIndex,435 {
436 timeToLive: (exit) =>436 capacity: WORKSPACE_CACHE_MAX_KEYS,
437 Exit.isSuccess(exit) ? Duration.millis(WORKSPACE_CACHE_TTL_MS) : Duration.zero,437 timeToLive: (exit) =>
438 });438 Exit.isSuccess(exit) ? Duration.millis(WORKSPACE_CACHE_TTL_MS) : Duration.zero,
439 },
440 );
439441
440 const normalizeWorkspaceRoot = Effect.fn("WorkspaceEntries.normalizeWorkspaceRoot")(function* (442 const normalizeWorkspaceRoot = Effect.fn("WorkspaceEntries.normalizeWorkspaceRoot")(function* (
441 cwd: string,443 cwd: string,
apps/server/src/workspace/Services/WorkspaceEntries.ts+2-2
...@@ -6,7 +6,7 @@...@@ -6,7 +6,7 @@
6 *6 *
7 * @module WorkspaceEntries7 * @module WorkspaceEntries
8 */8 */
9import { Schema, ServiceMap } from "effect";9import { Schema, Context } from "effect";
10import type { Effect } from "effect";10import type { Effect } from "effect";
1111
12import type { ProjectSearchEntriesInput, ProjectSearchEntriesResult } from "@t3tools/contracts";12import type { ProjectSearchEntriesInput, ProjectSearchEntriesResult } from "@t3tools/contracts";
...@@ -43,6 +43,6 @@ export interface WorkspaceEntriesShape {...@@ -43,6 +43,6 @@ export interface WorkspaceEntriesShape {
43/**43/**
44 * WorkspaceEntries - Service tag for cached workspace entry search.44 * WorkspaceEntries - Service tag for cached workspace entry search.
45 */45 */
46export class WorkspaceEntries extends ServiceMap.Service<WorkspaceEntries, WorkspaceEntriesShape>()(46export class WorkspaceEntries extends Context.Service<WorkspaceEntries, WorkspaceEntriesShape>()(
47 "t3/workspace/Services/WorkspaceEntries",47 "t3/workspace/Services/WorkspaceEntries",
48) {}48) {}
apps/server/src/workspace/Services/WorkspaceFileSystem.ts+2-2
...@@ -6,7 +6,7 @@...@@ -6,7 +6,7 @@
6 *6 *
7 * @module WorkspaceFileSystem7 * @module WorkspaceFileSystem
8 */8 */
9import { Schema, ServiceMap } from "effect";9import { Schema, Context } from "effect";
10import type { Effect } from "effect";10import type { Effect } from "effect";
1111
12import type { ProjectWriteFileInput, ProjectWriteFileResult } from "@t3tools/contracts";12import type { ProjectWriteFileInput, ProjectWriteFileResult } from "@t3tools/contracts";
...@@ -44,7 +44,7 @@ export interface WorkspaceFileSystemShape {...@@ -44,7 +44,7 @@ export interface WorkspaceFileSystemShape {
44/**44/**
45 * WorkspaceFileSystem - Service tag for workspace file operations.45 * WorkspaceFileSystem - Service tag for workspace file operations.
46 */46 */
47export class WorkspaceFileSystem extends ServiceMap.Service<47export class WorkspaceFileSystem extends Context.Service<
48 WorkspaceFileSystem,48 WorkspaceFileSystem,
49 WorkspaceFileSystemShape49 WorkspaceFileSystemShape
50>()("t3/workspace/Services/WorkspaceFileSystem") {}50>()("t3/workspace/Services/WorkspaceFileSystem") {}
apps/server/src/workspace/Services/WorkspacePaths.ts+2-2
...@@ -6,7 +6,7 @@...@@ -6,7 +6,7 @@
6 *6 *
7 * @module WorkspacePaths7 * @module WorkspacePaths
8 */8 */
9import { Schema, ServiceMap } from "effect";9import { Schema, Context } from "effect";
10import type { Effect } from "effect";10import type { Effect } from "effect";
1111
12export class WorkspaceRootNotExistsError extends Schema.TaggedErrorClass<WorkspaceRootNotExistsError>()(12export class WorkspaceRootNotExistsError extends Schema.TaggedErrorClass<WorkspaceRootNotExistsError>()(
...@@ -80,6 +80,6 @@ export interface WorkspacePathsShape {...@@ -80,6 +80,6 @@ export interface WorkspacePathsShape {
80/**80/**
81 * WorkspacePaths - Service tag for workspace path normalization and resolution.81 * WorkspacePaths - Service tag for workspace path normalization and resolution.
82 */82 */
83export class WorkspacePaths extends ServiceMap.Service<WorkspacePaths, WorkspacePathsShape>()(83export class WorkspacePaths extends Context.Service<WorkspacePaths, WorkspacePathsShape>()(
84 "t3/workspace/Services/WorkspacePaths",84 "t3/workspace/Services/WorkspacePaths",
85) {}85) {}
apps/server/src/ws.ts+2-2
...@@ -128,7 +128,7 @@ const makeWsRpcLayer = (currentSessionId: AuthSessionId) =>...@@ -128,7 +128,7 @@ const makeWsRpcLayer = (currentSessionId: AuthSessionId) =>
128 const bootstrapCredentials = yield* BootstrapCredentialService;128 const bootstrapCredentials = yield* BootstrapCredentialService;
129 const sessions = yield* SessionCredentialService;129 const sessions = yield* SessionCredentialService;
130 const serverCommandId = (tag: string) =>130 const serverCommandId = (tag: string) =>
131 CommandId.makeUnsafe(`server:${tag}:${crypto.randomUUID()}`);131 CommandId.make(`server:${tag}:${crypto.randomUUID()}`);
132132
133 const loadAuthAccessSnapshot = () =>133 const loadAuthAccessSnapshot = () =>
134 Effect.all({134 Effect.all({
...@@ -149,7 +149,7 @@ const makeWsRpcLayer = (currentSessionId: AuthSessionId) =>...@@ -149,7 +149,7 @@ const makeWsRpcLayer = (currentSessionId: AuthSessionId) =>
149 commandId: serverCommandId("setup-script-activity"),149 commandId: serverCommandId("setup-script-activity"),
150 threadId: input.threadId,150 threadId: input.threadId,
151 activity: {151 activity: {
152 id: EventId.makeUnsafe(crypto.randomUUID()),152 id: EventId.make(crypto.randomUUID()),
153 tone: input.tone,153 tone: input.tone,
154 kind: input.kind,154 kind: input.kind,
155 summary: input.summary,155 summary: input.summary,
apps/web/src/clientPersistenceStorage.test.ts+1-1
...@@ -1,7 +1,7 @@...@@ -1,7 +1,7 @@
1import { EnvironmentId, type PersistedSavedEnvironmentRecord } from "@t3tools/contracts";1import { EnvironmentId, type PersistedSavedEnvironmentRecord } from "@t3tools/contracts";
2import { afterEach, describe, expect, it, vi } from "vitest";2import { afterEach, describe, expect, it, vi } from "vitest";
33
4const testEnvironmentId = EnvironmentId.makeUnsafe("environment-1");4const testEnvironmentId = EnvironmentId.make("environment-1");
55
6const savedRegistryRecord: PersistedSavedEnvironmentRecord = {6const savedRegistryRecord: PersistedSavedEnvironmentRecord = {
7 environmentId: testEnvironmentId,7 environmentId: testEnvironmentId,
apps/web/src/components/BranchToolbar.logic.test.ts+2-2
...@@ -13,8 +13,8 @@ import {...@@ -13,8 +13,8 @@ import {
13 shouldIncludeBranchPickerItem,13 shouldIncludeBranchPickerItem,
14} from "./BranchToolbar.logic";14} from "./BranchToolbar.logic";
1515
16const localEnvironmentId = EnvironmentId.makeUnsafe("environment-local");16const localEnvironmentId = EnvironmentId.make("environment-local");
17const remoteEnvironmentId = EnvironmentId.makeUnsafe("environment-remote");17const remoteEnvironmentId = EnvironmentId.make("environment-remote");
1818
19describe("resolveDraftEnvModeAfterBranchChange", () => {19describe("resolveDraftEnvModeAfterBranchChange", () => {
20 it("switches to local mode when returning from an existing worktree to the main worktree", () => {20 it("switches to local mode when returning from an existing worktree to the main worktree", () => {
apps/web/src/components/ChatView.browser.tsx+7-7
...@@ -58,7 +58,7 @@ vi.mock("../lib/gitStatusState", () => ({...@@ -58,7 +58,7 @@ vi.mock("../lib/gitStatusState", () => ({
5858
59const THREAD_ID = "thread-browser-test" as ThreadId;59const THREAD_ID = "thread-browser-test" as ThreadId;
60const PROJECT_ID = "project-1" as ProjectId;60const PROJECT_ID = "project-1" as ProjectId;
61const LOCAL_ENVIRONMENT_ID = EnvironmentId.makeUnsafe("environment-local");61const LOCAL_ENVIRONMENT_ID = EnvironmentId.make("environment-local");
62const THREAD_REF = scopeThreadRef(LOCAL_ENVIRONMENT_ID, THREAD_ID);62const THREAD_REF = scopeThreadRef(LOCAL_ENVIRONMENT_ID, THREAD_ID);
63const THREAD_KEY = scopedThreadKey(THREAD_REF);63const THREAD_KEY = scopedThreadKey(THREAD_REF);
64const UUID_ROUTE_RE = /^\/draft\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;64const UUID_ROUTE_RE = /^\/draft\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
...@@ -143,7 +143,7 @@ function isoAt(offsetSeconds: number): string {...@@ -143,7 +143,7 @@ function isoAt(offsetSeconds: number): string {
143function createBaseServerConfig(): ServerConfig {143function createBaseServerConfig(): ServerConfig {
144 return {144 return {
145 environment: {145 environment: {
146 environmentId: EnvironmentId.makeUnsafe("environment-local"),146 environmentId: EnvironmentId.make("environment-local"),
147 label: "Local environment",147 label: "Local environment",
148 platform: { os: "darwin" as const, arch: "arm64" as const },148 platform: { os: "darwin" as const, arch: "arm64" as const },
149 serverVersion: "0.0.0-test",149 serverVersion: "0.0.0-test",
...@@ -341,7 +341,7 @@ function buildFixture(snapshot: OrchestrationReadModel): TestFixture {...@@ -341,7 +341,7 @@ function buildFixture(snapshot: OrchestrationReadModel): TestFixture {
341 serverConfig: createBaseServerConfig(),341 serverConfig: createBaseServerConfig(),
342 welcome: {342 welcome: {
343 environment: {343 environment: {
344 environmentId: EnvironmentId.makeUnsafe("environment-local"),344 environmentId: EnvironmentId.make("environment-local"),
345 label: "Local environment",345 label: "Local environment",
346 platform: { os: "darwin" as const, arch: "arm64" as const },346 platform: { os: "darwin" as const, arch: "arm64" as const },
347 serverVersion: "0.0.0-test",347 serverVersion: "0.0.0-test",
...@@ -402,7 +402,7 @@ function addThreadToSnapshot(...@@ -402,7 +402,7 @@ function addThreadToSnapshot(
402function createThreadCreatedEvent(threadId: ThreadId, sequence: number): OrchestrationEvent {402function createThreadCreatedEvent(threadId: ThreadId, sequence: number): OrchestrationEvent {
403 return {403 return {
404 sequence,404 sequence,
405 eventId: EventId.makeUnsafe(`event-thread-created-${sequence}`),405 eventId: EventId.make(`event-thread-created-${sequence}`),
406 aggregateKind: "thread",406 aggregateKind: "thread",
407 aggregateId: threadId,407 aggregateId: threadId,
408 occurredAt: NOW_ISO,408 occurredAt: NOW_ISO,
...@@ -432,7 +432,7 @@ function createThreadCreatedEvent(threadId: ThreadId, sequence: number): Orchest...@@ -432,7 +432,7 @@ function createThreadCreatedEvent(threadId: ThreadId, sequence: number): Orchest
432function createThreadSessionSetEvent(threadId: ThreadId, sequence: number): OrchestrationEvent {432function createThreadSessionSetEvent(threadId: ThreadId, sequence: number): OrchestrationEvent {
433 return {433 return {
434 sequence,434 sequence,
435 eventId: EventId.makeUnsafe(`event-thread-session-set-${sequence}`),435 eventId: EventId.make(`event-thread-session-set-${sequence}`),
436 aggregateKind: "thread",436 aggregateKind: "thread",
437 aggregateId: threadId,437 aggregateId: threadId,
438 occurredAt: NOW_ISO,438 occurredAt: NOW_ISO,
...@@ -498,7 +498,7 @@ function draftIdFromPath(pathname: string) {...@@ -498,7 +498,7 @@ function draftIdFromPath(pathname: string) {
498 if (!draftId) {498 if (!draftId) {
499 throw new Error(`Expected thread path, received "${pathname}".`);499 throw new Error(`Expected thread path, received "${pathname}".`);
500 }500 }
501 return DraftId.makeUnsafe(draftId);501 return DraftId.make(draftId);
502}502}
503503
504function draftThreadIdFor(draftId: ReturnType<typeof draftIdFromPath>): ThreadId {504function draftThreadIdFor(draftId: ReturnType<typeof draftIdFromPath>): ThreadId {
...@@ -666,7 +666,7 @@ function createSnapshotWithPendingUserInput(): OrchestrationReadModel {...@@ -666,7 +666,7 @@ function createSnapshotWithPendingUserInput(): OrchestrationReadModel {
666 interactionMode: "plan",666 interactionMode: "plan",
667 activities: [667 activities: [
668 {668 {
669 id: EventId.makeUnsafe("activity-user-input-requested"),669 id: EventId.make("activity-user-input-requested"),
670 tone: "info",670 tone: "info",
671 kind: "user-input.requested",671 kind: "user-input.requested",
672 summary: "User input requested",672 summary: "User input requested",
apps/web/src/components/ChatView.logic.test.ts+45-56
...@@ -15,7 +15,7 @@ import {...@@ -15,7 +15,7 @@ import {
15 waitForStartedServerThread,15 waitForStartedServerThread,
16} from "./ChatView.logic";16} from "./ChatView.logic";
1717
18const localEnvironmentId = EnvironmentId.makeUnsafe("environment-local");18const localEnvironmentId = EnvironmentId.make("environment-local");
1919
20describe("deriveComposerSendState", () => {20describe("deriveComposerSendState", () => {
21 it("treats expired terminal pills as non-sendable content", () => {21 it("treats expired terminal pills as non-sendable content", () => {
...@@ -25,7 +25,7 @@ describe("deriveComposerSendState", () => {...@@ -25,7 +25,7 @@ describe("deriveComposerSendState", () => {
25 terminalContexts: [25 terminalContexts: [
26 {26 {
27 id: "ctx-expired",27 id: "ctx-expired",
28 threadId: ThreadId.makeUnsafe("thread-1"),28 threadId: ThreadId.make("thread-1"),
29 terminalId: "default",29 terminalId: "default",
30 terminalLabel: "Terminal 1",30 terminalLabel: "Terminal 1",
31 lineStart: 4,31 lineStart: 4,
...@@ -49,7 +49,7 @@ describe("deriveComposerSendState", () => {...@@ -49,7 +49,7 @@ describe("deriveComposerSendState", () => {
49 terminalContexts: [49 terminalContexts: [
50 {50 {
51 id: "ctx-expired",51 id: "ctx-expired",
52 threadId: ThreadId.makeUnsafe("thread-1"),52 threadId: ThreadId.make("thread-1"),
53 terminalId: "default",53 terminalId: "default",
54 terminalLabel: "Terminal 1",54 terminalLabel: "Terminal 1",
55 lineStart: 4,55 lineStart: 4,
...@@ -86,23 +86,20 @@ describe("reconcileMountedTerminalThreadIds", () => {...@@ -86,23 +86,20 @@ describe("reconcileMountedTerminalThreadIds", () => {
86 it("keeps previously mounted open threads and adds the active open thread", () => {86 it("keeps previously mounted open threads and adds the active open thread", () => {
87 expect(87 expect(
88 reconcileMountedTerminalThreadIds({88 reconcileMountedTerminalThreadIds({
89 currentThreadIds: [89 currentThreadIds: [ThreadId.make("thread-hidden"), ThreadId.make("thread-stale")],
90 ThreadId.makeUnsafe("thread-hidden"),90 openThreadIds: [ThreadId.make("thread-hidden"), ThreadId.make("thread-active")],
91 ThreadId.makeUnsafe("thread-stale"),91 activeThreadId: ThreadId.make("thread-active"),
92 ],
93 openThreadIds: [ThreadId.makeUnsafe("thread-hidden"), ThreadId.makeUnsafe("thread-active")],
94 activeThreadId: ThreadId.makeUnsafe("thread-active"),
95 activeThreadTerminalOpen: true,92 activeThreadTerminalOpen: true,
96 }),93 }),
97 ).toEqual([ThreadId.makeUnsafe("thread-hidden"), ThreadId.makeUnsafe("thread-active")]);94 ).toEqual([ThreadId.make("thread-hidden"), ThreadId.make("thread-active")]);
98 });95 });
9996
100 it("drops mounted threads once their terminal drawer is no longer open", () => {97 it("drops mounted threads once their terminal drawer is no longer open", () => {
101 expect(98 expect(
102 reconcileMountedTerminalThreadIds({99 reconcileMountedTerminalThreadIds({
103 currentThreadIds: [ThreadId.makeUnsafe("thread-closed")],100 currentThreadIds: [ThreadId.make("thread-closed")],
104 openThreadIds: [],101 openThreadIds: [],
105 activeThreadId: ThreadId.makeUnsafe("thread-closed"),102 activeThreadId: ThreadId.make("thread-closed"),
106 activeThreadTerminalOpen: false,103 activeThreadTerminalOpen: false,
107 }),104 }),
108 ).toEqual([]);105 ).toEqual([]);
...@@ -112,55 +109,47 @@ describe("reconcileMountedTerminalThreadIds", () => {...@@ -112,55 +109,47 @@ describe("reconcileMountedTerminalThreadIds", () => {
112 expect(109 expect(
113 reconcileMountedTerminalThreadIds({110 reconcileMountedTerminalThreadIds({
114 currentThreadIds: [111 currentThreadIds: [
115 ThreadId.makeUnsafe("thread-1"),112 ThreadId.make("thread-1"),
116 ThreadId.makeUnsafe("thread-2"),113 ThreadId.make("thread-2"),
117 ThreadId.makeUnsafe("thread-3"),114 ThreadId.make("thread-3"),
118 ],115 ],
119 openThreadIds: [116 openThreadIds: [
120 ThreadId.makeUnsafe("thread-1"),117 ThreadId.make("thread-1"),
121 ThreadId.makeUnsafe("thread-2"),118 ThreadId.make("thread-2"),
122 ThreadId.makeUnsafe("thread-3"),119 ThreadId.make("thread-3"),
123 ThreadId.makeUnsafe("thread-4"),120 ThreadId.make("thread-4"),
124 ],121 ],
125 activeThreadId: ThreadId.makeUnsafe("thread-4"),122 activeThreadId: ThreadId.make("thread-4"),
126 activeThreadTerminalOpen: true,123 activeThreadTerminalOpen: true,
127 maxHiddenThreadCount: 2,124 maxHiddenThreadCount: 2,
128 }),125 }),
129 ).toEqual([126 ).toEqual([ThreadId.make("thread-2"), ThreadId.make("thread-3"), ThreadId.make("thread-4")]);
130 ThreadId.makeUnsafe("thread-2"),
131 ThreadId.makeUnsafe("thread-3"),
132 ThreadId.makeUnsafe("thread-4"),
133 ]);
134 });127 });
135128
136 it("moves the active thread to the end so it is treated as most recently used", () => {129 it("moves the active thread to the end so it is treated as most recently used", () => {
137 expect(130 expect(
138 reconcileMountedTerminalThreadIds({131 reconcileMountedTerminalThreadIds({
139 currentThreadIds: [132 currentThreadIds: [
140 ThreadId.makeUnsafe("thread-a"),133 ThreadId.make("thread-a"),
141 ThreadId.makeUnsafe("thread-b"),134 ThreadId.make("thread-b"),
142 ThreadId.makeUnsafe("thread-c"),135 ThreadId.make("thread-c"),
143 ],136 ],
144 openThreadIds: [137 openThreadIds: [
145 ThreadId.makeUnsafe("thread-a"),138 ThreadId.make("thread-a"),
146 ThreadId.makeUnsafe("thread-b"),139 ThreadId.make("thread-b"),
147 ThreadId.makeUnsafe("thread-c"),140 ThreadId.make("thread-c"),
148 ],141 ],
149 activeThreadId: ThreadId.makeUnsafe("thread-a"),142 activeThreadId: ThreadId.make("thread-a"),
150 activeThreadTerminalOpen: true,143 activeThreadTerminalOpen: true,
151 maxHiddenThreadCount: 2,144 maxHiddenThreadCount: 2,
152 }),145 }),
153 ).toEqual([146 ).toEqual([ThreadId.make("thread-b"), ThreadId.make("thread-c"), ThreadId.make("thread-a")]);
154 ThreadId.makeUnsafe("thread-b"),
155 ThreadId.makeUnsafe("thread-c"),
156 ThreadId.makeUnsafe("thread-a"),
157 ]);
158 });147 });
159148
160 it("defaults to the hidden mounted terminal cap", () => {149 it("defaults to the hidden mounted terminal cap", () => {
161 const currentThreadIds = Array.from(150 const currentThreadIds = Array.from(
162 { length: MAX_HIDDEN_MOUNTED_TERMINAL_THREADS + 2 },151 { length: MAX_HIDDEN_MOUNTED_TERMINAL_THREADS + 2 },
163 (_, index) => ThreadId.makeUnsafe(`thread-${index + 1}`),152 (_, index) => ThreadId.make(`thread-${index + 1}`),
164 );153 );
165154
166 expect(155 expect(
...@@ -176,7 +165,7 @@ describe("reconcileMountedTerminalThreadIds", () => {...@@ -176,7 +165,7 @@ describe("reconcileMountedTerminalThreadIds", () => {
176165
177describe("shouldWriteThreadErrorToCurrentServerThread", () => {166describe("shouldWriteThreadErrorToCurrentServerThread", () => {
178 it("routes errors to the active server thread when route and target match", () => {167 it("routes errors to the active server thread when route and target match", () => {
179 const threadId = ThreadId.makeUnsafe("thread-1");168 const threadId = ThreadId.make("thread-1");
180 const routeThreadRef = scopeThreadRef(localEnvironmentId, threadId);169 const routeThreadRef = scopeThreadRef(localEnvironmentId, threadId);
181170
182 expect(171 expect(
...@@ -192,7 +181,7 @@ describe("shouldWriteThreadErrorToCurrentServerThread", () => {...@@ -192,7 +181,7 @@ describe("shouldWriteThreadErrorToCurrentServerThread", () => {
192 });181 });
193182
194 it("does not route draft-thread errors into server-backed state", () => {183 it("does not route draft-thread errors into server-backed state", () => {
195 const threadId = ThreadId.makeUnsafe("thread-1");184 const threadId = ThreadId.make("thread-1");
196185
197 expect(186 expect(
198 shouldWriteThreadErrorToCurrentServerThread({187 shouldWriteThreadErrorToCurrentServerThread({
...@@ -214,10 +203,10 @@ const makeThread = (input?: {...@@ -214,10 +203,10 @@ const makeThread = (input?: {
214 completedAt: string | null;203 completedAt: string | null;
215 } | null;204 } | null;
216}): Thread => ({205}): Thread => ({
217 id: input?.id ?? ThreadId.makeUnsafe("thread-1"),206 id: input?.id ?? ThreadId.make("thread-1"),
218 environmentId: localEnvironmentId,207 environmentId: localEnvironmentId,
219 codexThreadId: null,208 codexThreadId: null,
220 projectId: ProjectId.makeUnsafe("project-1"),209 projectId: ProjectId.make("project-1"),
221 title: "Thread",210 title: "Thread",
222 modelSelection: { provider: "codex" as const, model: "gpt-5.4" },211 modelSelection: { provider: "codex" as const, model: "gpt-5.4" },
223 runtimeMode: "full-access" as const,212 runtimeMode: "full-access" as const,
...@@ -242,7 +231,7 @@ const makeThread = (input?: {...@@ -242,7 +231,7 @@ const makeThread = (input?: {
242});231});
243232
244function setStoreThreads(threads: ReadonlyArray<ReturnType<typeof makeThread>>) {233function setStoreThreads(threads: ReadonlyArray<ReturnType<typeof makeThread>>) {
245 const projectId = ProjectId.makeUnsafe("project-1");234 const projectId = ProjectId.make("project-1");
246 const environmentState: EnvironmentState = {235 const environmentState: EnvironmentState = {
247 projectIds: [projectId],236 projectIds: [projectId],
248 projectById: {237 projectById: {
...@@ -355,12 +344,12 @@ afterEach(() => {...@@ -355,12 +344,12 @@ afterEach(() => {
355344
356describe("waitForStartedServerThread", () => {345describe("waitForStartedServerThread", () => {
357 it("resolves immediately when the thread is already started", async () => {346 it("resolves immediately when the thread is already started", async () => {
358 const threadId = ThreadId.makeUnsafe("thread-started");347 const threadId = ThreadId.make("thread-started");
359 setStoreThreads([348 setStoreThreads([
360 makeThread({349 makeThread({
361 id: threadId,350 id: threadId,
362 latestTurn: {351 latestTurn: {
363 turnId: TurnId.makeUnsafe("turn-started"),352 turnId: TurnId.make("turn-started"),
364 state: "running",353 state: "running",
365 requestedAt: "2026-03-29T00:00:01.000Z",354 requestedAt: "2026-03-29T00:00:01.000Z",
366 startedAt: "2026-03-29T00:00:01.000Z",355 startedAt: "2026-03-29T00:00:01.000Z",
...@@ -375,7 +364,7 @@ describe("waitForStartedServerThread", () => {...@@ -375,7 +364,7 @@ describe("waitForStartedServerThread", () => {
375 });364 });
376365
377 it("waits for the thread to start via subscription updates", async () => {366 it("waits for the thread to start via subscription updates", async () => {
378 const threadId = ThreadId.makeUnsafe("thread-wait");367 const threadId = ThreadId.make("thread-wait");
379 setStoreThreads([makeThread({ id: threadId })]);368 setStoreThreads([makeThread({ id: threadId })]);
380369
381 const promise = waitForStartedServerThread(scopeThreadRef(localEnvironmentId, threadId), 500);370 const promise = waitForStartedServerThread(scopeThreadRef(localEnvironmentId, threadId), 500);
...@@ -384,7 +373,7 @@ describe("waitForStartedServerThread", () => {...@@ -384,7 +373,7 @@ describe("waitForStartedServerThread", () => {
384 makeThread({373 makeThread({
385 id: threadId,374 id: threadId,
386 latestTurn: {375 latestTurn: {
387 turnId: TurnId.makeUnsafe("turn-started"),376 turnId: TurnId.make("turn-started"),
388 state: "running",377 state: "running",
389 requestedAt: "2026-03-29T00:00:01.000Z",378 requestedAt: "2026-03-29T00:00:01.000Z",
390 startedAt: "2026-03-29T00:00:01.000Z",379 startedAt: "2026-03-29T00:00:01.000Z",
...@@ -397,7 +386,7 @@ describe("waitForStartedServerThread", () => {...@@ -397,7 +386,7 @@ describe("waitForStartedServerThread", () => {
397 });386 });
398387
399 it("handles the thread starting between the initial read and subscription setup", async () => {388 it("handles the thread starting between the initial read and subscription setup", async () => {
400 const threadId = ThreadId.makeUnsafe("thread-race");389 const threadId = ThreadId.make("thread-race");
401 setStoreThreads([makeThread({ id: threadId })]);390 setStoreThreads([makeThread({ id: threadId })]);
402391
403 const originalSubscribe = useStore.subscribe.bind(useStore);392 const originalSubscribe = useStore.subscribe.bind(useStore);
...@@ -409,7 +398,7 @@ describe("waitForStartedServerThread", () => {...@@ -409,7 +398,7 @@ describe("waitForStartedServerThread", () => {
409 makeThread({398 makeThread({
410 id: threadId,399 id: threadId,
411 latestTurn: {400 latestTurn: {
412 turnId: TurnId.makeUnsafe("turn-race"),401 turnId: TurnId.make("turn-race"),
413 state: "running",402 state: "running",
414 requestedAt: "2026-03-29T00:00:01.000Z",403 requestedAt: "2026-03-29T00:00:01.000Z",
415 startedAt: "2026-03-29T00:00:01.000Z",404 startedAt: "2026-03-29T00:00:01.000Z",
...@@ -429,7 +418,7 @@ describe("waitForStartedServerThread", () => {...@@ -429,7 +418,7 @@ describe("waitForStartedServerThread", () => {
429 it("returns false after the timeout when the thread never starts", async () => {418 it("returns false after the timeout when the thread never starts", async () => {
430 vi.useFakeTimers();419 vi.useFakeTimers();
431420
432 const threadId = ThreadId.makeUnsafe("thread-timeout");421 const threadId = ThreadId.make("thread-timeout");
433 setStoreThreads([makeThread({ id: threadId })]);422 setStoreThreads([makeThread({ id: threadId })]);
434 const promise = waitForStartedServerThread(scopeThreadRef(localEnvironmentId, threadId), 500);423 const promise = waitForStartedServerThread(scopeThreadRef(localEnvironmentId, threadId), 500);
435424
...@@ -440,9 +429,9 @@ describe("waitForStartedServerThread", () => {...@@ -440,9 +429,9 @@ describe("waitForStartedServerThread", () => {
440});429});
441430
442describe("hasServerAcknowledgedLocalDispatch", () => {431describe("hasServerAcknowledgedLocalDispatch", () => {
443 const projectId = ProjectId.makeUnsafe("project-1");432 const projectId = ProjectId.make("project-1");
444 const previousLatestTurn = {433 const previousLatestTurn = {
445 turnId: TurnId.makeUnsafe("turn-1"),434 turnId: TurnId.make("turn-1"),
446 state: "completed" as const,435 state: "completed" as const,
447 requestedAt: "2026-03-29T00:00:00.000Z",436 requestedAt: "2026-03-29T00:00:00.000Z",
448 startedAt: "2026-03-29T00:00:01.000Z",437 startedAt: "2026-03-29T00:00:01.000Z",
...@@ -460,7 +449,7 @@ describe("hasServerAcknowledgedLocalDispatch", () => {...@@ -460,7 +449,7 @@ describe("hasServerAcknowledgedLocalDispatch", () => {
460449
461 it("does not clear local dispatch before server state changes", () => {450 it("does not clear local dispatch before server state changes", () => {
462 const localDispatch = createLocalDispatchSnapshot({451 const localDispatch = createLocalDispatchSnapshot({
463 id: ThreadId.makeUnsafe("thread-1"),452 id: ThreadId.make("thread-1"),
464 environmentId: localEnvironmentId,453 environmentId: localEnvironmentId,
465 codexThreadId: null,454 codexThreadId: null,
466 projectId,455 projectId,
...@@ -497,7 +486,7 @@ describe("hasServerAcknowledgedLocalDispatch", () => {...@@ -497,7 +486,7 @@ describe("hasServerAcknowledgedLocalDispatch", () => {
497486
498 it("clears local dispatch when a new turn is already settled", () => {487 it("clears local dispatch when a new turn is already settled", () => {
499 const localDispatch = createLocalDispatchSnapshot({488 const localDispatch = createLocalDispatchSnapshot({
500 id: ThreadId.makeUnsafe("thread-1"),489 id: ThreadId.make("thread-1"),
501 environmentId: localEnvironmentId,490 environmentId: localEnvironmentId,
502 codexThreadId: null,491 codexThreadId: null,
503 projectId,492 projectId,
...@@ -525,7 +514,7 @@ describe("hasServerAcknowledgedLocalDispatch", () => {...@@ -525,7 +514,7 @@ describe("hasServerAcknowledgedLocalDispatch", () => {
525 phase: "ready",514 phase: "ready",
526 latestTurn: {515 latestTurn: {
527 ...previousLatestTurn,516 ...previousLatestTurn,
528 turnId: TurnId.makeUnsafe("turn-2"),517 turnId: TurnId.make("turn-2"),
529 requestedAt: "2026-03-29T00:01:00.000Z",518 requestedAt: "2026-03-29T00:01:00.000Z",
530 startedAt: "2026-03-29T00:01:01.000Z",519 startedAt: "2026-03-29T00:01:01.000Z",
531 completedAt: "2026-03-29T00:01:30.000Z",520 completedAt: "2026-03-29T00:01:30.000Z",
...@@ -543,7 +532,7 @@ describe("hasServerAcknowledgedLocalDispatch", () => {...@@ -543,7 +532,7 @@ describe("hasServerAcknowledgedLocalDispatch", () => {
543532
544 it("clears local dispatch when the session changes without an observed running phase", () => {533 it("clears local dispatch when the session changes without an observed running phase", () => {
545 const localDispatch = createLocalDispatchSnapshot({534 const localDispatch = createLocalDispatchSnapshot({
546 id: ThreadId.makeUnsafe("thread-1"),535 id: ThreadId.make("thread-1"),
547 environmentId: localEnvironmentId,536 environmentId: localEnvironmentId,
548 codexThreadId: null,537 codexThreadId: null,
549 projectId,538 projectId,
apps/web/src/components/GitActionsControl.browser.tsx+1-1
...@@ -4,7 +4,7 @@ import { useState } from "react";...@@ -4,7 +4,7 @@ import { useState } from "react";
4import { afterEach, describe, expect, it, vi } from "vitest";4import { afterEach, describe, expect, it, vi } from "vitest";
5import { render } from "vitest-browser-react";5import { render } from "vitest-browser-react";
66
7const SHARED_THREAD_ID = ThreadId.makeUnsafe("thread-shared");7const SHARED_THREAD_ID = ThreadId.make("thread-shared");
8const ENVIRONMENT_A = "environment-local" as never;8const ENVIRONMENT_A = "environment-local" as never;
9const ENVIRONMENT_B = "environment-remote" as never;9const ENVIRONMENT_B = "environment-remote" as never;
10const GIT_CWD = "/repo/project";10const GIT_CWD = "/repo/project";
apps/web/src/components/KeybindingsToast.browser.tsx+1-1
...@@ -37,7 +37,7 @@ vi.mock("../lib/gitStatusState", () => ({...@@ -37,7 +37,7 @@ vi.mock("../lib/gitStatusState", () => ({
3737
38const THREAD_ID = "thread-kb-toast-test" as ThreadId;38const THREAD_ID = "thread-kb-toast-test" as ThreadId;
39const PROJECT_ID = "project-1" as ProjectId;39const PROJECT_ID = "project-1" as ProjectId;
40const LOCAL_ENVIRONMENT_ID = EnvironmentId.makeUnsafe("environment-local");40const LOCAL_ENVIRONMENT_ID = EnvironmentId.make("environment-local");
41const NOW_ISO = "2026-03-04T12:00:00.000Z";41const NOW_ISO = "2026-03-04T12:00:00.000Z";
4242
43interface TestFixture {43interface TestFixture {
apps/web/src/components/Sidebar.logic.test.ts+109-117
...@@ -28,7 +28,7 @@ import {...@@ -28,7 +28,7 @@ import {
28 type Thread,28 type Thread,
29} from "../types";29} from "../types";
3030
31const localEnvironmentId = EnvironmentId.makeUnsafe("environment-local");31const localEnvironmentId = EnvironmentId.make("environment-local");
3232
33function makeLatestTurn(overrides?: {33function makeLatestTurn(overrides?: {
34 completedAt?: string | null;34 completedAt?: string | null;
...@@ -235,42 +235,42 @@ describe("orderItemsByPreferredIds", () => {...@@ -235,42 +235,42 @@ describe("orderItemsByPreferredIds", () => {
235 it("keeps preferred ids first, skips stale ids, and preserves the relative order of remaining items", () => {235 it("keeps preferred ids first, skips stale ids, and preserves the relative order of remaining items", () => {
236 const ordered = orderItemsByPreferredIds({236 const ordered = orderItemsByPreferredIds({
237 items: [237 items: [
238 { id: ProjectId.makeUnsafe("project-1"), name: "One" },238 { id: ProjectId.make("project-1"), name: "One" },
239 { id: ProjectId.makeUnsafe("project-2"), name: "Two" },239 { id: ProjectId.make("project-2"), name: "Two" },
240 { id: ProjectId.makeUnsafe("project-3"), name: "Three" },240 { id: ProjectId.make("project-3"), name: "Three" },
241 ],241 ],
242 preferredIds: [242 preferredIds: [
243 ProjectId.makeUnsafe("project-3"),243 ProjectId.make("project-3"),
244 ProjectId.makeUnsafe("project-missing"),244 ProjectId.make("project-missing"),
245 ProjectId.makeUnsafe("project-1"),245 ProjectId.make("project-1"),
246 ],246 ],
247 getId: (project) => project.id,247 getId: (project) => project.id,
248 });248 });
249249
250 expect(ordered.map((project) => project.id)).toEqual([250 expect(ordered.map((project) => project.id)).toEqual([
251 ProjectId.makeUnsafe("project-3"),251 ProjectId.make("project-3"),
252 ProjectId.makeUnsafe("project-1"),252 ProjectId.make("project-1"),
253 ProjectId.makeUnsafe("project-2"),253 ProjectId.make("project-2"),
254 ]);254 ]);
255 });255 });
256256
257 it("does not duplicate items when preferred ids repeat", () => {257 it("does not duplicate items when preferred ids repeat", () => {
258 const ordered = orderItemsByPreferredIds({258 const ordered = orderItemsByPreferredIds({
259 items: [259 items: [
260 { id: ProjectId.makeUnsafe("project-1"), name: "One" },260 { id: ProjectId.make("project-1"), name: "One" },
261 { id: ProjectId.makeUnsafe("project-2"), name: "Two" },261 { id: ProjectId.make("project-2"), name: "Two" },
262 ],262 ],
263 preferredIds: [263 preferredIds: [
264 ProjectId.makeUnsafe("project-2"),264 ProjectId.make("project-2"),
265 ProjectId.makeUnsafe("project-1"),265 ProjectId.make("project-1"),
266 ProjectId.makeUnsafe("project-2"),266 ProjectId.make("project-2"),
267 ],267 ],
268 getId: (project) => project.id,268 getId: (project) => project.id,
269 });269 });
270270
271 expect(ordered.map((project) => project.id)).toEqual([271 expect(ordered.map((project) => project.id)).toEqual([
272 ProjectId.makeUnsafe("project-2"),272 ProjectId.make("project-2"),
273 ProjectId.makeUnsafe("project-1"),273 ProjectId.make("project-1"),
274 ]);274 ]);
275 });275 });
276});276});
...@@ -278,9 +278,9 @@ describe("orderItemsByPreferredIds", () => {...@@ -278,9 +278,9 @@ describe("orderItemsByPreferredIds", () => {
278describe("resolveAdjacentThreadId", () => {278describe("resolveAdjacentThreadId", () => {
279 it("resolves adjacent thread ids in ordered sidebar traversal", () => {279 it("resolves adjacent thread ids in ordered sidebar traversal", () => {
280 const threads = [280 const threads = [
281 ThreadId.makeUnsafe("thread-1"),281 ThreadId.make("thread-1"),
282 ThreadId.makeUnsafe("thread-2"),282 ThreadId.make("thread-2"),
283 ThreadId.makeUnsafe("thread-3"),283 ThreadId.make("thread-3"),
284 ];284 ];
285285
286 expect(286 expect(
...@@ -327,21 +327,21 @@ describe("getVisibleSidebarThreadIds", () => {...@@ -327,21 +327,21 @@ describe("getVisibleSidebarThreadIds", () => {
327 getVisibleSidebarThreadIds([327 getVisibleSidebarThreadIds([
328 {328 {
329 renderedThreadIds: [329 renderedThreadIds: [
330 ThreadId.makeUnsafe("thread-12"),330 ThreadId.make("thread-12"),
331 ThreadId.makeUnsafe("thread-11"),331 ThreadId.make("thread-11"),
332 ThreadId.makeUnsafe("thread-10"),332 ThreadId.make("thread-10"),
333 ],333 ],
334 },334 },
335 {335 {
336 renderedThreadIds: [ThreadId.makeUnsafe("thread-8"), ThreadId.makeUnsafe("thread-6")],336 renderedThreadIds: [ThreadId.make("thread-8"), ThreadId.make("thread-6")],
337 },337 },
338 ]),338 ]),
339 ).toEqual([339 ).toEqual([
340 ThreadId.makeUnsafe("thread-12"),340 ThreadId.make("thread-12"),
341 ThreadId.makeUnsafe("thread-11"),341 ThreadId.make("thread-11"),
342 ThreadId.makeUnsafe("thread-10"),342 ThreadId.make("thread-10"),
343 ThreadId.makeUnsafe("thread-8"),343 ThreadId.make("thread-8"),
344 ThreadId.makeUnsafe("thread-6"),344 ThreadId.make("thread-6"),
345 ]);345 ]);
346 });346 });
347347
...@@ -350,17 +350,14 @@ describe("getVisibleSidebarThreadIds", () => {...@@ -350,17 +350,14 @@ describe("getVisibleSidebarThreadIds", () => {
350 getVisibleSidebarThreadIds([350 getVisibleSidebarThreadIds([
351 {351 {
352 shouldShowThreadPanel: false,352 shouldShowThreadPanel: false,
353 renderedThreadIds: [353 renderedThreadIds: [ThreadId.make("thread-hidden-2"), ThreadId.make("thread-hidden-1")],
354 ThreadId.makeUnsafe("thread-hidden-2"),
355 ThreadId.makeUnsafe("thread-hidden-1"),
356 ],
357 },354 },
358 {355 {
359 shouldShowThreadPanel: true,356 shouldShowThreadPanel: true,
360 renderedThreadIds: [ThreadId.makeUnsafe("thread-12"), ThreadId.makeUnsafe("thread-11")],357 renderedThreadIds: [ThreadId.make("thread-12"), ThreadId.make("thread-11")],
361 },358 },
362 ]),359 ]),
363 ).toEqual([ThreadId.makeUnsafe("thread-12"), ThreadId.makeUnsafe("thread-11")]);360 ).toEqual([ThreadId.make("thread-12"), ThreadId.make("thread-11")]);
364 });361 });
365});362});
366363
...@@ -574,43 +571,41 @@ describe("getVisibleThreadsForProject", () => {...@@ -574,43 +571,41 @@ describe("getVisibleThreadsForProject", () => {
574 it("includes the active thread even when it falls below the folded preview", () => {571 it("includes the active thread even when it falls below the folded preview", () => {
575 const threads = Array.from({ length: 8 }, (_, index) =>572 const threads = Array.from({ length: 8 }, (_, index) =>
576 makeThread({573 makeThread({
577 id: ThreadId.makeUnsafe(`thread-${index + 1}`),574 id: ThreadId.make(`thread-${index + 1}`),
578 title: `Thread ${index + 1}`,575 title: `Thread ${index + 1}`,
579 }),576 }),
580 );577 );
581578
582 const result = getVisibleThreadsForProject({579 const result = getVisibleThreadsForProject({
583 threads,580 threads,
584 activeThreadId: ThreadId.makeUnsafe("thread-8"),581 activeThreadId: ThreadId.make("thread-8"),
585 isThreadListExpanded: false,582 isThreadListExpanded: false,
586 previewLimit: 6,583 previewLimit: 6,
587 });584 });
588585
589 expect(result.hasHiddenThreads).toBe(true);586 expect(result.hasHiddenThreads).toBe(true);
590 expect(result.visibleThreads.map((thread) => thread.id)).toEqual([587 expect(result.visibleThreads.map((thread) => thread.id)).toEqual([
591 ThreadId.makeUnsafe("thread-1"),588 ThreadId.make("thread-1"),
592 ThreadId.makeUnsafe("thread-2"),589 ThreadId.make("thread-2"),
593 ThreadId.makeUnsafe("thread-3"),590 ThreadId.make("thread-3"),
594 ThreadId.makeUnsafe("thread-4"),591 ThreadId.make("thread-4"),
595 ThreadId.makeUnsafe("thread-5"),592 ThreadId.make("thread-5"),
596 ThreadId.makeUnsafe("thread-6"),593 ThreadId.make("thread-6"),
597 ThreadId.makeUnsafe("thread-8"),594 ThreadId.make("thread-8"),
598 ]);
599 expect(result.hiddenThreads.map((thread) => thread.id)).toEqual([
600 ThreadId.makeUnsafe("thread-7"),
601 ]);595 ]);
596 expect(result.hiddenThreads.map((thread) => thread.id)).toEqual([ThreadId.make("thread-7")]);
602 });597 });
603598
604 it("returns all threads when the list is expanded", () => {599 it("returns all threads when the list is expanded", () => {
605 const threads = Array.from({ length: 8 }, (_, index) =>600 const threads = Array.from({ length: 8 }, (_, index) =>
606 makeThread({601 makeThread({
607 id: ThreadId.makeUnsafe(`thread-${index + 1}`),602 id: ThreadId.make(`thread-${index + 1}`),
608 }),603 }),
609 );604 );
610605
611 const result = getVisibleThreadsForProject({606 const result = getVisibleThreadsForProject({
612 threads,607 threads,
613 activeThreadId: ThreadId.makeUnsafe("thread-8"),608 activeThreadId: ThreadId.make("thread-8"),
614 isThreadListExpanded: true,609 isThreadListExpanded: true,
615 previewLimit: 6,610 previewLimit: 6,
616 });611 });
...@@ -626,7 +621,7 @@ describe("getVisibleThreadsForProject", () => {...@@ -626,7 +621,7 @@ describe("getVisibleThreadsForProject", () => {
626function makeProject(overrides: Partial<Project> = {}): Project {621function makeProject(overrides: Partial<Project> = {}): Project {
627 const { defaultModelSelection, ...rest } = overrides;622 const { defaultModelSelection, ...rest } = overrides;
628 return {623 return {
629 id: ProjectId.makeUnsafe("project-1"),624 id: ProjectId.make("project-1"),
630 environmentId: localEnvironmentId,625 environmentId: localEnvironmentId,
631 name: "Project",626 name: "Project",
632 cwd: "/tmp/project",627 cwd: "/tmp/project",
...@@ -644,10 +639,10 @@ function makeProject(overrides: Partial<Project> = {}): Project {...@@ -644,10 +639,10 @@ function makeProject(overrides: Partial<Project> = {}): Project {
644639
645function makeThread(overrides: Partial<Thread> = {}): Thread {640function makeThread(overrides: Partial<Thread> = {}): Thread {
646 return {641 return {
647 id: ThreadId.makeUnsafe("thread-1"),642 id: ThreadId.make("thread-1"),
648 environmentId: localEnvironmentId,643 environmentId: localEnvironmentId,
649 codexThreadId: null,644 codexThreadId: null,
650 projectId: ProjectId.makeUnsafe("project-1"),645 projectId: ProjectId.make("project-1"),
651 title: "Thread",646 title: "Thread",
652 modelSelection: {647 modelSelection: {
653 provider: "codex",648 provider: "codex",
...@@ -677,7 +672,7 @@ describe("sortThreadsForSidebar", () => {...@@ -677,7 +672,7 @@ describe("sortThreadsForSidebar", () => {
677 const sorted = sortThreadsForSidebar(672 const sorted = sortThreadsForSidebar(
678 [673 [
679 makeThread({674 makeThread({
680 id: ThreadId.makeUnsafe("thread-1"),675 id: ThreadId.make("thread-1"),
681 createdAt: "2026-03-09T10:00:00.000Z",676 createdAt: "2026-03-09T10:00:00.000Z",
682 updatedAt: "2026-03-09T10:10:00.000Z",677 updatedAt: "2026-03-09T10:10:00.000Z",
683 messages: [678 messages: [
...@@ -692,7 +687,7 @@ describe("sortThreadsForSidebar", () => {...@@ -692,7 +687,7 @@ describe("sortThreadsForSidebar", () => {
692 ],687 ],
693 }),688 }),
694 makeThread({689 makeThread({
695 id: ThreadId.makeUnsafe("thread-2"),690 id: ThreadId.make("thread-2"),
696 createdAt: "2026-03-09T10:05:00.000Z",691 createdAt: "2026-03-09T10:05:00.000Z",
697 updatedAt: "2026-03-09T10:05:00.000Z",692 updatedAt: "2026-03-09T10:05:00.000Z",
698 messages: [693 messages: [
...@@ -711,8 +706,8 @@ describe("sortThreadsForSidebar", () => {...@@ -711,8 +706,8 @@ describe("sortThreadsForSidebar", () => {
711 );706 );
712707
713 expect(sorted.map((thread) => thread.id)).toEqual([708 expect(sorted.map((thread) => thread.id)).toEqual([
714 ThreadId.makeUnsafe("thread-2"),709 ThreadId.make("thread-2"),
715 ThreadId.makeUnsafe("thread-1"),710 ThreadId.make("thread-1"),
716 ]);711 ]);
717 });712 });
718713
...@@ -720,7 +715,7 @@ describe("sortThreadsForSidebar", () => {...@@ -720,7 +715,7 @@ describe("sortThreadsForSidebar", () => {
720 const sorted = sortThreadsForSidebar(715 const sorted = sortThreadsForSidebar(
721 [716 [
722 makeThread({717 makeThread({
723 id: ThreadId.makeUnsafe("thread-1"),718 id: ThreadId.make("thread-1"),
724 createdAt: "2026-03-09T10:00:00.000Z",719 createdAt: "2026-03-09T10:00:00.000Z",
725 updatedAt: "2026-03-09T10:01:00.000Z",720 updatedAt: "2026-03-09T10:01:00.000Z",
726 messages: [721 messages: [
...@@ -735,7 +730,7 @@ describe("sortThreadsForSidebar", () => {...@@ -735,7 +730,7 @@ describe("sortThreadsForSidebar", () => {
735 ],730 ],
736 }),731 }),
737 makeThread({732 makeThread({
738 id: ThreadId.makeUnsafe("thread-2"),733 id: ThreadId.make("thread-2"),
739 createdAt: "2026-03-09T10:05:00.000Z",734 createdAt: "2026-03-09T10:05:00.000Z",
740 updatedAt: "2026-03-09T10:05:00.000Z",735 updatedAt: "2026-03-09T10:05:00.000Z",
741 messages: [],736 messages: [],
...@@ -745,8 +740,8 @@ describe("sortThreadsForSidebar", () => {...@@ -745,8 +740,8 @@ describe("sortThreadsForSidebar", () => {
745 );740 );
746741
747 expect(sorted.map((thread) => thread.id)).toEqual([742 expect(sorted.map((thread) => thread.id)).toEqual([
748 ThreadId.makeUnsafe("thread-2"),743 ThreadId.make("thread-2"),
749 ThreadId.makeUnsafe("thread-1"),744 ThreadId.make("thread-1"),
750 ]);745 ]);
751 });746 });
752747
...@@ -754,13 +749,13 @@ describe("sortThreadsForSidebar", () => {...@@ -754,13 +749,13 @@ describe("sortThreadsForSidebar", () => {
754 const sorted = sortThreadsForSidebar(749 const sorted = sortThreadsForSidebar(
755 [750 [
756 makeThread({751 makeThread({
757 id: ThreadId.makeUnsafe("thread-1"),752 id: ThreadId.make("thread-1"),
758 createdAt: "" as never,753 createdAt: "" as never,
759 updatedAt: undefined,754 updatedAt: undefined,
760 messages: [],755 messages: [],
761 }),756 }),
762 makeThread({757 makeThread({
763 id: ThreadId.makeUnsafe("thread-2"),758 id: ThreadId.make("thread-2"),
764 createdAt: "" as never,759 createdAt: "" as never,
765 updatedAt: undefined,760 updatedAt: undefined,
766 messages: [],761 messages: [],
...@@ -770,8 +765,8 @@ describe("sortThreadsForSidebar", () => {...@@ -770,8 +765,8 @@ describe("sortThreadsForSidebar", () => {
770 );765 );
771766
772 expect(sorted.map((thread) => thread.id)).toEqual([767 expect(sorted.map((thread) => thread.id)).toEqual([
773 ThreadId.makeUnsafe("thread-2"),768 ThreadId.make("thread-2"),
774 ThreadId.makeUnsafe("thread-1"),769 ThreadId.make("thread-1"),
775 ]);770 ]);
776 });771 });
777772
...@@ -779,12 +774,12 @@ describe("sortThreadsForSidebar", () => {...@@ -779,12 +774,12 @@ describe("sortThreadsForSidebar", () => {
779 const sorted = sortThreadsForSidebar(774 const sorted = sortThreadsForSidebar(
780 [775 [
781 makeThread({776 makeThread({
782 id: ThreadId.makeUnsafe("thread-1"),777 id: ThreadId.make("thread-1"),
783 createdAt: "2026-03-09T10:05:00.000Z",778 createdAt: "2026-03-09T10:05:00.000Z",
784 updatedAt: "2026-03-09T10:05:00.000Z",779 updatedAt: "2026-03-09T10:05:00.000Z",
785 }),780 }),
786 makeThread({781 makeThread({
787 id: ThreadId.makeUnsafe("thread-2"),782 id: ThreadId.make("thread-2"),
788 createdAt: "2026-03-09T10:00:00.000Z",783 createdAt: "2026-03-09T10:00:00.000Z",
789 updatedAt: "2026-03-09T10:10:00.000Z",784 updatedAt: "2026-03-09T10:10:00.000Z",
790 }),785 }),
...@@ -793,8 +788,8 @@ describe("sortThreadsForSidebar", () => {...@@ -793,8 +788,8 @@ describe("sortThreadsForSidebar", () => {
793 );788 );
794789
795 expect(sorted.map((thread) => thread.id)).toEqual([790 expect(sorted.map((thread) => thread.id)).toEqual([
796 ThreadId.makeUnsafe("thread-1"),791 ThreadId.make("thread-1"),
797 ThreadId.makeUnsafe("thread-2"),792 ThreadId.make("thread-2"),
798 ]);793 ]);
799 });794 });
800});795});
...@@ -804,80 +799,77 @@ describe("getFallbackThreadIdAfterDelete", () => {...@@ -804,80 +799,77 @@ describe("getFallbackThreadIdAfterDelete", () => {
804 const fallbackThreadId = getFallbackThreadIdAfterDelete({799 const fallbackThreadId = getFallbackThreadIdAfterDelete({
805 threads: [800 threads: [
806 makeThread({801 makeThread({
807 id: ThreadId.makeUnsafe("thread-oldest"),802 id: ThreadId.make("thread-oldest"),
808 projectId: ProjectId.makeUnsafe("project-1"),803 projectId: ProjectId.make("project-1"),
809 createdAt: "2026-03-09T10:00:00.000Z",804 createdAt: "2026-03-09T10:00:00.000Z",
810 messages: [],805 messages: [],
811 }),806 }),
812 makeThread({807 makeThread({
813 id: ThreadId.makeUnsafe("thread-active"),808 id: ThreadId.make("thread-active"),
814 projectId: ProjectId.makeUnsafe("project-1"),809 projectId: ProjectId.make("project-1"),
815 createdAt: "2026-03-09T10:05:00.000Z",810 createdAt: "2026-03-09T10:05:00.000Z",
816 messages: [],811 messages: [],
817 }),812 }),
818 makeThread({813 makeThread({
819 id: ThreadId.makeUnsafe("thread-newest"),814 id: ThreadId.make("thread-newest"),
820 projectId: ProjectId.makeUnsafe("project-1"),815 projectId: ProjectId.make("project-1"),
821 createdAt: "2026-03-09T10:10:00.000Z",816 createdAt: "2026-03-09T10:10:00.000Z",
822 messages: [],817 messages: [],
823 }),818 }),
824 makeThread({819 makeThread({
825 id: ThreadId.makeUnsafe("thread-other-project"),820 id: ThreadId.make("thread-other-project"),
826 projectId: ProjectId.makeUnsafe("project-2"),821 projectId: ProjectId.make("project-2"),
827 createdAt: "2026-03-09T10:20:00.000Z",822 createdAt: "2026-03-09T10:20:00.000Z",
828 messages: [],823 messages: [],
829 }),824 }),
830 ],825 ],
831 deletedThreadId: ThreadId.makeUnsafe("thread-active"),826 deletedThreadId: ThreadId.make("thread-active"),
832 sortOrder: "created_at",827 sortOrder: "created_at",
833 });828 });
834829
835 expect(fallbackThreadId).toBe(ThreadId.makeUnsafe("thread-newest"));830 expect(fallbackThreadId).toBe(ThreadId.make("thread-newest"));
836 });831 });
837832
838 it("skips other threads being deleted in the same action", () => {833 it("skips other threads being deleted in the same action", () => {
839 const fallbackThreadId = getFallbackThreadIdAfterDelete({834 const fallbackThreadId = getFallbackThreadIdAfterDelete({
840 threads: [835 threads: [
841 makeThread({836 makeThread({
842 id: ThreadId.makeUnsafe("thread-active"),837 id: ThreadId.make("thread-active"),
843 projectId: ProjectId.makeUnsafe("project-1"),838 projectId: ProjectId.make("project-1"),
844 createdAt: "2026-03-09T10:05:00.000Z",839 createdAt: "2026-03-09T10:05:00.000Z",
845 messages: [],840 messages: [],
846 }),841 }),
847 makeThread({842 makeThread({
848 id: ThreadId.makeUnsafe("thread-newest"),843 id: ThreadId.make("thread-newest"),
849 projectId: ProjectId.makeUnsafe("project-1"),844 projectId: ProjectId.make("project-1"),
850 createdAt: "2026-03-09T10:10:00.000Z",845 createdAt: "2026-03-09T10:10:00.000Z",
851 messages: [],846 messages: [],
852 }),847 }),
853 makeThread({848 makeThread({
854 id: ThreadId.makeUnsafe("thread-next"),849 id: ThreadId.make("thread-next"),
855 projectId: ProjectId.makeUnsafe("project-1"),850 projectId: ProjectId.make("project-1"),
856 createdAt: "2026-03-09T10:07:00.000Z",851 createdAt: "2026-03-09T10:07:00.000Z",
857 messages: [],852 messages: [],
858 }),853 }),
859 ],854 ],
860 deletedThreadId: ThreadId.makeUnsafe("thread-active"),855 deletedThreadId: ThreadId.make("thread-active"),
861 deletedThreadIds: new Set([856 deletedThreadIds: new Set([ThreadId.make("thread-active"), ThreadId.make("thread-newest")]),
862 ThreadId.makeUnsafe("thread-active"),
863 ThreadId.makeUnsafe("thread-newest"),
864 ]),
865 sortOrder: "created_at",857 sortOrder: "created_at",
866 });858 });
867859
868 expect(fallbackThreadId).toBe(ThreadId.makeUnsafe("thread-next"));860 expect(fallbackThreadId).toBe(ThreadId.make("thread-next"));
869 });861 });
870});862});
871863
872describe("sortProjectsForSidebar", () => {864describe("sortProjectsForSidebar", () => {
873 it("sorts projects by the most recent user message across their threads", () => {865 it("sorts projects by the most recent user message across their threads", () => {
874 const projects = [866 const projects = [
875 makeProject({ id: ProjectId.makeUnsafe("project-1"), name: "Older project" }),867 makeProject({ id: ProjectId.make("project-1"), name: "Older project" }),
876 makeProject({ id: ProjectId.makeUnsafe("project-2"), name: "Newer project" }),868 makeProject({ id: ProjectId.make("project-2"), name: "Newer project" }),
877 ];869 ];
878 const threads = [870 const threads = [
879 makeThread({871 makeThread({
880 projectId: ProjectId.makeUnsafe("project-1"),872 projectId: ProjectId.make("project-1"),
881 updatedAt: "2026-03-09T10:20:00.000Z",873 updatedAt: "2026-03-09T10:20:00.000Z",
882 messages: [874 messages: [
883 {875 {
...@@ -891,8 +883,8 @@ describe("sortProjectsForSidebar", () => {...@@ -891,8 +883,8 @@ describe("sortProjectsForSidebar", () => {
891 ],883 ],
892 }),884 }),
893 makeThread({885 makeThread({
894 id: ThreadId.makeUnsafe("thread-2"),886 id: ThreadId.make("thread-2"),
895 projectId: ProjectId.makeUnsafe("project-2"),887 projectId: ProjectId.make("project-2"),
896 updatedAt: "2026-03-09T10:05:00.000Z",888 updatedAt: "2026-03-09T10:05:00.000Z",
897 messages: [889 messages: [
898 {890 {
...@@ -910,8 +902,8 @@ describe("sortProjectsForSidebar", () => {...@@ -910,8 +902,8 @@ describe("sortProjectsForSidebar", () => {
910 const sorted = sortProjectsForSidebar(projects, threads, "updated_at");902 const sorted = sortProjectsForSidebar(projects, threads, "updated_at");
911903
912 expect(sorted.map((project) => project.id)).toEqual([904 expect(sorted.map((project) => project.id)).toEqual([
913 ProjectId.makeUnsafe("project-2"),905 ProjectId.make("project-2"),
914 ProjectId.makeUnsafe("project-1"),906 ProjectId.make("project-1"),
915 ]);907 ]);
916 });908 });
917909
...@@ -919,12 +911,12 @@ describe("sortProjectsForSidebar", () => {...@@ -919,12 +911,12 @@ describe("sortProjectsForSidebar", () => {
919 const sorted = sortProjectsForSidebar(911 const sorted = sortProjectsForSidebar(
920 [912 [
921 makeProject({913 makeProject({
922 id: ProjectId.makeUnsafe("project-1"),914 id: ProjectId.make("project-1"),
923 name: "Older project",915 name: "Older project",
924 updatedAt: "2026-03-09T10:01:00.000Z",916 updatedAt: "2026-03-09T10:01:00.000Z",
925 }),917 }),
926 makeProject({918 makeProject({
927 id: ProjectId.makeUnsafe("project-2"),919 id: ProjectId.make("project-2"),
928 name: "Newer project",920 name: "Newer project",
929 updatedAt: "2026-03-09T10:05:00.000Z",921 updatedAt: "2026-03-09T10:05:00.000Z",
930 }),922 }),
...@@ -934,8 +926,8 @@ describe("sortProjectsForSidebar", () => {...@@ -934,8 +926,8 @@ describe("sortProjectsForSidebar", () => {
934 );926 );
935927
936 expect(sorted.map((project) => project.id)).toEqual([928 expect(sorted.map((project) => project.id)).toEqual([
937 ProjectId.makeUnsafe("project-2"),929 ProjectId.make("project-2"),
938 ProjectId.makeUnsafe("project-1"),930 ProjectId.make("project-1"),
939 ]);931 ]);
940 });932 });
941933
...@@ -943,13 +935,13 @@ describe("sortProjectsForSidebar", () => {...@@ -943,13 +935,13 @@ describe("sortProjectsForSidebar", () => {
943 const sorted = sortProjectsForSidebar(935 const sorted = sortProjectsForSidebar(
944 [936 [
945 makeProject({937 makeProject({
946 id: ProjectId.makeUnsafe("project-2"),938 id: ProjectId.make("project-2"),
947 name: "Beta",939 name: "Beta",
948 createdAt: undefined,940 createdAt: undefined,
949 updatedAt: undefined,941 updatedAt: undefined,
950 }),942 }),
951 makeProject({943 makeProject({
952 id: ProjectId.makeUnsafe("project-1"),944 id: ProjectId.make("project-1"),
953 name: "Alpha",945 name: "Alpha",
954 createdAt: undefined,946 createdAt: undefined,
955 updatedAt: undefined,947 updatedAt: undefined,
...@@ -960,22 +952,22 @@ describe("sortProjectsForSidebar", () => {...@@ -960,22 +952,22 @@ describe("sortProjectsForSidebar", () => {
960 );952 );
961953
962 expect(sorted.map((project) => project.id)).toEqual([954 expect(sorted.map((project) => project.id)).toEqual([
963 ProjectId.makeUnsafe("project-1"),955 ProjectId.make("project-1"),
964 ProjectId.makeUnsafe("project-2"),956 ProjectId.make("project-2"),
965 ]);957 ]);
966 });958 });
967959
968 it("preserves manual project ordering", () => {960 it("preserves manual project ordering", () => {
969 const projects = [961 const projects = [
970 makeProject({ id: ProjectId.makeUnsafe("project-2"), name: "Second" }),962 makeProject({ id: ProjectId.make("project-2"), name: "Second" }),
971 makeProject({ id: ProjectId.makeUnsafe("project-1"), name: "First" }),963 makeProject({ id: ProjectId.make("project-1"), name: "First" }),
972 ];964 ];
973965
974 const sorted = sortProjectsForSidebar(projects, [], "manual");966 const sorted = sortProjectsForSidebar(projects, [], "manual");
975967
976 expect(sorted.map((project) => project.id)).toEqual([968 expect(sorted.map((project) => project.id)).toEqual([
977 ProjectId.makeUnsafe("project-2"),969 ProjectId.make("project-2"),
978 ProjectId.makeUnsafe("project-1"),970 ProjectId.make("project-1"),
979 ]);971 ]);
980 });972 });
981973
...@@ -983,26 +975,26 @@ describe("sortProjectsForSidebar", () => {...@@ -983,26 +975,26 @@ describe("sortProjectsForSidebar", () => {
983 const sorted = sortProjectsForSidebar(975 const sorted = sortProjectsForSidebar(
984 [976 [
985 makeProject({977 makeProject({
986 id: ProjectId.makeUnsafe("project-1"),978 id: ProjectId.make("project-1"),
987 name: "Visible project",979 name: "Visible project",
988 updatedAt: "2026-03-09T10:01:00.000Z",980 updatedAt: "2026-03-09T10:01:00.000Z",
989 }),981 }),
990 makeProject({982 makeProject({
991 id: ProjectId.makeUnsafe("project-2"),983 id: ProjectId.make("project-2"),
992 name: "Archived-only project",984 name: "Archived-only project",
993 updatedAt: "2026-03-09T10:00:00.000Z",985 updatedAt: "2026-03-09T10:00:00.000Z",
994 }),986 }),
995 ],987 ],
996 [988 [
997 makeThread({989 makeThread({
998 id: ThreadId.makeUnsafe("thread-visible"),990 id: ThreadId.make("thread-visible"),
999 projectId: ProjectId.makeUnsafe("project-1"),991 projectId: ProjectId.make("project-1"),
1000 updatedAt: "2026-03-09T10:02:00.000Z",992 updatedAt: "2026-03-09T10:02:00.000Z",
1001 archivedAt: null,993 archivedAt: null,
1002 }),994 }),
1003 makeThread({995 makeThread({
1004 id: ThreadId.makeUnsafe("thread-archived"),996 id: ThreadId.make("thread-archived"),
1005 projectId: ProjectId.makeUnsafe("project-2"),997 projectId: ProjectId.make("project-2"),
1006 updatedAt: "2026-03-09T10:10:00.000Z",998 updatedAt: "2026-03-09T10:10:00.000Z",
1007 archivedAt: "2026-03-09T10:11:00.000Z",999 archivedAt: "2026-03-09T10:11:00.000Z",
1008 }),1000 }),
...@@ -1011,8 +1003,8 @@ describe("sortProjectsForSidebar", () => {...@@ -1011,8 +1003,8 @@ describe("sortProjectsForSidebar", () => {
1011 );1003 );
10121004
1013 expect(sorted.map((project) => project.id)).toEqual([1005 expect(sorted.map((project) => project.id)).toEqual([
1014 ProjectId.makeUnsafe("project-1"),1006 ProjectId.make("project-1"),
1015 ProjectId.makeUnsafe("project-2"),1007 ProjectId.make("project-2"),
1016 ]);1008 ]);
1017 });1009 });
10181010
apps/web/src/components/ThreadTerminalDrawer.browser.tsx+1-1
...@@ -121,7 +121,7 @@ vi.mock("~/localApi", () => ({...@@ -121,7 +121,7 @@ vi.mock("~/localApi", () => ({
121121
122import { TerminalViewport } from "./ThreadTerminalDrawer";122import { TerminalViewport } from "./ThreadTerminalDrawer";
123123
124const THREAD_ID = ThreadId.makeUnsafe("thread-terminal-browser");124const THREAD_ID = ThreadId.make("thread-terminal-browser");
125125
126function createEnvironmentApi() {126function createEnvironmentApi() {
127 return {127 return {
apps/web/src/components/chat/ChangedFilesTree.test.tsx+2-2
...@@ -39,7 +39,7 @@ describe("ChangedFilesTree", () => {...@@ -39,7 +39,7 @@ describe("ChangedFilesTree", () => {
39 ({ files, visibleLabels, hiddenLabels }) => {39 ({ files, visibleLabels, hiddenLabels }) => {
40 const markup = renderToStaticMarkup(40 const markup = renderToStaticMarkup(
41 <ChangedFilesTree41 <ChangedFilesTree
42 turnId={TurnId.makeUnsafe("turn-1")}42 turnId={TurnId.make("turn-1")}
43 files={files}43 files={files}
44 allDirectoriesExpanded={false}44 allDirectoriesExpanded={false}
45 resolvedTheme="light"45 resolvedTheme="light"
...@@ -100,7 +100,7 @@ describe("ChangedFilesTree", () => {...@@ -100,7 +100,7 @@ describe("ChangedFilesTree", () => {
100 ({ files, visibleLabels }) => {100 ({ files, visibleLabels }) => {
101 const markup = renderToStaticMarkup(101 const markup = renderToStaticMarkup(
102 <ChangedFilesTree102 <ChangedFilesTree
103 turnId={TurnId.makeUnsafe("turn-1")}103 turnId={TurnId.make("turn-1")}
104 files={files}104 files={files}
105 allDirectoriesExpanded105 allDirectoriesExpanded
106 resolvedTheme="light"106 resolvedTheme="light"
apps/web/src/components/chat/CompactComposerControlsMenu.browser.tsx+2-2
...@@ -15,10 +15,10 @@ import { CompactComposerControlsMenu } from "./CompactComposerControlsMenu";...@@ -15,10 +15,10 @@ import { CompactComposerControlsMenu } from "./CompactComposerControlsMenu";
15import { TraitsMenuContent } from "./TraitsPicker";15import { TraitsMenuContent } from "./TraitsPicker";
16import { useComposerDraftStore } from "../../composerDraftStore";16import { useComposerDraftStore } from "../../composerDraftStore";
1717
18const LOCAL_ENVIRONMENT_ID = EnvironmentId.makeUnsafe("environment-local");18const LOCAL_ENVIRONMENT_ID = EnvironmentId.make("environment-local");
1919
20async function mountMenu(props?: { modelSelection?: ModelSelection; prompt?: string }) {20async function mountMenu(props?: { modelSelection?: ModelSelection; prompt?: string }) {
21 const threadId = ThreadId.makeUnsafe("thread-compact-menu");21 const threadId = ThreadId.make("thread-compact-menu");
22 const threadRef = scopeThreadRef(LOCAL_ENVIRONMENT_ID, threadId);22 const threadRef = scopeThreadRef(LOCAL_ENVIRONMENT_ID, threadId);
23 const threadKey = scopedThreadKey(threadRef);23 const threadKey = scopedThreadKey(threadRef);
24 const provider = props?.modelSelection?.provider ?? "claudeAgent";24 const provider = props?.modelSelection?.provider ?? "claudeAgent";
apps/web/src/components/chat/ComposerPendingTerminalContexts.test.tsx+1-1
...@@ -10,7 +10,7 @@ describe("ComposerPendingTerminalContextChip", () => {...@@ -10,7 +10,7 @@ describe("ComposerPendingTerminalContextChip", () => {
10 <ComposerPendingTerminalContextChip10 <ComposerPendingTerminalContextChip
11 context={{11 context={{
12 id: "ctx-expired",12 id: "ctx-expired",
13 threadId: ThreadId.makeUnsafe("thread-1"),13 threadId: ThreadId.make("thread-1"),
14 terminalId: "default",14 terminalId: "default",
15 terminalLabel: "Terminal 1",15 terminalLabel: "Terminal 1",
16 lineStart: 2,16 lineStart: 2,
apps/web/src/components/chat/MessagesTimeline.test.tsx+1-1
...@@ -60,7 +60,7 @@ describe("MessagesTimeline", () => {...@@ -60,7 +60,7 @@ describe("MessagesTimeline", () => {
60 kind: "message",60 kind: "message",
61 createdAt: "2026-03-17T19:12:28.000Z",61 createdAt: "2026-03-17T19:12:28.000Z",
62 message: {62 message: {
63 id: MessageId.makeUnsafe("message-2"),63 id: MessageId.make("message-2"),
64 role: "user",64 role: "user",
65 text: [65 text: [
66 "yoo what's @terminal-1:1-5 mean",66 "yoo what's @terminal-1:1-5 mean",
apps/web/src/components/chat/MessagesTimeline.virtualization.browser.tsx+1-1
...@@ -101,7 +101,7 @@ function createMessage(input: {...@@ -101,7 +101,7 @@ function createMessage(input: {
101 attachments?: ChatMessage["attachments"];101 attachments?: ChatMessage["attachments"];
102}): ChatMessage {102}): ChatMessage {
103 return {103 return {
104 id: MessageId.makeUnsafe(input.id),104 id: MessageId.make(input.id),
105 role: input.role,105 role: input.role,
106 text: input.text,106 text: input.text,
107 ...(input.attachments ? { attachments: input.attachments } : {}),107 ...(input.attachments ? { attachments: input.attachments } : {}),
apps/web/src/components/chat/TraitsPicker.browser.tsx+3-3
...@@ -28,11 +28,11 @@ import { DEFAULT_CLIENT_SETTINGS } from "@t3tools/contracts/settings";...@@ -28,11 +28,11 @@ import { DEFAULT_CLIENT_SETTINGS } from "@t3tools/contracts/settings";
2828
29// ── Claude TraitsPicker tests ─────────────────────────────────────────29// ── Claude TraitsPicker tests ─────────────────────────────────────────
3030
31const LOCAL_ENVIRONMENT_ID = EnvironmentId.makeUnsafe("environment-local");31const LOCAL_ENVIRONMENT_ID = EnvironmentId.make("environment-local");
32const CLAUDE_THREAD_ID = ThreadId.makeUnsafe("thread-claude-traits");32const CLAUDE_THREAD_ID = ThreadId.make("thread-claude-traits");
33const CLAUDE_THREAD_REF = scopeThreadRef(LOCAL_ENVIRONMENT_ID, CLAUDE_THREAD_ID);33const CLAUDE_THREAD_REF = scopeThreadRef(LOCAL_ENVIRONMENT_ID, CLAUDE_THREAD_ID);
34const CLAUDE_THREAD_KEY = scopedThreadKey(CLAUDE_THREAD_REF);34const CLAUDE_THREAD_KEY = scopedThreadKey(CLAUDE_THREAD_REF);
35const CODEX_THREAD_ID = ThreadId.makeUnsafe("thread-codex-traits");35const CODEX_THREAD_ID = ThreadId.make("thread-codex-traits");
36const CODEX_THREAD_REF = scopeThreadRef(LOCAL_ENVIRONMENT_ID, CODEX_THREAD_ID);36const CODEX_THREAD_REF = scopeThreadRef(LOCAL_ENVIRONMENT_ID, CODEX_THREAD_ID);
37const CODEX_THREAD_KEY = scopedThreadKey(CODEX_THREAD_REF);37const CODEX_THREAD_KEY = scopedThreadKey(CODEX_THREAD_REF);
38const TEST_PROVIDERS: ReadonlyArray<ServerProvider> = [38const TEST_PROVIDERS: ReadonlyArray<ServerProvider> = [
apps/web/src/components/settings/SettingsPanels.browser.tsx+5-5
...@@ -92,7 +92,7 @@ const authAccessHarness = vi.hoisted(() => {...@@ -92,7 +92,7 @@ const authAccessHarness = vi.hoisted(() => {
92 revision,92 revision,
93 type: "clientRemoved",93 type: "clientRemoved",
94 payload: {94 payload: {
95 sessionId: AuthSessionId.makeUnsafe(sessionId),95 sessionId: AuthSessionId.make(sessionId),
96 },96 },
97 });97 });
98 revision += 1;98 revision += 1;
...@@ -119,13 +119,13 @@ vi.mock("../../environments/runtime", () => {...@@ -119,13 +119,13 @@ vi.mock("../../environments/runtime", () => {
119 id: "environment-local",119 id: "environment-local",
120 label: "Local environment",120 label: "Local environment",
121 source: "manual" as const,121 source: "manual" as const,
122 environmentId: EnvironmentId.makeUnsafe("environment-local"),122 environmentId: EnvironmentId.make("environment-local"),
123 target: {123 target: {
124 httpBaseUrl: "http://localhost:3000",124 httpBaseUrl: "http://localhost:3000",
125 wsBaseUrl: "ws://localhost:3000",125 wsBaseUrl: "ws://localhost:3000",
126 },126 },
127 },127 },
128 environmentId: EnvironmentId.makeUnsafe("environment-local"),128 environmentId: EnvironmentId.make("environment-local"),
129 client: {129 client: {
130 server: {130 server: {
131 subscribeAuthAccess: (listener: Parameters<typeof authAccessHarness.subscribe>[0]) =>131 subscribeAuthAccess: (listener: Parameters<typeof authAccessHarness.subscribe>[0]) =>
...@@ -171,7 +171,7 @@ vi.mock("../../environments/runtime", () => {...@@ -171,7 +171,7 @@ vi.mock("../../environments/runtime", () => {
171function createBaseServerConfig(): ServerConfig {171function createBaseServerConfig(): ServerConfig {
172 return {172 return {
173 environment: {173 environment: {
174 environmentId: EnvironmentId.makeUnsafe("environment-local"),174 environmentId: EnvironmentId.make("environment-local"),
175 label: "Local environment",175 label: "Local environment",
176 platform: { os: "darwin" as const, arch: "arm64" as const },176 platform: { os: "darwin" as const, arch: "arm64" as const },
177 serverVersion: "0.0.0-test",177 serverVersion: "0.0.0-test",
...@@ -245,7 +245,7 @@ function makeClientSession(input: {...@@ -245,7 +245,7 @@ function makeClientSession(input: {
245 deviceType: "unknown",245 deviceType: "unknown",
246 ...input.client,246 ...input.client,
247 },247 },
248 sessionId: AuthSessionId.makeUnsafe(input.sessionId),248 sessionId: AuthSessionId.make(input.sessionId),
249 issuedAt: makeUtc(input.issuedAt),249 issuedAt: makeUtc(input.issuedAt),
250 expiresAt: makeUtc(input.expiresAt),250 expiresAt: makeUtc(input.expiresAt),
251 lastConnectedAt:251 lastConnectedAt:
apps/web/src/composerDraftStore.test.ts+22-22
...@@ -70,7 +70,7 @@ function makeTerminalContext(input: {...@@ -70,7 +70,7 @@ function makeTerminalContext(input: {
70}): TerminalContextDraft {70}): TerminalContextDraft {
71 return {71 return {
72 id: input.id,72 id: input.id,
73 threadId: ThreadId.makeUnsafe("thread-dedupe"),73 threadId: ThreadId.make("thread-dedupe"),
74 terminalId: input.terminalId ?? "default",74 terminalId: input.terminalId ?? "default",
75 terminalLabel: input.terminalLabel ?? "Terminal 1",75 terminalLabel: input.terminalLabel ?? "Terminal 1",
76 lineStart: input.lineStart ?? 4,76 lineStart: input.lineStart ?? 4,
...@@ -106,9 +106,9 @@ function providerModelOptions(options: ProviderModelOptions): ProviderModelOptio...@@ -106,9 +106,9 @@ function providerModelOptions(options: ProviderModelOptions): ProviderModelOptio
106 return options;106 return options;
107}107}
108108
109const TEST_ENVIRONMENT_ID = EnvironmentId.makeUnsafe("environment-local");109const TEST_ENVIRONMENT_ID = EnvironmentId.make("environment-local");
110const OTHER_TEST_ENVIRONMENT_ID = EnvironmentId.makeUnsafe("environment-remote");110const OTHER_TEST_ENVIRONMENT_ID = EnvironmentId.make("environment-remote");
111const LEGACY_TEST_ENVIRONMENT_ID = EnvironmentId.makeUnsafe("__legacy__");111const LEGACY_TEST_ENVIRONMENT_ID = EnvironmentId.make("__legacy__");
112112
113function threadKeyFor(113function threadKeyFor(
114 threadId: ThreadId,114 threadId: ThreadId,
...@@ -130,7 +130,7 @@ function draftByKey(key: string) {...@@ -130,7 +130,7 @@ function draftByKey(key: string) {
130}130}
131131
132describe("composerDraftStore addImages", () => {132describe("composerDraftStore addImages", () => {
133 const threadId = ThreadId.makeUnsafe("thread-dedupe");133 const threadId = ThreadId.make("thread-dedupe");
134 const threadRef = scopeThreadRef(TEST_ENVIRONMENT_ID, threadId);134 const threadRef = scopeThreadRef(TEST_ENVIRONMENT_ID, threadId);
135 let originalRevokeObjectUrl: typeof URL.revokeObjectURL;135 let originalRevokeObjectUrl: typeof URL.revokeObjectURL;
136 let revokeSpy: ReturnType<typeof vi.fn<(url: string) => void>>;136 let revokeSpy: ReturnType<typeof vi.fn<(url: string) => void>>;
...@@ -216,7 +216,7 @@ describe("composerDraftStore addImages", () => {...@@ -216,7 +216,7 @@ describe("composerDraftStore addImages", () => {
216});216});
217217
218describe("composerDraftStore clearComposerContent", () => {218describe("composerDraftStore clearComposerContent", () => {
219 const threadId = ThreadId.makeUnsafe("thread-clear");219 const threadId = ThreadId.make("thread-clear");
220 const threadRef = scopeThreadRef(TEST_ENVIRONMENT_ID, threadId);220 const threadRef = scopeThreadRef(TEST_ENVIRONMENT_ID, threadId);
221 let originalRevokeObjectUrl: typeof URL.revokeObjectURL;221 let originalRevokeObjectUrl: typeof URL.revokeObjectURL;
222 let revokeSpy: ReturnType<typeof vi.fn<(url: string) => void>>;222 let revokeSpy: ReturnType<typeof vi.fn<(url: string) => void>>;
...@@ -248,7 +248,7 @@ describe("composerDraftStore clearComposerContent", () => {...@@ -248,7 +248,7 @@ describe("composerDraftStore clearComposerContent", () => {
248});248});
249249
250describe("composerDraftStore syncPersistedAttachments", () => {250describe("composerDraftStore syncPersistedAttachments", () => {
251 const threadId = ThreadId.makeUnsafe("thread-sync-persisted");251 const threadId = ThreadId.make("thread-sync-persisted");
252 const threadRef = scopeThreadRef(TEST_ENVIRONMENT_ID, threadId);252 const threadRef = scopeThreadRef(TEST_ENVIRONMENT_ID, threadId);
253253
254 beforeEach(() => {254 beforeEach(() => {
...@@ -304,7 +304,7 @@ describe("composerDraftStore syncPersistedAttachments", () => {...@@ -304,7 +304,7 @@ describe("composerDraftStore syncPersistedAttachments", () => {
304});304});
305305
306describe("composerDraftStore terminal contexts", () => {306describe("composerDraftStore terminal contexts", () => {
307 const threadId = ThreadId.makeUnsafe("thread-dedupe");307 const threadId = ThreadId.make("thread-dedupe");
308 const threadRef = scopeThreadRef(TEST_ENVIRONMENT_ID, threadId);308 const threadRef = scopeThreadRef(TEST_ENVIRONMENT_ID, threadId);
309309
310 beforeEach(() => {310 beforeEach(() => {
...@@ -482,18 +482,18 @@ describe("composerDraftStore terminal contexts", () => {...@@ -482,18 +482,18 @@ describe("composerDraftStore terminal contexts", () => {
482});482});
483483
484describe("composerDraftStore project draft thread mapping", () => {484describe("composerDraftStore project draft thread mapping", () => {
485 const projectId = ProjectId.makeUnsafe("project-a");485 const projectId = ProjectId.make("project-a");
486 const otherProjectId = ProjectId.makeUnsafe("project-b");486 const otherProjectId = ProjectId.make("project-b");
487 const projectRef = scopeProjectRef(TEST_ENVIRONMENT_ID, projectId);487 const projectRef = scopeProjectRef(TEST_ENVIRONMENT_ID, projectId);
488 const otherProjectRef = scopeProjectRef(TEST_ENVIRONMENT_ID, otherProjectId);488 const otherProjectRef = scopeProjectRef(TEST_ENVIRONMENT_ID, otherProjectId);
489 const remoteProjectRef = scopeProjectRef(OTHER_TEST_ENVIRONMENT_ID, projectId);489 const remoteProjectRef = scopeProjectRef(OTHER_TEST_ENVIRONMENT_ID, projectId);
490 const threadId = ThreadId.makeUnsafe("thread-a");490 const threadId = ThreadId.make("thread-a");
491 const otherThreadId = ThreadId.makeUnsafe("thread-b");491 const otherThreadId = ThreadId.make("thread-b");
492 const draftId = DraftId.makeUnsafe("draft-a");492 const draftId = DraftId.make("draft-a");
493 const otherDraftId = DraftId.makeUnsafe("draft-b");493 const otherDraftId = DraftId.make("draft-b");
494 const sharedDraftId = DraftId.makeUnsafe("draft-shared");494 const sharedDraftId = DraftId.make("draft-shared");
495 const localDraftId = DraftId.makeUnsafe("draft-local");495 const localDraftId = DraftId.make("draft-local");
496 const remoteDraftId = DraftId.makeUnsafe("draft-remote");496 const remoteDraftId = DraftId.make("draft-remote");
497497
498 beforeEach(() => {498 beforeEach(() => {
499 resetComposerDraftStore();499 resetComposerDraftStore();
...@@ -847,7 +847,7 @@ describe("composerDraftStore project draft thread mapping", () => {...@@ -847,7 +847,7 @@ describe("composerDraftStore project draft thread mapping", () => {
847});847});
848848
849describe("composerDraftStore modelSelection", () => {849describe("composerDraftStore modelSelection", () => {
850 const threadId = ThreadId.makeUnsafe("thread-model-options");850 const threadId = ThreadId.make("thread-model-options");
851 const threadRef = scopeThreadRef(TEST_ENVIRONMENT_ID, threadId);851 const threadRef = scopeThreadRef(TEST_ENVIRONMENT_ID, threadId);
852852
853 beforeEach(() => {853 beforeEach(() => {
...@@ -1078,7 +1078,7 @@ describe("composerDraftStore modelSelection", () => {...@@ -1078,7 +1078,7 @@ describe("composerDraftStore modelSelection", () => {
1078});1078});
10791079
1080describe("composerDraftStore setModelSelection", () => {1080describe("composerDraftStore setModelSelection", () => {
1081 const threadId = ThreadId.makeUnsafe("thread-model");1081 const threadId = ThreadId.make("thread-model");
1082 const threadRef = scopeThreadRef(TEST_ENVIRONMENT_ID, threadId);1082 const threadRef = scopeThreadRef(TEST_ENVIRONMENT_ID, threadId);
10831083
1084 beforeEach(() => {1084 beforeEach(() => {
...@@ -1133,7 +1133,7 @@ describe("composerDraftStore sticky composer settings", () => {...@@ -1133,7 +1133,7 @@ describe("composerDraftStore sticky composer settings", () => {
11331133
1134 it("applies sticky activeProvider to new drafts", () => {1134 it("applies sticky activeProvider to new drafts", () => {
1135 const store = useComposerDraftStore.getState();1135 const store = useComposerDraftStore.getState();
1136 const threadId = ThreadId.makeUnsafe("thread-sticky-active-provider");1136 const threadId = ThreadId.make("thread-sticky-active-provider");
1137 const threadRef = scopeThreadRef(TEST_ENVIRONMENT_ID, threadId);1137 const threadRef = scopeThreadRef(TEST_ENVIRONMENT_ID, threadId);
11381138
1139 store.setStickyModelSelection(modelSelection("claudeAgent", "claude-opus-4-6"));1139 store.setStickyModelSelection(modelSelection("claudeAgent", "claude-opus-4-6"));
...@@ -1149,7 +1149,7 @@ describe("composerDraftStore sticky composer settings", () => {...@@ -1149,7 +1149,7 @@ describe("composerDraftStore sticky composer settings", () => {
1149});1149});
11501150
1151describe("composerDraftStore provider-scoped option updates", () => {1151describe("composerDraftStore provider-scoped option updates", () => {
1152 const threadId = ThreadId.makeUnsafe("thread-provider");1152 const threadId = ThreadId.make("thread-provider");
1153 const threadRef = scopeThreadRef(TEST_ENVIRONMENT_ID, threadId);1153 const threadRef = scopeThreadRef(TEST_ENVIRONMENT_ID, threadId);
11541154
1155 beforeEach(() => {1155 beforeEach(() => {
...@@ -1175,7 +1175,7 @@ describe("composerDraftStore provider-scoped option updates", () => {...@@ -1175,7 +1175,7 @@ describe("composerDraftStore provider-scoped option updates", () => {
1175});1175});
11761176
1177describe("composerDraftStore runtime and interaction settings", () => {1177describe("composerDraftStore runtime and interaction settings", () => {
1178 const threadId = ThreadId.makeUnsafe("thread-settings");1178 const threadId = ThreadId.make("thread-settings");
1179 const threadRef = scopeThreadRef(TEST_ENVIRONMENT_ID, threadId);1179 const threadRef = scopeThreadRef(TEST_ENVIRONMENT_ID, threadId);
11801180
1181 beforeEach(() => {1181 beforeEach(() => {
apps/web/src/composerDraftStore.ts+13-16
...@@ -926,7 +926,7 @@ function normalizeComposerTarget(...@@ -926,7 +926,7 @@ function normalizeComposerTarget(
926): ComposerThreadTarget | null {926): ComposerThreadTarget | null {
927 if (typeof target === "string") {927 if (typeof target === "string") {
928 const draftId = target.trim();928 const draftId = target.trim();
929 return draftId.length > 0 ? DraftId.makeUnsafe(draftId) : null;929 return draftId.length > 0 ? DraftId.make(draftId) : null;
930 }930 }
931 return target;931 return target;
932}932}
...@@ -1804,7 +1804,7 @@ const composerDraftStore = create<ComposerDraftStoreState>()(...@@ -1804,7 +1804,7 @@ const composerDraftStore = create<ComposerDraftStoreState>()(
1804 if (!draftThread || isDraftThreadPromoting(draftThread)) {1804 if (!draftThread || isDraftThreadPromoting(draftThread)) {
1805 return null;1805 return null;
1806 }1806 }
1807 return toProjectDraftSession(DraftId.makeUnsafe(draftId), draftThread);1807 return toProjectDraftSession(DraftId.make(draftId), draftThread);
1808 },1808 },
1809 getDraftThreadByProjectRef: (projectRef) => {1809 getDraftThreadByProjectRef: (projectRef) => {
1810 return get().getDraftSessionByProjectRef(projectRef);1810 return get().getDraftSessionByProjectRef(projectRef);
...@@ -1818,7 +1818,7 @@ const composerDraftStore = create<ComposerDraftStoreState>()(...@@ -1818,7 +1818,7 @@ const composerDraftStore = create<ComposerDraftStoreState>()(
1818 draftThread.projectId === projectRef.projectId &&1818 draftThread.projectId === projectRef.projectId &&
1819 draftThread.environmentId === projectRef.environmentId1819 draftThread.environmentId === projectRef.environmentId
1820 ) {1820 ) {
1821 return toProjectDraftSession(DraftId.makeUnsafe(draftId), draftThread);1821 return toProjectDraftSession(DraftId.make(draftId), draftThread);
1822 }1822 }
1823 }1823 }
1824 return null;1824 return null;
...@@ -1837,7 +1837,7 @@ const composerDraftStore = create<ComposerDraftStoreState>()(...@@ -1837,7 +1837,7 @@ const composerDraftStore = create<ComposerDraftStoreState>()(
1837 },1837 },
1838 getDraftThread: (threadRef) => {1838 getDraftThread: (threadRef) => {
1839 if (typeof threadRef === "string") {1839 if (typeof threadRef === "string") {
1840 return get().getDraftSession(DraftId.makeUnsafe(threadRef));1840 return get().getDraftSession(DraftId.make(threadRef));
1841 }1841 }
1842 return get().getDraftSessionByRef(threadRef);1842 return get().getDraftSessionByRef(threadRef);
1843 },1843 },
...@@ -1863,7 +1863,7 @@ const composerDraftStore = create<ComposerDraftStoreState>()(...@@ -1863,7 +1863,7 @@ const composerDraftStore = create<ComposerDraftStoreState>()(
1863 state.logicalProjectDraftThreadKeyByLogicalProjectKey[normalizedLogicalProjectKey];1863 state.logicalProjectDraftThreadKeyByLogicalProjectKey[normalizedLogicalProjectKey];
1864 const nextDraftThread = createDraftThreadState(1864 const nextDraftThread = createDraftThreadState(
1865 projectRef,1865 projectRef,
1866 options?.threadId ?? existingThread?.threadId ?? ThreadId.makeUnsafe(draftId),1866 options?.threadId ?? existingThread?.threadId ?? ThreadId.make(draftId),
1867 normalizedLogicalProjectKey,1867 normalizedLogicalProjectKey,
1868 existingThread,1868 existingThread,
1869 options,1869 options,
...@@ -2438,10 +2438,9 @@ const composerDraftStore = create<ComposerDraftStoreState>()(...@@ -2438,10 +2438,9 @@ const composerDraftStore = create<ComposerDraftStoreState>()(
2438 if (!threadKey || !threadId) {2438 if (!threadKey || !threadId) {
2439 return;2439 return;
2440 }2440 }
2441 get().addImages(2441 get().addImages(typeof threadRef === "string" ? DraftId.make(threadKey) : threadRef, [
2442 typeof threadRef === "string" ? DraftId.makeUnsafe(threadKey) : threadRef,2442 image,
2443 [image],2443 ]);
2444 );
2445 },2444 },
2446 addImages: (threadRef, images) => {2445 addImages: (threadRef, images) => {
2447 const threadKey = resolveComposerDraftKey(get(), threadRef) ?? "";2446 const threadKey = resolveComposerDraftKey(get(), threadRef) ?? "";
...@@ -2566,7 +2565,7 @@ const composerDraftStore = create<ComposerDraftStoreState>()(...@@ -2566,7 +2565,7 @@ const composerDraftStore = create<ComposerDraftStoreState>()(
2566 return;2565 return;
2567 }2566 }
2568 get().addTerminalContexts(2567 get().addTerminalContexts(
2569 typeof threadRef === "string" ? DraftId.makeUnsafe(threadKey) : threadRef,2568 typeof threadRef === "string" ? DraftId.make(threadKey) : threadRef,
2570 [context],2569 [context],
2571 );2570 );
2572 },2571 },
...@@ -2799,9 +2798,7 @@ export function useEffectiveComposerModelState(input: {...@@ -2799,9 +2798,7 @@ export function useEffectiveComposerModelState(input: {
2799 projectModelSelection: ModelSelection | null | undefined;2798 projectModelSelection: ModelSelection | null | undefined;
2800 settings: UnifiedSettings;2799 settings: UnifiedSettings;
2801}): EffectiveComposerModelState {2800}): EffectiveComposerModelState {
2802 const draft = useComposerDraftModelState(2801 const draft = useComposerDraftModelState(input.threadRef ?? input.draftId ?? DraftId.make(""));
2803 input.threadRef ?? input.draftId ?? DraftId.makeUnsafe(""),
2804 );
28052802
2806 return useMemo(2803 return useMemo(
2807 () =>2804 () =>
...@@ -2836,7 +2833,7 @@ export function markPromotedDraftThread(threadId: ThreadId): void {...@@ -2836,7 +2833,7 @@ export function markPromotedDraftThread(threadId: ThreadId): void {
2836 const draftThreadTargets: ComposerThreadTarget[] = [];2833 const draftThreadTargets: ComposerThreadTarget[] = [];
2837 for (const [draftId, draftThread] of Object.entries(store.draftThreadsByThreadKey)) {2834 for (const [draftId, draftThread] of Object.entries(store.draftThreadsByThreadKey)) {
2838 if (draftThread.threadId === threadId) {2835 if (draftThread.threadId === threadId) {
2839 draftThreadTargets.push(DraftId.makeUnsafe(draftId));2836 draftThreadTargets.push(DraftId.make(draftId));
2840 }2837 }
2841 }2838 }
2842 if (draftThreadTargets.length === 0) {2839 if (draftThreadTargets.length === 0) {
...@@ -2854,7 +2851,7 @@ export function markPromotedDraftThreadByRef(threadRef: ScopedThreadRef): void {...@@ -2854,7 +2851,7 @@ export function markPromotedDraftThreadByRef(threadRef: ScopedThreadRef): void {
2854 draftThread.environmentId === threadRef.environmentId &&2851 draftThread.environmentId === threadRef.environmentId &&
2855 draftThread.threadId === threadRef.threadId2852 draftThread.threadId === threadRef.threadId
2856 ) {2853 ) {
2857 draftStore.markDraftThreadPromoting(DraftId.makeUnsafe(draftId), threadRef);2854 draftStore.markDraftThreadPromoting(DraftId.make(draftId), threadRef);
2858 }2855 }
2859 }2856 }
2860}2857}
...@@ -2879,7 +2876,7 @@ export function finalizePromotedDraftThreadByRef(threadRef: ScopedThreadRef): vo...@@ -2879,7 +2876,7 @@ export function finalizePromotedDraftThreadByRef(threadRef: ScopedThreadRef): vo
2879 draftThread.promotedTo.environmentId === threadRef.environmentId &&2876 draftThread.promotedTo.environmentId === threadRef.environmentId &&
2880 draftThread.promotedTo.threadId === threadRef.threadId2877 draftThread.promotedTo.threadId === threadRef.threadId
2881 ) {2878 ) {
2882 draftStore.finalizePromotedDraftThread(DraftId.makeUnsafe(draftId));2879 draftStore.finalizePromotedDraftThread(DraftId.make(draftId));
2883 }2880 }
2884 }2881 }
2885}2882}
apps/web/src/diffRouteSearch.ts+1-1
...@@ -28,7 +28,7 @@ export function stripDiffSearchParams<T extends Record<string, unknown>>(...@@ -28,7 +28,7 @@ export function stripDiffSearchParams<T extends Record<string, unknown>>(
28export function parseDiffRouteSearch(search: Record<string, unknown>): DiffRouteSearch {28export function parseDiffRouteSearch(search: Record<string, unknown>): DiffRouteSearch {
29 const diff = isDiffOpenValue(search.diff) ? "1" : undefined;29 const diff = isDiffOpenValue(search.diff) ? "1" : undefined;
30 const diffTurnIdRaw = diff ? normalizeSearchString(search.diffTurnId) : undefined;30 const diffTurnIdRaw = diff ? normalizeSearchString(search.diffTurnId) : undefined;
31 const diffTurnId = diffTurnIdRaw ? TurnId.makeUnsafe(diffTurnIdRaw) : undefined;31 const diffTurnId = diffTurnIdRaw ? TurnId.make(diffTurnIdRaw) : undefined;
32 const diffFilePath = diff && diffTurnId ? normalizeSearchString(search.diffFilePath) : undefined;32 const diffFilePath = diff && diffTurnId ? normalizeSearchString(search.diffFilePath) : undefined;
3333
34 return {34 return {
apps/web/src/environmentGrouping.test.ts+13-13
...@@ -16,19 +16,19 @@ import { DEFAULT_INTERACTION_MODE } from "./types";...@@ -16,19 +16,19 @@ import { DEFAULT_INTERACTION_MODE } from "./types";
1616
17// ── Fixture Identifiers ──────────────────────────────────────────────17// ── Fixture Identifiers ──────────────────────────────────────────────
1818
19const primaryEnvId = EnvironmentId.makeUnsafe("env-primary");19const primaryEnvId = EnvironmentId.make("env-primary");
20const remoteEnvId = EnvironmentId.makeUnsafe("env-remote");20const remoteEnvId = EnvironmentId.make("env-remote");
2121
22const sharedProjectPrimaryId = ProjectId.makeUnsafe("shared-proj-primary");22const sharedProjectPrimaryId = ProjectId.make("shared-proj-primary");
23const sharedProjectRemoteId = ProjectId.makeUnsafe("shared-proj-remote");23const sharedProjectRemoteId = ProjectId.make("shared-proj-remote");
24const localOnlyProjectId = ProjectId.makeUnsafe("local-only-proj");24const localOnlyProjectId = ProjectId.make("local-only-proj");
25const remoteOnlyProjectId = ProjectId.makeUnsafe("remote-only-proj");25const remoteOnlyProjectId = ProjectId.make("remote-only-proj");
2626
27const threadP1 = ThreadId.makeUnsafe("thread-shared-primary-1");27const threadP1 = ThreadId.make("thread-shared-primary-1");
28const threadP2 = ThreadId.makeUnsafe("thread-shared-primary-2");28const threadP2 = ThreadId.make("thread-shared-primary-2");
29const threadR1 = ThreadId.makeUnsafe("thread-shared-remote-1");29const threadR1 = ThreadId.make("thread-shared-remote-1");
30const threadL1 = ThreadId.makeUnsafe("thread-local-only-1");30const threadL1 = ThreadId.make("thread-local-only-1");
31const threadRO1 = ThreadId.makeUnsafe("thread-remote-only-1");31const threadRO1 = ThreadId.make("thread-remote-only-1");
3232
33const SHARED_REPO_CANONICAL_KEY = "github.com/example/shared-repo";33const SHARED_REPO_CANONICAL_KEY = "github.com/example/shared-repo";
3434
...@@ -328,7 +328,7 @@ describe("environment grouping", () => {...@@ -328,7 +328,7 @@ describe("environment grouping", () => {
328328
329 it("returns empty array for nonexistent environment", () => {329 it("returns empty array for nonexistent environment", () => {
330 const state = makeFixtureState();330 const state = makeFixtureState();
331 const ref = scopeProjectRef(EnvironmentId.makeUnsafe("nonexistent"), sharedProjectPrimaryId);331 const ref = scopeProjectRef(EnvironmentId.make("nonexistent"), sharedProjectPrimaryId);
332 expect(selectSidebarThreadsForProjectRef(state, ref)).toEqual([]);332 expect(selectSidebarThreadsForProjectRef(state, ref)).toEqual([]);
333 });333 });
334 });334 });
...@@ -381,7 +381,7 @@ describe("environment grouping", () => {...@@ -381,7 +381,7 @@ describe("environment grouping", () => {
381 const state = makeFixtureState();381 const state = makeFixtureState();
382 const refs = [382 const refs = [
383 scopeProjectRef(primaryEnvId, sharedProjectPrimaryId),383 scopeProjectRef(primaryEnvId, sharedProjectPrimaryId),
384 scopeProjectRef(EnvironmentId.makeUnsafe("nonexistent"), ProjectId.makeUnsafe("nope")),384 scopeProjectRef(EnvironmentId.make("nonexistent"), ProjectId.make("nope")),
385 ];385 ];
386 const threads = selectSidebarThreadsForProjectRefs(state, refs);386 const threads = selectSidebarThreadsForProjectRefs(state, refs);
387 // Only returns threads from the valid ref387 // Only returns threads from the valid ref
apps/web/src/environments/primary/bootstrap.test.ts+1-1
...@@ -63,7 +63,7 @@ describe("environmentBootstrap", () => {...@@ -63,7 +63,7 @@ describe("environmentBootstrap", () => {
63 desktopBridge: undefined,63 desktopBridge: undefined,
64 });64 });
65 writePrimaryEnvironmentDescriptor({65 writePrimaryEnvironmentDescriptor({
66 environmentId: EnvironmentId.makeUnsafe("environment-local"),66 environmentId: EnvironmentId.make("environment-local"),
67 label: "Bootstrapped environment",67 label: "Bootstrapped environment",
68 platform: {68 platform: {
69 os: "darwin",69 os: "darwin",
apps/web/src/environments/runtime/catalog.test.ts+4-4
...@@ -41,7 +41,7 @@ describe("environment runtime catalog stores", () => {...@@ -41,7 +41,7 @@ describe("environment runtime catalog stores", () => {
41 });41 });
4242
43 it("resets the saved environment registry store state", () => {43 it("resets the saved environment registry store state", () => {
44 const environmentId = EnvironmentId.makeUnsafe("environment-1");44 const environmentId = EnvironmentId.make("environment-1");
4545
46 useSavedEnvironmentRegistryStore.getState().upsert({46 useSavedEnvironmentRegistryStore.getState().upsert({
47 environmentId,47 environmentId,
...@@ -60,7 +60,7 @@ describe("environment runtime catalog stores", () => {...@@ -60,7 +60,7 @@ describe("environment runtime catalog stores", () => {
60 });60 });
6161
62 it("resets the saved environment runtime store state", () => {62 it("resets the saved environment runtime store state", () => {
63 const environmentId = EnvironmentId.makeUnsafe("environment-1");63 const environmentId = EnvironmentId.make("environment-1");
6464
65 useSavedEnvironmentRuntimeStore.getState().patch(environmentId, {65 useSavedEnvironmentRuntimeStore.getState().patch(environmentId, {
66 connectionState: "connected",66 connectionState: "connected",
...@@ -82,7 +82,7 @@ describe("environment runtime catalog stores", () => {...@@ -82,7 +82,7 @@ describe("environment runtime catalog stores", () => {
8282
83 expect(() =>83 expect(() =>
84 useSavedEnvironmentRegistryStore.getState().upsert({84 useSavedEnvironmentRegistryStore.getState().upsert({
85 environmentId: EnvironmentId.makeUnsafe("environment-1"),85 environmentId: EnvironmentId.make("environment-1"),
86 label: "Remote environment",86 label: "Remote environment",
87 httpBaseUrl: "https://remote.example.com/",87 httpBaseUrl: "https://remote.example.com/",
88 wsBaseUrl: "wss://remote.example.com/",88 wsBaseUrl: "wss://remote.example.com/",
...@@ -121,7 +121,7 @@ describe("environment runtime catalog stores", () => {...@@ -121,7 +121,7 @@ describe("environment runtime catalog stores", () => {
121121
122 const hydrationPromise = waitForSavedEnvironmentRegistryHydration();122 const hydrationPromise = waitForSavedEnvironmentRegistryHydration();
123123
124 const environmentId = EnvironmentId.makeUnsafe("environment-1");124 const environmentId = EnvironmentId.make("environment-1");
125 const record = {125 const record = {
126 environmentId,126 environmentId,
127 label: "Remote environment",127 label: "Remote environment",
apps/web/src/environments/runtime/connection.test.ts+7-7
...@@ -30,7 +30,7 @@ function createTestClient(options?: {...@@ -30,7 +30,7 @@ function createTestClient(options?: {
30 server: {30 server: {
31 getConfig: vi.fn(async () => ({31 getConfig: vi.fn(async () => ({
32 environment: {32 environment: {
33 environmentId: EnvironmentId.makeUnsafe("env-1"),33 environmentId: EnvironmentId.make("env-1"),
34 },34 },
35 })),35 })),
36 subscribeConfig: (listener: (event: any) => void) => {36 subscribeConfig: (listener: (event: any) => void) => {
...@@ -133,7 +133,7 @@ function createTestClient(options?: {...@@ -133,7 +133,7 @@ function createTestClient(options?: {
133133
134describe("createEnvironmentConnection", () => {134describe("createEnvironmentConnection", () => {
135 it("bootstraps a snapshot immediately for a new connection", async () => {135 it("bootstraps a snapshot immediately for a new connection", async () => {
136 const environmentId = EnvironmentId.makeUnsafe("env-1");136 const environmentId = EnvironmentId.make("env-1");
137 const { client, getSnapshot } = createTestClient();137 const { client, getSnapshot } = createTestClient();
138 const syncSnapshot = vi.fn();138 const syncSnapshot = vi.fn();
139139
...@@ -168,7 +168,7 @@ describe("createEnvironmentConnection", () => {...@@ -168,7 +168,7 @@ describe("createEnvironmentConnection", () => {
168 });168 });
169169
170 it("rejects welcome/config identity drift", async () => {170 it("rejects welcome/config identity drift", async () => {
171 const environmentId = EnvironmentId.makeUnsafe("env-1");171 const environmentId = EnvironmentId.make("env-1");
172 const { client, emitWelcome } = createTestClient();172 const { client, emitWelcome } = createTestClient();
173173
174 const connection = createEnvironmentConnection({174 const connection = createEnvironmentConnection({
...@@ -189,7 +189,7 @@ describe("createEnvironmentConnection", () => {...@@ -189,7 +189,7 @@ describe("createEnvironmentConnection", () => {
189 applyTerminalEvent: vi.fn(),189 applyTerminalEvent: vi.fn(),
190 });190 });
191191
192 expect(() => emitWelcome(EnvironmentId.makeUnsafe("env-2"))).toThrow(192 expect(() => emitWelcome(EnvironmentId.make("env-2"))).toThrow(
193 "Environment connection env-1 changed identity to env-2 via server lifecycle welcome.",193 "Environment connection env-1 changed identity to env-2 via server lifecycle welcome.",
194 );194 );
195195
...@@ -197,7 +197,7 @@ describe("createEnvironmentConnection", () => {...@@ -197,7 +197,7 @@ describe("createEnvironmentConnection", () => {
197 });197 });
198198
199 it("rejects ensureBootstrapped when snapshot recovery fails", async () => {199 it("rejects ensureBootstrapped when snapshot recovery fails", async () => {
200 const environmentId = EnvironmentId.makeUnsafe("env-1");200 const environmentId = EnvironmentId.make("env-1");
201 const snapshotError = new Error("snapshot failed");201 const snapshotError = new Error("snapshot failed");
202 const { client } = createTestClient({202 const { client } = createTestClient({
203 getSnapshot: async () => {203 getSnapshot: async () => {
...@@ -229,7 +229,7 @@ describe("createEnvironmentConnection", () => {...@@ -229,7 +229,7 @@ describe("createEnvironmentConnection", () => {
229 });229 });
230230
231 it("retries replay recovery after transport disconnects during resubscribe", async () => {231 it("retries replay recovery after transport disconnects during resubscribe", async () => {
232 const environmentId = EnvironmentId.makeUnsafe("env-1");232 const environmentId = EnvironmentId.make("env-1");
233 let replayAttempts = 0;233 let replayAttempts = 0;
234 const applyEventBatch = vi.fn();234 const applyEventBatch = vi.fn();
235 const { client, replayEvents, triggerDomainResubscribe } = createTestClient({235 const { client, replayEvents, triggerDomainResubscribe } = createTestClient({
...@@ -287,7 +287,7 @@ describe("createEnvironmentConnection", () => {...@@ -287,7 +287,7 @@ describe("createEnvironmentConnection", () => {
287 await connection.dispose();287 await connection.dispose();
288 });288 });
289 it("swallows replay recovery failures triggered by resubscribe", async () => {289 it("swallows replay recovery failures triggered by resubscribe", async () => {
290 const environmentId = EnvironmentId.makeUnsafe("env-1");290 const environmentId = EnvironmentId.make("env-1");
291 const snapshotError = new Error("snapshot failed");291 const snapshotError = new Error("snapshot failed");
292 let snapshotCalls = 0;292 let snapshotCalls = 0;
293 const { client, triggerDomainResubscribe } = createTestClient({293 const { client, triggerDomainResubscribe } = createTestClient({
apps/web/src/environments/runtime/service.addSavedEnvironment.test.ts+2-2
...@@ -68,7 +68,7 @@ describe("addSavedEnvironment", () => {...@@ -68,7 +68,7 @@ describe("addSavedEnvironment", () => {
68 credential: "pairing-code",68 credential: "pairing-code",
69 });69 });
70 mockFetchRemoteEnvironmentDescriptor.mockResolvedValue({70 mockFetchRemoteEnvironmentDescriptor.mockResolvedValue({
71 environmentId: EnvironmentId.makeUnsafe("environment-1"),71 environmentId: EnvironmentId.make("environment-1"),
72 label: "Remote environment",72 label: "Remote environment",
73 });73 });
74 mockBootstrapRemoteBearerSession.mockResolvedValue({74 mockBootstrapRemoteBearerSession.mockResolvedValue({
...@@ -94,7 +94,7 @@ describe("addSavedEnvironment", () => {...@@ -94,7 +94,7 @@ describe("addSavedEnvironment", () => {
9494
95 expect(mockPersistSavedEnvironmentRecord).toHaveBeenCalledTimes(1);95 expect(mockPersistSavedEnvironmentRecord).toHaveBeenCalledTimes(1);
96 expect(mockWriteSavedEnvironmentBearerToken).toHaveBeenCalledWith(96 expect(mockWriteSavedEnvironmentBearerToken).toHaveBeenCalledWith(
97 EnvironmentId.makeUnsafe("environment-1"),97 EnvironmentId.make("environment-1"),
98 "bearer-token",98 "bearer-token",
99 );99 );
100 expect(mockSetSavedEnvironmentRegistry).toHaveBeenCalledWith([]);100 expect(mockSetSavedEnvironmentRegistry).toHaveBeenCalledWith([]);
apps/web/src/environments/runtime/service.ts+1-1
...@@ -281,7 +281,7 @@ function createEnvironmentConnectionHandlers() {...@@ -281,7 +281,7 @@ function createEnvironmentConnectionHandlers() {
281 reconcileSnapshotDerivedState();281 reconcileSnapshotDerivedState();
282 },282 },
283 applyTerminalEvent: (event: TerminalEvent, environmentId: EnvironmentId) => {283 applyTerminalEvent: (event: TerminalEvent, environmentId: EnvironmentId) => {
284 const threadRef = scopeThreadRef(environmentId, ThreadId.makeUnsafe(event.threadId));284 const threadRef = scopeThreadRef(environmentId, ThreadId.make(event.threadId));
285 const serverThread = selectThreadByRef(useStore.getState(), threadRef);285 const serverThread = selectThreadByRef(useStore.getState(), threadRef);
286 const hasDraftThread =286 const hasDraftThread =
287 useComposerDraftStore.getState().getDraftThreadByRef(threadRef) !== null;287 useComposerDraftStore.getState().getDraftThreadByRef(threadRef) !== null;
apps/web/src/historyBootstrap.test.ts+1-1
...@@ -3,7 +3,7 @@ import { describe, expect, it } from "vitest";...@@ -3,7 +3,7 @@ import { describe, expect, it } from "vitest";
33
4import { buildBootstrapInput } from "./historyBootstrap";4import { buildBootstrapInput } from "./historyBootstrap";
55
6const messageId = (value: string) => MessageId.makeUnsafe(value);6const messageId = (value: string) => MessageId.make(value);
77
8describe("buildBootstrapInput", () => {8describe("buildBootstrapInput", () => {
9 it("includes full transcript when under budget", () => {9 it("includes full transcript when under budget", () => {
apps/web/src/lib/contextWindow.test.ts+2-2
...@@ -5,12 +5,12 @@ import { deriveLatestContextWindowSnapshot, formatContextWindowTokens } from "./...@@ -5,12 +5,12 @@ import { deriveLatestContextWindowSnapshot, formatContextWindowTokens } from "./
55
6function makeActivity(id: string, kind: string, payload: unknown): OrchestrationThreadActivity {6function makeActivity(id: string, kind: string, payload: unknown): OrchestrationThreadActivity {
7 return {7 return {
8 id: EventId.makeUnsafe(id),8 id: EventId.make(id),
9 tone: "info",9 tone: "info",
10 kind,10 kind,
11 summary: kind,11 summary: kind,
12 payload,12 payload,
13 turnId: TurnId.makeUnsafe("turn-1"),13 turnId: TurnId.make("turn-1"),
14 createdAt: "2026-03-23T00:00:00.000Z",14 createdAt: "2026-03-23T00:00:00.000Z",
15 };15 };
16}16}
apps/web/src/lib/gitReactQuery.test.ts+2-2
...@@ -34,8 +34,8 @@ const BRANCH_SEARCH_RESULT: InfiniteData<GitListBranchesResult, number> = {...@@ -34,8 +34,8 @@ const BRANCH_SEARCH_RESULT: InfiniteData<GitListBranchesResult, number> = {
34 pages: [BRANCH_QUERY_RESULT],34 pages: [BRANCH_QUERY_RESULT],
35 pageParams: [0],35 pageParams: [0],
36};36};
37const ENVIRONMENT_A = EnvironmentId.makeUnsafe("environment-a");37const ENVIRONMENT_A = EnvironmentId.make("environment-a");
38const ENVIRONMENT_B = EnvironmentId.makeUnsafe("environment-b");38const ENVIRONMENT_B = EnvironmentId.make("environment-b");
3939
40describe("gitMutationKeys", () => {40describe("gitMutationKeys", () => {
41 it("scopes stacked action keys by cwd", () => {41 it("scopes stacked action keys by cwd", () => {
apps/web/src/lib/gitStatusState.test.ts+2-2
...@@ -34,8 +34,8 @@ function registerListener<T>(listeners: Set<(event: T) => void>, listener: (even...@@ -34,8 +34,8 @@ function registerListener<T>(listeners: Set<(event: T) => void>, listener: (even
34}34}
3535
36const gitStatusListeners = new Set<(event: GitStatusResult) => void>();36const gitStatusListeners = new Set<(event: GitStatusResult) => void>();
37const ENVIRONMENT_ID = EnvironmentId.makeUnsafe("environment-local");37const ENVIRONMENT_ID = EnvironmentId.make("environment-local");
38const OTHER_ENVIRONMENT_ID = EnvironmentId.makeUnsafe("environment-remote");38const OTHER_ENVIRONMENT_ID = EnvironmentId.make("environment-remote");
39const TARGET = { environmentId: ENVIRONMENT_ID, cwd: "/repo" } as const;39const TARGET = { environmentId: ENVIRONMENT_ID, cwd: "/repo" } as const;
40const FRESH_TARGET = { environmentId: ENVIRONMENT_ID, cwd: "/fresh" } as const;40const FRESH_TARGET = { environmentId: ENVIRONMENT_ID, cwd: "/fresh" } as const;
4141
apps/web/src/lib/providerReactQuery.test.ts+2-2
...@@ -4,8 +4,8 @@ import { afterEach, describe, expect, it, vi } from "vitest";...@@ -4,8 +4,8 @@ import { afterEach, describe, expect, it, vi } from "vitest";
4import { checkpointDiffQueryOptions, providerQueryKeys } from "./providerReactQuery";4import { checkpointDiffQueryOptions, providerQueryKeys } from "./providerReactQuery";
5import * as environmentApi from "../environmentApi";5import * as environmentApi from "../environmentApi";
66
7const threadId = ThreadId.makeUnsafe("thread-id");7const threadId = ThreadId.make("thread-id");
8const environmentId = EnvironmentId.makeUnsafe("environment-local");8const environmentId = EnvironmentId.make("environment-local");
99
10function mockNativeApi(input: {10function mockNativeApi(input: {
11 getTurnDiff: ReturnType<typeof vi.fn>;11 getTurnDiff: ReturnType<typeof vi.fn>;
apps/web/src/lib/terminalContext.test.ts+1-1
...@@ -25,7 +25,7 @@ import {...@@ -25,7 +25,7 @@ import {
25function makeContext(overrides?: Partial<TerminalContextDraft>): TerminalContextDraft {25function makeContext(overrides?: Partial<TerminalContextDraft>): TerminalContextDraft {
26 return {26 return {
27 id: "context-1",27 id: "context-1",
28 threadId: ThreadId.makeUnsafe("thread-1"),28 threadId: ThreadId.make("thread-1"),
29 terminalId: "default",29 terminalId: "default",
30 terminalLabel: "Terminal 1",30 terminalLabel: "Terminal 1",
31 lineStart: 12,31 lineStart: 12,
apps/web/src/lib/terminalStateCleanup.test.ts+1-1
...@@ -4,7 +4,7 @@ import { describe, expect, it } from "vitest";...@@ -4,7 +4,7 @@ import { describe, expect, it } from "vitest";
44
5import { collectActiveTerminalThreadIds } from "./terminalStateCleanup";5import { collectActiveTerminalThreadIds } from "./terminalStateCleanup";
66
7const threadId = (id: string): ThreadId => ThreadId.makeUnsafe(id);7const threadId = (id: string): ThreadId => ThreadId.make(id);
8const threadKey = (environmentId: string, id: string): string =>8const threadKey = (environmentId: string, id: string): string =>
9 scopedThreadKey(scopeThreadRef(environmentId as never, threadId(id)));9 scopedThreadKey(scopeThreadRef(environmentId as never, threadId(id)));
1010
apps/web/src/lib/utils.ts+5-5
...@@ -28,12 +28,12 @@ export function randomUUID(): string {...@@ -28,12 +28,12 @@ export function randomUUID(): string {
28 return Effect.runSync(Random.nextUUIDv4);28 return Effect.runSync(Random.nextUUIDv4);
29}29}
3030
31export const newCommandId = (): CommandId => CommandId.makeUnsafe(randomUUID());31export const newCommandId = (): CommandId => CommandId.make(randomUUID());
3232
33export const newProjectId = (): ProjectId => ProjectId.makeUnsafe(randomUUID());33export const newProjectId = (): ProjectId => ProjectId.make(randomUUID());
3434
35export const newThreadId = (): ThreadId => ThreadId.makeUnsafe(randomUUID());35export const newThreadId = (): ThreadId => ThreadId.make(randomUUID());
3636
37export const newDraftId = (): DraftId => DraftId.makeUnsafe(randomUUID());37export const newDraftId = (): DraftId => DraftId.make(randomUUID());
3838
39export const newMessageId = (): MessageId => MessageId.makeUnsafe(randomUUID());39export const newMessageId = (): MessageId => MessageId.make(randomUUID());
apps/web/src/localApi.test.ts+18-22
...@@ -104,10 +104,10 @@ vi.mock("./environments/runtime", () => ({...@@ -104,10 +104,10 @@ vi.mock("./environments/runtime", () => ({
104 httpBaseUrl: "http://localhost:3000",104 httpBaseUrl: "http://localhost:3000",
105 wsBaseUrl: "ws://localhost:3000",105 wsBaseUrl: "ws://localhost:3000",
106 },106 },
107 environmentId: EnvironmentId.makeUnsafe("environment-local"),107 environmentId: EnvironmentId.make("environment-local"),
108 },108 },
109 client: rpcClientMock,109 client: rpcClientMock,
110 environmentId: EnvironmentId.makeUnsafe("environment-local"),110 environmentId: EnvironmentId.make("environment-local"),
111 ensureBootstrapped: async () => undefined,111 ensureBootstrapped: async () => undefined,
112 reconnect: async () => undefined,112 reconnect: async () => undefined,
113 dispose: async () => undefined,113 dispose: async () => undefined,
...@@ -214,7 +214,7 @@ const defaultProviders: ReadonlyArray<ServerProvider> = [...@@ -214,7 +214,7 @@ const defaultProviders: ReadonlyArray<ServerProvider> = [
214];214];
215215
216const baseEnvironment = {216const baseEnvironment = {
217 environmentId: EnvironmentId.makeUnsafe("environment-local"),217 environmentId: EnvironmentId.make("environment-local"),
218 label: "Local environment",218 label: "Local environment",
219 platform: {219 platform: {
220 os: "darwin" as const,220 os: "darwin" as const,
...@@ -315,9 +315,9 @@ describe("wsApi", () => {...@@ -315,9 +315,9 @@ describe("wsApi", () => {
315315
316 const orchestrationEvent = {316 const orchestrationEvent = {
317 sequence: 1,317 sequence: 1,
318 eventId: EventId.makeUnsafe("event-1"),318 eventId: EventId.make("event-1"),
319 aggregateKind: "project",319 aggregateKind: "project",
320 aggregateId: ProjectId.makeUnsafe("project-1"),320 aggregateId: ProjectId.make("project-1"),
321 occurredAt: "2026-02-24T00:00:00.000Z",321 occurredAt: "2026-02-24T00:00:00.000Z",
322 commandId: null,322 commandId: null,
323 causationEventId: null,323 causationEventId: null,
...@@ -325,7 +325,7 @@ describe("wsApi", () => {...@@ -325,7 +325,7 @@ describe("wsApi", () => {
325 metadata: {},325 metadata: {},
326 type: "project.created",326 type: "project.created",
327 payload: {327 payload: {
328 projectId: ProjectId.makeUnsafe("project-1"),328 projectId: ProjectId.make("project-1"),
329 title: "Project",329 title: "Project",
330 workspaceRoot: "/tmp/workspace",330 workspaceRoot: "/tmp/workspace",
331 defaultModelSelection: {331 defaultModelSelection: {
...@@ -390,8 +390,8 @@ describe("wsApi", () => {...@@ -390,8 +390,8 @@ describe("wsApi", () => {
390 const api = createEnvironmentApi(rpcClientMock as never);390 const api = createEnvironmentApi(rpcClientMock as never);
391 const command = {391 const command = {
392 type: "project.create",392 type: "project.create",
393 commandId: CommandId.makeUnsafe("cmd-1"),393 commandId: CommandId.make("cmd-1"),
394 projectId: ProjectId.makeUnsafe("project-1"),394 projectId: ProjectId.make("project-1"),
395 title: "Project",395 title: "Project",
396 workspaceRoot: "/tmp/project",396 workspaceRoot: "/tmp/project",
397 defaultModelSelection: {397 defaultModelSelection: {
...@@ -429,7 +429,7 @@ describe("wsApi", () => {...@@ -429,7 +429,7 @@ describe("wsApi", () => {
429429
430 const api = createEnvironmentApi(rpcClientMock as never);430 const api = createEnvironmentApi(rpcClientMock as never);
431 await api.orchestration.getFullThreadDiff({431 await api.orchestration.getFullThreadDiff({
432 threadId: ThreadId.makeUnsafe("thread-1"),432 threadId: ThreadId.make("thread-1"),
433 toTurnCount: 1,433 toTurnCount: 1,
434 });434 });
435435
...@@ -535,14 +535,12 @@ describe("wsApi", () => {...@@ -535,14 +535,12 @@ describe("wsApi", () => {
535 });535 });
536 await api.persistence.getSavedEnvironmentRegistry();536 await api.persistence.getSavedEnvironmentRegistry();
537 await api.persistence.setSavedEnvironmentRegistry([]);537 await api.persistence.setSavedEnvironmentRegistry([]);
538 await api.persistence.getSavedEnvironmentSecret(EnvironmentId.makeUnsafe("environment-local"));538 await api.persistence.getSavedEnvironmentSecret(EnvironmentId.make("environment-local"));
539 await api.persistence.setSavedEnvironmentSecret(539 await api.persistence.setSavedEnvironmentSecret(
540 EnvironmentId.makeUnsafe("environment-local"),540 EnvironmentId.make("environment-local"),
541 "bearer-token",541 "bearer-token",
542 );542 );
543 await api.persistence.removeSavedEnvironmentSecret(543 await api.persistence.removeSavedEnvironmentSecret(EnvironmentId.make("environment-local"));
544 EnvironmentId.makeUnsafe("environment-local"),
545 );
546544
547 expect(getClientSettings).toHaveBeenCalledWith();545 expect(getClientSettings).toHaveBeenCalledWith();
548 expect(setClientSettings).toHaveBeenCalledWith({546 expect(setClientSettings).toHaveBeenCalledWith({
...@@ -574,7 +572,7 @@ describe("wsApi", () => {...@@ -574,7 +572,7 @@ describe("wsApi", () => {
574 });572 });
575 await api.persistence.setSavedEnvironmentRegistry([573 await api.persistence.setSavedEnvironmentRegistry([
576 {574 {
577 environmentId: EnvironmentId.makeUnsafe("environment-local"),575 environmentId: EnvironmentId.make("environment-local"),
578 label: "Primary",576 label: "Primary",
579 httpBaseUrl: "http://localhost:3000",577 httpBaseUrl: "http://localhost:3000",
580 wsBaseUrl: "ws://localhost:3000",578 wsBaseUrl: "ws://localhost:3000",
...@@ -583,7 +581,7 @@ describe("wsApi", () => {...@@ -583,7 +581,7 @@ describe("wsApi", () => {
583 },581 },
584 ]);582 ]);
585 await api.persistence.setSavedEnvironmentSecret(583 await api.persistence.setSavedEnvironmentSecret(
586 EnvironmentId.makeUnsafe("environment-local"),584 EnvironmentId.make("environment-local"),
587 "bearer-token",585 "bearer-token",
588 );586 );
589587
...@@ -597,7 +595,7 @@ describe("wsApi", () => {...@@ -597,7 +595,7 @@ describe("wsApi", () => {
597 });595 });
598 await expect(api.persistence.getSavedEnvironmentRegistry()).resolves.toEqual([596 await expect(api.persistence.getSavedEnvironmentRegistry()).resolves.toEqual([
599 {597 {
600 environmentId: EnvironmentId.makeUnsafe("environment-local"),598 environmentId: EnvironmentId.make("environment-local"),
601 label: "Primary",599 label: "Primary",
602 httpBaseUrl: "http://localhost:3000",600 httpBaseUrl: "http://localhost:3000",
603 wsBaseUrl: "ws://localhost:3000",601 wsBaseUrl: "ws://localhost:3000",
...@@ -606,15 +604,13 @@ describe("wsApi", () => {...@@ -606,15 +604,13 @@ describe("wsApi", () => {
606 },604 },
607 ]);605 ]);
608 await expect(606 await expect(
609 api.persistence.getSavedEnvironmentSecret(EnvironmentId.makeUnsafe("environment-local")),607 api.persistence.getSavedEnvironmentSecret(EnvironmentId.make("environment-local")),
610 ).resolves.toBe("bearer-token");608 ).resolves.toBe("bearer-token");
611609
612 await api.persistence.removeSavedEnvironmentSecret(610 await api.persistence.removeSavedEnvironmentSecret(EnvironmentId.make("environment-local"));
613 EnvironmentId.makeUnsafe("environment-local"),
614 );
615611
616 await expect(612 await expect(
617 api.persistence.getSavedEnvironmentSecret(EnvironmentId.makeUnsafe("environment-local")),613 api.persistence.getSavedEnvironmentSecret(EnvironmentId.make("environment-local")),
618 ).resolves.toBeNull();614 ).resolves.toBeNull();
619 });615 });
620});616});
apps/web/src/orchestrationEventEffects.test.ts+12-12
...@@ -19,14 +19,14 @@ function makeEvent<T extends OrchestrationEvent["type"]>(...@@ -19,14 +19,14 @@ function makeEvent<T extends OrchestrationEvent["type"]>(
19 const sequence = overrides.sequence ?? 1;19 const sequence = overrides.sequence ?? 1;
20 return {20 return {
21 sequence,21 sequence,
22 eventId: EventId.makeUnsafe(`event-${sequence}`),22 eventId: EventId.make(`event-${sequence}`),
23 aggregateKind: "thread",23 aggregateKind: "thread",
24 aggregateId:24 aggregateId:
25 "threadId" in payload25 "threadId" in payload
26 ? payload.threadId26 ? payload.threadId
27 : "projectId" in payload27 : "projectId" in payload
28 ? payload.projectId28 ? payload.projectId
29 : ProjectId.makeUnsafe("project-1"),29 : ProjectId.make("project-1"),
30 occurredAt: "2026-02-27T00:00:00.000Z",30 occurredAt: "2026-02-27T00:00:00.000Z",
31 commandId: null,31 commandId: null,
32 causationEventId: null,32 causationEventId: null,
...@@ -40,14 +40,14 @@ function makeEvent<T extends OrchestrationEvent["type"]>(...@@ -40,14 +40,14 @@ function makeEvent<T extends OrchestrationEvent["type"]>(
4040
41describe("deriveOrchestrationBatchEffects", () => {41describe("deriveOrchestrationBatchEffects", () => {
42 it("targets draft promotion and terminal cleanup from thread lifecycle events", () => {42 it("targets draft promotion and terminal cleanup from thread lifecycle events", () => {
43 const createdThreadId = ThreadId.makeUnsafe("thread-created");43 const createdThreadId = ThreadId.make("thread-created");
44 const deletedThreadId = ThreadId.makeUnsafe("thread-deleted");44 const deletedThreadId = ThreadId.make("thread-deleted");
45 const archivedThreadId = ThreadId.makeUnsafe("thread-archived");45 const archivedThreadId = ThreadId.make("thread-archived");
4646
47 const effects = deriveOrchestrationBatchEffects([47 const effects = deriveOrchestrationBatchEffects([
48 makeEvent("thread.created", {48 makeEvent("thread.created", {
49 threadId: createdThreadId,49 threadId: createdThreadId,
50 projectId: ProjectId.makeUnsafe("project-1"),50 projectId: ProjectId.make("project-1"),
51 title: "Created thread",51 title: "Created thread",
52 modelSelection: { provider: "codex", model: "gpt-5-codex" },52 modelSelection: { provider: "codex", model: "gpt-5-codex" },
53 runtimeMode: "full-access",53 runtimeMode: "full-access",
...@@ -75,7 +75,7 @@ describe("deriveOrchestrationBatchEffects", () => {...@@ -75,7 +75,7 @@ describe("deriveOrchestrationBatchEffects", () => {
75 });75 });
7676
77 it("keeps only the final lifecycle outcome for a thread within one batch", () => {77 it("keeps only the final lifecycle outcome for a thread within one batch", () => {
78 const threadId = ThreadId.makeUnsafe("thread-1");78 const threadId = ThreadId.make("thread-1");
7979
80 const effects = deriveOrchestrationBatchEffects([80 const effects = deriveOrchestrationBatchEffects([
81 makeEvent("thread.deleted", {81 makeEvent("thread.deleted", {
...@@ -84,7 +84,7 @@ describe("deriveOrchestrationBatchEffects", () => {...@@ -84,7 +84,7 @@ describe("deriveOrchestrationBatchEffects", () => {
84 }),84 }),
85 makeEvent("thread.created", {85 makeEvent("thread.created", {
86 threadId,86 threadId,
87 projectId: ProjectId.makeUnsafe("project-1"),87 projectId: ProjectId.make("project-1"),
88 title: "Recreated thread",88 title: "Recreated thread",
89 modelSelection: { provider: "codex", model: "gpt-5-codex" },89 modelSelection: { provider: "codex", model: "gpt-5-codex" },
90 runtimeMode: "full-access",90 runtimeMode: "full-access",
...@@ -96,12 +96,12 @@ describe("deriveOrchestrationBatchEffects", () => {...@@ -96,12 +96,12 @@ describe("deriveOrchestrationBatchEffects", () => {
96 }),96 }),
97 makeEvent("thread.turn-diff-completed", {97 makeEvent("thread.turn-diff-completed", {
98 threadId,98 threadId,
99 turnId: TurnId.makeUnsafe("turn-1"),99 turnId: TurnId.make("turn-1"),
100 checkpointTurnCount: 1,100 checkpointTurnCount: 1,
101 checkpointRef: CheckpointRef.makeUnsafe("checkpoint-1"),101 checkpointRef: CheckpointRef.make("checkpoint-1"),
102 status: "ready",102 status: "ready",
103 files: [],103 files: [],
104 assistantMessageId: MessageId.makeUnsafe("assistant-1"),104 assistantMessageId: MessageId.make("assistant-1"),
105 completedAt: "2026-02-27T00:00:03.000Z",105 completedAt: "2026-02-27T00:00:03.000Z",
106 }),106 }),
107 ]);107 ]);
...@@ -113,7 +113,7 @@ describe("deriveOrchestrationBatchEffects", () => {...@@ -113,7 +113,7 @@ describe("deriveOrchestrationBatchEffects", () => {
113 });113 });
114114
115 it("does not retain archive cleanup when a thread is unarchived later in the same batch", () => {115 it("does not retain archive cleanup when a thread is unarchived later in the same batch", () => {
116 const threadId = ThreadId.makeUnsafe("thread-1");116 const threadId = ThreadId.make("thread-1");
117117
118 const effects = deriveOrchestrationBatchEffects([118 const effects = deriveOrchestrationBatchEffects([
119 makeEvent("thread.archived", {119 makeEvent("thread.archived", {
apps/web/src/projectScripts.ts+1-1
...@@ -22,7 +22,7 @@ function normalizeScriptId(value: string): string {...@@ -22,7 +22,7 @@ function normalizeScriptId(value: string): string {
22}22}
2323
24export const commandForProjectScript = (scriptId: string): KeybindingCommand =>24export const commandForProjectScript = (scriptId: string): KeybindingCommand =>
25 SCRIPT_RUN_COMMAND_PATTERN.makeUnsafe(`script.${scriptId}.run`);25 SCRIPT_RUN_COMMAND_PATTERN.make(`script.${scriptId}.run`);
2626
27export function projectScriptIdFromCommand(command: string): string | null {27export function projectScriptIdFromCommand(command: string): string | null {
28 const trimmed = command.trim();28 const trimmed = command.trim();
apps/web/src/routes/_chat.draft.$draftId.tsx+1-1
...@@ -11,7 +11,7 @@ import { buildThreadRouteParams } from "../threadRoutes";...@@ -11,7 +11,7 @@ import { buildThreadRouteParams } from "../threadRoutes";
11function DraftChatThreadRouteView() {11function DraftChatThreadRouteView() {
12 const navigate = useNavigate();12 const navigate = useNavigate();
13 const { draftId: rawDraftId } = Route.useParams();13 const { draftId: rawDraftId } = Route.useParams();
14 const draftId = DraftId.makeUnsafe(rawDraftId);14 const draftId = DraftId.make(rawDraftId);
15 const draftSession = useComposerDraftStore((store) => store.getDraftSession(draftId));15 const draftSession = useComposerDraftStore((store) => store.getDraftSession(draftId));
16 const serverThread = useStore(16 const serverThread = useStore(
17 useMemo(17 useMemo(
apps/web/src/rpc/protocol.ts+4-4
...@@ -154,8 +154,8 @@ export function createWsRpcProtocolLayer(...@@ -154,8 +154,8 @@ export function createWsRpcProtocolLayer(
154 }),154 }),
155 (protocol) => ({155 (protocol) => ({
156 ...protocol,156 ...protocol,
157 run: (writeResponse) =>157 run: (clientId, writeResponse) =>
158 protocol.run((response) => {158 protocol.run(clientId, (response) => {
159 if (response._tag === "Chunk" || response._tag === "Exit") {159 if (response._tag === "Chunk" || response._tag === "Exit") {
160 acknowledgeRpcRequest(response.requestId);160 acknowledgeRpcRequest(response.requestId);
161 } else if (response._tag === "ClientProtocolError" || response._tag === "Defect") {161 } else if (response._tag === "ClientProtocolError" || response._tag === "Defect") {
...@@ -163,11 +163,11 @@ export function createWsRpcProtocolLayer(...@@ -163,11 +163,11 @@ export function createWsRpcProtocolLayer(
163 }163 }
164 return writeResponse(response);164 return writeResponse(response);
165 }),165 }),
166 send: (request, transferables) => {166 send: (clientId, request, transferables) => {
167 if (request._tag === "Request") {167 if (request._tag === "Request") {
168 trackRpcRequestSent(request.id, request.tag);168 trackRpcRequestSent(request.id, request.tag);
169 }169 }
170 return protocol.send(request, transferables);170 return protocol.send(clientId, request, transferables);
171 },171 },
172 }),172 }),
173 ),173 ),
apps/web/src/rpc/serverState.test.ts+7-7
...@@ -52,7 +52,7 @@ const defaultProviders: ReadonlyArray<ServerProvider> = [...@@ -52,7 +52,7 @@ const defaultProviders: ReadonlyArray<ServerProvider> = [
52];52];
5353
54const baseEnvironment = {54const baseEnvironment = {
55 environmentId: EnvironmentId.makeUnsafe("environment-local"),55 environmentId: EnvironmentId.make("environment-local"),
56 label: "Local environment",56 label: "Local environment",
57 platform: {57 platform: {
58 os: "darwin" as const,58 os: "darwin" as const,
...@@ -217,8 +217,8 @@ describe("serverState", () => {...@@ -217,8 +217,8 @@ describe("serverState", () => {
217 environment: baseEnvironment,217 environment: baseEnvironment,
218 cwd: "/tmp/workspace",218 cwd: "/tmp/workspace",
219 projectName: "t3-code",219 projectName: "t3-code",
220 bootstrapProjectId: ProjectId.makeUnsafe("project-1"),220 bootstrapProjectId: ProjectId.make("project-1"),
221 bootstrapThreadId: ThreadId.makeUnsafe("thread-1"),221 bootstrapThreadId: ThreadId.make("thread-1"),
222 },222 },
223 });223 });
224224
...@@ -226,8 +226,8 @@ describe("serverState", () => {...@@ -226,8 +226,8 @@ describe("serverState", () => {
226 environment: baseEnvironment,226 environment: baseEnvironment,
227 cwd: "/tmp/workspace",227 cwd: "/tmp/workspace",
228 projectName: "t3-code",228 projectName: "t3-code",
229 bootstrapProjectId: ProjectId.makeUnsafe("project-1"),229 bootstrapProjectId: ProjectId.make("project-1"),
230 bootstrapThreadId: ThreadId.makeUnsafe("thread-1"),230 bootstrapThreadId: ThreadId.make("thread-1"),
231 });231 });
232232
233 const lateListener = vi.fn();233 const lateListener = vi.fn();
...@@ -236,8 +236,8 @@ describe("serverState", () => {...@@ -236,8 +236,8 @@ describe("serverState", () => {
236 environment: baseEnvironment,236 environment: baseEnvironment,
237 cwd: "/tmp/workspace",237 cwd: "/tmp/workspace",
238 projectName: "t3-code",238 projectName: "t3-code",
239 bootstrapProjectId: ProjectId.makeUnsafe("project-1"),239 bootstrapProjectId: ProjectId.make("project-1"),
240 bootstrapThreadId: ThreadId.makeUnsafe("thread-1"),240 bootstrapThreadId: ThreadId.make("thread-1"),
241 });241 });
242242
243 unsubscribeLate();243 unsubscribeLate();
apps/web/src/session-logic.test.ts+43-43
...@@ -35,13 +35,13 @@ function makeActivity(overrides: {...@@ -35,13 +35,13 @@ function makeActivity(overrides: {
35}): OrchestrationThreadActivity {35}): OrchestrationThreadActivity {
36 const payload = overrides.payload ?? {};36 const payload = overrides.payload ?? {};
37 return {37 return {
38 id: EventId.makeUnsafe(overrides.id ?? crypto.randomUUID()),38 id: EventId.make(overrides.id ?? crypto.randomUUID()),
39 createdAt: overrides.createdAt ?? "2026-02-23T00:00:00.000Z",39 createdAt: overrides.createdAt ?? "2026-02-23T00:00:00.000Z",
40 kind: overrides.kind ?? "tool.started",40 kind: overrides.kind ?? "tool.started",
41 summary: overrides.summary ?? "Tool call",41 summary: overrides.summary ?? "Tool call",
42 tone: overrides.tone ?? "tool",42 tone: overrides.tone ?? "tool",
43 payload,43 payload,
44 turnId: overrides.turnId ? TurnId.makeUnsafe(overrides.turnId) : null,44 turnId: overrides.turnId ? TurnId.make(overrides.turnId) : null,
45 ...(overrides.sequence !== undefined ? { sequence: overrides.sequence } : {}),45 ...(overrides.sequence !== undefined ? { sequence: overrides.sequence } : {}),
46 };46 };
47}47}
...@@ -336,7 +336,7 @@ describe("deriveActivePlanState", () => {...@@ -336,7 +336,7 @@ describe("deriveActivePlanState", () => {
336 }),336 }),
337 ];337 ];
338338
339 expect(deriveActivePlanState(activities, TurnId.makeUnsafe("turn-1"))).toEqual({339 expect(deriveActivePlanState(activities, TurnId.make("turn-1"))).toEqual({
340 createdAt: "2026-02-23T00:00:02.000Z",340 createdAt: "2026-02-23T00:00:02.000Z",
341 turnId: "turn-1",341 turnId: "turn-1",
342 explanation: "Refined plan",342 explanation: "Refined plan",
...@@ -352,7 +352,7 @@ describe("findLatestProposedPlan", () => {...@@ -352,7 +352,7 @@ describe("findLatestProposedPlan", () => {
352 [352 [
353 {353 {
354 id: "plan:thread-1:turn:turn-1",354 id: "plan:thread-1:turn:turn-1",
355 turnId: TurnId.makeUnsafe("turn-1"),355 turnId: TurnId.make("turn-1"),
356 planMarkdown: "# Older",356 planMarkdown: "# Older",
357 implementedAt: null,357 implementedAt: null,
358 implementationThreadId: null,358 implementationThreadId: null,
...@@ -361,7 +361,7 @@ describe("findLatestProposedPlan", () => {...@@ -361,7 +361,7 @@ describe("findLatestProposedPlan", () => {
361 },361 },
362 {362 {
363 id: "plan:thread-1:turn:turn-1",363 id: "plan:thread-1:turn:turn-1",
364 turnId: TurnId.makeUnsafe("turn-1"),364 turnId: TurnId.make("turn-1"),
365 planMarkdown: "# Latest",365 planMarkdown: "# Latest",
366 implementedAt: null,366 implementedAt: null,
367 implementationThreadId: null,367 implementationThreadId: null,
...@@ -370,7 +370,7 @@ describe("findLatestProposedPlan", () => {...@@ -370,7 +370,7 @@ describe("findLatestProposedPlan", () => {
370 },370 },
371 {371 {
372 id: "plan:thread-1:turn:turn-2",372 id: "plan:thread-1:turn:turn-2",
373 turnId: TurnId.makeUnsafe("turn-2"),373 turnId: TurnId.make("turn-2"),
374 planMarkdown: "# Different turn",374 planMarkdown: "# Different turn",
375 implementedAt: null,375 implementedAt: null,
376 implementationThreadId: null,376 implementationThreadId: null,
...@@ -378,7 +378,7 @@ describe("findLatestProposedPlan", () => {...@@ -378,7 +378,7 @@ describe("findLatestProposedPlan", () => {
378 updatedAt: "2026-02-23T00:00:03.000Z",378 updatedAt: "2026-02-23T00:00:03.000Z",
379 },379 },
380 ],380 ],
381 TurnId.makeUnsafe("turn-1"),381 TurnId.make("turn-1"),
382 ),382 ),
383 ).toEqual({383 ).toEqual({
384 id: "plan:thread-1:turn:turn-1",384 id: "plan:thread-1:turn:turn-1",
...@@ -396,7 +396,7 @@ describe("findLatestProposedPlan", () => {...@@ -396,7 +396,7 @@ describe("findLatestProposedPlan", () => {
396 [396 [
397 {397 {
398 id: "plan:thread-1:turn:turn-1",398 id: "plan:thread-1:turn:turn-1",
399 turnId: TurnId.makeUnsafe("turn-1"),399 turnId: TurnId.make("turn-1"),
400 planMarkdown: "# First",400 planMarkdown: "# First",
401 implementedAt: null,401 implementedAt: null,
402 implementationThreadId: null,402 implementationThreadId: null,
...@@ -405,7 +405,7 @@ describe("findLatestProposedPlan", () => {...@@ -405,7 +405,7 @@ describe("findLatestProposedPlan", () => {
405 },405 },
406 {406 {
407 id: "plan:thread-1:turn:turn-2",407 id: "plan:thread-1:turn:turn-2",
408 turnId: TurnId.makeUnsafe("turn-2"),408 turnId: TurnId.make("turn-2"),
409 planMarkdown: "# Latest",409 planMarkdown: "# Latest",
410 implementedAt: null,410 implementedAt: null,
411 implementationThreadId: null,411 implementationThreadId: null,
...@@ -425,7 +425,7 @@ describe("hasActionableProposedPlan", () => {...@@ -425,7 +425,7 @@ describe("hasActionableProposedPlan", () => {
425 expect(425 expect(
426 hasActionableProposedPlan({426 hasActionableProposedPlan({
427 id: "plan-1",427 id: "plan-1",
428 turnId: TurnId.makeUnsafe("turn-1"),428 turnId: TurnId.make("turn-1"),
429 planMarkdown: "# Plan",429 planMarkdown: "# Plan",
430 implementedAt: null,430 implementedAt: null,
431 implementationThreadId: null,431 implementationThreadId: null,
...@@ -439,10 +439,10 @@ describe("hasActionableProposedPlan", () => {...@@ -439,10 +439,10 @@ describe("hasActionableProposedPlan", () => {
439 expect(439 expect(
440 hasActionableProposedPlan({440 hasActionableProposedPlan({
441 id: "plan-1",441 id: "plan-1",
442 turnId: TurnId.makeUnsafe("turn-1"),442 turnId: TurnId.make("turn-1"),
443 planMarkdown: "# Plan",443 planMarkdown: "# Plan",
444 implementedAt: "2026-02-23T00:00:02.000Z",444 implementedAt: "2026-02-23T00:00:02.000Z",
445 implementationThreadId: ThreadId.makeUnsafe("thread-implement"),445 implementationThreadId: ThreadId.make("thread-implement"),
446 createdAt: "2026-02-23T00:00:00.000Z",446 createdAt: "2026-02-23T00:00:00.000Z",
447 updatedAt: "2026-02-23T00:00:02.000Z",447 updatedAt: "2026-02-23T00:00:02.000Z",
448 }),448 }),
...@@ -456,25 +456,25 @@ describe("findSidebarProposedPlan", () => {...@@ -456,25 +456,25 @@ describe("findSidebarProposedPlan", () => {
456 findSidebarProposedPlan({456 findSidebarProposedPlan({
457 threads: [457 threads: [
458 {458 {
459 id: ThreadId.makeUnsafe("thread-1"),459 id: ThreadId.make("thread-1"),
460 proposedPlans: [460 proposedPlans: [
461 {461 {
462 id: "plan-1",462 id: "plan-1",
463 turnId: TurnId.makeUnsafe("turn-plan"),463 turnId: TurnId.make("turn-plan"),
464 planMarkdown: "# Source plan",464 planMarkdown: "# Source plan",
465 implementedAt: "2026-02-23T00:00:03.000Z",465 implementedAt: "2026-02-23T00:00:03.000Z",
466 implementationThreadId: ThreadId.makeUnsafe("thread-2"),466 implementationThreadId: ThreadId.make("thread-2"),
467 createdAt: "2026-02-23T00:00:01.000Z",467 createdAt: "2026-02-23T00:00:01.000Z",
468 updatedAt: "2026-02-23T00:00:02.000Z",468 updatedAt: "2026-02-23T00:00:02.000Z",
469 },469 },
470 ],470 ],
471 },471 },
472 {472 {
473 id: ThreadId.makeUnsafe("thread-2"),473 id: ThreadId.make("thread-2"),
474 proposedPlans: [474 proposedPlans: [
475 {475 {
476 id: "plan-2",476 id: "plan-2",
477 turnId: TurnId.makeUnsafe("turn-other"),477 turnId: TurnId.make("turn-other"),
478 planMarkdown: "# Latest elsewhere",478 planMarkdown: "# Latest elsewhere",
479 implementedAt: null,479 implementedAt: null,
480 implementationThreadId: null,480 implementationThreadId: null,
...@@ -485,14 +485,14 @@ describe("findSidebarProposedPlan", () => {...@@ -485,14 +485,14 @@ describe("findSidebarProposedPlan", () => {
485 },485 },
486 ],486 ],
487 latestTurn: {487 latestTurn: {
488 turnId: TurnId.makeUnsafe("turn-implementation"),488 turnId: TurnId.make("turn-implementation"),
489 sourceProposedPlan: {489 sourceProposedPlan: {
490 threadId: ThreadId.makeUnsafe("thread-1"),490 threadId: ThreadId.make("thread-1"),
491 planId: "plan-1",491 planId: "plan-1",
492 },492 },
493 },493 },
494 latestTurnSettled: false,494 latestTurnSettled: false,
495 threadId: ThreadId.makeUnsafe("thread-1"),495 threadId: ThreadId.make("thread-1"),
496 }),496 }),
497 ).toEqual({497 ).toEqual({
498 id: "plan-1",498 id: "plan-1",
...@@ -510,11 +510,11 @@ describe("findSidebarProposedPlan", () => {...@@ -510,11 +510,11 @@ describe("findSidebarProposedPlan", () => {
510 findSidebarProposedPlan({510 findSidebarProposedPlan({
511 threads: [511 threads: [
512 {512 {
513 id: ThreadId.makeUnsafe("thread-1"),513 id: ThreadId.make("thread-1"),
514 proposedPlans: [514 proposedPlans: [
515 {515 {
516 id: "plan-1",516 id: "plan-1",
517 turnId: TurnId.makeUnsafe("turn-plan"),517 turnId: TurnId.make("turn-plan"),
518 planMarkdown: "# Older",518 planMarkdown: "# Older",
519 implementedAt: null,519 implementedAt: null,
520 implementationThreadId: null,520 implementationThreadId: null,
...@@ -523,7 +523,7 @@ describe("findSidebarProposedPlan", () => {...@@ -523,7 +523,7 @@ describe("findSidebarProposedPlan", () => {
523 },523 },
524 {524 {
525 id: "plan-2",525 id: "plan-2",
526 turnId: TurnId.makeUnsafe("turn-latest"),526 turnId: TurnId.make("turn-latest"),
527 planMarkdown: "# Latest",527 planMarkdown: "# Latest",
528 implementedAt: null,528 implementedAt: null,
529 implementationThreadId: null,529 implementationThreadId: null,
...@@ -534,14 +534,14 @@ describe("findSidebarProposedPlan", () => {...@@ -534,14 +534,14 @@ describe("findSidebarProposedPlan", () => {
534 },534 },
535 ],535 ],
536 latestTurn: {536 latestTurn: {
537 turnId: TurnId.makeUnsafe("turn-implementation"),537 turnId: TurnId.make("turn-implementation"),
538 sourceProposedPlan: {538 sourceProposedPlan: {
539 threadId: ThreadId.makeUnsafe("thread-1"),539 threadId: ThreadId.make("thread-1"),
540 planId: "plan-1",540 planId: "plan-1",
541 },541 },
542 },542 },
543 latestTurnSettled: true,543 latestTurnSettled: true,
544 threadId: ThreadId.makeUnsafe("thread-1"),544 threadId: ThreadId.make("thread-1"),
545 })?.planMarkdown,545 })?.planMarkdown,
546 ).toBe("# Latest");546 ).toBe("# Latest");
547 });547 });
...@@ -609,7 +609,7 @@ describe("deriveWorkLogEntries", () => {...@@ -609,7 +609,7 @@ describe("deriveWorkLogEntries", () => {
609 makeActivity({ id: "no-turn", summary: "Checkpoint captured", tone: "info" }),609 makeActivity({ id: "no-turn", summary: "Checkpoint captured", tone: "info" }),
610 ];610 ];
611611
612 const entries = deriveWorkLogEntries(activities, TurnId.makeUnsafe("turn-2"));612 const entries = deriveWorkLogEntries(activities, TurnId.make("turn-2"));
613 expect(entries.map((entry) => entry.id)).toEqual(["turn-2"]);613 expect(entries.map((entry) => entry.id)).toEqual(["turn-2"]);
614 });614 });
615615
...@@ -1022,7 +1022,7 @@ describe("deriveTimelineEntries", () => {...@@ -1022,7 +1022,7 @@ describe("deriveTimelineEntries", () => {
1022 const entries = deriveTimelineEntries(1022 const entries = deriveTimelineEntries(
1023 [1023 [
1024 {1024 {
1025 id: MessageId.makeUnsafe("message-1"),1025 id: MessageId.make("message-1"),
1026 role: "assistant",1026 role: "assistant",
1027 text: "hello",1027 text: "hello",
1028 createdAt: "2026-02-23T00:00:01.000Z",1028 createdAt: "2026-02-23T00:00:01.000Z",
...@@ -1032,7 +1032,7 @@ describe("deriveTimelineEntries", () => {...@@ -1032,7 +1032,7 @@ describe("deriveTimelineEntries", () => {
1032 [1032 [
1033 {1033 {
1034 id: "plan:thread-1:turn:turn-1",1034 id: "plan:thread-1:turn:turn-1",
1035 turnId: TurnId.makeUnsafe("turn-1"),1035 turnId: TurnId.make("turn-1"),
1036 planMarkdown: "# Ship it",1036 planMarkdown: "# Ship it",
1037 implementedAt: null,1037 implementedAt: null,
1038 implementationThreadId: null,1038 implementationThreadId: null,
...@@ -1065,14 +1065,14 @@ describe("deriveTimelineEntries", () => {...@@ -1065,14 +1065,14 @@ describe("deriveTimelineEntries", () => {
1065 const entries = deriveTimelineEntries(1065 const entries = deriveTimelineEntries(
1066 [1066 [
1067 {1067 {
1068 id: MessageId.makeUnsafe("assistant-earlier"),1068 id: MessageId.make("assistant-earlier"),
1069 role: "assistant",1069 role: "assistant",
1070 text: "progress update",1070 text: "progress update",
1071 createdAt: "2026-02-23T00:00:01.000Z",1071 createdAt: "2026-02-23T00:00:01.000Z",
1072 streaming: false,1072 streaming: false,
1073 },1073 },
1074 {1074 {
1075 id: MessageId.makeUnsafe("assistant-final"),1075 id: MessageId.make("assistant-final"),
1076 role: "assistant",1076 role: "assistant",
1077 text: "final answer",1077 text: "final answer",
1078 createdAt: "2026-02-23T00:00:01.000Z",1078 createdAt: "2026-02-23T00:00:01.000Z",
...@@ -1085,7 +1085,7 @@ describe("deriveTimelineEntries", () => {...@@ -1085,7 +1085,7 @@ describe("deriveTimelineEntries", () => {
10851085
1086 expect(1086 expect(
1087 deriveCompletionDividerBeforeEntryId(entries, {1087 deriveCompletionDividerBeforeEntryId(entries, {
1088 assistantMessageId: MessageId.makeUnsafe("assistant-final"),1088 assistantMessageId: MessageId.make("assistant-final"),
1089 startedAt: "2026-02-23T00:00:00.000Z",1089 startedAt: "2026-02-23T00:00:00.000Z",
1090 completedAt: "2026-02-23T00:00:02.000Z",1090 completedAt: "2026-02-23T00:00:02.000Z",
1091 }),1091 }),
...@@ -1112,7 +1112,7 @@ describe("deriveWorkLogEntries context window handling", () => {...@@ -1112,7 +1112,7 @@ describe("deriveWorkLogEntries context window handling", () => {
1112 tone: "tool",1112 tone: "tool",
1113 }),1113 }),
1114 ],1114 ],
1115 TurnId.makeUnsafe("turn-1"),1115 TurnId.make("turn-1"),
1116 );1116 );
11171117
1118 expect(entries).toHaveLength(1);1118 expect(entries).toHaveLength(1);
...@@ -1130,7 +1130,7 @@ describe("deriveWorkLogEntries context window handling", () => {...@@ -1130,7 +1130,7 @@ describe("deriveWorkLogEntries context window handling", () => {
1130 tone: "info",1130 tone: "info",
1131 }),1131 }),
1132 ],1132 ],
1133 TurnId.makeUnsafe("turn-1"),1133 TurnId.make("turn-1"),
1134 );1134 );
11351135
1136 expect(entries).toHaveLength(1);1136 expect(entries).toHaveLength(1);
...@@ -1154,14 +1154,14 @@ describe("hasToolActivityForTurn", () => {...@@ -1154,14 +1154,14 @@ describe("hasToolActivityForTurn", () => {
1154 makeActivity({ id: "info-1", turnId: "turn-2", kind: "turn.completed", tone: "info" }),1154 makeActivity({ id: "info-1", turnId: "turn-2", kind: "turn.completed", tone: "info" }),
1155 ];1155 ];
11561156
1157 expect(hasToolActivityForTurn(activities, TurnId.makeUnsafe("turn-1"))).toBe(true);1157 expect(hasToolActivityForTurn(activities, TurnId.make("turn-1"))).toBe(true);
1158 expect(hasToolActivityForTurn(activities, TurnId.makeUnsafe("turn-2"))).toBe(false);1158 expect(hasToolActivityForTurn(activities, TurnId.make("turn-2"))).toBe(false);
1159 });1159 });
1160});1160});
11611161
1162describe("isLatestTurnSettled", () => {1162describe("isLatestTurnSettled", () => {
1163 const latestTurn = {1163 const latestTurn = {
1164 turnId: TurnId.makeUnsafe("turn-1"),1164 turnId: TurnId.make("turn-1"),
1165 startedAt: "2026-02-27T21:10:00.000Z",1165 startedAt: "2026-02-27T21:10:00.000Z",
1166 completedAt: "2026-02-27T21:10:06.000Z",1166 completedAt: "2026-02-27T21:10:06.000Z",
1167 } as const;1167 } as const;
...@@ -1170,7 +1170,7 @@ describe("isLatestTurnSettled", () => {...@@ -1170,7 +1170,7 @@ describe("isLatestTurnSettled", () => {
1170 expect(1170 expect(
1171 isLatestTurnSettled(latestTurn, {1171 isLatestTurnSettled(latestTurn, {
1172 orchestrationStatus: "running",1172 orchestrationStatus: "running",
1173 activeTurnId: TurnId.makeUnsafe("turn-1"),1173 activeTurnId: TurnId.make("turn-1"),
1174 }),1174 }),
1175 ).toBe(false);1175 ).toBe(false);
1176 });1176 });
...@@ -1179,7 +1179,7 @@ describe("isLatestTurnSettled", () => {...@@ -1179,7 +1179,7 @@ describe("isLatestTurnSettled", () => {
1179 expect(1179 expect(
1180 isLatestTurnSettled(latestTurn, {1180 isLatestTurnSettled(latestTurn, {
1181 orchestrationStatus: "running",1181 orchestrationStatus: "running",
1182 activeTurnId: TurnId.makeUnsafe("turn-2"),1182 activeTurnId: TurnId.make("turn-2"),
1183 }),1183 }),
1184 ).toBe(false);1184 ).toBe(false);
1185 });1185 });
...@@ -1197,7 +1197,7 @@ describe("isLatestTurnSettled", () => {...@@ -1197,7 +1197,7 @@ describe("isLatestTurnSettled", () => {
1197 expect(1197 expect(
1198 isLatestTurnSettled(1198 isLatestTurnSettled(
1199 {1199 {
1200 turnId: TurnId.makeUnsafe("turn-1"),1200 turnId: TurnId.make("turn-1"),
1201 startedAt: null,1201 startedAt: null,
1202 completedAt: "2026-02-27T21:10:06.000Z",1202 completedAt: "2026-02-27T21:10:06.000Z",
1203 },1203 },
...@@ -1209,7 +1209,7 @@ describe("isLatestTurnSettled", () => {...@@ -1209,7 +1209,7 @@ describe("isLatestTurnSettled", () => {
12091209
1210describe("deriveActiveWorkStartedAt", () => {1210describe("deriveActiveWorkStartedAt", () => {
1211 const latestTurn = {1211 const latestTurn = {
1212 turnId: TurnId.makeUnsafe("turn-1"),1212 turnId: TurnId.make("turn-1"),
1213 startedAt: "2026-02-27T21:10:00.000Z",1213 startedAt: "2026-02-27T21:10:00.000Z",
1214 completedAt: "2026-02-27T21:10:06.000Z",1214 completedAt: "2026-02-27T21:10:06.000Z",
1215 } as const;1215 } as const;
...@@ -1220,7 +1220,7 @@ describe("deriveActiveWorkStartedAt", () => {...@@ -1220,7 +1220,7 @@ describe("deriveActiveWorkStartedAt", () => {
1220 latestTurn,1220 latestTurn,
1221 {1221 {
1222 orchestrationStatus: "running",1222 orchestrationStatus: "running",
1223 activeTurnId: TurnId.makeUnsafe("turn-1"),1223 activeTurnId: TurnId.make("turn-1"),
1224 },1224 },
1225 "2026-02-27T21:11:00.000Z",1225 "2026-02-27T21:11:00.000Z",
1226 ),1226 ),
...@@ -1244,7 +1244,7 @@ describe("deriveActiveWorkStartedAt", () => {...@@ -1244,7 +1244,7 @@ describe("deriveActiveWorkStartedAt", () => {
1244 expect(1244 expect(
1245 deriveActiveWorkStartedAt(1245 deriveActiveWorkStartedAt(
1246 {1246 {
1247 turnId: TurnId.makeUnsafe("turn-1"),1247 turnId: TurnId.make("turn-1"),
1248 startedAt: "2026-02-27T21:10:00.000Z",1248 startedAt: "2026-02-27T21:10:00.000Z",
1249 completedAt: "2026-02-27T21:10:06.000Z",1249 completedAt: "2026-02-27T21:10:06.000Z",
1250 },1250 },
apps/web/src/session-logic.ts+2-2
...@@ -193,7 +193,7 @@ export function derivePendingApprovals(...@@ -193,7 +193,7 @@ export function derivePendingApprovals(
193 : null;193 : null;
194 const requestId =194 const requestId =
195 payload && typeof payload.requestId === "string"195 payload && typeof payload.requestId === "string"
196 ? ApprovalRequestId.makeUnsafe(payload.requestId)196 ? ApprovalRequestId.make(payload.requestId)
197 : null;197 : null;
198 const requestKind =198 const requestKind =
199 payload &&199 payload &&
...@@ -299,7 +299,7 @@ export function derivePendingUserInputs(...@@ -299,7 +299,7 @@ export function derivePendingUserInputs(
299 : null;299 : null;
300 const requestId =300 const requestId =
301 payload && typeof payload.requestId === "string"301 payload && typeof payload.requestId === "string"
302 ? ApprovalRequestId.makeUnsafe(payload.requestId)302 ? ApprovalRequestId.make(payload.requestId)
303 : null;303 : null;
304 const detail = payload && typeof payload.detail === "string" ? payload.detail : undefined;304 const detail = payload && typeof payload.detail === "string" ? payload.detail : undefined;
305305
apps/web/src/store.test.ts+76-76
...@@ -26,8 +26,8 @@ import {...@@ -26,8 +26,8 @@ import {
26} from "./store";26} from "./store";
27import { DEFAULT_INTERACTION_MODE, DEFAULT_RUNTIME_MODE, type Thread } from "./types";27import { DEFAULT_INTERACTION_MODE, DEFAULT_RUNTIME_MODE, type Thread } from "./types";
2828
29const localEnvironmentId = EnvironmentId.makeUnsafe("environment-local");29const localEnvironmentId = EnvironmentId.make("environment-local");
30const remoteEnvironmentId = EnvironmentId.makeUnsafe("environment-remote");30const remoteEnvironmentId = EnvironmentId.make("environment-remote");
3131
32function withActiveEnvironmentState(32function withActiveEnvironmentState(
33 environmentState: EnvironmentState,33 environmentState: EnvironmentState,
...@@ -59,10 +59,10 @@ function withActiveEnvironmentState(...@@ -59,10 +59,10 @@ function withActiveEnvironmentState(
5959
60function makeThread(overrides: Partial<Thread> = {}): Thread {60function makeThread(overrides: Partial<Thread> = {}): Thread {
61 return {61 return {
62 id: ThreadId.makeUnsafe("thread-1"),62 id: ThreadId.make("thread-1"),
63 environmentId: localEnvironmentId,63 environmentId: localEnvironmentId,
64 codexThreadId: null,64 codexThreadId: null,
65 projectId: ProjectId.makeUnsafe("project-1"),65 projectId: ProjectId.make("project-1"),
66 title: "Thread",66 title: "Thread",
67 modelSelection: {67 modelSelection: {
68 provider: "codex",68 provider: "codex",
...@@ -86,7 +86,7 @@ function makeThread(overrides: Partial<Thread> = {}): Thread {...@@ -86,7 +86,7 @@ function makeThread(overrides: Partial<Thread> = {}): Thread {
86}86}
8787
88function makeState(thread: Thread): AppState {88function makeState(thread: Thread): AppState {
89 const projectId = ProjectId.makeUnsafe("project-1");89 const projectId = ProjectId.make("project-1");
90 const project = {90 const project = {
91 id: projectId,91 id: projectId,
92 environmentId: thread.environmentId,92 environmentId: thread.environmentId,
...@@ -226,14 +226,14 @@ function makeEvent<T extends OrchestrationEvent["type"]>(...@@ -226,14 +226,14 @@ function makeEvent<T extends OrchestrationEvent["type"]>(
226 const sequence = overrides.sequence ?? 1;226 const sequence = overrides.sequence ?? 1;
227 return {227 return {
228 sequence,228 sequence,
229 eventId: EventId.makeUnsafe(`event-${sequence}`),229 eventId: EventId.make(`event-${sequence}`),
230 aggregateKind: "thread",230 aggregateKind: "thread",
231 aggregateId:231 aggregateId:
232 "threadId" in payload232 "threadId" in payload
233 ? payload.threadId233 ? payload.threadId
234 : "projectId" in payload234 : "projectId" in payload
235 ? payload.projectId235 ? payload.projectId
236 : ProjectId.makeUnsafe("project-1"),236 : ProjectId.make("project-1"),
237 occurredAt: "2026-02-27T00:00:00.000Z",237 occurredAt: "2026-02-27T00:00:00.000Z",
238 commandId: null,238 commandId: null,
239 causationEventId: null,239 causationEventId: null,
...@@ -247,8 +247,8 @@ function makeEvent<T extends OrchestrationEvent["type"]>(...@@ -247,8 +247,8 @@ function makeEvent<T extends OrchestrationEvent["type"]>(
247247
248function makeReadModelThread(overrides: Partial<OrchestrationReadModel["threads"][number]>) {248function makeReadModelThread(overrides: Partial<OrchestrationReadModel["threads"][number]>) {
249 return {249 return {
250 id: ThreadId.makeUnsafe("thread-1"),250 id: ThreadId.make("thread-1"),
251 projectId: ProjectId.makeUnsafe("project-1"),251 projectId: ProjectId.make("project-1"),
252 title: "Thread",252 title: "Thread",
253 modelSelection: {253 modelSelection: {
254 provider: "codex",254 provider: "codex",
...@@ -278,7 +278,7 @@ function makeReadModel(thread: OrchestrationReadModel["threads"][number]): Orche...@@ -278,7 +278,7 @@ function makeReadModel(thread: OrchestrationReadModel["threads"][number]): Orche
278 updatedAt: "2026-02-27T00:00:00.000Z",278 updatedAt: "2026-02-27T00:00:00.000Z",
279 projects: [279 projects: [
280 {280 {
281 id: ProjectId.makeUnsafe("project-1"),281 id: ProjectId.make("project-1"),
282 title: "Project",282 title: "Project",
283 workspaceRoot: "/tmp/project",283 workspaceRoot: "/tmp/project",
284 defaultModelSelection: {284 defaultModelSelection: {
...@@ -299,7 +299,7 @@ function makeReadModelProject(...@@ -299,7 +299,7 @@ function makeReadModelProject(
299 overrides: Partial<OrchestrationReadModel["projects"][number]>,299 overrides: Partial<OrchestrationReadModel["projects"][number]>,
300): OrchestrationReadModel["projects"][number] {300): OrchestrationReadModel["projects"][number] {
301 return {301 return {
302 id: ProjectId.makeUnsafe("project-1"),302 id: ProjectId.make("project-1"),
303 title: "Project",303 title: "Project",
304 workspaceRoot: "/tmp/project",304 workspaceRoot: "/tmp/project",
305 defaultModelSelection: {305 defaultModelSelection: {
...@@ -316,7 +316,7 @@ function makeReadModelProject(...@@ -316,7 +316,7 @@ function makeReadModelProject(
316316
317describe("setThreadBranch", () => {317describe("setThreadBranch", () => {
318 it("updates only the scoped thread environment", () => {318 it("updates only the scoped thread environment", () => {
319 const sharedThreadId = ThreadId.makeUnsafe("thread-shared");319 const sharedThreadId = ThreadId.make("thread-shared");
320 const localThread = makeThread({320 const localThread = makeThread({
321 id: sharedThreadId,321 id: sharedThreadId,
322 environmentId: localEnvironmentId,322 environmentId: localEnvironmentId,
...@@ -397,7 +397,7 @@ describe("store read model sync", () => {...@@ -397,7 +397,7 @@ describe("store read model sync", () => {
397 model: "sonnet",397 model: "sonnet",
398 },398 },
399 session: {399 session: {
400 threadId: ThreadId.makeUnsafe("thread-1"),400 threadId: ThreadId.make("thread-1"),
401 status: "ready",401 status: "ready",
402 providerName: "claudeAgent",402 providerName: "claudeAgent",
403 runtimeMode: "approval-required",403 runtimeMode: "approval-required",
...@@ -444,9 +444,9 @@ describe("store read model sync", () => {...@@ -444,9 +444,9 @@ describe("store read model sync", () => {
444 });444 });
445445
446 it("replaces projects using snapshot order during recovery", () => {446 it("replaces projects using snapshot order during recovery", () => {
447 const project1 = ProjectId.makeUnsafe("project-1");447 const project1 = ProjectId.make("project-1");
448 const project2 = ProjectId.makeUnsafe("project-2");448 const project2 = ProjectId.make("project-2");
449 const project3 = ProjectId.makeUnsafe("project-3");449 const project3 = ProjectId.make("project-3");
450 const initialState: AppState = makeEmptyState({450 const initialState: AppState = makeEmptyState({
451 projectIds: [project2, project1],451 projectIds: [project2, project1],
452 projectById: {452 projectById: {
...@@ -516,7 +516,7 @@ describe("incremental orchestration updates", () => {...@@ -516,7 +516,7 @@ describe("incremental orchestration updates", () => {
516 const next = applyOrchestrationEvent(516 const next = applyOrchestrationEvent(
517 state,517 state,
518 makeEvent("thread.meta-updated", {518 makeEvent("thread.meta-updated", {
519 threadId: ThreadId.makeUnsafe("thread-1"),519 threadId: ThreadId.make("thread-1"),
520 title: "Updated title",520 title: "Updated title",
521 updatedAt: "2026-02-27T00:00:01.000Z",521 updatedAt: "2026-02-27T00:00:01.000Z",
522 }),522 }),
...@@ -533,7 +533,7 @@ describe("incremental orchestration updates", () => {...@@ -533,7 +533,7 @@ describe("incremental orchestration updates", () => {
533 const nextAfterProjectDelete = applyOrchestrationEvent(533 const nextAfterProjectDelete = applyOrchestrationEvent(
534 state,534 state,
535 makeEvent("project.deleted", {535 makeEvent("project.deleted", {
536 projectId: ProjectId.makeUnsafe("project-missing"),536 projectId: ProjectId.make("project-missing"),
537 deletedAt: "2026-02-27T00:00:01.000Z",537 deletedAt: "2026-02-27T00:00:01.000Z",
538 }),538 }),
539 localEnvironmentId,539 localEnvironmentId,
...@@ -541,7 +541,7 @@ describe("incremental orchestration updates", () => {...@@ -541,7 +541,7 @@ describe("incremental orchestration updates", () => {
541 const nextAfterThreadDelete = applyOrchestrationEvent(541 const nextAfterThreadDelete = applyOrchestrationEvent(
542 state,542 state,
543 makeEvent("thread.deleted", {543 makeEvent("thread.deleted", {
544 threadId: ThreadId.makeUnsafe("thread-missing"),544 threadId: ThreadId.make("thread-missing"),
545 deletedAt: "2026-02-27T00:00:01.000Z",545 deletedAt: "2026-02-27T00:00:01.000Z",
546 }),546 }),
547 localEnvironmentId,547 localEnvironmentId,
...@@ -552,8 +552,8 @@ describe("incremental orchestration updates", () => {...@@ -552,8 +552,8 @@ describe("incremental orchestration updates", () => {
552 });552 });
553553
554 it("reuses an existing project row when project.created arrives with a new id for the same cwd", () => {554 it("reuses an existing project row when project.created arrives with a new id for the same cwd", () => {
555 const originalProjectId = ProjectId.makeUnsafe("project-1");555 const originalProjectId = ProjectId.make("project-1");
556 const recreatedProjectId = ProjectId.makeUnsafe("project-2");556 const recreatedProjectId = ProjectId.make("project-2");
557 const state: AppState = makeEmptyState({557 const state: AppState = makeEmptyState({
558 projectIds: [originalProjectId],558 projectIds: [originalProjectId],
559 projectById: {559 projectById: {
...@@ -602,9 +602,9 @@ describe("incremental orchestration updates", () => {...@@ -602,9 +602,9 @@ describe("incremental orchestration updates", () => {
602 });602 });
603603
604 it("removes stale project index entries when thread.created recreates a thread under a new project", () => {604 it("removes stale project index entries when thread.created recreates a thread under a new project", () => {
605 const originalProjectId = ProjectId.makeUnsafe("project-1");605 const originalProjectId = ProjectId.make("project-1");
606 const recreatedProjectId = ProjectId.makeUnsafe("project-2");606 const recreatedProjectId = ProjectId.make("project-2");
607 const threadId = ThreadId.makeUnsafe("thread-1");607 const threadId = ThreadId.make("thread-1");
608 const thread = makeThread({608 const thread = makeThread({
609 id: threadId,609 id: threadId,
610 projectId: originalProjectId,610 projectId: originalProjectId,
...@@ -671,20 +671,20 @@ describe("incremental orchestration updates", () => {...@@ -671,20 +671,20 @@ describe("incremental orchestration updates", () => {
671671
672 it("updates only the affected thread for message events", () => {672 it("updates only the affected thread for message events", () => {
673 const thread1 = makeThread({673 const thread1 = makeThread({
674 id: ThreadId.makeUnsafe("thread-1"),674 id: ThreadId.make("thread-1"),
675 messages: [675 messages: [
676 {676 {
677 id: MessageId.makeUnsafe("message-1"),677 id: MessageId.make("message-1"),
678 role: "assistant",678 role: "assistant",
679 text: "hello",679 text: "hello",
680 turnId: TurnId.makeUnsafe("turn-1"),680 turnId: TurnId.make("turn-1"),
681 createdAt: "2026-02-27T00:00:00.000Z",681 createdAt: "2026-02-27T00:00:00.000Z",
682 completedAt: "2026-02-27T00:00:00.000Z",682 completedAt: "2026-02-27T00:00:00.000Z",
683 streaming: false,683 streaming: false,
684 },684 },
685 ],685 ],
686 });686 });
687 const thread2 = makeThread({ id: ThreadId.makeUnsafe("thread-2") });687 const thread2 = makeThread({ id: ThreadId.make("thread-2") });
688 const baseState = makeState(thread1);688 const baseState = makeState(thread1);
689 const baseEnvironmentState = localEnvironmentStateOf(baseState);689 const baseEnvironmentState = localEnvironmentStateOf(baseState);
690 const state = withActiveEnvironmentState(baseEnvironmentState, {690 const state = withActiveEnvironmentState(baseEnvironmentState, {
...@@ -762,10 +762,10 @@ describe("incremental orchestration updates", () => {...@@ -762,10 +762,10 @@ describe("incremental orchestration updates", () => {
762 state,762 state,
763 makeEvent("thread.message-sent", {763 makeEvent("thread.message-sent", {
764 threadId: thread1.id,764 threadId: thread1.id,
765 messageId: MessageId.makeUnsafe("message-1"),765 messageId: MessageId.make("message-1"),
766 role: "assistant",766 role: "assistant",
767 text: " world",767 text: " world",
768 turnId: TurnId.makeUnsafe("turn-1"),768 turnId: TurnId.make("turn-1"),
769 streaming: true,769 streaming: true,
770 createdAt: "2026-02-27T00:00:01.000Z",770 createdAt: "2026-02-27T00:00:01.000Z",
771 updatedAt: "2026-02-27T00:00:01.000Z",771 updatedAt: "2026-02-27T00:00:01.000Z",
...@@ -794,7 +794,7 @@ describe("incremental orchestration updates", () => {...@@ -794,7 +794,7 @@ describe("incremental orchestration updates", () => {
794 it("applies replay batches in sequence and updates session state", () => {794 it("applies replay batches in sequence and updates session state", () => {
795 const thread = makeThread({795 const thread = makeThread({
796 latestTurn: {796 latestTurn: {
797 turnId: TurnId.makeUnsafe("turn-1"),797 turnId: TurnId.make("turn-1"),
798 state: "running",798 state: "running",
799 requestedAt: "2026-02-27T00:00:00.000Z",799 requestedAt: "2026-02-27T00:00:00.000Z",
800 startedAt: "2026-02-27T00:00:00.000Z",800 startedAt: "2026-02-27T00:00:00.000Z",
...@@ -816,7 +816,7 @@ describe("incremental orchestration updates", () => {...@@ -816,7 +816,7 @@ describe("incremental orchestration updates", () => {
816 status: "running",816 status: "running",
817 providerName: "codex",817 providerName: "codex",
818 runtimeMode: "full-access",818 runtimeMode: "full-access",
819 activeTurnId: TurnId.makeUnsafe("turn-1"),819 activeTurnId: TurnId.make("turn-1"),
820 lastError: null,820 lastError: null,
821 updatedAt: "2026-02-27T00:00:02.000Z",821 updatedAt: "2026-02-27T00:00:02.000Z",
822 },822 },
...@@ -827,10 +827,10 @@ describe("incremental orchestration updates", () => {...@@ -827,10 +827,10 @@ describe("incremental orchestration updates", () => {
827 "thread.message-sent",827 "thread.message-sent",
828 {828 {
829 threadId: thread.id,829 threadId: thread.id,
830 messageId: MessageId.makeUnsafe("assistant-1"),830 messageId: MessageId.make("assistant-1"),
831 role: "assistant",831 role: "assistant",
832 text: "done",832 text: "done",
833 turnId: TurnId.makeUnsafe("turn-1"),833 turnId: TurnId.make("turn-1"),
834 streaming: false,834 streaming: false,
835 createdAt: "2026-02-27T00:00:03.000Z",835 createdAt: "2026-02-27T00:00:03.000Z",
836 updatedAt: "2026-02-27T00:00:03.000Z",836 updatedAt: "2026-02-27T00:00:03.000Z",
...@@ -850,7 +850,7 @@ describe("incremental orchestration updates", () => {...@@ -850,7 +850,7 @@ describe("incremental orchestration updates", () => {
850 const state = makeState(850 const state = makeState(
851 makeThread({851 makeThread({
852 latestTurn: {852 latestTurn: {
853 turnId: TurnId.makeUnsafe("turn-2"),853 turnId: TurnId.make("turn-2"),
854 state: "running",854 state: "running",
855 requestedAt: "2026-02-27T00:00:02.000Z",855 requestedAt: "2026-02-27T00:00:02.000Z",
856 startedAt: "2026-02-27T00:00:03.000Z",856 startedAt: "2026-02-27T00:00:03.000Z",
...@@ -863,13 +863,13 @@ describe("incremental orchestration updates", () => {...@@ -863,13 +863,13 @@ describe("incremental orchestration updates", () => {
863 const next = applyOrchestrationEvent(863 const next = applyOrchestrationEvent(
864 state,864 state,
865 makeEvent("thread.turn-diff-completed", {865 makeEvent("thread.turn-diff-completed", {
866 threadId: ThreadId.makeUnsafe("thread-1"),866 threadId: ThreadId.make("thread-1"),
867 turnId: TurnId.makeUnsafe("turn-1"),867 turnId: TurnId.make("turn-1"),
868 checkpointTurnCount: 1,868 checkpointTurnCount: 1,
869 checkpointRef: CheckpointRef.makeUnsafe("checkpoint-1"),869 checkpointRef: CheckpointRef.make("checkpoint-1"),
870 status: "ready",870 status: "ready",
871 files: [],871 files: [],
872 assistantMessageId: MessageId.makeUnsafe("assistant-1"),872 assistantMessageId: MessageId.make("assistant-1"),
873 completedAt: "2026-02-27T00:00:04.000Z",873 completedAt: "2026-02-27T00:00:04.000Z",
874 }),874 }),
875 localEnvironmentId,875 localEnvironmentId,
...@@ -880,7 +880,7 @@ describe("incremental orchestration updates", () => {...@@ -880,7 +880,7 @@ describe("incremental orchestration updates", () => {
880 });880 });
881881
882 it("rebinds live turn diffs to the authoritative assistant message when it arrives later", () => {882 it("rebinds live turn diffs to the authoritative assistant message when it arrives later", () => {
883 const turnId = TurnId.makeUnsafe("turn-1");883 const turnId = TurnId.make("turn-1");
884 const state = makeState(884 const state = makeState(
885 makeThread({885 makeThread({
886 latestTurn: {886 latestTurn: {
...@@ -889,7 +889,7 @@ describe("incremental orchestration updates", () => {...@@ -889,7 +889,7 @@ describe("incremental orchestration updates", () => {
889 requestedAt: "2026-02-27T00:00:00.000Z",889 requestedAt: "2026-02-27T00:00:00.000Z",
890 startedAt: "2026-02-27T00:00:00.000Z",890 startedAt: "2026-02-27T00:00:00.000Z",
891 completedAt: "2026-02-27T00:00:02.000Z",891 completedAt: "2026-02-27T00:00:02.000Z",
892 assistantMessageId: MessageId.makeUnsafe("assistant:turn-1"),892 assistantMessageId: MessageId.make("assistant:turn-1"),
893 },893 },
894 turnDiffSummaries: [894 turnDiffSummaries: [
895 {895 {
...@@ -897,8 +897,8 @@ describe("incremental orchestration updates", () => {...@@ -897,8 +897,8 @@ describe("incremental orchestration updates", () => {
897 completedAt: "2026-02-27T00:00:02.000Z",897 completedAt: "2026-02-27T00:00:02.000Z",
898 status: "ready",898 status: "ready",
899 checkpointTurnCount: 1,899 checkpointTurnCount: 1,
900 checkpointRef: CheckpointRef.makeUnsafe("checkpoint-1"),900 checkpointRef: CheckpointRef.make("checkpoint-1"),
901 assistantMessageId: MessageId.makeUnsafe("assistant:turn-1"),901 assistantMessageId: MessageId.make("assistant:turn-1"),
902 files: [{ path: "src/app.ts", additions: 1, deletions: 0 }],902 files: [{ path: "src/app.ts", additions: 1, deletions: 0 }],
903 },903 },
904 ],904 ],
...@@ -908,8 +908,8 @@ describe("incremental orchestration updates", () => {...@@ -908,8 +908,8 @@ describe("incremental orchestration updates", () => {
908 const next = applyOrchestrationEvent(908 const next = applyOrchestrationEvent(
909 state,909 state,
910 makeEvent("thread.message-sent", {910 makeEvent("thread.message-sent", {
911 threadId: ThreadId.makeUnsafe("thread-1"),911 threadId: ThreadId.make("thread-1"),
912 messageId: MessageId.makeUnsafe("assistant-real"),912 messageId: MessageId.make("assistant-real"),
913 role: "assistant",913 role: "assistant",
914 text: "final answer",914 text: "final answer",
915 turnId,915 turnId,
...@@ -921,10 +921,10 @@ describe("incremental orchestration updates", () => {...@@ -921,10 +921,10 @@ describe("incremental orchestration updates", () => {
921 );921 );
922922
923 expect(threadsOf(next)[0]?.turnDiffSummaries[0]?.assistantMessageId).toBe(923 expect(threadsOf(next)[0]?.turnDiffSummaries[0]?.assistantMessageId).toBe(
924 MessageId.makeUnsafe("assistant-real"),924 MessageId.make("assistant-real"),
925 );925 );
926 expect(threadsOf(next)[0]?.latestTurn?.assistantMessageId).toBe(926 expect(threadsOf(next)[0]?.latestTurn?.assistantMessageId).toBe(
927 MessageId.makeUnsafe("assistant-real"),927 MessageId.make("assistant-real"),
928 );928 );
929 });929 });
930930
...@@ -933,28 +933,28 @@ describe("incremental orchestration updates", () => {...@@ -933,28 +933,28 @@ describe("incremental orchestration updates", () => {
933 makeThread({933 makeThread({
934 messages: [934 messages: [
935 {935 {
936 id: MessageId.makeUnsafe("user-1"),936 id: MessageId.make("user-1"),
937 role: "user",937 role: "user",
938 text: "first",938 text: "first",
939 turnId: TurnId.makeUnsafe("turn-1"),939 turnId: TurnId.make("turn-1"),
940 createdAt: "2026-02-27T00:00:00.000Z",940 createdAt: "2026-02-27T00:00:00.000Z",
941 completedAt: "2026-02-27T00:00:00.000Z",941 completedAt: "2026-02-27T00:00:00.000Z",
942 streaming: false,942 streaming: false,
943 },943 },
944 {944 {
945 id: MessageId.makeUnsafe("assistant-1"),945 id: MessageId.make("assistant-1"),
946 role: "assistant",946 role: "assistant",
947 text: "first reply",947 text: "first reply",
948 turnId: TurnId.makeUnsafe("turn-1"),948 turnId: TurnId.make("turn-1"),
949 createdAt: "2026-02-27T00:00:01.000Z",949 createdAt: "2026-02-27T00:00:01.000Z",
950 completedAt: "2026-02-27T00:00:01.000Z",950 completedAt: "2026-02-27T00:00:01.000Z",
951 streaming: false,951 streaming: false,
952 },952 },
953 {953 {
954 id: MessageId.makeUnsafe("user-2"),954 id: MessageId.make("user-2"),
955 role: "user",955 role: "user",
956 text: "second",956 text: "second",
957 turnId: TurnId.makeUnsafe("turn-2"),957 turnId: TurnId.make("turn-2"),
958 createdAt: "2026-02-27T00:00:02.000Z",958 createdAt: "2026-02-27T00:00:02.000Z",
959 completedAt: "2026-02-27T00:00:02.000Z",959 completedAt: "2026-02-27T00:00:02.000Z",
960 streaming: false,960 streaming: false,
...@@ -963,7 +963,7 @@ describe("incremental orchestration updates", () => {...@@ -963,7 +963,7 @@ describe("incremental orchestration updates", () => {
963 proposedPlans: [963 proposedPlans: [
964 {964 {
965 id: "plan-1",965 id: "plan-1",
966 turnId: TurnId.makeUnsafe("turn-1"),966 turnId: TurnId.make("turn-1"),
967 planMarkdown: "plan 1",967 planMarkdown: "plan 1",
968 implementedAt: null,968 implementedAt: null,
969 implementationThreadId: null,969 implementationThreadId: null,
...@@ -972,7 +972,7 @@ describe("incremental orchestration updates", () => {...@@ -972,7 +972,7 @@ describe("incremental orchestration updates", () => {
972 },972 },
973 {973 {
974 id: "plan-2",974 id: "plan-2",
975 turnId: TurnId.makeUnsafe("turn-2"),975 turnId: TurnId.make("turn-2"),
976 planMarkdown: "plan 2",976 planMarkdown: "plan 2",
977 implementedAt: null,977 implementedAt: null,
978 implementationThreadId: null,978 implementationThreadId: null,
...@@ -982,39 +982,39 @@ describe("incremental orchestration updates", () => {...@@ -982,39 +982,39 @@ describe("incremental orchestration updates", () => {
982 ],982 ],
983 activities: [983 activities: [
984 {984 {
985 id: EventId.makeUnsafe("activity-1"),985 id: EventId.make("activity-1"),
986 tone: "info",986 tone: "info",
987 kind: "step",987 kind: "step",
988 summary: "one",988 summary: "one",
989 payload: {},989 payload: {},
990 turnId: TurnId.makeUnsafe("turn-1"),990 turnId: TurnId.make("turn-1"),
991 createdAt: "2026-02-27T00:00:00.000Z",991 createdAt: "2026-02-27T00:00:00.000Z",
992 },992 },
993 {993 {
994 id: EventId.makeUnsafe("activity-2"),994 id: EventId.make("activity-2"),
995 tone: "info",995 tone: "info",
996 kind: "step",996 kind: "step",
997 summary: "two",997 summary: "two",
998 payload: {},998 payload: {},
999 turnId: TurnId.makeUnsafe("turn-2"),999 turnId: TurnId.make("turn-2"),
1000 createdAt: "2026-02-27T00:00:02.000Z",1000 createdAt: "2026-02-27T00:00:02.000Z",
1001 },1001 },
1002 ],1002 ],
1003 turnDiffSummaries: [1003 turnDiffSummaries: [
1004 {1004 {
1005 turnId: TurnId.makeUnsafe("turn-1"),1005 turnId: TurnId.make("turn-1"),
1006 completedAt: "2026-02-27T00:00:01.000Z",1006 completedAt: "2026-02-27T00:00:01.000Z",
1007 status: "ready",1007 status: "ready",
1008 checkpointTurnCount: 1,1008 checkpointTurnCount: 1,
1009 checkpointRef: CheckpointRef.makeUnsafe("ref-1"),1009 checkpointRef: CheckpointRef.make("ref-1"),
1010 files: [],1010 files: [],
1011 },1011 },
1012 {1012 {
1013 turnId: TurnId.makeUnsafe("turn-2"),1013 turnId: TurnId.make("turn-2"),
1014 completedAt: "2026-02-27T00:00:03.000Z",1014 completedAt: "2026-02-27T00:00:03.000Z",
1015 status: "ready",1015 status: "ready",
1016 checkpointTurnCount: 2,1016 checkpointTurnCount: 2,
1017 checkpointRef: CheckpointRef.makeUnsafe("ref-2"),1017 checkpointRef: CheckpointRef.make("ref-2"),
1018 files: [],1018 files: [],
1019 },1019 },
1020 ],1020 ],
...@@ -1024,7 +1024,7 @@ describe("incremental orchestration updates", () => {...@@ -1024,7 +1024,7 @@ describe("incremental orchestration updates", () => {
1024 const next = applyOrchestrationEvent(1024 const next = applyOrchestrationEvent(
1025 state,1025 state,
1026 makeEvent("thread.reverted", {1026 makeEvent("thread.reverted", {
1027 threadId: ThreadId.makeUnsafe("thread-1"),1027 threadId: ThreadId.make("thread-1"),
1028 turnCount: 1,1028 turnCount: 1,
1029 }),1029 }),
1030 localEnvironmentId,1030 localEnvironmentId,
...@@ -1036,46 +1036,46 @@ describe("incremental orchestration updates", () => {...@@ -1036,46 +1036,46 @@ describe("incremental orchestration updates", () => {
1036 ]);1036 ]);
1037 expect(threadsOf(next)[0]?.proposedPlans.map((plan) => plan.id)).toEqual(["plan-1"]);1037 expect(threadsOf(next)[0]?.proposedPlans.map((plan) => plan.id)).toEqual(["plan-1"]);
1038 expect(threadsOf(next)[0]?.activities.map((activity) => activity.id)).toEqual([1038 expect(threadsOf(next)[0]?.activities.map((activity) => activity.id)).toEqual([
1039 EventId.makeUnsafe("activity-1"),1039 EventId.make("activity-1"),
1040 ]);1040 ]);
1041 expect(threadsOf(next)[0]?.turnDiffSummaries.map((summary) => summary.turnId)).toEqual([1041 expect(threadsOf(next)[0]?.turnDiffSummaries.map((summary) => summary.turnId)).toEqual([
1042 TurnId.makeUnsafe("turn-1"),1042 TurnId.make("turn-1"),
1043 ]);1043 ]);
1044 });1044 });
10451045
1046 it("clears pending source proposed plans after revert before a new session-set event", () => {1046 it("clears pending source proposed plans after revert before a new session-set event", () => {
1047 const thread = makeThread({1047 const thread = makeThread({
1048 latestTurn: {1048 latestTurn: {
1049 turnId: TurnId.makeUnsafe("turn-2"),1049 turnId: TurnId.make("turn-2"),
1050 state: "completed",1050 state: "completed",
1051 requestedAt: "2026-02-27T00:00:02.000Z",1051 requestedAt: "2026-02-27T00:00:02.000Z",
1052 startedAt: "2026-02-27T00:00:02.000Z",1052 startedAt: "2026-02-27T00:00:02.000Z",
1053 completedAt: "2026-02-27T00:00:03.000Z",1053 completedAt: "2026-02-27T00:00:03.000Z",
1054 assistantMessageId: MessageId.makeUnsafe("assistant-2"),1054 assistantMessageId: MessageId.make("assistant-2"),
1055 sourceProposedPlan: {1055 sourceProposedPlan: {
1056 threadId: ThreadId.makeUnsafe("thread-source"),1056 threadId: ThreadId.make("thread-source"),
1057 planId: "plan-2" as never,1057 planId: "plan-2" as never,
1058 },1058 },
1059 },1059 },
1060 pendingSourceProposedPlan: {1060 pendingSourceProposedPlan: {
1061 threadId: ThreadId.makeUnsafe("thread-source"),1061 threadId: ThreadId.make("thread-source"),
1062 planId: "plan-2" as never,1062 planId: "plan-2" as never,
1063 },1063 },
1064 turnDiffSummaries: [1064 turnDiffSummaries: [
1065 {1065 {
1066 turnId: TurnId.makeUnsafe("turn-1"),1066 turnId: TurnId.make("turn-1"),
1067 completedAt: "2026-02-27T00:00:01.000Z",1067 completedAt: "2026-02-27T00:00:01.000Z",
1068 status: "ready",1068 status: "ready",
1069 checkpointTurnCount: 1,1069 checkpointTurnCount: 1,
1070 checkpointRef: CheckpointRef.makeUnsafe("ref-1"),1070 checkpointRef: CheckpointRef.make("ref-1"),
1071 files: [],1071 files: [],
1072 },1072 },
1073 {1073 {
1074 turnId: TurnId.makeUnsafe("turn-2"),1074 turnId: TurnId.make("turn-2"),
1075 completedAt: "2026-02-27T00:00:03.000Z",1075 completedAt: "2026-02-27T00:00:03.000Z",
1076 status: "ready",1076 status: "ready",
1077 checkpointTurnCount: 2,1077 checkpointTurnCount: 2,
1078 checkpointRef: CheckpointRef.makeUnsafe("ref-2"),1078 checkpointRef: CheckpointRef.make("ref-2"),
1079 files: [],1079 files: [],
1080 },1080 },
1081 ],1081 ],
...@@ -1100,7 +1100,7 @@ describe("incremental orchestration updates", () => {...@@ -1100,7 +1100,7 @@ describe("incremental orchestration updates", () => {
1100 status: "running",1100 status: "running",
1101 providerName: "codex",1101 providerName: "codex",
1102 runtimeMode: "full-access",1102 runtimeMode: "full-access",
1103 activeTurnId: TurnId.makeUnsafe("turn-3"),1103 activeTurnId: TurnId.make("turn-3"),
1104 lastError: null,1104 lastError: null,
1105 updatedAt: "2026-02-27T00:00:04.000Z",1105 updatedAt: "2026-02-27T00:00:04.000Z",
1106 },1106 },
...@@ -1109,7 +1109,7 @@ describe("incremental orchestration updates", () => {...@@ -1109,7 +1109,7 @@ describe("incremental orchestration updates", () => {
1109 );1109 );
11101110
1111 expect(threadsOf(next)[0]?.latestTurn).toMatchObject({1111 expect(threadsOf(next)[0]?.latestTurn).toMatchObject({
1112 turnId: TurnId.makeUnsafe("turn-3"),1112 turnId: TurnId.make("turn-3"),
1113 state: "running",1113 state: "running",
1114 });1114 });
1115 expect(threadsOf(next)[0]?.latestTurn?.sourceProposedPlan).toBeUndefined();1115 expect(threadsOf(next)[0]?.latestTurn?.sourceProposedPlan).toBeUndefined();
apps/web/src/terminalStateStore.test.ts+1-1
...@@ -9,7 +9,7 @@ import {...@@ -9,7 +9,7 @@ import {
9 useTerminalStateStore,9 useTerminalStateStore,
10} from "./terminalStateStore";10} from "./terminalStateStore";
1111
12const THREAD_ID = ThreadId.makeUnsafe("thread-1");12const THREAD_ID = ThreadId.make("thread-1");
13const THREAD_REF = scopeThreadRef("environment-a" as never, THREAD_ID);13const THREAD_REF = scopeThreadRef("environment-a" as never, THREAD_ID);
14const OTHER_THREAD_REF = scopeThreadRef("environment-b" as never, THREAD_ID);14const OTHER_THREAD_REF = scopeThreadRef("environment-b" as never, THREAD_ID);
1515
apps/web/src/threadRoutes.test.ts+2-2
...@@ -12,7 +12,7 @@ import {...@@ -12,7 +12,7 @@ import {
1212
13describe("threadRoutes", () => {13describe("threadRoutes", () => {
14 it("builds canonical thread route params from a scoped ref", () => {14 it("builds canonical thread route params from a scoped ref", () => {
15 const ref = scopeThreadRef("env-1" as never, ThreadId.makeUnsafe("thread-1"));15 const ref = scopeThreadRef("env-1" as never, ThreadId.make("thread-1"));
1616
17 expect(buildThreadRouteParams(ref)).toEqual({17 expect(buildThreadRouteParams(ref)).toEqual({
18 environmentId: "env-1",18 environmentId: "env-1",
...@@ -36,7 +36,7 @@ describe("threadRoutes", () => {...@@ -36,7 +36,7 @@ describe("threadRoutes", () => {
36 });36 });
3737
38 it("builds canonical draft route params from a draft id", () => {38 it("builds canonical draft route params from a draft id", () => {
39 expect(buildDraftThreadRouteParams(DraftId.makeUnsafe("draft-1"))).toEqual({39 expect(buildDraftThreadRouteParams(DraftId.make("draft-1"))).toEqual({
40 draftId: "draft-1",40 draftId: "draft-1",
41 });41 });
42 });42 });
apps/web/src/threadSelectionStore.test.ts+6-6
...@@ -3,11 +3,11 @@ import { beforeEach, describe, expect, it } from "vitest";...@@ -3,11 +3,11 @@ import { beforeEach, describe, expect, it } from "vitest";
33
4import { useThreadSelectionStore } from "./threadSelectionStore";4import { useThreadSelectionStore } from "./threadSelectionStore";
55
6const THREAD_A = ThreadId.makeUnsafe("thread-a");6const THREAD_A = ThreadId.make("thread-a");
7const THREAD_B = ThreadId.makeUnsafe("thread-b");7const THREAD_B = ThreadId.make("thread-b");
8const THREAD_C = ThreadId.makeUnsafe("thread-c");8const THREAD_C = ThreadId.make("thread-c");
9const THREAD_D = ThreadId.makeUnsafe("thread-d");9const THREAD_D = ThreadId.make("thread-d");
10const THREAD_E = ThreadId.makeUnsafe("thread-e");10const THREAD_E = ThreadId.make("thread-e");
1111
12const ORDERED = [THREAD_A, THREAD_B, THREAD_C, THREAD_D, THREAD_E] as const;12const ORDERED = [THREAD_A, THREAD_B, THREAD_C, THREAD_D, THREAD_E] as const;
1313
...@@ -173,7 +173,7 @@ describe("threadSelectionStore", () => {...@@ -173,7 +173,7 @@ describe("threadSelectionStore", () => {
173 it("falls back to toggle when target is not in the ordered list", () => {173 it("falls back to toggle when target is not in the ordered list", () => {
174 const store = useThreadSelectionStore.getState();174 const store = useThreadSelectionStore.getState();
175 store.toggleThread(THREAD_B); // anchor = B175 store.toggleThread(THREAD_B); // anchor = B
176 const unknownThread = ThreadId.makeUnsafe("thread-unknown");176 const unknownThread = ThreadId.make("thread-unknown");
177 store.rangeSelectTo(unknownThread, ORDERED);177 store.rangeSelectTo(unknownThread, ORDERED);
178178
179 const state = useThreadSelectionStore.getState();179 const state = useThreadSelectionStore.getState();
apps/web/src/uiStateStore.test.ts+17-17
...@@ -22,7 +22,7 @@ function makeUiState(overrides: Partial<UiState> = {}): UiState {...@@ -22,7 +22,7 @@ function makeUiState(overrides: Partial<UiState> = {}): UiState {
2222
23describe("uiStateStore pure functions", () => {23describe("uiStateStore pure functions", () => {
24 it("markThreadUnread moves lastVisitedAt before completion for a completed thread", () => {24 it("markThreadUnread moves lastVisitedAt before completion for a completed thread", () => {
25 const threadId = ThreadId.makeUnsafe("thread-1");25 const threadId = ThreadId.make("thread-1");
26 const latestTurnCompletedAt = "2026-02-25T12:30:00.000Z";26 const latestTurnCompletedAt = "2026-02-25T12:30:00.000Z";
27 const initialState = makeUiState({27 const initialState = makeUiState({
28 threadLastVisitedAtById: {28 threadLastVisitedAtById: {
...@@ -36,7 +36,7 @@ describe("uiStateStore pure functions", () => {...@@ -36,7 +36,7 @@ describe("uiStateStore pure functions", () => {
36 });36 });
3737
38 it("markThreadUnread does not change a thread without a completed turn", () => {38 it("markThreadUnread does not change a thread without a completed turn", () => {
39 const threadId = ThreadId.makeUnsafe("thread-1");39 const threadId = ThreadId.make("thread-1");
40 const initialState = makeUiState({40 const initialState = makeUiState({
41 threadLastVisitedAtById: {41 threadLastVisitedAtById: {
42 [threadId]: "2026-02-25T12:35:00.000Z",42 [threadId]: "2026-02-25T12:35:00.000Z",
...@@ -49,9 +49,9 @@ describe("uiStateStore pure functions", () => {...@@ -49,9 +49,9 @@ describe("uiStateStore pure functions", () => {
49 });49 });
5050
51 it("reorderProjects moves a project to a target index", () => {51 it("reorderProjects moves a project to a target index", () => {
52 const project1 = ProjectId.makeUnsafe("project-1");52 const project1 = ProjectId.make("project-1");
53 const project2 = ProjectId.makeUnsafe("project-2");53 const project2 = ProjectId.make("project-2");
54 const project3 = ProjectId.makeUnsafe("project-3");54 const project3 = ProjectId.make("project-3");
55 const initialState = makeUiState({55 const initialState = makeUiState({
56 projectOrder: [project1, project2, project3],56 projectOrder: [project1, project2, project3],
57 });57 });
...@@ -62,9 +62,9 @@ describe("uiStateStore pure functions", () => {...@@ -62,9 +62,9 @@ describe("uiStateStore pure functions", () => {
62 });62 });
6363
64 it("syncProjects preserves current project order during snapshot recovery", () => {64 it("syncProjects preserves current project order during snapshot recovery", () => {
65 const project1 = ProjectId.makeUnsafe("project-1");65 const project1 = ProjectId.make("project-1");
66 const project2 = ProjectId.makeUnsafe("project-2");66 const project2 = ProjectId.make("project-2");
67 const project3 = ProjectId.makeUnsafe("project-3");67 const project3 = ProjectId.make("project-3");
68 const initialState = makeUiState({68 const initialState = makeUiState({
69 projectExpandedById: {69 projectExpandedById: {
70 [project1]: true,70 [project1]: true,
...@@ -84,9 +84,9 @@ describe("uiStateStore pure functions", () => {...@@ -84,9 +84,9 @@ describe("uiStateStore pure functions", () => {
84 });84 });
8585
86 it("syncProjects preserves manual order when a project is recreated with the same cwd", () => {86 it("syncProjects preserves manual order when a project is recreated with the same cwd", () => {
87 const oldProject1 = ProjectId.makeUnsafe("project-1");87 const oldProject1 = ProjectId.make("project-1");
88 const oldProject2 = ProjectId.makeUnsafe("project-2");88 const oldProject2 = ProjectId.make("project-2");
89 const recreatedProject2 = ProjectId.makeUnsafe("project-2b");89 const recreatedProject2 = ProjectId.make("project-2b");
90 const initialState = syncProjects(90 const initialState = syncProjects(
91 makeUiState({91 makeUiState({
92 projectExpandedById: {92 projectExpandedById: {
...@@ -111,7 +111,7 @@ describe("uiStateStore pure functions", () => {...@@ -111,7 +111,7 @@ describe("uiStateStore pure functions", () => {
111 });111 });
112112
113 it("syncProjects returns a new state when only project cwd changes", () => {113 it("syncProjects returns a new state when only project cwd changes", () => {
114 const project1 = ProjectId.makeUnsafe("project-1");114 const project1 = ProjectId.make("project-1");
115 const initialState = syncProjects(115 const initialState = syncProjects(
116 makeUiState({116 makeUiState({
117 projectExpandedById: {117 projectExpandedById: {
...@@ -130,8 +130,8 @@ describe("uiStateStore pure functions", () => {...@@ -130,8 +130,8 @@ describe("uiStateStore pure functions", () => {
130 });130 });
131131
132 it("syncThreads prunes missing thread UI state", () => {132 it("syncThreads prunes missing thread UI state", () => {
133 const thread1 = ThreadId.makeUnsafe("thread-1");133 const thread1 = ThreadId.make("thread-1");
134 const thread2 = ThreadId.makeUnsafe("thread-2");134 const thread2 = ThreadId.make("thread-2");
135 const initialState = makeUiState({135 const initialState = makeUiState({
136 threadLastVisitedAtById: {136 threadLastVisitedAtById: {
137 [thread1]: "2026-02-25T12:35:00.000Z",137 [thread1]: "2026-02-25T12:35:00.000Z",
...@@ -147,7 +147,7 @@ describe("uiStateStore pure functions", () => {...@@ -147,7 +147,7 @@ describe("uiStateStore pure functions", () => {
147 });147 });
148148
149 it("syncThreads seeds visit state for unseen snapshot threads", () => {149 it("syncThreads seeds visit state for unseen snapshot threads", () => {
150 const thread1 = ThreadId.makeUnsafe("thread-1");150 const thread1 = ThreadId.make("thread-1");
151 const initialState = makeUiState();151 const initialState = makeUiState();
152152
153 const next = syncThreads(initialState, [153 const next = syncThreads(initialState, [
...@@ -163,7 +163,7 @@ describe("uiStateStore pure functions", () => {...@@ -163,7 +163,7 @@ describe("uiStateStore pure functions", () => {
163 });163 });
164164
165 it("setProjectExpanded updates expansion without touching order", () => {165 it("setProjectExpanded updates expansion without touching order", () => {
166 const project1 = ProjectId.makeUnsafe("project-1");166 const project1 = ProjectId.make("project-1");
167 const initialState = makeUiState({167 const initialState = makeUiState({
168 projectExpandedById: {168 projectExpandedById: {
169 [project1]: true,169 [project1]: true,
...@@ -178,7 +178,7 @@ describe("uiStateStore pure functions", () => {...@@ -178,7 +178,7 @@ describe("uiStateStore pure functions", () => {
178 });178 });
179179
180 it("clearThreadUi removes visit state for deleted threads", () => {180 it("clearThreadUi removes visit state for deleted threads", () => {
181 const thread1 = ThreadId.makeUnsafe("thread-1");181 const thread1 = ThreadId.make("thread-1");
182 const initialState = makeUiState({182 const initialState = makeUiState({
183 threadLastVisitedAtById: {183 threadLastVisitedAtById: {
184 [thread1]: "2026-02-25T12:35:00.000Z",184 [thread1]: "2026-02-25T12:35:00.000Z",
apps/web/src/worktreeCleanup.test.ts+12-12
...@@ -4,14 +4,14 @@ import { describe, expect, it } from "vitest";...@@ -4,14 +4,14 @@ import { describe, expect, it } from "vitest";
4import { DEFAULT_INTERACTION_MODE, DEFAULT_RUNTIME_MODE, type Thread } from "./types";4import { DEFAULT_INTERACTION_MODE, DEFAULT_RUNTIME_MODE, type Thread } from "./types";
5import { formatWorktreePathForDisplay, getOrphanedWorktreePathForThread } from "./worktreeCleanup";5import { formatWorktreePathForDisplay, getOrphanedWorktreePathForThread } from "./worktreeCleanup";
66
7const localEnvironmentId = EnvironmentId.makeUnsafe("environment-local");7const localEnvironmentId = EnvironmentId.make("environment-local");
88
9function makeThread(overrides: Partial<Thread> = {}): Thread {9function makeThread(overrides: Partial<Thread> = {}): Thread {
10 return {10 return {
11 id: ThreadId.makeUnsafe("thread-1"),11 id: ThreadId.make("thread-1"),
12 environmentId: localEnvironmentId,12 environmentId: localEnvironmentId,
13 codexThreadId: null,13 codexThreadId: null,
14 projectId: ProjectId.makeUnsafe("project-1"),14 projectId: ProjectId.make("project-1"),
15 title: "Thread",15 title: "Thread",
16 modelSelection: {16 modelSelection: {
17 provider: "codex",17 provider: "codex",
...@@ -36,49 +36,49 @@ function makeThread(overrides: Partial<Thread> = {}): Thread {...@@ -36,49 +36,49 @@ function makeThread(overrides: Partial<Thread> = {}): Thread {
3636
37describe("getOrphanedWorktreePathForThread", () => {37describe("getOrphanedWorktreePathForThread", () => {
38 it("returns null when the target thread does not exist", () => {38 it("returns null when the target thread does not exist", () => {
39 const result = getOrphanedWorktreePathForThread([], ThreadId.makeUnsafe("missing-thread"));39 const result = getOrphanedWorktreePathForThread([], ThreadId.make("missing-thread"));
40 expect(result).toBeNull();40 expect(result).toBeNull();
41 });41 });
4242
43 it("returns null when the target thread has no worktree", () => {43 it("returns null when the target thread has no worktree", () => {
44 const threads = [makeThread()];44 const threads = [makeThread()];
45 const result = getOrphanedWorktreePathForThread(threads, ThreadId.makeUnsafe("thread-1"));45 const result = getOrphanedWorktreePathForThread(threads, ThreadId.make("thread-1"));
46 expect(result).toBeNull();46 expect(result).toBeNull();
47 });47 });
4848
49 it("returns the path when no other thread links to that worktree", () => {49 it("returns the path when no other thread links to that worktree", () => {
50 const threads = [makeThread({ worktreePath: "/tmp/repo/worktrees/feature-a" })];50 const threads = [makeThread({ worktreePath: "/tmp/repo/worktrees/feature-a" })];
51 const result = getOrphanedWorktreePathForThread(threads, ThreadId.makeUnsafe("thread-1"));51 const result = getOrphanedWorktreePathForThread(threads, ThreadId.make("thread-1"));
52 expect(result).toBe("/tmp/repo/worktrees/feature-a");52 expect(result).toBe("/tmp/repo/worktrees/feature-a");
53 });53 });
5454
55 it("returns null when another thread links to the same worktree", () => {55 it("returns null when another thread links to the same worktree", () => {
56 const threads = [56 const threads = [
57 makeThread({57 makeThread({
58 id: ThreadId.makeUnsafe("thread-1"),58 id: ThreadId.make("thread-1"),
59 worktreePath: "/tmp/repo/worktrees/feature-a",59 worktreePath: "/tmp/repo/worktrees/feature-a",
60 }),60 }),
61 makeThread({61 makeThread({
62 id: ThreadId.makeUnsafe("thread-2"),62 id: ThreadId.make("thread-2"),
63 worktreePath: "/tmp/repo/worktrees/feature-a",63 worktreePath: "/tmp/repo/worktrees/feature-a",
64 }),64 }),
65 ];65 ];
66 const result = getOrphanedWorktreePathForThread(threads, ThreadId.makeUnsafe("thread-1"));66 const result = getOrphanedWorktreePathForThread(threads, ThreadId.make("thread-1"));
67 expect(result).toBeNull();67 expect(result).toBeNull();
68 });68 });
6969
70 it("ignores threads linked to different worktrees", () => {70 it("ignores threads linked to different worktrees", () => {
71 const threads = [71 const threads = [
72 makeThread({72 makeThread({
73 id: ThreadId.makeUnsafe("thread-1"),73 id: ThreadId.make("thread-1"),
74 worktreePath: "/tmp/repo/worktrees/feature-a",74 worktreePath: "/tmp/repo/worktrees/feature-a",
75 }),75 }),
76 makeThread({76 makeThread({
77 id: ThreadId.makeUnsafe("thread-2"),77 id: ThreadId.make("thread-2"),
78 worktreePath: "/tmp/repo/worktrees/feature-b",78 worktreePath: "/tmp/repo/worktrees/feature-b",
79 }),79 }),
80 ];80 ];
81 const result = getOrphanedWorktreePathForThread(threads, ThreadId.makeUnsafe("thread-1"));81 const result = getOrphanedWorktreePathForThread(threads, ThreadId.make("thread-1"));
82 expect(result).toBe("/tmp/repo/worktrees/feature-a");82 expect(result).toBe("/tmp/repo/worktrees/feature-a");
83 });83 });
84});84});
apps/web/test/authHttpHandlers.ts+1-1
...@@ -7,7 +7,7 @@ import { HttpResponse, http } from "msw";...@@ -7,7 +7,7 @@ import { HttpResponse, http } from "msw";
77
8const TEST_SESSION_EXPIRES_AT = "2026-05-01T12:00:00.000Z";8const TEST_SESSION_EXPIRES_AT = "2026-05-01T12:00:00.000Z";
9const TEST_ENVIRONMENT_DESCRIPTOR: ExecutionEnvironmentDescriptor = {9const TEST_ENVIRONMENT_DESCRIPTOR: ExecutionEnvironmentDescriptor = {
10 environmentId: EnvironmentId.makeUnsafe("environment-local"),10 environmentId: EnvironmentId.make("environment-local"),
11 label: "Local environment",11 label: "Local environment",
12 platform: {12 platform: {
13 os: "darwin",13 os: "darwin",
bun.lock+14-14
...@@ -189,16 +189,16 @@...@@ -189,16 +189,16 @@
189 "vite": "^8.0.0",189 "vite": "^8.0.0",
190 },190 },
191 "catalog": {191 "catalog": {
192 "@effect/atom-react": "4.0.0-beta.43",192 "@effect/atom-react": "4.0.0-beta.45",
193 "@effect/language-service": "0.84.2",193 "@effect/language-service": "0.84.2",
194 "@effect/platform-bun": "4.0.0-beta.43",194 "@effect/platform-bun": "4.0.0-beta.45",
195 "@effect/platform-node": "4.0.0-beta.43",195 "@effect/platform-node": "4.0.0-beta.45",
196 "@effect/platform-node-shared": "4.0.0-beta.43",196 "@effect/platform-node-shared": "4.0.0-beta.45",
197 "@effect/sql-sqlite-bun": "4.0.0-beta.43",197 "@effect/sql-sqlite-bun": "4.0.0-beta.45",
198 "@effect/vitest": "4.0.0-beta.43",198 "@effect/vitest": "4.0.0-beta.45",
199 "@types/bun": "^1.3.9",199 "@types/bun": "^1.3.9",
200 "@types/node": "^24.10.13",200 "@types/node": "^24.10.13",
201 "effect": "4.0.0-beta.43",201 "effect": "4.0.0-beta.45",
202 "tsdown": "^0.20.3",202 "tsdown": "^0.20.3",
203 "typescript": "^5.7.3",203 "typescript": "^5.7.3",
204 "vitest": "^4.0.0",204 "vitest": "^4.0.0",
...@@ -284,19 +284,19 @@...@@ -284,19 +284,19 @@
284284
285 "@dnd-kit/utilities": ["@dnd-kit/utilities@3.2.2", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "react": ">=16.8.0" } }, "sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg=="],285 "@dnd-kit/utilities": ["@dnd-kit/utilities@3.2.2", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "react": ">=16.8.0" } }, "sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg=="],
286286
287 "@effect/atom-react": ["@effect/atom-react@4.0.0-beta.43", "", { "peerDependencies": { "effect": "^4.0.0-beta.43", "react": "^19.2.4", "scheduler": "*" } }, "sha512-xSrRbGXuo4d0g4ph66TQST1GNSjtQZrZj8V7OiAQFuzMYcZ0kwRIPUUFwOBtbWHK43/zNENdNWOnlXh/iYM1dw=="],287 "@effect/atom-react": ["@effect/atom-react@4.0.0-beta.45", "", { "peerDependencies": { "effect": "^4.0.0-beta.45", "react": "^19.2.4", "scheduler": "*" } }, "sha512-jCoiae9UNeis9Yoal1p5V/P4eJK9nX89wORom7X5LpR2Ok920SZJc8dIeYkRXRAp9BCYJ/yWcO8gGLf7do71vg=="],
288288
289 "@effect/language-service": ["@effect/language-service@0.84.2", "", { "bin": { "effect-language-service": "cli.js" } }, "sha512-l04qNxpiA8rY5yXWckRPJ7Mk5MNerXuNymSFf+IdflfI5i8jgL1bpBNLuP6ijg7wgjdHc/KmTnCj2kT0SCntuA=="],289 "@effect/language-service": ["@effect/language-service@0.84.2", "", { "bin": { "effect-language-service": "cli.js" } }, "sha512-l04qNxpiA8rY5yXWckRPJ7Mk5MNerXuNymSFf+IdflfI5i8jgL1bpBNLuP6ijg7wgjdHc/KmTnCj2kT0SCntuA=="],
290290
291 "@effect/platform-bun": ["@effect/platform-bun@4.0.0-beta.43", "", { "dependencies": { "@effect/platform-node-shared": "^4.0.0-beta.43" }, "peerDependencies": { "effect": "^4.0.0-beta.43" } }, "sha512-nMZ9JsD6CzJNQ+5pDUFbPw7PSZdQdTQ092MbYrocVtvlf6qEFU/hji3ITvRIOX7eabyQ8AUyp55qFPQUeq+GIA=="],291 "@effect/platform-bun": ["@effect/platform-bun@4.0.0-beta.45", "", { "dependencies": { "@effect/platform-node-shared": "^4.0.0-beta.45" }, "peerDependencies": { "effect": "^4.0.0-beta.45" } }, "sha512-dZsbkJ+o4JfKE1OIAlMq8YWhmEfXxtTYennYADK/M/XlOtkFLsc2jkZppDvMznVni6JpFxWc7wd5rUf8jBVP5g=="],
292292
293 "@effect/platform-node": ["@effect/platform-node@4.0.0-beta.43", "", { "dependencies": { "@effect/platform-node-shared": "^4.0.0-beta.43", "mime": "^4.1.0", "undici": "^7.24.0" }, "peerDependencies": { "effect": "^4.0.0-beta.43", "ioredis": "^5.7.0" } }, "sha512-Uq6E1rjaIpjHauzjwoB2HzAg3battYt2Boy8XO50GoHiWCXKE6WapYZ0/AnaBx5v5qg2sOfqpuiLsUf9ZgxOkA=="],293 "@effect/platform-node": ["@effect/platform-node@4.0.0-beta.45", "", { "dependencies": { "@effect/platform-node-shared": "^4.0.0-beta.45", "mime": "^4.1.0", "undici": "^7.24.0" }, "peerDependencies": { "effect": "^4.0.0-beta.45", "ioredis": "^5.7.0" } }, "sha512-P07NMG4eoy62iLX9szak0hkr7hrwgq5+XMkm7URVug/3zqfZVxEG2kxn9JzVK1lF5WbaVrEKwjt3IkEJxzSPtg=="],
294294
295 "@effect/platform-node-shared": ["@effect/platform-node-shared@4.0.0-beta.43", "", { "dependencies": { "@types/ws": "^8.18.1", "ws": "^8.19.0" }, "peerDependencies": { "effect": "^4.0.0-beta.43" } }, "sha512-A9q0GEb61pYcQ06Dr6gXj1nKlDI3KHsar1sk3qb1ZY+kVSR64tBAylI8zGon23KY+NPtTUj/sEIToB7jc3Qt5w=="],295 "@effect/platform-node-shared": ["@effect/platform-node-shared@4.0.0-beta.45", "", { "dependencies": { "@types/ws": "^8.18.1", "ws": "^8.19.0" }, "peerDependencies": { "effect": "^4.0.0-beta.45" } }, "sha512-0T4RG18o+aCVUSlmPxA7uAjHJkyE3MS/uRrR5kCNSZQNG3X71wdIbu82wE/MnV6+6YSSPDx/6TVdZWYkJF0JWw=="],
296296
297 "@effect/sql-sqlite-bun": ["@effect/sql-sqlite-bun@4.0.0-beta.43", "", { "peerDependencies": { "effect": "^4.0.0-beta.43" } }, "sha512-Ryb7x0rBjW5NHegbzvz9y2Yv4KpwZ1UiPk7/Rv+Mkux74Qkjdmotmrvy9P+M5A9TZyL2Av+GpaLTPJKsL0PG+Q=="],297 "@effect/sql-sqlite-bun": ["@effect/sql-sqlite-bun@4.0.0-beta.45", "", { "peerDependencies": { "effect": "^4.0.0-beta.45" } }, "sha512-sJBBztwI4YxjdjhdyjWelh3KKoZucmmZS2ONeNDIzYgqFlsrwEEUrhVkWf5ch9Io+kUUzeb6q7/SedFoLUWO7g=="],
298298
299 "@effect/vitest": ["@effect/vitest@4.0.0-beta.43", "", { "peerDependencies": { "effect": "^4.0.0-beta.43", "vitest": "^3.0.0 || ^4.0.0" } }, "sha512-XN2LAwiUWPqbV2jrsYYRjrVydQ8MIgwr83MVImtUaOQco4vk43+8OHlXQMRN/u2HnGK29KT+O2yTMMBdk2Q6Sw=="],299 "@effect/vitest": ["@effect/vitest@4.0.0-beta.45", "", { "peerDependencies": { "effect": "^4.0.0-beta.45", "vitest": "^3.0.0 || ^4.0.0" } }, "sha512-38JA5Z6c2FoEhpVKbl7rjTrMU8Al5T30Kwtb6N3B9kOkyr2SsmvNsP9T65bh03IBh3ak9U//acrkmjS3hLQwKA=="],
300300
301 "@electron/get": ["@electron/get@2.0.3", "", { "dependencies": { "debug": "^4.1.1", "env-paths": "^2.2.0", "fs-extra": "^8.1.0", "got": "^11.8.5", "progress": "^2.0.3", "semver": "^6.2.0", "sumchecker": "^3.0.1" }, "optionalDependencies": { "global-agent": "^3.0.0" } }, "sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ=="],301 "@electron/get": ["@electron/get@2.0.3", "", { "dependencies": { "debug": "^4.1.1", "env-paths": "^2.2.0", "fs-extra": "^8.1.0", "got": "^11.8.5", "progress": "^2.0.3", "semver": "^6.2.0", "sumchecker": "^3.0.1" }, "optionalDependencies": { "global-agent": "^3.0.0" } }, "sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ=="],
302302
...@@ -1018,7 +1018,7 @@...@@ -1018,7 +1018,7 @@
10181018
1019 "dts-resolver": ["dts-resolver@2.1.3", "", { "peerDependencies": { "oxc-resolver": ">=11.0.0" }, "optionalPeers": ["oxc-resolver"] }, "sha512-bihc7jPC90VrosXNzK0LTE2cuLP6jr0Ro8jk+kMugHReJVLIpHz/xadeq3MhuwyO4TD4OA3L1Q8pBBFRc08Tsw=="],1019 "dts-resolver": ["dts-resolver@2.1.3", "", { "peerDependencies": { "oxc-resolver": ">=11.0.0" }, "optionalPeers": ["oxc-resolver"] }, "sha512-bihc7jPC90VrosXNzK0LTE2cuLP6jr0Ro8jk+kMugHReJVLIpHz/xadeq3MhuwyO4TD4OA3L1Q8pBBFRc08Tsw=="],
10201020
1021 "effect": ["effect@4.0.0-beta.43", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "fast-check": "^4.5.3", "find-my-way-ts": "^0.1.6", "ini": "^6.0.0", "kubernetes-types": "^1.30.0", "msgpackr": "^1.11.8", "multipasta": "^0.2.7", "toml": "^3.0.0", "uuid": "^13.0.0", "yaml": "^2.8.2" } }, "sha512-AJYyDimIwJOn87uUz/JzmgDc5GfjxJbXvEbTvNzMa+M3Uer344bLo/O5mMRkqc1vBleA+Ygs4+dbE3QsqOkKTQ=="],1021 "effect": ["effect@4.0.0-beta.45", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "fast-check": "^4.5.3", "find-my-way-ts": "^0.1.6", "ini": "^6.0.0", "kubernetes-types": "^1.30.0", "msgpackr": "^1.11.8", "multipasta": "^0.2.7", "toml": "^3.0.0", "uuid": "^13.0.0", "yaml": "^2.8.2" } }, "sha512-vvNrUWqnzBIW1hRMa+zw0CLRW6HLgdu7hQ6K7PT/rS+UY/73Ma11O+Oi9oc9zwL8KcN37M47UDseAdlF0bGNWw=="],
10221022
1023 "electron": ["electron@40.6.0", "", { "dependencies": { "@electron/get": "^2.0.0", "@types/node": "^24.9.0", "extract-zip": "^2.0.1" }, "bin": { "electron": "cli.js" } }, "sha512-ett8W+yOFGDuM0vhJMamYSkrbV3LoaffzJd9GfjI96zRAxyrNqUSKqBpf/WGbQCweDxX2pkUCUfrv4wwKpsFZA=="],1023 "electron": ["electron@40.6.0", "", { "dependencies": { "@electron/get": "^2.0.0", "@types/node": "^24.9.0", "extract-zip": "^2.0.1" }, "bin": { "electron": "cli.js" } }, "sha512-ett8W+yOFGDuM0vhJMamYSkrbV3LoaffzJd9GfjI96zRAxyrNqUSKqBpf/WGbQCweDxX2pkUCUfrv4wwKpsFZA=="],
10241024
package.json+7-7
...@@ -8,13 +8,13 @@...@@ -8,13 +8,13 @@
8 "scripts"8 "scripts"
9 ],9 ],
10 "catalog": {10 "catalog": {
11 "effect": "4.0.0-beta.43",11 "effect": "4.0.0-beta.45",
12 "@effect/atom-react": "4.0.0-beta.43",12 "@effect/atom-react": "4.0.0-beta.45",
13 "@effect/platform-bun": "4.0.0-beta.43",13 "@effect/platform-bun": "4.0.0-beta.45",
14 "@effect/platform-node": "4.0.0-beta.43",14 "@effect/platform-node": "4.0.0-beta.45",
15 "@effect/platform-node-shared": "4.0.0-beta.43",15 "@effect/platform-node-shared": "4.0.0-beta.45",
16 "@effect/sql-sqlite-bun": "4.0.0-beta.43",16 "@effect/sql-sqlite-bun": "4.0.0-beta.45",
17 "@effect/vitest": "4.0.0-beta.43",17 "@effect/vitest": "4.0.0-beta.45",
18 "@effect/language-service": "0.84.2",18 "@effect/language-service": "0.84.2",
19 "@types/bun": "^1.3.9",19 "@types/bun": "^1.3.9",
20 "@types/node": "^24.10.13",20 "@types/node": "^24.10.13",
packages/client-runtime/src/knownEnvironment.test.ts+5-5
...@@ -61,9 +61,9 @@ describe("known environment bootstrap helpers", () => {...@@ -61,9 +61,9 @@ describe("known environment bootstrap helpers", () => {
61});61});
6262
63describe("scoped refs", () => {63describe("scoped refs", () => {
64 const environmentId = EnvironmentId.makeUnsafe("environment-test");64 const environmentId = EnvironmentId.make("environment-test");
65 const projectRef = scopeProjectRef(environmentId, ProjectId.makeUnsafe("project-1"));65 const projectRef = scopeProjectRef(environmentId, ProjectId.make("project-1"));
66 const threadRef = scopeThreadRef(environmentId, ThreadId.makeUnsafe("thread-1"));66 const threadRef = scopeThreadRef(environmentId, ThreadId.make("thread-1"));
6767
68 it("builds stable scoped project and thread keys", () => {68 it("builds stable scoped project and thread keys", () => {
69 expect(scopedRefKey(projectRef)).toBe("environment-test:project-1");69 expect(scopedRefKey(projectRef)).toBe("environment-test:project-1");
...@@ -75,11 +75,11 @@ describe("scoped refs", () => {...@@ -75,11 +75,11 @@ describe("scoped refs", () => {
75 it("returns typed scoped refs", () => {75 it("returns typed scoped refs", () => {
76 expect(projectRef).toEqual({76 expect(projectRef).toEqual({
77 environmentId,77 environmentId,
78 projectId: ProjectId.makeUnsafe("project-1"),78 projectId: ProjectId.make("project-1"),
79 });79 });
80 expect(threadRef).toEqual({80 expect(threadRef).toEqual({
81 environmentId,81 environmentId,
82 threadId: ThreadId.makeUnsafe("thread-1"),82 threadId: ThreadId.make("thread-1"),
83 });83 });
84 });84 });
8585
packages/contracts/src/baseSchemas.ts+3-2
...@@ -12,8 +12,9 @@ export type IsoDateTime = typeof IsoDateTime.Type;...@@ -12,8 +12,9 @@ export type IsoDateTime = typeof IsoDateTime.Type;
12/**12/**
13 * Construct a branded identifier. Enforces non-empty trimmed strings13 * Construct a branded identifier. Enforces non-empty trimmed strings
14 */14 */
15const makeEntityId = <Brand extends string>(brand: Brand) =>15const makeEntityId = <Brand extends string>(brand: Brand) => {
16 TrimmedNonEmptyString.pipe(Schema.brand(brand));16 return TrimmedNonEmptyString.pipe(Schema.brand(brand));
17};
1718
18export const ThreadId = makeEntityId("ThreadId");19export const ThreadId = makeEntityId("ThreadId");
19export type ThreadId = typeof ThreadId.Type;20export type ThreadId = typeof ThreadId.Type;
packages/contracts/src/environment.ts+2-2
...@@ -1,4 +1,4 @@...@@ -1,4 +1,4 @@
1import { Schema } from "effect";1import { Effect, Schema } from "effect";
22
3import { EnvironmentId, ProjectId, ThreadId, TrimmedNonEmptyString } from "./baseSchemas";3import { EnvironmentId, ProjectId, ThreadId, TrimmedNonEmptyString } from "./baseSchemas";
44
...@@ -20,7 +20,7 @@ export const ExecutionEnvironmentPlatform = Schema.Struct({...@@ -20,7 +20,7 @@ export const ExecutionEnvironmentPlatform = Schema.Struct({
20export type ExecutionEnvironmentPlatform = typeof ExecutionEnvironmentPlatform.Type;20export type ExecutionEnvironmentPlatform = typeof ExecutionEnvironmentPlatform.Type;
2121
22export const ExecutionEnvironmentCapabilities = Schema.Struct({22export const ExecutionEnvironmentCapabilities = Schema.Struct({
23 repositoryIdentity: Schema.Boolean.pipe(Schema.withDecodingDefault(() => false)),23 repositoryIdentity: Schema.Boolean.pipe(Schema.withDecodingDefault(Effect.succeed(false))),
24});24});
25export type ExecutionEnvironmentCapabilities = typeof ExecutionEnvironmentCapabilities.Type;25export type ExecutionEnvironmentCapabilities = typeof ExecutionEnvironmentCapabilities.Type;
2626
packages/contracts/src/orchestration.ts+19-15
...@@ -1,4 +1,4 @@...@@ -1,4 +1,4 @@
1import { Option, Schema, SchemaIssue, Struct } from "effect";1import { Effect, Option, Schema, SchemaIssue, Struct } from "effect";
2import { ClaudeModelOptions, CodexModelOptions } from "./model";2import { ClaudeModelOptions, CodexModelOptions } from "./model";
3import { RepositoryIdentity } from "./environment";3import { RepositoryIdentity } from "./environment";
4import {4import {
...@@ -177,8 +177,10 @@ export const OrchestrationProposedPlan = Schema.Struct({...@@ -177,8 +177,10 @@ export const OrchestrationProposedPlan = Schema.Struct({
177 id: OrchestrationProposedPlanId,177 id: OrchestrationProposedPlanId,
178 turnId: Schema.NullOr(TurnId),178 turnId: Schema.NullOr(TurnId),
179 planMarkdown: TrimmedNonEmptyString,179 planMarkdown: TrimmedNonEmptyString,
180 implementedAt: Schema.NullOr(IsoDateTime).pipe(Schema.withDecodingDefault(() => null)),180 implementedAt: Schema.NullOr(IsoDateTime).pipe(Schema.withDecodingDefault(Effect.succeed(null))),
181 implementationThreadId: Schema.NullOr(ThreadId).pipe(Schema.withDecodingDefault(() => null)),181 implementationThreadId: Schema.NullOr(ThreadId).pipe(
182 Schema.withDecodingDefault(Effect.succeed(null)),
183 ),
182 createdAt: IsoDateTime,184 createdAt: IsoDateTime,
183 updatedAt: IsoDateTime,185 updatedAt: IsoDateTime,
184});186});
...@@ -204,7 +206,7 @@ export const OrchestrationSession = Schema.Struct({...@@ -204,7 +206,7 @@ export const OrchestrationSession = Schema.Struct({
204 threadId: ThreadId,206 threadId: ThreadId,
205 status: OrchestrationSessionStatus,207 status: OrchestrationSessionStatus,
206 providerName: Schema.NullOr(TrimmedNonEmptyString),208 providerName: Schema.NullOr(TrimmedNonEmptyString),
207 runtimeMode: RuntimeMode.pipe(Schema.withDecodingDefault(() => DEFAULT_RUNTIME_MODE)),209 runtimeMode: RuntimeMode.pipe(Schema.withDecodingDefault(Effect.succeed(DEFAULT_RUNTIME_MODE))),
208 activeTurnId: Schema.NullOr(TurnId),210 activeTurnId: Schema.NullOr(TurnId),
209 lastError: Schema.NullOr(TrimmedNonEmptyString),211 lastError: Schema.NullOr(TrimmedNonEmptyString),
210 updatedAt: IsoDateTime,212 updatedAt: IsoDateTime,
...@@ -279,17 +281,19 @@ export const OrchestrationThread = Schema.Struct({...@@ -279,17 +281,19 @@ export const OrchestrationThread = Schema.Struct({
279 modelSelection: ModelSelection,281 modelSelection: ModelSelection,
280 runtimeMode: RuntimeMode,282 runtimeMode: RuntimeMode,
281 interactionMode: ProviderInteractionMode.pipe(283 interactionMode: ProviderInteractionMode.pipe(
282 Schema.withDecodingDefault(() => DEFAULT_PROVIDER_INTERACTION_MODE),284 Schema.withDecodingDefault(Effect.succeed(DEFAULT_PROVIDER_INTERACTION_MODE)),
283 ),285 ),
284 branch: Schema.NullOr(TrimmedNonEmptyString),286 branch: Schema.NullOr(TrimmedNonEmptyString),
285 worktreePath: Schema.NullOr(TrimmedNonEmptyString),287 worktreePath: Schema.NullOr(TrimmedNonEmptyString),
286 latestTurn: Schema.NullOr(OrchestrationLatestTurn),288 latestTurn: Schema.NullOr(OrchestrationLatestTurn),
287 createdAt: IsoDateTime,289 createdAt: IsoDateTime,
288 updatedAt: IsoDateTime,290 updatedAt: IsoDateTime,
289 archivedAt: Schema.NullOr(IsoDateTime).pipe(Schema.withDecodingDefault(() => null)),291 archivedAt: Schema.NullOr(IsoDateTime).pipe(Schema.withDecodingDefault(Effect.succeed(null))),
290 deletedAt: Schema.NullOr(IsoDateTime),292 deletedAt: Schema.NullOr(IsoDateTime),
291 messages: Schema.Array(OrchestrationMessage),293 messages: Schema.Array(OrchestrationMessage),
292 proposedPlans: Schema.Array(OrchestrationProposedPlan).pipe(Schema.withDecodingDefault(() => [])),294 proposedPlans: Schema.Array(OrchestrationProposedPlan).pipe(
295 Schema.withDecodingDefault(Effect.succeed([])),
296 ),
293 activities: Schema.Array(OrchestrationThreadActivity),297 activities: Schema.Array(OrchestrationThreadActivity),
294 checkpoints: Schema.Array(OrchestrationCheckpointSummary),298 checkpoints: Schema.Array(OrchestrationCheckpointSummary),
295 session: Schema.NullOr(OrchestrationSession),299 session: Schema.NullOr(OrchestrationSession),
...@@ -339,7 +343,7 @@ const ThreadCreateCommand = Schema.Struct({...@@ -339,7 +343,7 @@ const ThreadCreateCommand = Schema.Struct({
339 modelSelection: ModelSelection,343 modelSelection: ModelSelection,
340 runtimeMode: RuntimeMode,344 runtimeMode: RuntimeMode,
341 interactionMode: ProviderInteractionMode.pipe(345 interactionMode: ProviderInteractionMode.pipe(
342 Schema.withDecodingDefault(() => DEFAULT_PROVIDER_INTERACTION_MODE),346 Schema.withDecodingDefault(Effect.succeed(DEFAULT_PROVIDER_INTERACTION_MODE)),
343 ),347 ),
344 branch: Schema.NullOr(TrimmedNonEmptyString),348 branch: Schema.NullOr(TrimmedNonEmptyString),
345 worktreePath: Schema.NullOr(TrimmedNonEmptyString),349 worktreePath: Schema.NullOr(TrimmedNonEmptyString),
...@@ -427,9 +431,9 @@ export const ThreadTurnStartCommand = Schema.Struct({...@@ -427,9 +431,9 @@ export const ThreadTurnStartCommand = Schema.Struct({
427 }),431 }),
428 modelSelection: Schema.optional(ModelSelection),432 modelSelection: Schema.optional(ModelSelection),
429 titleSeed: Schema.optional(TrimmedNonEmptyString),433 titleSeed: Schema.optional(TrimmedNonEmptyString),
430 runtimeMode: RuntimeMode.pipe(Schema.withDecodingDefault(() => DEFAULT_RUNTIME_MODE)),434 runtimeMode: RuntimeMode.pipe(Schema.withDecodingDefault(Effect.succeed(DEFAULT_RUNTIME_MODE))),
431 interactionMode: ProviderInteractionMode.pipe(435 interactionMode: ProviderInteractionMode.pipe(
432 Schema.withDecodingDefault(() => DEFAULT_PROVIDER_INTERACTION_MODE),436 Schema.withDecodingDefault(Effect.succeed(DEFAULT_PROVIDER_INTERACTION_MODE)),
433 ),437 ),
434 bootstrap: Schema.optional(ThreadTurnStartBootstrap),438 bootstrap: Schema.optional(ThreadTurnStartBootstrap),
435 sourceProposedPlan: Schema.optional(SourceProposedPlanReference),439 sourceProposedPlan: Schema.optional(SourceProposedPlanReference),
...@@ -680,9 +684,9 @@ export const ThreadCreatedPayload = Schema.Struct({...@@ -680,9 +684,9 @@ export const ThreadCreatedPayload = Schema.Struct({
680 projectId: ProjectId,684 projectId: ProjectId,
681 title: TrimmedNonEmptyString,685 title: TrimmedNonEmptyString,
682 modelSelection: ModelSelection,686 modelSelection: ModelSelection,
683 runtimeMode: RuntimeMode.pipe(Schema.withDecodingDefault(() => DEFAULT_RUNTIME_MODE)),687 runtimeMode: RuntimeMode.pipe(Schema.withDecodingDefault(Effect.succeed(DEFAULT_RUNTIME_MODE))),
684 interactionMode: ProviderInteractionMode.pipe(688 interactionMode: ProviderInteractionMode.pipe(
685 Schema.withDecodingDefault(() => DEFAULT_PROVIDER_INTERACTION_MODE),689 Schema.withDecodingDefault(Effect.succeed(DEFAULT_PROVIDER_INTERACTION_MODE)),
686 ),690 ),
687 branch: Schema.NullOr(TrimmedNonEmptyString),691 branch: Schema.NullOr(TrimmedNonEmptyString),
688 worktreePath: Schema.NullOr(TrimmedNonEmptyString),692 worktreePath: Schema.NullOr(TrimmedNonEmptyString),
...@@ -724,7 +728,7 @@ export const ThreadRuntimeModeSetPayload = Schema.Struct({...@@ -724,7 +728,7 @@ export const ThreadRuntimeModeSetPayload = Schema.Struct({
724export const ThreadInteractionModeSetPayload = Schema.Struct({728export const ThreadInteractionModeSetPayload = Schema.Struct({
725 threadId: ThreadId,729 threadId: ThreadId,
726 interactionMode: ProviderInteractionMode.pipe(730 interactionMode: ProviderInteractionMode.pipe(
727 Schema.withDecodingDefault(() => DEFAULT_PROVIDER_INTERACTION_MODE),731 Schema.withDecodingDefault(Effect.succeed(DEFAULT_PROVIDER_INTERACTION_MODE)),
728 ),732 ),
729 updatedAt: IsoDateTime,733 updatedAt: IsoDateTime,
730});734});
...@@ -746,9 +750,9 @@ export const ThreadTurnStartRequestedPayload = Schema.Struct({...@@ -746,9 +750,9 @@ export const ThreadTurnStartRequestedPayload = Schema.Struct({
746 messageId: MessageId,750 messageId: MessageId,
747 modelSelection: Schema.optional(ModelSelection),751 modelSelection: Schema.optional(ModelSelection),
748 titleSeed: Schema.optional(TrimmedNonEmptyString),752 titleSeed: Schema.optional(TrimmedNonEmptyString),
749 runtimeMode: RuntimeMode.pipe(Schema.withDecodingDefault(() => DEFAULT_RUNTIME_MODE)),753 runtimeMode: RuntimeMode.pipe(Schema.withDecodingDefault(Effect.succeed(DEFAULT_RUNTIME_MODE))),
750 interactionMode: ProviderInteractionMode.pipe(754 interactionMode: ProviderInteractionMode.pipe(
751 Schema.withDecodingDefault(() => DEFAULT_PROVIDER_INTERACTION_MODE),755 Schema.withDecodingDefault(Effect.succeed(DEFAULT_PROVIDER_INTERACTION_MODE)),
752 ),756 ),
753 sourceProposedPlan: Schema.optional(SourceProposedPlanReference),757 sourceProposedPlan: Schema.optional(SourceProposedPlanReference),
754 createdAt: IsoDateTime,758 createdAt: IsoDateTime,
packages/contracts/src/providerRuntime.ts+2-2
...@@ -1,4 +1,4 @@...@@ -1,4 +1,4 @@
1import { Option, Schema } from "effect";1import { Effect, Schema } from "effect";
2import {2import {
3 EventId,3 EventId,
4 IsoDateTime,4 IsoDateTime,
...@@ -435,7 +435,7 @@ export const UserInputQuestion = Schema.Struct({...@@ -435,7 +435,7 @@ export const UserInputQuestion = Schema.Struct({
435 question: TrimmedNonEmptyStringSchema,435 question: TrimmedNonEmptyStringSchema,
436 options: Schema.Array(UserInputQuestionOption),436 options: Schema.Array(UserInputQuestionOption),
437 multiSelect: Schema.optional(Schema.Boolean).pipe(437 multiSelect: Schema.optional(Schema.Boolean).pipe(
438 Schema.withConstructorDefault(() => Option.some(false)),438 Schema.withConstructorDefault(Effect.succeed(false)),
439 ),439 ),
440});440});
441export type UserInputQuestion = typeof UserInputQuestion.Type;441export type UserInputQuestion = typeof UserInputQuestion.Type;
packages/contracts/src/settings.ts+28-24
...@@ -24,16 +24,18 @@ export type SidebarThreadSortOrder = typeof SidebarThreadSortOrder.Type;...@@ -24,16 +24,18 @@ export type SidebarThreadSortOrder = typeof SidebarThreadSortOrder.Type;
24export const DEFAULT_SIDEBAR_THREAD_SORT_ORDER: SidebarThreadSortOrder = "updated_at";24export const DEFAULT_SIDEBAR_THREAD_SORT_ORDER: SidebarThreadSortOrder = "updated_at";
2525
26export const ClientSettingsSchema = Schema.Struct({26export const ClientSettingsSchema = Schema.Struct({
27 confirmThreadArchive: Schema.Boolean.pipe(Schema.withDecodingDefault(() => false)),27 confirmThreadArchive: Schema.Boolean.pipe(Schema.withDecodingDefault(Effect.succeed(false))),
28 confirmThreadDelete: Schema.Boolean.pipe(Schema.withDecodingDefault(() => true)),28 confirmThreadDelete: Schema.Boolean.pipe(Schema.withDecodingDefault(Effect.succeed(true))),
29 diffWordWrap: Schema.Boolean.pipe(Schema.withDecodingDefault(() => false)),29 diffWordWrap: Schema.Boolean.pipe(Schema.withDecodingDefault(Effect.succeed(false))),
30 sidebarProjectSortOrder: SidebarProjectSortOrder.pipe(30 sidebarProjectSortOrder: SidebarProjectSortOrder.pipe(
31 Schema.withDecodingDefault(() => DEFAULT_SIDEBAR_PROJECT_SORT_ORDER),31 Schema.withDecodingDefault(Effect.succeed(DEFAULT_SIDEBAR_PROJECT_SORT_ORDER)),
32 ),32 ),
33 sidebarThreadSortOrder: SidebarThreadSortOrder.pipe(33 sidebarThreadSortOrder: SidebarThreadSortOrder.pipe(
34 Schema.withDecodingDefault(() => DEFAULT_SIDEBAR_THREAD_SORT_ORDER),34 Schema.withDecodingDefault(Effect.succeed(DEFAULT_SIDEBAR_THREAD_SORT_ORDER)),
35 ),
36 timestampFormat: TimestampFormat.pipe(
37 Schema.withDecodingDefault(Effect.succeed(DEFAULT_TIMESTAMP_FORMAT)),
35 ),38 ),
36 timestampFormat: TimestampFormat.pipe(Schema.withDecodingDefault(() => DEFAULT_TIMESTAMP_FORMAT)),
37});39});
38export type ClientSettings = typeof ClientSettingsSchema.Type;40export type ClientSettings = typeof ClientSettingsSchema.Type;
3941
...@@ -53,48 +55,50 @@ const makeBinaryPathSetting = (fallback: string) =>...@@ -53,48 +55,50 @@ const makeBinaryPathSetting = (fallback: string) =>
53 encode: (value) => Effect.succeed(value),55 encode: (value) => Effect.succeed(value),
54 }),56 }),
55 ),57 ),
56 Schema.withDecodingDefault(() => fallback),58 Schema.withDecodingDefault(Effect.succeed(fallback)),
57 );59 );
5860
59export const CodexSettings = Schema.Struct({61export const CodexSettings = Schema.Struct({
60 enabled: Schema.Boolean.pipe(Schema.withDecodingDefault(() => true)),62 enabled: Schema.Boolean.pipe(Schema.withDecodingDefault(Effect.succeed(true))),
61 binaryPath: makeBinaryPathSetting("codex"),63 binaryPath: makeBinaryPathSetting("codex"),
62 homePath: TrimmedString.pipe(Schema.withDecodingDefault(() => "")),64 homePath: TrimmedString.pipe(Schema.withDecodingDefault(Effect.succeed(""))),
63 customModels: Schema.Array(Schema.String).pipe(Schema.withDecodingDefault(() => [])),65 customModels: Schema.Array(Schema.String).pipe(Schema.withDecodingDefault(Effect.succeed([]))),
64});66});
65export type CodexSettings = typeof CodexSettings.Type;67export type CodexSettings = typeof CodexSettings.Type;
6668
67export const ClaudeSettings = Schema.Struct({69export const ClaudeSettings = Schema.Struct({
68 enabled: Schema.Boolean.pipe(Schema.withDecodingDefault(() => true)),70 enabled: Schema.Boolean.pipe(Schema.withDecodingDefault(Effect.succeed(true))),
69 binaryPath: makeBinaryPathSetting("claude"),71 binaryPath: makeBinaryPathSetting("claude"),
70 customModels: Schema.Array(Schema.String).pipe(Schema.withDecodingDefault(() => [])),72 customModels: Schema.Array(Schema.String).pipe(Schema.withDecodingDefault(Effect.succeed([]))),
71});73});
72export type ClaudeSettings = typeof ClaudeSettings.Type;74export type ClaudeSettings = typeof ClaudeSettings.Type;
7375
74export const ObservabilitySettings = Schema.Struct({76export const ObservabilitySettings = Schema.Struct({
75 otlpTracesUrl: TrimmedString.pipe(Schema.withDecodingDefault(() => "")),77 otlpTracesUrl: TrimmedString.pipe(Schema.withDecodingDefault(Effect.succeed(""))),
76 otlpMetricsUrl: TrimmedString.pipe(Schema.withDecodingDefault(() => "")),78 otlpMetricsUrl: TrimmedString.pipe(Schema.withDecodingDefault(Effect.succeed(""))),
77});79});
78export type ObservabilitySettings = typeof ObservabilitySettings.Type;80export type ObservabilitySettings = typeof ObservabilitySettings.Type;
7981
80export const ServerSettings = Schema.Struct({82export const ServerSettings = Schema.Struct({
81 enableAssistantStreaming: Schema.Boolean.pipe(Schema.withDecodingDefault(() => false)),83 enableAssistantStreaming: Schema.Boolean.pipe(Schema.withDecodingDefault(Effect.succeed(false))),
82 defaultThreadEnvMode: ThreadEnvMode.pipe(84 defaultThreadEnvMode: ThreadEnvMode.pipe(
83 Schema.withDecodingDefault(() => "local" as const satisfies ThreadEnvMode),85 Schema.withDecodingDefault(Effect.succeed("local" as const satisfies ThreadEnvMode)),
84 ),86 ),
85 textGenerationModelSelection: ModelSelection.pipe(87 textGenerationModelSelection: ModelSelection.pipe(
86 Schema.withDecodingDefault(() => ({88 Schema.withDecodingDefault(
87 provider: "codex" as const,89 Effect.succeed({
88 model: DEFAULT_GIT_TEXT_GENERATION_MODEL_BY_PROVIDER.codex,90 provider: "codex" as const,
89 })),91 model: DEFAULT_GIT_TEXT_GENERATION_MODEL_BY_PROVIDER.codex,
92 }),
93 ),
90 ),94 ),
9195
92 // Provider specific settings96 // Provider specific settings
93 providers: Schema.Struct({97 providers: Schema.Struct({
94 codex: CodexSettings.pipe(Schema.withDecodingDefault(() => ({}))),98 codex: CodexSettings.pipe(Schema.withDecodingDefault(Effect.succeed({}))),
95 claudeAgent: ClaudeSettings.pipe(Schema.withDecodingDefault(() => ({}))),99 claudeAgent: ClaudeSettings.pipe(Schema.withDecodingDefault(Effect.succeed({}))),
96 }).pipe(Schema.withDecodingDefault(() => ({}))),100 }).pipe(Schema.withDecodingDefault(Effect.succeed({}))),
97 observability: ObservabilitySettings.pipe(Schema.withDecodingDefault(() => ({}))),101 observability: ObservabilitySettings.pipe(Schema.withDecodingDefault(Effect.succeed({}))),
98});102});
99export type ServerSettings = typeof ServerSettings.Type;103export type ServerSettings = typeof ServerSettings.Type;
100104
packages/contracts/src/terminal.ts+2-2
...@@ -1,4 +1,4 @@...@@ -1,4 +1,4 @@
1import { Schema } from "effect";1import { Effect, Schema } from "effect";
2import { TrimmedNonEmptyString } from "./baseSchemas";2import { TrimmedNonEmptyString } from "./baseSchemas";
33
4export const DEFAULT_TERMINAL_ID = "default";4export const DEFAULT_TERMINAL_ID = "default";
...@@ -20,7 +20,7 @@ const TerminalEnvSchema = Schema.Record(TerminalEnvKeySchema, TerminalEnvValueSc...@@ -20,7 +20,7 @@ const TerminalEnvSchema = Schema.Record(TerminalEnvKeySchema, TerminalEnvValueSc
20);20);
2121
22const TerminalIdWithDefaultSchema = TerminalIdSchema.pipe(22const TerminalIdWithDefaultSchema = TerminalIdSchema.pipe(
23 Schema.withDecodingDefault(() => DEFAULT_TERMINAL_ID),23 Schema.withDecodingDefault(Effect.succeed(DEFAULT_TERMINAL_ID)),
24);24);
2525
26export const TerminalThreadInput = Schema.Struct({26export const TerminalThreadInput = Schema.Struct({
packages/shared/src/Net.ts+2-2
...@@ -1,6 +1,6 @@...@@ -1,6 +1,6 @@
1import * as Net from "node:net";1import * as Net from "node:net";
22
3import { Data, Effect, Layer, ServiceMap } from "effect";3import { Data, Effect, Layer, Context } from "effect";
44
5export class NetError extends Data.TaggedError("NetError")<{5export class NetError extends Data.TaggedError("NetError")<{
6 readonly message: string;6 readonly message: string;
...@@ -85,7 +85,7 @@ export interface NetServiceShape {...@@ -85,7 +85,7 @@ export interface NetServiceShape {
85/**85/**
86 * NetService - Service tag for startup networking helpers.86 * NetService - Service tag for startup networking helpers.
87 */87 */
88export class NetService extends ServiceMap.Service<NetService, NetServiceShape>()(88export class NetService extends Context.Service<NetService, NetServiceShape>()(
89 "@t3tools/shared/Net/NetService",89 "@t3tools/shared/Net/NetService",
90) {90) {
91 static readonly layer = Layer.sync(NetService, () => {91 static readonly layer = Layer.sync(NetService, () => {
scripts/build-desktop-artifact.test.ts created+45
...@@ -0,0 +1,45 @@
1import * as NodeServices from "@effect/platform-node/NodeServices";
2import { assert, it } from "@effect/vitest";
3import { ConfigProvider, Effect, Option } from "effect";
4
5import { resolveBuildOptions } from "./build-desktop-artifact.ts";
6
7it.layer(NodeServices.layer)("build-desktop-artifact", (it) => {
8 it.effect("preserves explicit false boolean flags over true env defaults", () =>
9 Effect.gen(function* () {
10 const resolved = yield* resolveBuildOptions({
11 platform: Option.some("mac"),
12 target: Option.none(),
13 arch: Option.some("arm64"),
14 buildVersion: Option.none(),
15 outputDir: Option.some("release-test"),
16 skipBuild: Option.some(false),
17 keepStage: Option.some(false),
18 signed: Option.some(false),
19 verbose: Option.some(false),
20 mockUpdates: Option.some(false),
21 mockUpdateServerPort: Option.none(),
22 }).pipe(
23 Effect.provide(
24 ConfigProvider.layer(
25 ConfigProvider.fromEnv({
26 env: {
27 T3CODE_DESKTOP_SKIP_BUILD: "true",
28 T3CODE_DESKTOP_KEEP_STAGE: "true",
29 T3CODE_DESKTOP_SIGNED: "true",
30 T3CODE_DESKTOP_VERBOSE: "true",
31 T3CODE_DESKTOP_MOCK_UPDATES: "true",
32 },
33 }),
34 ),
35 ),
36 );
37
38 assert.equal(resolved.skipBuild, false);
39 assert.equal(resolved.keepStage, false);
40 assert.equal(resolved.signed, false);
41 assert.equal(resolved.verbose, false);
42 assert.equal(resolved.mockUpdates, false);
43 }),
44 );
45});
scripts/build-desktop-artifact.ts+11-7
...@@ -214,11 +214,13 @@ const BuildEnvConfig = Config.all({...@@ -214,11 +214,13 @@ const BuildEnvConfig = Config.all({
214});214});
215215
216const resolveBooleanFlag = (flag: Option.Option<boolean>, envValue: boolean) =>216const resolveBooleanFlag = (flag: Option.Option<boolean>, envValue: boolean) =>
217 Option.getOrElse(Option.filter(flag, Boolean), () => envValue);217 Option.getOrElse(flag, () => envValue);
218const mergeOptions = <A>(a: Option.Option<A>, b: Option.Option<A>, defaultValue: A) =>218const mergeOptions = <A>(a: Option.Option<A>, b: Option.Option<A>, defaultValue: A) =>
219 Option.getOrElse(a, () => Option.getOrElse(b, () => defaultValue));219 Option.getOrElse(a, () => Option.getOrElse(b, () => defaultValue));
220220
221const resolveBuildOptions = Effect.fn("resolveBuildOptions")(function* (input: BuildCliInput) {221export const resolveBuildOptions = Effect.fn("resolveBuildOptions")(function* (
222 input: BuildCliInput,
223) {
222 const path = yield* Path.Path;224 const path = yield* Path.Path;
223 const repoRoot = yield* RepoRoot;225 const repoRoot = yield* RepoRoot;
224 const env = yield* BuildEnvConfig.asEffect();226 const env = yield* BuildEnvConfig.asEffect();
...@@ -838,8 +840,10 @@ const buildDesktopArtifactCli = Command.make("build-desktop-artifact", {...@@ -838,8 +840,10 @@ const buildDesktopArtifactCli = Command.make("build-desktop-artifact", {
838840
839const cliRuntimeLayer = Layer.mergeAll(Logger.layer([Logger.consolePretty()]), NodeServices.layer);841const cliRuntimeLayer = Layer.mergeAll(Logger.layer([Logger.consolePretty()]), NodeServices.layer);
840842
841Command.run(buildDesktopArtifactCli, { version: "0.0.0" }).pipe(843if (import.meta.main) {
842 Effect.scoped,844 Command.run(buildDesktopArtifactCli, { version: "0.0.0" }).pipe(
843 Effect.provide(cliRuntimeLayer),845 Effect.scoped,
844 NodeRuntime.runMain,846 Effect.provide(cliRuntimeLayer),
845);847 NodeRuntime.runMain,
848 );
849}
scripts/dev-runner.test.ts+3-1
...@@ -121,7 +121,9 @@ it.layer(NodeServices.layer)("dev-runner", (it) => {...@@ -121,7 +121,9 @@ it.layer(NodeServices.layer)("dev-runner", (it) => {
121 Effect.gen(function* () {121 Effect.gen(function* () {
122 const env = yield* createDevRunnerEnv({122 const env = yield* createDevRunnerEnv({
123 mode: "dev",123 mode: "dev",
124 baseEnv: {},124 baseEnv: {
125 T3CODE_LOG_WS_EVENTS: "1",
126 },
125 serverOffset: 0,127 serverOffset: 0,
126 webOffset: 0,128 webOffset: 0,
127 t3Home: undefined,129 t3Home: undefined,
scripts/dev-runner.ts+3-44
...@@ -356,35 +356,6 @@ interface DevRunnerCliInput {...@@ -356,35 +356,6 @@ interface DevRunnerCliInput {
356 readonly turboArgs: ReadonlyArray<string>;356 readonly turboArgs: ReadonlyArray<string>;
357}357}
358358
359const readOptionalBooleanEnv = (name: string): boolean | undefined => {
360 const value = process.env[name];
361 if (value === undefined) {
362 return undefined;
363 }
364 if (value === "1" || value.toLowerCase() === "true") {
365 return true;
366 }
367 if (value === "0" || value.toLowerCase() === "false") {
368 return false;
369 }
370 return undefined;
371};
372
373const resolveOptionalBooleanOverride = (
374 explicitValue: boolean | undefined,
375 envValue: boolean | undefined,
376): boolean | undefined => {
377 if (explicitValue === true) {
378 return true;
379 }
380
381 if (explicitValue === false) {
382 return envValue;
383 }
384
385 return envValue;
386};
387
388export function runDevRunnerWithInput(input: DevRunnerCliInput) {359export function runDevRunnerWithInput(input: DevRunnerCliInput) {
389 return Effect.gen(function* () {360 return Effect.gen(function* () {
390 const { portOffset, devInstance } = yield* OffsetConfig.asEffect().pipe(361 const { portOffset, devInstance } = yield* OffsetConfig.asEffect().pipe(
...@@ -406,12 +377,6 @@ export function runDevRunnerWithInput(input: DevRunnerCliInput) {...@@ -406,12 +377,6 @@ export function runDevRunnerWithInput(input: DevRunnerCliInput) {
406 }),377 }),
407 });378 });
408379
409 const envOverrides = {
410 noBrowser: readOptionalBooleanEnv("T3CODE_NO_BROWSER"),
411 autoBootstrapProjectFromCwd: readOptionalBooleanEnv("T3CODE_AUTO_BOOTSTRAP_PROJECT_FROM_CWD"),
412 logWebSocketEvents: readOptionalBooleanEnv("T3CODE_LOG_WS_EVENTS"),
413 };
414
415 const { serverOffset, webOffset } = yield* resolveModePortOffsets({380 const { serverOffset, webOffset } = yield* resolveModePortOffsets({
416 mode: input.mode,381 mode: input.mode,
417 startOffset: offset,382 startOffset: offset,
...@@ -425,15 +390,9 @@ export function runDevRunnerWithInput(input: DevRunnerCliInput) {...@@ -425,15 +390,9 @@ export function runDevRunnerWithInput(input: DevRunnerCliInput) {
425 serverOffset,390 serverOffset,
426 webOffset,391 webOffset,
427 t3Home: input.t3Home,392 t3Home: input.t3Home,
428 noBrowser: resolveOptionalBooleanOverride(input.noBrowser, envOverrides.noBrowser),393 noBrowser: input.noBrowser,
429 autoBootstrapProjectFromCwd: resolveOptionalBooleanOverride(394 autoBootstrapProjectFromCwd: input.autoBootstrapProjectFromCwd,
430 input.autoBootstrapProjectFromCwd,395 logWebSocketEvents: input.logWebSocketEvents,
431 envOverrides.autoBootstrapProjectFromCwd,
432 ),
433 logWebSocketEvents: resolveOptionalBooleanOverride(
434 input.logWebSocketEvents,
435 envOverrides.logWebSocketEvents,
436 ),
437 host: input.host,396 host: input.host,
438 port: input.port,397 port: input.port,
439 devUrl: input.devUrl,398 devUrl: input.devUrl,