authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-03-05 21:27:30-08:00
committergravatar for jmarminge@gmail.comJulius Marminge <jmarminge@gmail.com> 2026-03-05 23:00:41-08:00
log6496aedc92513fe138a511c7afb9f51152d08e88
tree37f8e5fad9adcfab4a1ad169063c368c5cde5301
parente972fad30075aeff9831f38052ef05badabd236f

split: move Claude provider surface to sibling stack

Co-authored-by: codex <codex@users.noreply.github.com>

23 files changed, 48 insertions(+), 506 deletions(-)

apps/server/integration/OrchestrationEngineHarness.integration.ts+1-1
...@@ -188,7 +188,7 @@ export interface OrchestrationIntegrationHarness {...@@ -188,7 +188,7 @@ export interface OrchestrationIntegrationHarness {
188}188}
189189
190interface MakeOrchestrationIntegrationHarnessOptions {190interface MakeOrchestrationIntegrationHarnessOptions {
191 readonly provider?: "codex" | "claudeCode";191 readonly provider?: "codex" | "cursor";
192 readonly realCodex?: boolean;192 readonly realCodex?: boolean;
193}193}
194194
apps/server/integration/TestProviderAdapter.integration.ts+5-5
...@@ -35,7 +35,7 @@ export interface TestTurnResponse {...@@ -35,7 +35,7 @@ export interface TestTurnResponse {
35export type FixtureProviderRuntimeEvent = {35export type FixtureProviderRuntimeEvent = {
36 readonly type: string;36 readonly type: string;
37 readonly eventId: EventId;37 readonly eventId: EventId;
38 readonly provider: "codex" | "claudeCode" | "cursor";38 readonly provider: "codex" | "cursor";
39 readonly createdAt: string;39 readonly createdAt: string;
40 readonly threadId: string;40 readonly threadId: string;
41 readonly turnId?: string | undefined;41 readonly turnId?: string | undefined;
...@@ -178,7 +178,7 @@ function normalizeFixtureEvent(rawEvent: Record<string, unknown>): ProviderRunti...@@ -178,7 +178,7 @@ function normalizeFixtureEvent(rawEvent: Record<string, unknown>): ProviderRunti
178178
179export interface TestProviderAdapterHarness {179export interface TestProviderAdapterHarness {
180 readonly adapter: ProviderAdapterShape<ProviderAdapterError>;180 readonly adapter: ProviderAdapterShape<ProviderAdapterError>;
181 readonly provider: "codex" | "claudeCode";181 readonly provider: "codex" | "cursor";
182 readonly queueTurnResponse: (182 readonly queueTurnResponse: (
183 threadId: ThreadId,183 threadId: ThreadId,
184 response: TestTurnResponse,184 response: TestTurnResponse,
...@@ -198,7 +198,7 @@ export interface TestProviderAdapterHarness {...@@ -198,7 +198,7 @@ export interface TestProviderAdapterHarness {
198}198}
199199
200interface MakeTestProviderAdapterHarnessOptions {200interface MakeTestProviderAdapterHarnessOptions {
201 readonly provider?: "codex" | "claudeCode";201 readonly provider?: "codex" | "cursor";
202}202}
203203
204function nowIso(): string {204function nowIso(): string {
...@@ -206,7 +206,7 @@ function nowIso(): string {...@@ -206,7 +206,7 @@ function nowIso(): string {
206}206}
207207
208function sessionNotFound(208function sessionNotFound(
209 provider: "codex" | "claudeCode",209 provider: "codex" | "cursor",
210 threadId: ThreadId,210 threadId: ThreadId,
211): ProviderAdapterSessionNotFoundError {211): ProviderAdapterSessionNotFoundError {
212 return new ProviderAdapterSessionNotFoundError({212 return new ProviderAdapterSessionNotFoundError({
...@@ -216,7 +216,7 @@ function sessionNotFound(...@@ -216,7 +216,7 @@ function sessionNotFound(
216}216}
217217
218function missingSessionEffect(218function missingSessionEffect(
219 provider: "codex" | "claudeCode",219 provider: "codex" | "cursor",
220 threadId: ThreadId,220 threadId: ThreadId,
221): Effect.Effect<never, ProviderAdapterError> {221): Effect.Effect<never, ProviderAdapterError> {
222 return Effect.fail(sessionNotFound(provider, threadId));222 return Effect.fail(sessionNotFound(provider, threadId));
apps/server/integration/orchestrationEngine.integration.test.ts+1-414
...@@ -32,7 +32,7 @@ const PROJECT_ID = asProjectId("project-1");...@@ -32,7 +32,7 @@ const PROJECT_ID = asProjectId("project-1");
32const THREAD_ID = ThreadId.makeUnsafe("thread-1");32const THREAD_ID = ThreadId.makeUnsafe("thread-1");
33const FIXTURE_TURN_ID = "fixture-turn";33const FIXTURE_TURN_ID = "fixture-turn";
34const APPROVAL_REQUEST_ID = asApprovalRequestId("req-approval-1");34const APPROVAL_REQUEST_ID = asApprovalRequestId("req-approval-1");
35type IntegrationProvider = "codex" | "claudeCode";35type IntegrationProvider = "codex" | "cursor";
3636
37function nowIso() {37function nowIso() {
38 return new Date().toISOString();38 return new Date().toISOString();
...@@ -842,416 +842,3 @@ it.live(...@@ -842,416 +842,3 @@ it.live(
842 }),842 }),
843 ),843 ),
844);844);
845
846it.live("starts a claudeCode session on first turn when provider is requested", () =>
847 withHarness(
848 (harness) =>
849 Effect.gen(function* () {
850 yield* seedProjectAndThread(harness);
851
852 yield* harness.adapterHarness.queueTurnResponseForNextSession({
853 events: [
854 {
855 type: "turn.started",
856 ...runtimeBase("evt-claude-start-1", "2026-02-24T10:10:00.000Z", "claudeCode"),
857 threadId: THREAD_ID,
858 turnId: FIXTURE_TURN_ID,
859 },
860 {
861 type: "message.delta",
862 ...runtimeBase("evt-claude-start-2", "2026-02-24T10:10:00.050Z", "claudeCode"),
863 threadId: THREAD_ID,
864 turnId: FIXTURE_TURN_ID,
865 delta: "Claude first turn.\n",
866 },
867 {
868 type: "turn.completed",
869 ...runtimeBase("evt-claude-start-3", "2026-02-24T10:10:00.100Z", "claudeCode"),
870 threadId: THREAD_ID,
871 turnId: FIXTURE_TURN_ID,
872 status: "completed",
873 },
874 ],
875 });
876
877 yield* startTurn({
878 harness,
879 commandId: "cmd-turn-start-claude-initial",
880 messageId: "msg-user-claude-initial",
881 text: "Use Claude",
882 provider: "claudeCode",
883 });
884
885 const thread = yield* harness.waitForThread(
886 THREAD_ID,
887 (entry) =>
888 entry.session?.providerName === "claudeCode" &&
889 entry.session.status === "ready" &&
890 entry.messages.some(
891 (message) => message.role === "assistant" && message.text === "Claude first turn.\n",
892 ),
893 );
894 assert.equal(thread.session?.providerName, "claudeCode");
895 }),
896 "claudeCode",
897 ),
898);
899
900it.live("recovers claudeCode sessions after provider stopAll using persisted resume state", () =>
901 withHarness(
902 (harness) =>
903 Effect.gen(function* () {
904 yield* seedProjectAndThread(harness);
905
906 yield* harness.adapterHarness.queueTurnResponseForNextSession({
907 events: [
908 {
909 type: "turn.started",
910 ...runtimeBase("evt-claude-recover-1", "2026-02-24T10:11:00.000Z", "claudeCode"),
911 threadId: THREAD_ID,
912 turnId: FIXTURE_TURN_ID,
913 },
914 {
915 type: "message.delta",
916 ...runtimeBase("evt-claude-recover-2", "2026-02-24T10:11:00.050Z", "claudeCode"),
917 threadId: THREAD_ID,
918 turnId: FIXTURE_TURN_ID,
919 delta: "Turn before restart.\n",
920 },
921 {
922 type: "turn.completed",
923 ...runtimeBase("evt-claude-recover-3", "2026-02-24T10:11:00.100Z", "claudeCode"),
924 threadId: THREAD_ID,
925 turnId: FIXTURE_TURN_ID,
926 status: "completed",
927 },
928 ],
929 });
930
931 yield* startTurn({
932 harness,
933 commandId: "cmd-turn-start-claude-recover-1",
934 messageId: "msg-user-claude-recover-1",
935 text: "Before restart",
936 provider: "claudeCode",
937 });
938
939 yield* harness.waitForThread(
940 THREAD_ID,
941 (entry) => entry.latestTurn?.turnId === "turn-1" && entry.session?.threadId === "thread-1",
942 );
943
944 yield* harness.providerService.stopAll();
945 yield* waitForSync(
946 () => harness.adapterHarness.listActiveSessionIds(),
947 (sessionIds) => sessionIds.length === 0,
948 "provider stopAll",
949 );
950
951 yield* harness.adapterHarness.queueTurnResponseForNextSession({
952 events: [
953 {
954 type: "turn.started",
955 ...runtimeBase("evt-claude-recover-4", "2026-02-24T10:11:01.000Z", "claudeCode"),
956 threadId: THREAD_ID,
957 turnId: FIXTURE_TURN_ID,
958 },
959 {
960 type: "message.delta",
961 ...runtimeBase("evt-claude-recover-5", "2026-02-24T10:11:01.050Z", "claudeCode"),
962 threadId: THREAD_ID,
963 turnId: FIXTURE_TURN_ID,
964 delta: "Turn after restart.\n",
965 },
966 {
967 type: "turn.completed",
968 ...runtimeBase("evt-claude-recover-6", "2026-02-24T10:11:01.100Z", "claudeCode"),
969 threadId: THREAD_ID,
970 turnId: FIXTURE_TURN_ID,
971 status: "completed",
972 },
973 ],
974 });
975
976 yield* startTurn({
977 harness,
978 commandId: "cmd-turn-start-claude-recover-2",
979 messageId: "msg-user-claude-recover-2",
980 text: "After restart",
981 });
982 yield* waitForSync(
983 () => harness.adapterHarness.getStartCount(),
984 (count) => count === 2,
985 "claude provider recovery start",
986 );
987
988 const recoveredThread = yield* harness.waitForThread(
989 THREAD_ID,
990 (entry) =>
991 entry.session?.providerName === "claudeCode" &&
992 entry.messages.some(
993 (message) => message.role === "user" && message.text === "After restart",
994 ) &&
995 !entry.activities.some((activity) => activity.kind === "provider.turn.start.failed"),
996 );
997 assert.equal(recoveredThread.session?.providerName, "claudeCode");
998 assert.equal(recoveredThread.session?.threadId, "thread-1");
999 }),
1000 "claudeCode",
1001 ),
1002);
1003
1004it.live("forwards claudeCode approval responses to the provider session", () =>
1005 withHarness(
1006 (harness) =>
1007 Effect.gen(function* () {
1008 yield* seedProjectAndThread(harness);
1009
1010 yield* harness.adapterHarness.queueTurnResponseForNextSession({
1011 events: [
1012 {
1013 type: "turn.started",
1014 ...runtimeBase("evt-claude-approval-1", "2026-02-24T10:12:00.000Z", "claudeCode"),
1015 threadId: THREAD_ID,
1016 turnId: FIXTURE_TURN_ID,
1017 },
1018 {
1019 type: "approval.requested",
1020 ...runtimeBase("evt-claude-approval-2", "2026-02-24T10:12:00.050Z", "claudeCode"),
1021 threadId: THREAD_ID,
1022 turnId: FIXTURE_TURN_ID,
1023 requestId: APPROVAL_REQUEST_ID,
1024 requestKind: "command",
1025 detail: "Approve Claude tool call",
1026 },
1027 {
1028 type: "turn.completed",
1029 ...runtimeBase("evt-claude-approval-3", "2026-02-24T10:12:00.100Z", "claudeCode"),
1030 threadId: THREAD_ID,
1031 turnId: FIXTURE_TURN_ID,
1032 status: "completed",
1033 },
1034 ],
1035 });
1036
1037 yield* startTurn({
1038 harness,
1039 commandId: "cmd-turn-start-claude-approval",
1040 messageId: "msg-user-claude-approval",
1041 text: "Need approval",
1042 provider: "claudeCode",
1043 });
1044
1045 const thread = yield* harness.waitForThread(THREAD_ID, (entry) =>
1046 entry.activities.some((activity) => activity.kind === "approval.requested"),
1047 );
1048 assert.equal(thread.session?.threadId, "thread-1");
1049
1050 yield* harness.engine.dispatch({
1051 type: "thread.approval.respond",
1052 commandId: CommandId.makeUnsafe("cmd-claude-approval-respond"),
1053 threadId: THREAD_ID,
1054 requestId: APPROVAL_REQUEST_ID,
1055 decision: "accept",
1056 createdAt: nowIso(),
1057 });
1058
1059 yield* harness.waitForPendingApproval(
1060 "req-approval-1",
1061 (row) => row.status === "resolved" && row.decision === "accept",
1062 );
1063
1064 const approvalResponses = yield* waitForSync(
1065 () => harness.adapterHarness.getApprovalResponses(THREAD_ID),
1066 (responses) => responses.length === 1,
1067 "claude provider approval response",
1068 );
1069 assert.equal(approvalResponses[0]?.decision, "accept");
1070 }),
1071 "claudeCode",
1072 ),
1073);
1074
1075it.live("forwards thread.turn.interrupt to claudeCode provider sessions", () =>
1076 withHarness(
1077 (harness) =>
1078 Effect.gen(function* () {
1079 yield* seedProjectAndThread(harness);
1080
1081 yield* harness.adapterHarness.queueTurnResponseForNextSession({
1082 events: [
1083 {
1084 type: "turn.started",
1085 ...runtimeBase("evt-claude-interrupt-1", "2026-02-24T10:13:00.000Z", "claudeCode"),
1086 threadId: THREAD_ID,
1087 turnId: FIXTURE_TURN_ID,
1088 },
1089 {
1090 type: "message.delta",
1091 ...runtimeBase("evt-claude-interrupt-2", "2026-02-24T10:13:00.050Z", "claudeCode"),
1092 threadId: THREAD_ID,
1093 turnId: FIXTURE_TURN_ID,
1094 delta: "Long running output.\n",
1095 },
1096 {
1097 type: "turn.completed",
1098 ...runtimeBase("evt-claude-interrupt-3", "2026-02-24T10:13:00.100Z", "claudeCode"),
1099 threadId: THREAD_ID,
1100 turnId: FIXTURE_TURN_ID,
1101 status: "completed",
1102 },
1103 ],
1104 });
1105
1106 yield* startTurn({
1107 harness,
1108 commandId: "cmd-turn-start-claude-interrupt",
1109 messageId: "msg-user-claude-interrupt",
1110 text: "Start long turn",
1111 provider: "claudeCode",
1112 });
1113
1114 const thread = yield* harness.waitForThread(
1115 THREAD_ID,
1116 (entry) => entry.session?.threadId === "thread-1",
1117 );
1118 assert.equal(thread.session?.threadId, "thread-1");
1119
1120 yield* harness.engine.dispatch({
1121 type: "thread.turn.interrupt",
1122 commandId: CommandId.makeUnsafe("cmd-turn-interrupt-claude"),
1123 threadId: THREAD_ID,
1124 createdAt: nowIso(),
1125 });
1126 yield* harness.waitForDomainEvent((event) => event.type === "thread.turn-interrupt-requested");
1127
1128 const interruptCalls = yield* waitForSync(
1129 () => harness.adapterHarness.getInterruptCalls(THREAD_ID),
1130 (calls) => calls.length === 1,
1131 "claude provider interrupt call",
1132 );
1133 assert.equal(interruptCalls.length, 1);
1134 }),
1135 "claudeCode",
1136 ),
1137);
1138
1139it.live("reverts claudeCode turns and rolls back provider conversation state", () =>
1140 withHarness(
1141 (harness) =>
1142 Effect.gen(function* () {
1143 yield* seedProjectAndThread(harness);
1144
1145 yield* harness.adapterHarness.queueTurnResponseForNextSession({
1146 events: [
1147 {
1148 type: "turn.started",
1149 ...runtimeBase("evt-claude-revert-1", "2026-02-24T10:14:00.000Z", "claudeCode"),
1150 threadId: THREAD_ID,
1151 turnId: FIXTURE_TURN_ID,
1152 },
1153 {
1154 type: "message.delta",
1155 ...runtimeBase("evt-claude-revert-2", "2026-02-24T10:14:00.050Z", "claudeCode"),
1156 threadId: THREAD_ID,
1157 turnId: FIXTURE_TURN_ID,
1158 delta: "README -> v2\n",
1159 },
1160 {
1161 type: "turn.completed",
1162 ...runtimeBase("evt-claude-revert-3", "2026-02-24T10:14:00.100Z", "claudeCode"),
1163 threadId: THREAD_ID,
1164 turnId: FIXTURE_TURN_ID,
1165 status: "completed",
1166 },
1167 ],
1168 mutateWorkspace: ({ cwd }) =>
1169 Effect.sync(() => {
1170 fs.writeFileSync(path.join(cwd, "README.md"), "v2\n", "utf8");
1171 }),
1172 });
1173
1174 yield* startTurn({
1175 harness,
1176 commandId: "cmd-turn-start-claude-revert-1",
1177 messageId: "msg-user-claude-revert-1",
1178 text: "First Claude edit",
1179 provider: "claudeCode",
1180 });
1181
1182 yield* harness.waitForThread(
1183 THREAD_ID,
1184 (entry) => entry.latestTurn?.turnId === "turn-1" && entry.session?.threadId === "thread-1",
1185 );
1186
1187 yield* harness.adapterHarness.queueTurnResponse(THREAD_ID, {
1188 events: [
1189 {
1190 type: "turn.started",
1191 ...runtimeBase("evt-claude-revert-4", "2026-02-24T10:14:01.000Z", "claudeCode"),
1192 threadId: THREAD_ID,
1193 turnId: FIXTURE_TURN_ID,
1194 },
1195 {
1196 type: "message.delta",
1197 ...runtimeBase("evt-claude-revert-5", "2026-02-24T10:14:01.050Z", "claudeCode"),
1198 threadId: THREAD_ID,
1199 turnId: FIXTURE_TURN_ID,
1200 delta: "README -> v3\n",
1201 },
1202 {
1203 type: "turn.completed",
1204 ...runtimeBase("evt-claude-revert-6", "2026-02-24T10:14:01.100Z", "claudeCode"),
1205 threadId: THREAD_ID,
1206 turnId: FIXTURE_TURN_ID,
1207 status: "completed",
1208 },
1209 ],
1210 mutateWorkspace: ({ cwd }) =>
1211 Effect.sync(() => {
1212 fs.writeFileSync(path.join(cwd, "README.md"), "v3\n", "utf8");
1213 }),
1214 });
1215
1216 yield* startTurn({
1217 harness,
1218 commandId: "cmd-turn-start-claude-revert-2",
1219 messageId: "msg-user-claude-revert-2",
1220 text: "Second Claude edit",
1221 });
1222
1223 yield* harness.waitForThread(
1224 THREAD_ID,
1225 (entry) =>
1226 entry.latestTurn?.turnId === "turn-2" &&
1227 entry.checkpoints.length === 2 &&
1228 entry.session?.providerName === "claudeCode",
1229 );
1230
1231 yield* harness.engine.dispatch({
1232 type: "thread.checkpoint.revert",
1233 commandId: CommandId.makeUnsafe("cmd-checkpoint-revert-claude"),
1234 threadId: THREAD_ID,
1235 turnCount: 1,
1236 createdAt: nowIso(),
1237 });
1238
1239 const revertedThread = yield* harness.waitForThread(
1240 THREAD_ID,
1241 (entry) =>
1242 entry.checkpoints.length === 1 && entry.checkpoints[0]?.checkpointTurnCount === 1,
1243 );
1244 assert.equal(revertedThread.checkpoints[0]?.checkpointTurnCount, 1);
1245 assert.equal(
1246 gitRefExists(harness.workspaceDir, checkpointRefForThreadTurn(THREAD_ID, 1)),
1247 true,
1248 );
1249 assert.equal(
1250 gitRefExists(harness.workspaceDir, checkpointRefForThreadTurn(THREAD_ID, 2)),
1251 false,
1252 );
1253 assert.deepEqual(harness.adapterHarness.getRollbackCalls(THREAD_ID), [1]);
1254 }),
1255 "claudeCode",
1256 ),
1257);
apps/server/src/orchestration/Layers/CheckpointReactor.test.ts+1-1
...@@ -43,7 +43,7 @@ const asTurnId = (value: string): TurnId => TurnId.makeUnsafe(value);...@@ -43,7 +43,7 @@ const asTurnId = (value: string): TurnId => TurnId.makeUnsafe(value);
43type LegacyProviderRuntimeEvent = {43type LegacyProviderRuntimeEvent = {
44 readonly type: string;44 readonly type: string;
45 readonly eventId: EventId;45 readonly eventId: EventId;
46 readonly provider: "codex" | "claudeCode" | "cursor";46 readonly provider: "codex" | "cursor";
47 readonly createdAt: string;47 readonly createdAt: string;
48 readonly threadId: ThreadId;48 readonly threadId: ThreadId;
49 readonly turnId?: string | undefined;49 readonly turnId?: string | undefined;
apps/server/src/orchestration/Layers/ProviderCommandReactor.ts-1
...@@ -212,7 +212,6 @@ const make = Effect.gen(function* () {...@@ -212,7 +212,6 @@ const make = Effect.gen(function* () {
212 const desiredRuntimeMode = thread.runtimeMode;212 const desiredRuntimeMode = thread.runtimeMode;
213 const currentProvider: ProviderKind | undefined =213 const currentProvider: ProviderKind | undefined =
214 thread.session?.providerName === "codex" ||214 thread.session?.providerName === "codex" ||
215 thread.session?.providerName === "claudeCode" ||
216 thread.session?.providerName === "cursor"215 thread.session?.providerName === "cursor"
217 ? thread.session.providerName216 ? thread.session.providerName
218 : undefined;217 : undefined;
apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.test.ts+1-1
...@@ -41,7 +41,7 @@ const asTurnId = (value: string): TurnId => TurnId.makeUnsafe(value);...@@ -41,7 +41,7 @@ const asTurnId = (value: string): TurnId => TurnId.makeUnsafe(value);
41type LegacyProviderRuntimeEvent = {41type LegacyProviderRuntimeEvent = {
42 readonly type: string;42 readonly type: string;
43 readonly eventId: EventId;43 readonly eventId: EventId;
44 readonly provider: "codex" | "claudeCode" | "cursor";44 readonly provider: "codex" | "cursor";
45 readonly createdAt: string;45 readonly createdAt: string;
46 readonly threadId: ThreadId;46 readonly threadId: ThreadId;
47 readonly turnId?: string | undefined;47 readonly turnId?: string | undefined;
apps/server/src/provider/Layers/CodexAdapter.test.ts+2-2
...@@ -162,7 +162,7 @@ validationLayer("CodexAdapterLive validation", (it) => {...@@ -162,7 +162,7 @@ validationLayer("CodexAdapterLive validation", (it) => {
162 const adapter = yield* CodexAdapter;162 const adapter = yield* CodexAdapter;
163 const result = yield* adapter163 const result = yield* adapter
164 .startSession({164 .startSession({
165 provider: "claudeCode",165 provider: "cursor",
166 threadId: asThreadId("thread-1"),166 threadId: asThreadId("thread-1"),
167 runtimeMode: "full-access",167 runtimeMode: "full-access",
168 })168 })
...@@ -174,7 +174,7 @@ validationLayer("CodexAdapterLive validation", (it) => {...@@ -174,7 +174,7 @@ validationLayer("CodexAdapterLive validation", (it) => {
174 new ProviderAdapterValidationError({174 new ProviderAdapterValidationError({
175 provider: "codex",175 provider: "codex",
176 operation: "startSession",176 operation: "startSession",
177 issue: "Expected provider 'codex' but received 'claudeCode'.",177 issue: "Expected provider 'codex' but received 'cursor'.",
178 }),178 }),
179 );179 );
180 assert.equal(validationManager.startSessionImpl.mock.calls.length, 0);180 assert.equal(validationManager.startSessionImpl.mock.calls.length, 0);
apps/server/src/provider/Layers/ProviderAdapterRegistry.ts+1-1
...@@ -1,7 +1,7 @@...@@ -1,7 +1,7 @@
1/**1/**
2 * ProviderAdapterRegistryLive - In-memory provider adapter lookup layer.2 * ProviderAdapterRegistryLive - In-memory provider adapter lookup layer.
3 *3 *
4 * Binds provider kinds (codex/claudeCode/...) to concrete adapter services.4 * Binds provider kinds (codex/cursor/...) to concrete adapter services.
5 * This layer only performs adapter lookup; it does not route session-scoped5 * This layer only performs adapter lookup; it does not route session-scoped
6 * calls or own provider lifecycle workflows.6 * calls or own provider lifecycle workflows.
7 *7 *
apps/server/src/provider/Layers/ProviderService.test.ts+1-1
...@@ -51,7 +51,7 @@ const asTurnId = (value: string): TurnId => TurnId.makeUnsafe(value);...@@ -51,7 +51,7 @@ const asTurnId = (value: string): TurnId => TurnId.makeUnsafe(value);
51type LegacyProviderRuntimeEvent = {51type LegacyProviderRuntimeEvent = {
52 readonly type: string;52 readonly type: string;
53 readonly eventId: EventId;53 readonly eventId: EventId;
54 readonly provider: "codex" | "claudeCode" | "cursor";54 readonly provider: "codex" | "cursor";
55 readonly createdAt: string;55 readonly createdAt: string;
56 readonly threadId: ThreadId;56 readonly threadId: ThreadId;
57 readonly turnId?: string | undefined;57 readonly turnId?: string | undefined;
apps/server/src/provider/Layers/ProviderSessionDirectory.ts+1-1
...@@ -25,7 +25,7 @@ function decodeProviderKind(...@@ -25,7 +25,7 @@ function decodeProviderKind(
25 providerName: string,25 providerName: string,
26 operation: string,26 operation: string,
27): Effect.Effect<ProviderKind, ProviderSessionDirectoryPersistenceError> {27): Effect.Effect<ProviderKind, ProviderSessionDirectoryPersistenceError> {
28 if (providerName === "codex" || providerName === "claudeCode" || providerName === "cursor") {28 if (providerName === "codex" || providerName === "cursor") {
29 return Effect.succeed(providerName);29 return Effect.succeed(providerName);
30 }30 }
31 return Effect.fail(31 return Effect.fail(
apps/web/src/appSettings.test.ts+3-9
...@@ -16,11 +16,7 @@ describe("normalizeCustomModelSlugs", () => {...@@ -16,11 +16,7 @@ describe("normalizeCustomModelSlugs", () => {
16 ).toEqual(["custom/internal-model"]);16 ).toEqual(["custom/internal-model"]);
17 });17 });
1818
19 it("normalizes provider-specific aliases for claude and cursor", () => {19 it("normalizes provider-specific aliases for cursor", () => {
20 expect(normalizeCustomModelSlugs(["sonnet"], "claudeCode")).toEqual([]);
21 expect(normalizeCustomModelSlugs(["claude/custom-sonnet"], "claudeCode")).toEqual([
22 "claude/custom-sonnet",
23 ]);
24 expect(normalizeCustomModelSlugs(["composer"], "cursor")).toEqual([]);20 expect(normalizeCustomModelSlugs(["composer"], "cursor")).toEqual([]);
25 expect(normalizeCustomModelSlugs(["cursor/custom-model"], "cursor")).toEqual([21 expect(normalizeCustomModelSlugs(["cursor/custom-model"], "cursor")).toEqual([
26 "cursor/custom-model",22 "cursor/custom-model",
...@@ -53,9 +49,9 @@ describe("getAppModelOptions", () => {...@@ -53,9 +49,9 @@ describe("getAppModelOptions", () => {
53 });49 });
5450
55 it("keeps a saved custom provider model available as an exact slug option", () => {51 it("keeps a saved custom provider model available as an exact slug option", () => {
56 const options = getAppModelOptions("claudeCode", ["claude/custom-opus"], "claude/custom-opus");52 const options = getAppModelOptions("cursor", ["cursor/custom-model"], "cursor/custom-model");
5753
58 expect(options.some((option) => option.slug === "claude/custom-opus" && option.isCustom)).toBe(54 expect(options.some((option) => option.slug === "cursor/custom-model" && option.isCustom)).toBe(
59 true,55 true,
60 );56 );
61 });57 });
...@@ -85,10 +81,8 @@ describe("getSlashModelOptions", () => {...@@ -85,10 +81,8 @@ describe("getSlashModelOptions", () => {
85 });81 });
8682
87 it("includes provider-specific custom slugs in non-codex model lists", () => {83 it("includes provider-specific custom slugs in non-codex model lists", () => {
88 const claudeOptions = getAppModelOptions("claudeCode", ["claude/custom-opus"]);
89 const cursorOptions = getAppModelOptions("cursor", ["cursor/custom-model"]);84 const cursorOptions = getAppModelOptions("cursor", ["cursor/custom-model"]);
9085
91 expect(claudeOptions.some((option) => option.slug === "claude/custom-opus")).toBe(true);
92 expect(cursorOptions.some((option) => option.slug === "cursor/custom-model")).toBe(true);86 expect(cursorOptions.some((option) => option.slug === "cursor/custom-model")).toBe(true);
93 });87 });
94});88});
apps/web/src/appSettings.ts-5
...@@ -8,7 +8,6 @@ const MAX_CUSTOM_MODEL_COUNT = 32;...@@ -8,7 +8,6 @@ const MAX_CUSTOM_MODEL_COUNT = 32;
8export const MAX_CUSTOM_MODEL_LENGTH = 256;8export const MAX_CUSTOM_MODEL_LENGTH = 256;
9const BUILT_IN_MODEL_SLUGS_BY_PROVIDER: Record<ProviderKind, ReadonlySet<string>> = {9const BUILT_IN_MODEL_SLUGS_BY_PROVIDER: Record<ProviderKind, ReadonlySet<string>> = {
10 codex: new Set(getModelOptions("codex").map((option) => option.slug)),10 codex: new Set(getModelOptions("codex").map((option) => option.slug)),
11 claudeCode: new Set(getModelOptions("claudeCode").map((option) => option.slug)),
12 cursor: new Set(getModelOptions("cursor").map((option) => option.slug)),11 cursor: new Set(getModelOptions("cursor").map((option) => option.slug)),
13};12};
1413
...@@ -26,9 +25,6 @@ const AppSettingsSchema = Schema.Struct({...@@ -26,9 +25,6 @@ const AppSettingsSchema = Schema.Struct({
26 customCodexModels: Schema.Array(Schema.String).pipe(25 customCodexModels: Schema.Array(Schema.String).pipe(
27 Schema.withConstructorDefault(() => Option.some([])),26 Schema.withConstructorDefault(() => Option.some([])),
28 ),27 ),
29 customClaudeModels: Schema.Array(Schema.String).pipe(
30 Schema.withConstructorDefault(() => Option.some([])),
31 ),
32 customCursorModels: Schema.Array(Schema.String).pipe(28 customCursorModels: Schema.Array(Schema.String).pipe(
33 Schema.withConstructorDefault(() => Option.some([])),29 Schema.withConstructorDefault(() => Option.some([])),
34 ),30 ),
...@@ -79,7 +75,6 @@ function normalizeAppSettings(settings: AppSettings): AppSettings {...@@ -79,7 +75,6 @@ function normalizeAppSettings(settings: AppSettings): AppSettings {
79 return {75 return {
80 ...settings,76 ...settings,
81 customCodexModels: normalizeCustomModelSlugs(settings.customCodexModels, "codex"),77 customCodexModels: normalizeCustomModelSlugs(settings.customCodexModels, "codex"),
82 customClaudeModels: normalizeCustomModelSlugs(settings.customClaudeModels, "claudeCode"),
83 customCursorModels: normalizeCustomModelSlugs(settings.customCursorModels, "cursor"),78 customCursorModels: normalizeCustomModelSlugs(settings.customCursorModels, "cursor"),
84 };79 };
85}80}
apps/web/src/components/ChatView.tsx+16-11
...@@ -77,6 +77,7 @@ import {...@@ -77,6 +77,7 @@ import {
77 findLatestProposedPlan,77 findLatestProposedPlan,
78 type PendingApproval,78 type PendingApproval,
79 type PendingUserInput,79 type PendingUserInput,
80 type ProviderPickerKind,
80 PROVIDER_OPTIONS,81 PROVIDER_OPTIONS,
81 deriveWorkLogEntries,82 deriveWorkLogEntries,
82 hasToolActivityForTurn,83 hasToolActivityForTurn,
...@@ -847,9 +848,8 @@ export default function ChatView({ threadId }: ChatViewProps) {...@@ -847,9 +848,8 @@ export default function ChatView({ threadId }: ChatViewProps) {
847 }, [modelOptionsByProvider, selectedModelForPicker, selectedProvider]);848 }, [modelOptionsByProvider, selectedModelForPicker, selectedProvider]);
848 const searchableModelOptions = useMemo(849 const searchableModelOptions = useMemo(
849 () =>850 () =>
850 PROVIDER_OPTIONS.filter(851 AVAILABLE_PROVIDER_OPTIONS.filter(
851 (option) =>852 (option) => lockedProvider === null || option.value === lockedProvider,
852 option.available && (lockedProvider === null || option.value === lockedProvider),
853 ).flatMap((option) =>853 ).flatMap((option) =>
854 modelOptionsByProvider[option.value].map(({ slug, name }) => ({854 modelOptionsByProvider[option.value].map(({ slug, name }) => ({
855 provider: option.value,855 provider: option.value,
...@@ -5278,7 +5278,17 @@ const MessagesTimeline = memo(function MessagesTimeline({...@@ -5278,7 +5278,17 @@ const MessagesTimeline = memo(function MessagesTimeline({
5278 );5278 );
5279});5279});
52805280
5281const AVAILABLE_PROVIDER_OPTIONS = PROVIDER_OPTIONS.filter((option) => option.available);5281function isAvailableProviderOption(
5282 option: (typeof PROVIDER_OPTIONS)[number],
5283): option is {
5284 value: ProviderKind;
5285 label: string;
5286 available: true;
5287} {
5288 return option.available && option.value !== "claudeCode";
5289}
5290
5291const AVAILABLE_PROVIDER_OPTIONS = PROVIDER_OPTIONS.filter(isAvailableProviderOption);
5282const UNAVAILABLE_PROVIDER_OPTIONS = PROVIDER_OPTIONS.filter((option) => !option.available);5292const UNAVAILABLE_PROVIDER_OPTIONS = PROVIDER_OPTIONS.filter((option) => !option.available);
5283const COMING_SOON_PROVIDER_OPTIONS = [5293const COMING_SOON_PROVIDER_OPTIONS = [
5284 { id: "opencode", label: "OpenCode", icon: OpenCodeIcon },5294 { id: "opencode", label: "OpenCode", icon: OpenCodeIcon },
...@@ -5287,13 +5297,11 @@ const COMING_SOON_PROVIDER_OPTIONS = [...@@ -5287,13 +5297,11 @@ const COMING_SOON_PROVIDER_OPTIONS = [
52875297
5288function getCustomModelOptionsByProvider(settings: {5298function getCustomModelOptionsByProvider(settings: {
5289 customCodexModels: readonly string[];5299 customCodexModels: readonly string[];
5290 customClaudeModels: readonly string[];
5291 customCursorModels: readonly string[];5300 customCursorModels: readonly string[];
5292}): Record<ProviderKind, ReadonlyArray<{ slug: string; name: string }>> {5301}): Record<ProviderKind, ReadonlyArray<{ slug: string; name: string }>> {
5293 const cursorFamilyOptions = getCursorModelFamilyOptions();5302 const cursorFamilyOptions = getCursorModelFamilyOptions();
5294 return {5303 return {
5295 codex: getAppModelOptions("codex", settings.customCodexModels),5304 codex: getAppModelOptions("codex", settings.customCodexModels),
5296 claudeCode: getAppModelOptions("claudeCode", settings.customClaudeModels),
5297 cursor: [5305 cursor: [
5298 ...cursorFamilyOptions,5306 ...cursorFamilyOptions,
5299 ...getAppModelOptions("cursor", settings.customCursorModels).filter(5307 ...getAppModelOptions("cursor", settings.customCursorModels).filter(
...@@ -5304,7 +5312,7 @@ function getCustomModelOptionsByProvider(settings: {...@@ -5304,7 +5312,7 @@ function getCustomModelOptionsByProvider(settings: {
5304 };5312 };
5305}5313}
53065314
5307const PROVIDER_ICON_BY_PROVIDER: Record<ProviderKind, Icon> = {5315const PROVIDER_ICON_BY_PROVIDER: Record<ProviderPickerKind, Icon> = {
5308 codex: OpenAI,5316 codex: OpenAI,
5309 claudeCode: ClaudeAI,5317 claudeCode: ClaudeAI,
5310 cursor: CursorIcon,5318 cursor: CursorIcon,
...@@ -5385,10 +5393,7 @@ const ProviderModelPicker = memo(function ProviderModelPicker(props: {...@@ -5385,10 +5393,7 @@ const ProviderModelPicker = memo(function ProviderModelPicker(props: {
5385 <span className="flex min-w-0 items-center gap-2">5393 <span className="flex min-w-0 items-center gap-2">
5386 <ProviderIcon5394 <ProviderIcon
5387 aria-hidden="true"5395 aria-hidden="true"
5388 className={cn(5396 className="size-4 shrink-0 text-muted-foreground/70"
5389 "size-4 shrink-0",
5390 props.provider === "claudeCode" ? "" : "text-muted-foreground/70",
5391 )}
5392 />5397 />
5393 <span className="truncate">{selectedModelLabel}</span>5398 <span className="truncate">{selectedModelLabel}</span>
5394 <ChevronDownIcon aria-hidden="true" className="size-3 opacity-60" />5399 <ChevronDownIcon aria-hidden="true" className="size-3 opacity-60" />
apps/web/src/composerDraftStore.ts+1-1
...@@ -208,7 +208,7 @@ function shouldRemoveDraft(draft: ComposerThreadDraftState): boolean {...@@ -208,7 +208,7 @@ function shouldRemoveDraft(draft: ComposerThreadDraftState): boolean {
208}208}
209209
210function normalizeProviderKind(value: unknown): ProviderKind | null {210function normalizeProviderKind(value: unknown): ProviderKind | null {
211 return value === "codex" || value === "claudeCode" || value === "cursor" ? value : null;211 return value === "codex" || value === "cursor" ? value : null;
212}212}
213213
214function revokeObjectPreviewUrl(previewUrl: string): void {214function revokeObjectPreviewUrl(previewUrl: string): void {
apps/web/src/routes/_chat.settings.tsx-7
...@@ -54,8 +54,6 @@ function getCustomModelsForProvider(...@@ -54,8 +54,6 @@ function getCustomModelsForProvider(
54 provider: ProviderKind,54 provider: ProviderKind,
55) {55) {
56 switch (provider) {56 switch (provider) {
57 case "claudeCode":
58 return settings.customClaudeModels;
59 case "cursor":57 case "cursor":
60 return settings.customCursorModels;58 return settings.customCursorModels;
61 case "codex":59 case "codex":
...@@ -69,8 +67,6 @@ function getDefaultCustomModelsForProvider(...@@ -69,8 +67,6 @@ function getDefaultCustomModelsForProvider(
69 provider: ProviderKind,67 provider: ProviderKind,
70) {68) {
71 switch (provider) {69 switch (provider) {
72 case "claudeCode":
73 return defaults.customClaudeModels;
74 case "cursor":70 case "cursor":
75 return defaults.customCursorModels;71 return defaults.customCursorModels;
76 case "codex":72 case "codex":
...@@ -81,8 +77,6 @@ function getDefaultCustomModelsForProvider(...@@ -81,8 +77,6 @@ function getDefaultCustomModelsForProvider(
8177
82function patchCustomModels(provider: ProviderKind, models: string[]) {78function patchCustomModels(provider: ProviderKind, models: string[]) {
83 switch (provider) {79 switch (provider) {
84 case "claudeCode":
85 return { customClaudeModels: models };
86 case "cursor":80 case "cursor":
87 return { customCursorModels: models };81 return { customCursorModels: models };
88 case "codex":82 case "codex":
...@@ -101,7 +95,6 @@ function SettingsRouteView() {...@@ -101,7 +95,6 @@ function SettingsRouteView() {
101 Record<ProviderKind, string>95 Record<ProviderKind, string>
102 >({96 >({
103 codex: "",97 codex: "",
104 claudeCode: "",
105 cursor: "",98 cursor: "",
106 });99 });
107 const [customModelErrorByProvider, setCustomModelErrorByProvider] = useState<100 const [customModelErrorByProvider, setCustomModelErrorByProvider] = useState<
apps/web/src/session-logic.ts+3-1
...@@ -10,8 +10,10 @@ import {...@@ -10,8 +10,10 @@ import {
1010
11import type { ChatMessage, ProposedPlan, SessionPhase, ThreadSession, TurnDiffSummary } from "./types";11import type { ChatMessage, ProposedPlan, SessionPhase, ThreadSession, TurnDiffSummary } from "./types";
1212
13export type ProviderPickerKind = ProviderKind | "claudeCode";
14
13export const PROVIDER_OPTIONS: Array<{15export const PROVIDER_OPTIONS: Array<{
14 value: ProviderKind;16 value: ProviderPickerKind;
15 label: string;17 label: string;
16 available: boolean;18 available: boolean;
17}> = [19}> = [
apps/web/src/store.test.ts+2-24
...@@ -129,7 +129,7 @@ describe("store pure functions", () => {...@@ -129,7 +129,7 @@ describe("store pure functions", () => {
129});129});
130130
131describe("store read model sync", () => {131describe("store read model sync", () => {
132 it("preserves claude model slugs without an active session", () => {132 it("falls back to the codex default for unsupported provider models without an active session", () => {
133 const initialState = makeState(makeThread());133 const initialState = makeState(makeThread());
134 const readModel = makeReadModel(134 const readModel = makeReadModel(
135 makeReadModelThread({135 makeReadModelThread({
...@@ -139,29 +139,7 @@ describe("store read model sync", () => {...@@ -139,29 +139,7 @@ describe("store read model sync", () => {
139139
140 const next = syncServerReadModel(initialState, readModel);140 const next = syncServerReadModel(initialState, readModel);
141141
142 expect(next.threads[0]?.model).toBe("claude-opus-4-6");142 expect(next.threads[0]?.model).toBe("gpt-5.3-codex");
143 });
144
145 it("resolves claude aliases when session provider is claudeCode", () => {
146 const initialState = makeState(makeThread());
147 const readModel = makeReadModel(
148 makeReadModelThread({
149 model: "sonnet",
150 session: {
151 threadId: ThreadId.makeUnsafe("thread-1"),
152 status: "ready",
153 providerName: "claudeCode",
154 runtimeMode: "approval-required",
155 activeTurnId: null,
156 lastError: null,
157 updatedAt: "2026-02-27T00:00:00.000Z",
158 },
159 }),
160 );
161
162 const next = syncServerReadModel(initialState, readModel);
163
164 expect(next.threads[0]?.model).toBe("claude-sonnet-4-6");
165 });143 });
166144
167 it("resolves cursor aliases when session provider is cursor", () => {145 it("resolves cursor aliases when session provider is cursor", () => {
apps/web/src/store.ts+3-11
...@@ -145,19 +145,16 @@ function toLegacySessionStatus(...@@ -145,19 +145,16 @@ function toLegacySessionStatus(
145}145}
146146
147function toLegacyProvider(providerName: string | null): ProviderKind {147function toLegacyProvider(providerName: string | null): ProviderKind {
148 if (providerName === "codex" || providerName === "claudeCode" || providerName === "cursor") {148 if (providerName === "codex" || providerName === "cursor") {
149 return providerName;149 return providerName;
150 }150 }
151 return "codex";151 return "codex";
152}152}
153153
154const CODEX_MODEL_SLUGS = new Set<string>(getModelOptions("codex").map((option) => option.slug));154const CODEX_MODEL_SLUGS = new Set<string>(getModelOptions("codex").map((option) => option.slug));
155const CLAUDE_MODEL_SLUGS = new Set<string>(getModelOptions("claudeCode").map((option) => option.slug));
156const CURSOR_MODEL_SLUGS = new Set<string>(getModelOptions("cursor").map((option) => option.slug));155const CURSOR_MODEL_SLUGS = new Set<string>(getModelOptions("cursor").map((option) => option.slug));
157const CURSOR_DISTINCT_MODEL_SLUGS = new Set(156const CURSOR_DISTINCT_MODEL_SLUGS = new Set(
158 [...CURSOR_MODEL_SLUGS].filter(157 [...CURSOR_MODEL_SLUGS].filter((slug) => !CODEX_MODEL_SLUGS.has(slug)),
159 (slug) => !CODEX_MODEL_SLUGS.has(slug) && !CLAUDE_MODEL_SLUGS.has(slug),
160 ),
161);158);
162159
163function inferProviderForThreadModel(input: {160function inferProviderForThreadModel(input: {
...@@ -166,7 +163,6 @@ function inferProviderForThreadModel(input: {...@@ -166,7 +163,6 @@ function inferProviderForThreadModel(input: {
166}): ProviderKind {163}): ProviderKind {
167 if (164 if (
168 input.sessionProviderName === "codex" ||165 input.sessionProviderName === "codex" ||
169 input.sessionProviderName === "claudeCode" ||
170 input.sessionProviderName === "cursor"166 input.sessionProviderName === "cursor"
171 ) {167 ) {
172 return input.sessionProviderName;168 return input.sessionProviderName;
...@@ -175,10 +171,6 @@ function inferProviderForThreadModel(input: {...@@ -175,10 +171,6 @@ function inferProviderForThreadModel(input: {
175 if (normalizedCursor && CURSOR_DISTINCT_MODEL_SLUGS.has(normalizedCursor)) {171 if (normalizedCursor && CURSOR_DISTINCT_MODEL_SLUGS.has(normalizedCursor)) {
176 return "cursor";172 return "cursor";
177 }173 }
178 const normalizedClaude = normalizeModelSlug(input.model, "claudeCode");
179 if (normalizedClaude && CLAUDE_MODEL_SLUGS.has(normalizedClaude)) {
180 return "claudeCode";
181 }
182 const normalizedCodex = normalizeModelSlug(input.model, "codex");174 const normalizedCodex = normalizeModelSlug(input.model, "codex");
183 if (normalizedCodex && CODEX_MODEL_SLUGS.has(normalizedCodex)) {175 if (normalizedCodex && CODEX_MODEL_SLUGS.has(normalizedCodex)) {
184 return "codex";176 return "codex";
...@@ -190,7 +182,7 @@ function inferProviderForThreadModel(input: {...@@ -190,7 +182,7 @@ function inferProviderForThreadModel(input: {
190 ) {182 ) {
191 return "cursor";183 return "cursor";
192 }184 }
193 return input.model.trim().startsWith("claude-") ? "claudeCode" : "codex";185 return "codex";
194}186}
195187
196function resolveWsHttpOrigin(): string {188function resolveWsHttpOrigin(): string {
packages/contracts/src/orchestration.test.ts+2-2
...@@ -128,11 +128,11 @@ it.effect("preserves explicit provider and runtime mode in thread.turn.start", (...@@ -128,11 +128,11 @@ it.effect("preserves explicit provider and runtime mode in thread.turn.start", (
128 text: "hello",128 text: "hello",
129 attachments: [],129 attachments: [],
130 },130 },
131 provider: "claudeCode",131 provider: "cursor",
132 runtimeMode: "full-access",132 runtimeMode: "full-access",
133 createdAt: "2026-01-01T00:00:00.000Z",133 createdAt: "2026-01-01T00:00:00.000Z",
134 });134 });
135 assert.strictEqual(parsed.provider, "claudeCode");135 assert.strictEqual(parsed.provider, "cursor");
136 assert.strictEqual(parsed.runtimeMode, "full-access");136 assert.strictEqual(parsed.runtimeMode, "full-access");
137 assert.strictEqual(parsed.interactionMode, DEFAULT_PROVIDER_INTERACTION_MODE);137 assert.strictEqual(parsed.interactionMode, DEFAULT_PROVIDER_INTERACTION_MODE);
138 }),138 }),
packages/contracts/src/orchestration.ts+1-1
...@@ -27,7 +27,7 @@ export const ORCHESTRATION_WS_CHANNELS = {...@@ -27,7 +27,7 @@ export const ORCHESTRATION_WS_CHANNELS = {
27 domainEvent: "orchestration.domainEvent",27 domainEvent: "orchestration.domainEvent",
28} as const;28} as const;
2929
30export const ProviderKind = Schema.Literals(["codex", "claudeCode", "cursor"]);30export const ProviderKind = Schema.Literals(["codex", "cursor"]);
31export type ProviderKind = typeof ProviderKind.Type;31export type ProviderKind = typeof ProviderKind.Type;
32export const DEFAULT_PROVIDER_KIND: ProviderKind = "codex";32export const DEFAULT_PROVIDER_KIND: ProviderKind = "codex";
33export const RuntimeMode = Schema.Literals(["approval-required", "full-access"]);33export const RuntimeMode = Schema.Literals(["approval-required", "full-access"]);
packages/contracts/src/providerRuntime.test.ts+3-3
...@@ -10,7 +10,7 @@ describe("ProviderRuntimeEvent", () => {...@@ -10,7 +10,7 @@ describe("ProviderRuntimeEvent", () => {
10 const parsed = decodeRuntimeEvent({10 const parsed = decodeRuntimeEvent({
11 type: "turn.plan.updated",11 type: "turn.plan.updated",
12 eventId: "event-1",12 eventId: "event-1",
13 provider: "claudeCode",13 provider: "codex",
14 sessionId: "runtime-session-1",14 sessionId: "runtime-session-1",
15 createdAt: "2026-02-28T00:00:00.000Z",15 createdAt: "2026-02-28T00:00:00.000Z",
16 threadId: "thread-1",16 threadId: "thread-1",
...@@ -56,7 +56,7 @@ describe("ProviderRuntimeEvent", () => {...@@ -56,7 +56,7 @@ describe("ProviderRuntimeEvent", () => {
56 const parsed = decodeRuntimeEvent({56 const parsed = decodeRuntimeEvent({
57 type: "user-input.requested",57 type: "user-input.requested",
58 eventId: "event-2",58 eventId: "event-2",
59 provider: "claudeCode",59 provider: "codex",
60 sessionId: "runtime-session-2",60 sessionId: "runtime-session-2",
61 createdAt: "2026-02-28T00:00:01.000Z",61 createdAt: "2026-02-28T00:00:01.000Z",
62 threadId: "thread-2",62 threadId: "thread-2",
...@@ -94,7 +94,7 @@ describe("ProviderRuntimeEvent", () => {...@@ -94,7 +94,7 @@ describe("ProviderRuntimeEvent", () => {
94 const parsed = decodeRuntimeEvent({94 const parsed = decodeRuntimeEvent({
95 type: "user-input.resolved",95 type: "user-input.resolved",
96 eventId: "event-3",96 eventId: "event-3",
97 provider: "claudeCode",97 provider: "codex",
98 sessionId: "runtime-session-2",98 sessionId: "runtime-session-2",
99 createdAt: "2026-02-28T00:00:02.000Z",99 createdAt: "2026-02-28T00:00:02.000Z",
100 threadId: "thread-2",100 threadId: "thread-2",
packages/contracts/src/providerRuntime.ts-2
...@@ -19,8 +19,6 @@ const RuntimeEventRawSource = Schema.Literals([...@@ -19,8 +19,6 @@ const RuntimeEventRawSource = Schema.Literals([
19 "codex.app-server.notification",19 "codex.app-server.notification",
20 "codex.app-server.request",20 "codex.app-server.request",
21 "codex.eventmsg",21 "codex.eventmsg",
22 "claude.sdk.message",
23 "claude.sdk.permission",
24 "codex.sdk.thread-event",22 "codex.sdk.thread-event",
25 "cursor.acp.notification",23 "cursor.acp.notification",
26 "cursor.acp.request",24 "cursor.acp.request",
packages/shared/src/model.test.ts-1
...@@ -84,7 +84,6 @@ describe("resolveModelSlug", () => {...@@ -84,7 +84,6 @@ describe("resolveModelSlug", () => {
84 it("keeps codex defaults for backward compatibility", () => {84 it("keeps codex defaults for backward compatibility", () => {
85 expect(getDefaultModel()).toBe(DEFAULT_MODEL);85 expect(getDefaultModel()).toBe(DEFAULT_MODEL);
86 expect(getModelOptions()).toEqual(MODEL_OPTIONS);86 expect(getModelOptions()).toEqual(MODEL_OPTIONS);
87 expect(getModelOptions("claudeCode")).toEqual([]);
88 expect(getModelOptions("cursor")).toEqual(MODEL_OPTIONS_BY_PROVIDER.cursor);87 expect(getModelOptions("cursor")).toEqual(MODEL_OPTIONS_BY_PROVIDER.cursor);
89 expect(getCursorModelFamilyOptions()).toEqual(CURSOR_MODEL_FAMILY_OPTIONS);88 expect(getCursorModelFamilyOptions()).toEqual(CURSOR_MODEL_FAMILY_OPTIONS);
90 });89 });