Unified · Split
| @@ -27,9 +27,7 @@ import Migration0012 from "./Migrations/012_ProjectionThreadsInteractionMode.ts" · 26 unmodified lines | ||
| 27 | 27 | import Migration0013 from "./Migrations/013_ProjectionThreadProposedPlans.ts"; |
| 28 | 28 | import Migration0014 from "./Migrations/014_ProjectionThreadProposedPlanImplementation.ts"; |
| 29 | 29 | import Migration0015 from "./Migrations/015_ProjectionTurnsSourceProposedPlan.ts"; |
| 30 | −import Migration0016 from "./Migrations/016_ProjectionProviders.ts"; | |
| 31 | −import Migration0017 from "./Migrations/017_ProjectionModelSelectionOptions.ts"; | |
| 32 | −import Migration0018 from "./Migrations/018_CanonicalizeLegacyModelSelections.ts"; | |
| 30 | +import Migration0016 from "./Migrations/016_CanonicalizeModelSelections.ts"; | |
| 33 | 31 | import { Effect } from "effect"; |
| 34 | 32 | |
| 35 | 33 | /** |
| @@ -58,9 +56,7 @@ const loader = Migrator.fromRecord({ · 22 unmodified lines | ||
| 58 | 56 | "13_ProjectionThreadProposedPlans": Migration0013, |
| 59 | 57 | "14_ProjectionThreadProposedPlanImplementation": Migration0014, |
| 60 | 58 | "15_ProjectionTurnsSourceProposedPlan": Migration0015, |
| 61 | − "16_ProjectionProviders": Migration0016, | |
| 62 | − "17_ProjectionModelSelectionOptions": Migration0017, | |
| 63 | − "18_CanonicalizeLegacyModelSelections": Migration0018, | |
| 59 | + "16_CanonicalizeModelSelections": Migration0016, | |
| 64 | 60 | }); |
| 65 | 61 | |
| 66 | 62 | /** |
| @@ -0,0 +1,182 @@ | ||
| 1 | +import { assert, it } from "@effect/vitest"; | |
| 2 | +import { Effect, Layer } from "effect"; | |
| 3 | +import * as SqlClient from "effect/unstable/sql/SqlClient"; | |
| 4 | + | |
| 5 | +import Migration0016 from "./016_CanonicalizeModelSelections.ts"; | |
| 6 | +import * as NodeSqliteClient from "../NodeSqliteClient.ts"; | |
| 7 | + | |
| 8 | +const layer = it.layer(Layer.mergeAll(NodeSqliteClient.layerMemory())); | |
| 9 | + | |
| 10 | +layer("016_CanonicalizeModelSelections", (it) => { | |
| 11 | + it.effect( | |
| 12 | + "migrates legacy projection rows and event payloads to the canonical model-selection shape", | |
| 13 | + () => | |
| 14 | + Effect.gen(function* () { | |
| 15 | + const sql = yield* SqlClient.SqlClient; | |
| 16 | + | |
| 17 | + yield* sql` | |
| 18 | + CREATE TABLE projection_projects ( | |
| 19 | + project_id TEXT PRIMARY KEY, | |
| 20 | + default_model TEXT | |
| 21 | + ) | |
| 22 | + `; | |
| 23 | + yield* sql` | |
| 24 | + CREATE TABLE projection_threads ( | |
| 25 | + thread_id TEXT PRIMARY KEY, | |
| 26 | + model TEXT NOT NULL | |
| 27 | + ) | |
| 28 | + `; | |
| 29 | + yield* sql` | |
| 30 | + CREATE TABLE projection_thread_sessions ( | |
| 31 | + thread_id TEXT PRIMARY KEY, | |
| 32 | + provider_name TEXT | |
| 33 | + ) | |
| 34 | + `; | |
| 35 | + yield* sql` | |
| 36 | + CREATE TABLE orchestration_events ( | |
| 37 | + event_type TEXT NOT NULL, | |
| 38 | + payload_json TEXT NOT NULL | |
| 39 | + ) | |
| 40 | + `; | |
| 41 | + | |
| 42 | + yield* sql` | |
| 43 | + INSERT INTO projection_projects (project_id, default_model) | |
| 44 | + VALUES | |
| 45 | + ('project-codex', 'gpt-5.4'), | |
| 46 | + ('project-claude', 'claude-sonnet-4-6'), | |
| 47 | + ('project-null', NULL) | |
| 48 | + `; | |
| 49 | + yield* sql` | |
| 50 | + UPDATE projection_projects | |
| 51 | + SET default_model = 'claude-opus-4-6' | |
| 52 | + WHERE project_id = 'project-claude' | |
| 53 | + `; | |
| 54 | + yield* sql` | |
| 55 | + INSERT INTO projection_threads (thread_id, model) | |
| 56 | + VALUES | |
| 57 | + ('thread-session', 'gpt-5.4'), | |
| 58 | + ('thread-claude', 'claude-opus-4-6'), | |
| 59 | + ('thread-codex', 'gpt-5.4'), | |
| 60 | + ('thread-legacy-options', 'claude-opus-4-6') | |
| 61 | + `; | |
| 62 | + yield* sql` | |
| 63 | + INSERT INTO projection_thread_sessions (thread_id, provider_name) | |
| 64 | + VALUES ('thread-session', 'claudeAgent') | |
| 65 | + `; | |
| 66 | + yield* sql` | |
| 67 | + INSERT INTO orchestration_events ( | |
| 68 | + event_type, | |
| 69 | + payload_json | |
| 70 | + ) | |
| 71 | + VALUES | |
| 72 | + ( | |
| 73 | + 'project.created', | |
| 74 | + '{"projectId":"project-1","title":"Project","workspaceRoot":"/tmp/project","defaultModel":"claude-opus-4-6","defaultModelOptions":{"codex":{"reasoningEffort":"high"},"claudeAgent":{"effort":"max"}},"scripts":[],"createdAt":"2026-01-01T00:00:00.000Z","updatedAt":"2026-01-01T00:00:00.000Z"}' | |
| 75 | + ), | |
| 76 | + ( | |
| 77 | + 'thread.created', | |
| 78 | + '{"threadId":"thread-1","projectId":"project-1","title":"Thread","model":"claude-opus-4-6","modelOptions":{"codex":{"reasoningEffort":"high"},"claudeAgent":{"effort":"max","thinking":false}},"runtimeMode":"full-access","interactionMode":"default","branch":null,"worktreePath":null,"createdAt":"2026-01-01T00:00:00.000Z","updatedAt":"2026-01-01T00:00:00.000Z"}' | |
| 79 | + ), | |
| 80 | + ( | |
| 81 | + 'thread.turn-start-requested', | |
| 82 | + '{"threadId":"thread-1","turnId":"turn-1","input":"hi","model":"gpt-5.4","modelOptions":{"codex":{"fastMode":true},"claudeAgent":{"effort":"max"}},"deliveryMode":"buffered"}' | |
| 83 | + ) | |
| 84 | + `; | |
| 85 | + | |
| 86 | + yield* Migration0016; | |
| 87 | + | |
| 88 | + const projectRows = yield* sql<{ | |
| 89 | + readonly projectId: string; | |
| 90 | + readonly defaultProvider: string | null; | |
| 91 | + }>` | |
| 92 | + SELECT | |
| 93 | + project_id AS "projectId", | |
| 94 | + default_provider AS "defaultProvider" | |
| 95 | + FROM projection_projects | |
| 96 | + ORDER BY project_id | |
| 97 | + `; | |
| 98 | + assert.deepStrictEqual(projectRows, [ | |
| 99 | + { projectId: "project-claude", defaultProvider: "claudeAgent" }, | |
| 100 | + { projectId: "project-codex", defaultProvider: "codex" }, | |
| 101 | + { projectId: "project-null", defaultProvider: null }, | |
| 102 | + ]); | |
| 103 | + | |
| 104 | + const threadRows = yield* sql<{ | |
| 105 | + readonly threadId: string; | |
| 106 | + readonly provider: string | null; | |
| 107 | + readonly modelOptions: string | null; | |
| 108 | + }>` | |
| 109 | + SELECT | |
| 110 | + thread_id AS "threadId", | |
| 111 | + provider, | |
| 112 | + model_options_json AS "modelOptions" | |
| 113 | + FROM projection_threads | |
| 114 | + ORDER BY thread_id | |
| 115 | + `; | |
| 116 | + assert.deepStrictEqual(threadRows, [ | |
| 117 | + { threadId: "thread-claude", provider: "claudeAgent", modelOptions: null }, | |
| 118 | + { threadId: "thread-codex", provider: "codex", modelOptions: null }, | |
| 119 | + { threadId: "thread-legacy-options", provider: "claudeAgent", modelOptions: null }, | |
| 120 | + { threadId: "thread-session", provider: "claudeAgent", modelOptions: null }, | |
| 121 | + ]); | |
| 122 | + | |
| 123 | + const eventRows = yield* sql<{ | |
| 124 | + readonly payloadJson: string; | |
| 125 | + }>` | |
| 126 | + SELECT payload_json AS "payloadJson" | |
| 127 | + FROM orchestration_events | |
| 128 | + ORDER BY rowid ASC | |
| 129 | + `; | |
| 130 | + | |
| 131 | + assert.deepStrictEqual(JSON.parse(eventRows[0]!.payloadJson), { | |
| 132 | + projectId: "project-1", | |
| 133 | + title: "Project", | |
| 134 | + workspaceRoot: "/tmp/project", | |
| 135 | + defaultModelSelection: { | |
| 136 | + provider: "claudeAgent", | |
| 137 | + model: "claude-opus-4-6", | |
| 138 | + options: { | |
| 139 | + effort: "max", | |
| 140 | + }, | |
| 141 | + }, | |
| 142 | + scripts: [], | |
| 143 | + createdAt: "2026-01-01T00:00:00.000Z", | |
| 144 | + updatedAt: "2026-01-01T00:00:00.000Z", | |
| 145 | + }); | |
| 146 | + | |
| 147 | + assert.deepStrictEqual(JSON.parse(eventRows[1]!.payloadJson), { | |
| 148 | + threadId: "thread-1", | |
| 149 | + projectId: "project-1", | |
| 150 | + title: "Thread", | |
| 151 | + modelSelection: { | |
| 152 | + provider: "claudeAgent", | |
| 153 | + model: "claude-opus-4-6", | |
| 154 | + options: { | |
| 155 | + effort: "max", | |
| 156 | + thinking: false, | |
| 157 | + }, | |
| 158 | + }, | |
| 159 | + runtimeMode: "full-access", | |
| 160 | + interactionMode: "default", | |
| 161 | + branch: null, | |
| 162 | + worktreePath: null, | |
| 163 | + createdAt: "2026-01-01T00:00:00.000Z", | |
| 164 | + updatedAt: "2026-01-01T00:00:00.000Z", | |
| 165 | + }); | |
| 166 | + | |
| 167 | + assert.deepStrictEqual(JSON.parse(eventRows[2]!.payloadJson), { | |
| 168 | + threadId: "thread-1", | |
| 169 | + turnId: "turn-1", | |
| 170 | + input: "hi", | |
| 171 | + modelSelection: { | |
| 172 | + provider: "codex", | |
| 173 | + model: "gpt-5.4", | |
| 174 | + options: { | |
| 175 | + fastMode: true, | |
| 176 | + }, | |
| 177 | + }, | |
| 178 | + deliveryMode: "buffered", | |
| 179 | + }); | |
| 180 | + }), | |
| 181 | + ); | |
| 182 | +}); | |
| @@ -4,24 +4,64 @@ import * as SqlClient from "effect/unstable/sql/SqlClient"; · 3 unmodified lines | ||
| 4 | 4 | export default Effect.gen(function* () { |
| 5 | 5 | const sql = yield* SqlClient.SqlClient; |
| 6 | 6 | |
| 7 | + yield* sql` | |
| 8 | + ALTER TABLE projection_projects | |
| 9 | + ADD COLUMN default_provider TEXT | |
| 10 | + `; | |
| 11 | + | |
| 7 | 12 | yield* sql` |
| 8 | 13 | UPDATE projection_projects |
| 9 | − SET | |
| 10 | − default_provider = CASE | |
| 11 | − WHEN default_model IS NULL THEN NULL | |
| 12 | − WHEN lower(default_model) LIKE '%claude%' THEN 'claudeAgent' | |
| 14 | + SET default_provider = CASE | |
| 15 | + WHEN default_model IS NULL THEN NULL | |
| 16 | + WHEN lower(default_model) LIKE '%claude%' THEN 'claudeAgent' | |
| 17 | + ELSE 'codex' | |
| 18 | + END | |
| 19 | + WHERE default_provider IS NULL | |
| 20 | + `; | |
| 21 | + | |
| 22 | + yield* sql` | |
| 23 | + ALTER TABLE projection_threads | |
| 24 | + ADD COLUMN provider TEXT | |
| 25 | + `; | |
| 26 | + | |
| 27 | + yield* sql` | |
| 28 | + UPDATE projection_threads | |
| 29 | + SET provider = COALESCE( | |
| 30 | + ( | |
| 31 | + SELECT provider_name | |
| 32 | + FROM projection_thread_sessions | |
| 33 | + WHERE projection_thread_sessions.thread_id = projection_threads.thread_id | |
| 34 | + ), | |
| 35 | + CASE | |
| 36 | + WHEN lower(model) LIKE '%claude%' THEN 'claudeAgent' | |
| 13 | 37 | ELSE 'codex' |
| 14 | 38 | END, |
| 39 | + 'codex' | |
| 40 | + ) | |
| 41 | + WHERE provider IS NULL | |
| 42 | + `; | |
| 43 | + | |
| 44 | + yield* sql` | |
| 45 | + ALTER TABLE projection_projects | |
| 46 | + ADD COLUMN default_model_options_json TEXT | |
| 47 | + `; | |
| 48 | + | |
| 49 | + yield* sql` | |
| 50 | + ALTER TABLE projection_threads | |
| 51 | + ADD COLUMN model_options_json TEXT | |
| 52 | + `; | |
| 53 | + | |
| 54 | + yield* sql` | |
| 55 | + UPDATE projection_projects | |
| 56 | + SET | |
| 15 | 57 | default_model_options_json = CASE |
| 16 | 58 | WHEN default_model_options_json IS NULL THEN NULL |
| 17 | 59 | WHEN json_valid(default_model_options_json) = 0 THEN default_model_options_json |
| 18 | 60 | WHEN json_type(default_model_options_json, '$.codex') IS NOT NULL |
| 19 | 61 | OR json_type(default_model_options_json, '$.claudeAgent') IS NOT NULL |
| 20 | 62 | THEN CASE |
| 21 | − WHEN lower(default_model) LIKE '%claude%' THEN json_extract( | |
| 22 | − default_model_options_json, | |
| 23 | − '$.claudeAgent' | |
| 24 | − ) | |
| 63 | + WHEN default_provider = 'claudeAgent' | |
| 64 | + THEN json_extract(default_model_options_json, '$.claudeAgent') | |
| 25 | 65 | ELSE json_extract(default_model_options_json, '$.codex') |
| 26 | 66 | END |
| 27 | 67 | ELSE default_model_options_json |
| @@ -32,17 +72,14 @@ export default Effect.gen(function* () { · 4 unmodified lines | ||
| 32 | 72 | yield* sql` |
| 33 | 73 | UPDATE projection_threads |
| 34 | 74 | SET |
| 35 | − provider = CASE | |
| 36 | − WHEN lower(model) LIKE '%claude%' THEN 'claudeAgent' | |
| 37 | − ELSE 'codex' | |
| 38 | − END, | |
| 39 | 75 | model_options_json = CASE |
| 40 | 76 | WHEN model_options_json IS NULL THEN NULL |
| 41 | 77 | WHEN json_valid(model_options_json) = 0 THEN model_options_json |
| 42 | 78 | WHEN json_type(model_options_json, '$.codex') IS NOT NULL |
| 43 | 79 | OR json_type(model_options_json, '$.claudeAgent') IS NOT NULL |
| 44 | 80 | THEN CASE |
| 45 | − WHEN lower(model) LIKE '%claude%' THEN json_extract(model_options_json, '$.claudeAgent') | |
| 81 | + WHEN provider = 'claudeAgent' | |
| 82 | + THEN json_extract(model_options_json, '$.claudeAgent') | |
| 46 | 83 | ELSE json_extract(model_options_json, '$.codex') |
| 47 | 84 | END |
| 48 | 85 | ELSE model_options_json |
| @@ -1,38 +0,0 @@ | ||
| 1 | −import * as Effect from "effect/Effect"; | |
| 2 | −import * as SqlClient from "effect/unstable/sql/SqlClient"; | |
| 3 | − | |
| 4 | −export default Effect.gen(function* () { | |
| 5 | − const sql = yield* SqlClient.SqlClient; | |
| 6 | − | |
| 7 | − yield* sql` | |
| 8 | − ALTER TABLE projection_projects | |
| 9 | − ADD COLUMN default_provider TEXT | |
| 10 | − `; | |
| 11 | − | |
| 12 | − yield* sql` | |
| 13 | − UPDATE projection_projects | |
| 14 | − SET default_provider = CASE | |
| 15 | − WHEN default_model IS NULL THEN NULL | |
| 16 | − ELSE 'codex' | |
| 17 | − END | |
| 18 | − WHERE default_provider IS NULL | |
| 19 | − `; | |
| 20 | − | |
| 21 | − yield* sql` | |
| 22 | − ALTER TABLE projection_threads | |
| 23 | − ADD COLUMN provider TEXT | |
| 24 | − `; | |
| 25 | − | |
| 26 | − yield* sql` | |
| 27 | − UPDATE projection_threads | |
| 28 | − SET provider = COALESCE( | |
| 29 | − ( | |
| 30 | − SELECT provider_name | |
| 31 | − FROM projection_thread_sessions | |
| 32 | − WHERE projection_thread_sessions.thread_id = projection_threads.thread_id | |
| 33 | − ), | |
| 34 | − 'codex' | |
| 35 | − ) | |
| 36 | − WHERE provider IS NULL | |
| 37 | − `; | |
| 38 | −}); | |
| @@ -1,28 +0,0 @@ | ||
| 1 | −import * as Effect from "effect/Effect"; | |
| 2 | −import * as SqlClient from "effect/unstable/sql/SqlClient"; | |
| 3 | − | |
| 4 | −export default Effect.gen(function* () { | |
| 5 | − const sql = yield* SqlClient.SqlClient; | |
| 6 | − | |
| 7 | − yield* sql` | |
| 8 | − ALTER TABLE projection_projects | |
| 9 | − ADD COLUMN default_model_options_json TEXT | |
| 10 | − `; | |
| 11 | − | |
| 12 | − yield* sql` | |
| 13 | − UPDATE projection_projects | |
| 14 | − SET default_model_options_json = NULL | |
| 15 | − WHERE default_model_options_json IS NULL | |
| 16 | − `; | |
| 17 | − | |
| 18 | − yield* sql` | |
| 19 | − ALTER TABLE projection_threads | |
| 20 | − ADD COLUMN model_options_json TEXT | |
| 21 | − `; | |
| 22 | − | |
| 23 | − yield* sql` | |
| 24 | − UPDATE projection_threads | |
| 25 | − SET model_options_json = NULL | |
| 26 | − WHERE model_options_json IS NULL | |
| 27 | − `; | |
| 28 | −}); | |
| @@ -1,180 +0,0 @@ | ||
| 1 | −import { assert, it } from "@effect/vitest"; | |
| 2 | −import { Effect, Layer } from "effect"; | |
| 3 | −import * as SqlClient from "effect/unstable/sql/SqlClient"; | |
| 4 | − | |
| 5 | −import Migration0018 from "./018_CanonicalizeLegacyModelSelections.ts"; | |
| 6 | −import * as NodeSqliteClient from "../NodeSqliteClient.ts"; | |
| 7 | − | |
| 8 | −const layer = it.layer(Layer.mergeAll(NodeSqliteClient.layerMemory())); | |
| 9 | − | |
| 10 | −layer("018_CanonicalizeLegacyModelSelections", (it) => { | |
| 11 | − it.effect("canonicalizes legacy projection rows and orchestration event payloads", () => | |
| 12 | − Effect.gen(function* () { | |
| 13 | − const sql = yield* SqlClient.SqlClient; | |
| 14 | − | |
| 15 | − yield* sql` | |
| 16 | − CREATE TABLE projection_projects ( | |
| 17 | − project_id TEXT PRIMARY KEY, | |
| 18 | − default_provider TEXT, | |
| 19 | − default_model TEXT, | |
| 20 | − default_model_options_json TEXT | |
| 21 | − ) | |
| 22 | − `; | |
| 23 | − yield* sql` | |
| 24 | − CREATE TABLE projection_threads ( | |
| 25 | − thread_id TEXT PRIMARY KEY, | |
| 26 | − provider TEXT, | |
| 27 | − model TEXT NOT NULL, | |
| 28 | − model_options_json TEXT | |
| 29 | − ) | |
| 30 | − `; | |
| 31 | − yield* sql` | |
| 32 | − CREATE TABLE orchestration_events ( | |
| 33 | − event_type TEXT NOT NULL, | |
| 34 | − payload_json TEXT NOT NULL | |
| 35 | − ) | |
| 36 | − `; | |
| 37 | − | |
| 38 | − yield* sql` | |
| 39 | − INSERT INTO projection_projects ( | |
| 40 | − project_id, | |
| 41 | − default_provider, | |
| 42 | − default_model, | |
| 43 | − default_model_options_json | |
| 44 | − ) | |
| 45 | − VALUES ( | |
| 46 | − 'project-1', | |
| 47 | − 'codex', | |
| 48 | − 'claude-opus-4-6', | |
| 49 | − '{"codex":{"reasoningEffort":"high"},"claudeAgent":{"effort":"max","thinking":false}}' | |
| 50 | − ) | |
| 51 | − `; | |
| 52 | − yield* sql` | |
| 53 | − INSERT INTO projection_threads ( | |
| 54 | − thread_id, | |
| 55 | − provider, | |
| 56 | − model, | |
| 57 | − model_options_json | |
| 58 | − ) | |
| 59 | − VALUES ( | |
| 60 | − 'thread-1', | |
| 61 | − 'codex', | |
| 62 | − 'claude-opus-4-6', | |
| 63 | − '{"codex":{"reasoningEffort":"high"},"claudeAgent":{"effort":"max"}}' | |
| 64 | − ) | |
| 65 | − `; | |
| 66 | − yield* sql` | |
| 67 | − INSERT INTO orchestration_events ( | |
| 68 | − event_type, | |
| 69 | − payload_json | |
| 70 | − ) | |
| 71 | − VALUES | |
| 72 | − ( | |
| 73 | − 'project.created', | |
| 74 | − '{"projectId":"project-1","title":"Project","workspaceRoot":"/tmp/project","defaultModel":"claude-opus-4-6","defaultModelOptions":{"codex":{"reasoningEffort":"high"},"claudeAgent":{"effort":"max"}},"scripts":[],"createdAt":"2026-01-01T00:00:00.000Z","updatedAt":"2026-01-01T00:00:00.000Z"}' | |
| 75 | − ), | |
| 76 | − ( | |
| 77 | − 'thread.created', | |
| 78 | − '{"threadId":"thread-1","projectId":"project-1","title":"Thread","model":"claude-opus-4-6","modelOptions":{"codex":{"reasoningEffort":"high"},"claudeAgent":{"effort":"max","thinking":false}},"runtimeMode":"full-access","interactionMode":"default","branch":null,"worktreePath":null,"createdAt":"2026-01-01T00:00:00.000Z","updatedAt":"2026-01-01T00:00:00.000Z"}' | |
| 79 | − ), | |
| 80 | − ( | |
| 81 | − 'thread.turn-start-requested', | |
| 82 | − '{"threadId":"thread-1","turnId":"turn-1","input":"hi","model":"gpt-5.4","modelOptions":{"codex":{"fastMode":true},"claudeAgent":{"effort":"max"}},"deliveryMode":"buffered"}' | |
| 83 | − ) | |
| 84 | − `; | |
| 85 | − | |
| 86 | − yield* Migration0018; | |
| 87 | − | |
| 88 | − const projectRows = yield* sql<{ | |
| 89 | − readonly defaultProvider: string | null; | |
| 90 | − readonly defaultModelOptions: string | null; | |
| 91 | − }>` | |
| 92 | − SELECT | |
| 93 | − default_provider AS "defaultProvider", | |
| 94 | − default_model_options_json AS "defaultModelOptions" | |
| 95 | − FROM projection_projects | |
| 96 | − WHERE project_id = 'project-1' | |
| 97 | − `; | |
| 98 | − assert.deepStrictEqual(projectRows[0], { | |
| 99 | − defaultProvider: "claudeAgent", | |
| 100 | − defaultModelOptions: '{"effort":"max","thinking":false}', | |
| 101 | − }); | |
| 102 | − | |
| 103 | − const threadRows = yield* sql<{ | |
| 104 | − readonly provider: string | null; | |
| 105 | − readonly modelOptions: string | null; | |
| 106 | − }>` | |
| 107 | − SELECT | |
| 108 | − provider, | |
| 109 | − model_options_json AS "modelOptions" | |
| 110 | − FROM projection_threads | |
| 111 | − WHERE thread_id = 'thread-1' | |
| 112 | − `; | |
| 113 | − assert.deepStrictEqual(threadRows[0], { | |
| 114 | − provider: "claudeAgent", | |
| 115 | − modelOptions: '{"effort":"max"}', | |
| 116 | − }); | |
| 117 | − | |
| 118 | − const eventRows = yield* sql<{ | |
| 119 | − readonly eventType: string; | |
| 120 | − readonly payloadJson: string; | |
| 121 | − }>` | |
| 122 | − SELECT | |
| 123 | − event_type AS "eventType", | |
| 124 | − payload_json AS "payloadJson" | |
| 125 | − FROM orchestration_events | |
| 126 | − ORDER BY rowid ASC | |
| 127 | − `; | |
| 128 | − | |
| 129 | − assert.deepStrictEqual(JSON.parse(eventRows[0]!.payloadJson), { | |
| 130 | − projectId: "project-1", | |
| 131 | − title: "Project", | |
| 132 | − workspaceRoot: "/tmp/project", | |
| 133 | − defaultModelSelection: { | |
| 134 | − provider: "claudeAgent", | |
| 135 | − model: "claude-opus-4-6", | |
| 136 | − options: { | |
| 137 | − effort: "max", | |
| 138 | − }, | |
| 139 | − }, | |
| 140 | − scripts: [], | |
| 141 | − createdAt: "2026-01-01T00:00:00.000Z", | |
| 142 | − updatedAt: "2026-01-01T00:00:00.000Z", | |
| 143 | − }); | |
| 144 | − | |
| 145 | − assert.deepStrictEqual(JSON.parse(eventRows[1]!.payloadJson), { | |
| 146 | − threadId: "thread-1", | |
| 147 | − projectId: "project-1", | |
| 148 | − title: "Thread", | |
| 149 | − modelSelection: { | |
| 150 | − provider: "claudeAgent", | |
| 151 | − model: "claude-opus-4-6", | |
| 152 | − options: { | |
| 153 | − effort: "max", | |
| 154 | − thinking: false, | |
| 155 | − }, | |
| 156 | − }, | |
| 157 | − runtimeMode: "full-access", | |
| 158 | − interactionMode: "default", | |
| 159 | − branch: null, | |
| 160 | − worktreePath: null, | |
| 161 | − createdAt: "2026-01-01T00:00:00.000Z", | |
| 162 | − updatedAt: "2026-01-01T00:00:00.000Z", | |
| 163 | − }); | |
| 164 | − | |
| 165 | − assert.deepStrictEqual(JSON.parse(eventRows[2]!.payloadJson), { | |
| 166 | − threadId: "thread-1", | |
| 167 | − turnId: "turn-1", | |
| 168 | − input: "hi", | |
| 169 | − modelSelection: { | |
| 170 | − provider: "codex", | |
| 171 | − model: "gpt-5.4", | |
| 172 | − options: { | |
| 173 | − fastMode: true, | |
| 174 | − }, | |
| 175 | − }, | |
| 176 | − deliveryMode: "buffered", | |
| 177 | − }); | |
| 178 | − }), | |
| 179 | − ); | |
| 180 | −}); | |