| 1 | import type { ManualSessionCreationView, SessionComposerState } from "../generated/desktopContract.generated"; |
| 2 | import type { SessionUIBindings } from "./sessionUIBindings"; |
| 3 | |
| 4 | export function makeSessionUIMock(create: (scope: string, root: string, id: string) => Promise<void>): SessionUIBindings { |
| 5 | const operations = new Map<string, ManualSessionCreationView>(); |
| 6 | const inputs = new Map<string, SessionComposerState>(); |
| 7 | const submissions = new Set<string>(); |
| 8 | const read: SessionUIBindings["GetSessionComposerState"] = async ref => structuredClone(inputs.get(`${ref.hostId}:${ref.sessionId}`) ?? { |
| 9 | ref, revision: "0", contentJson: "{}", contentVersion: 1, baseline: "mock", historyChanged: false, conflict: false, |
| 10 | }); |
| 11 | const save = (state: SessionComposerState) => { |
| 12 | const next = { ...state, revision: String(BigInt(state.revision) + 1n) }; |
| 13 | inputs.set(`${state.ref.hostId}:${state.ref.sessionId}`, next); return structuredClone(next); |
| 14 | }; |
| 15 | return { |
| 16 | async BeginManualSessionCreation(req) { |
| 17 | const prior = operations.get(req.operationId); if (prior) return structuredClone(prior); |
| 18 | const id = `manual-${req.operationId}`; |
| 19 | const op: ManualSessionCreationView = { operationId: req.operationId, workspaceId: req.workspaceId || req.workspaceRoot || "global", |
| 20 | scope: req.scope || "global", workspaceRoot: req.workspaceRoot || "", ref: { hostId: "local", sessionId: id }, topicId: id, phase: "starting", |
| 21 | settings: { model: "deepseek/deepseek-chat", mode: "normal", toolApprovalMode: "default", disabledMcp: {}, mcpOrder: [] } }; |
| 22 | operations.set(req.operationId, op); |
| 23 | await create(op.scope, op.workspaceRoot, id); op.phase = "ready"; return structuredClone(op); |
| 24 | }, |
| 25 | async GetManualSessionCreation(id) { const op = operations.get(id); if (!op) throw new Error("Creation not found"); return structuredClone(op); }, |
| 26 | async ListManualSessionCreations() { return [...operations.values()].filter(op => op.phase !== "ready").map(op => structuredClone(op)); }, |
| 27 | async RetryManualSessionCreation(id) { return this.GetManualSessionCreation(id); }, |
| 28 | GetSessionComposerState: read, |
| 29 | async ListSessionComposerConflicts() { return []; }, |
| 30 | async SaveSessionComposerState(req) { |
| 31 | const state = await read(req.ref); |
| 32 | if (state.revision !== req.expectedRevision) return { ...state, conflict: true }; |
| 33 | if (state.submissionId) throw new Error("Check pending submission"); |
| 34 | return save({ ...state, contentJson: req.contentJson, contentVersion: req.contentVersion, historyChanged: false }); |
| 35 | }, |
| 36 | async BeginSessionComposerSubmission(ref, revision, submissionId, request) { |
| 37 | const state = await read(ref); |
| 38 | if (state.submissionId === submissionId && state.submissionRevision === revision && state.submissionRequest === request) return state; |
| 39 | const key = `${ref.hostId}:${ref.sessionId}/${submissionId}`; |
| 40 | if (state.revision !== revision || state.submissionId || submissions.has(key)) throw new Error("Input changed"); |
| 41 | submissions.add(key); |
| 42 | return save({ ...state, submissionId, submissionRevision: revision, submissionRequest: request, submissionPhase: "pending" }); |
| 43 | }, |
| 44 | async CompleteSessionComposerSubmission(ref, id, outcome) { |
| 45 | const state = await read(ref); if (!state.submissionId) return state; |
| 46 | if (state.submissionId !== id) throw new Error("Input changed"); |
| 47 | return save({ ...state, contentJson: outcome === "accepted" ? "{}" : state.contentJson, |
| 48 | submissionId: outcome === "unknown" ? id : undefined, submissionPhase: outcome === "unknown" ? "unknown" : undefined }); |
| 49 | }, |
| 50 | }; |
| 51 | } |
| 52 |