diff --git a/src/features/chat/app-server/mappers/message-stream/review-result-items.ts b/src/features/chat/app-server/mappers/message-stream/review-result-items.ts index 4278f61c..5255385a 100644 --- a/src/features/chat/app-server/mappers/message-stream/review-result-items.ts +++ b/src/features/chat/app-server/mappers/message-stream/review-result-items.ts @@ -4,8 +4,8 @@ import { RUNNING_EXECUTION_STATE, type ExecutionStateByStatus, } from "../../../domain/message-stream/execution-state"; -import { pathsRelativeToRoot } from "../../../domain/message-stream/format/path-labels"; import { permissionRows } from "../../../domain/message-stream/format/permission-rows"; +import { pathRelativeToRoot } from "../../../../../shared/path/file-paths"; const AUTO_REVIEW_STATES: ExecutionStateByStatus = { inProgress: RUNNING_EXECUTION_STATE, @@ -150,7 +150,10 @@ function autoReviewActionRows(action: AutoReviewAction): MessageStreamAuditFact[ return [ { key: "action", value: "apply patch" }, { key: "cwd", value: action.cwd }, - { key: "files", value: action.files.length > 0 ? pathsRelativeToRoot([...action.files], action.cwd).join("\n") : "(none)" }, + { + key: "files", + value: action.files.length > 0 ? action.files.map((file) => pathRelativeToRoot(file, action.cwd)).join("\n") : "(none)", + }, ]; } if (action.type === "networkAccess") { diff --git a/src/features/chat/app-server/threads/history.ts b/src/features/chat/app-server/threads/history.ts deleted file mode 100644 index 8c582ac9..00000000 --- a/src/features/chat/app-server/threads/history.ts +++ /dev/null @@ -1,27 +0,0 @@ -import type { AppServerClient } from "../../../../app-server/connection/client"; -import type { ThreadTurnsPage } from "../../../../domain/threads/history"; -import type { MessageStreamItem } from "../../domain/message-stream/items"; -import { messageStreamItemsFromTurns } from "../mappers/message-stream/turn-items"; - -export interface ChatThreadHistoryPage { - items: MessageStreamItem[]; - nextCursor: string | null; - hadTurns: boolean; -} - -export async function readChatThreadHistoryPage( - client: AppServerClient, - threadId: string, - cursor: string | null, - limit = 20, -): Promise { - return chatThreadHistoryPageFromTurnsPage(await client.threadTurnsList(threadId, cursor, limit)); -} - -export function chatThreadHistoryPageFromTurnsPage(page: ThreadTurnsPage): ChatThreadHistoryPage { - return { - items: messageStreamItemsFromTurns(page.data), - nextCursor: page.nextCursor, - hadTurns: page.data.length > 0, - }; -} diff --git a/src/features/chat/app-server/threads/resume.ts b/src/features/chat/app-server/threads/projection.ts similarity index 50% rename from src/features/chat/app-server/threads/resume.ts rename to src/features/chat/app-server/threads/projection.ts index f20b9109..14430e45 100644 --- a/src/features/chat/app-server/threads/resume.ts +++ b/src/features/chat/app-server/threads/projection.ts @@ -1,8 +1,15 @@ import type { AppServerClient } from "../../../../app-server/connection/client"; import { threadActivationSnapshotFromAppServerResponse } from "../../../../app-server/threads"; import type { ThreadActivationSnapshot } from "../../../../domain/threads/activation"; -import type { ChatThreadHistoryPage } from "./history"; -import { chatThreadHistoryPageFromTurnsPage } from "./history"; +import type { ThreadTurnsPage } from "../../../../domain/threads/history"; +import type { MessageStreamItem } from "../../domain/message-stream/items"; +import { messageStreamItemsFromTurns } from "../mappers/message-stream/turn-items"; + +export interface ChatThreadHistoryPage { + items: MessageStreamItem[]; + nextCursor: string | null; + hadTurns: boolean; +} export interface ChatThreadResumeSnapshot { activation: ThreadActivationSnapshot; @@ -10,6 +17,23 @@ export interface ChatThreadResumeSnapshot { initialHistoryPage: ChatThreadHistoryPage | null; } +export async function readChatThreadHistoryPage( + client: AppServerClient, + threadId: string, + cursor: string | null, + limit = 20, +): Promise { + return chatThreadHistoryPageFromTurnsPage(await client.threadTurnsList(threadId, cursor, limit)); +} + +function chatThreadHistoryPageFromTurnsPage(page: ThreadTurnsPage): ChatThreadHistoryPage { + return { + items: messageStreamItemsFromTurns(page.data), + nextCursor: page.nextCursor, + hadTurns: page.data.length > 0, + }; +} + export async function resumeChatThread(client: AppServerClient, threadId: string, cwd: string): Promise { const response = await client.resumeThread(threadId, cwd); return { diff --git a/src/features/chat/application/conversation/composition.ts b/src/features/chat/application/conversation/composition.ts index 57a428a0..b72e72df 100644 --- a/src/features/chat/application/conversation/composition.ts +++ b/src/features/chat/application/conversation/composition.ts @@ -6,13 +6,10 @@ import type { ChatRuntimeSettingsActions } from "../runtime/settings-actions"; import type { ChatStateStore } from "../state/store"; import type { ThreadManagementActions } from "../threads/thread-management-actions"; import type { GoalActions } from "../threads/goal-actions"; -import { activeThreadId } from "../threads/state-selectors"; import { submitComposer, type ComposerSubmitActions, type ComposerSubmitActionsHost } from "./composer-submit-actions"; -import { canImplementPlanItemId } from "./plan-implementation-target"; import { executeSlashCommandWithState, type SlashCommandExecutorHost } from "./slash-command-executor"; import { createTurnSubmissionActions } from "./turn-submission-actions"; - -const IMPLEMENT_PLAN_PROMPT = "Please implement this plan."; +import { implementPlan, type PlanImplementationHost } from "./plan-implementation"; export interface ConversationTurnActionsContext { vaultPath: string; @@ -63,13 +60,6 @@ interface ConversationThreadStarter { startThread: (preview?: string, options?: { syncGoal?: boolean }) => Promise<{ threadId: string } | null>; } -export interface PlanImplementationHost { - stateStore: ChatStateStore; - connectedClient(): Promise; - sendTurnText(text: string): Promise; - requestDefaultCollaborationModeForNextTurn(): void; -} - interface PlanImplementation { implement: (itemId: string) => Promise; } @@ -164,12 +154,3 @@ async function startThreadForGoal(starter: ConversationThreadStarter, objective: const response = await starter.startThread(objective, { syncGoal: false }); return response?.threadId ?? null; } - -export async function implementPlan(host: PlanImplementationHost, itemId: string): Promise { - if (!canImplementPlanItemId(host.stateStore.getState(), itemId)) return; - if (!(await host.connectedClient()) || !activeThreadId(host.stateStore.getState())) return; - - host.requestDefaultCollaborationModeForNextTurn(); - host.stateStore.dispatch({ type: "ui/panel-set", panel: null }); - await host.sendTurnText(IMPLEMENT_PLAN_PROMPT); -} diff --git a/src/features/chat/application/conversation/plan-implementation-target.ts b/src/features/chat/application/conversation/plan-implementation.ts similarity index 50% rename from src/features/chat/application/conversation/plan-implementation-target.ts rename to src/features/chat/application/conversation/plan-implementation.ts index a436a085..42526ccf 100644 --- a/src/features/chat/application/conversation/plan-implementation-target.ts +++ b/src/features/chat/application/conversation/plan-implementation.ts @@ -1,9 +1,20 @@ +import type { AppServerClient } from "../../../../app-server/connection/client"; +import { latestImplementablePlanTargetFromItems, type PlanImplementationTarget } from "../../domain/message-stream/selectors"; +import type { ChatStateStore } from "../state/store"; import type { ChatActiveThreadState, ChatMessageStreamState, ChatRuntimeState, ChatState, ChatTurnState } from "../state/root-reducer"; import { chatTurnBusy } from "../state/root-reducer"; import { messageStreamItems } from "../state/message-stream"; -import { latestImplementablePlanTargetFromItems, type PlanImplementationTarget } from "../../domain/message-stream/selectors"; -export function canImplementPlanItemId(state: ChatState, itemId: string): boolean { +const IMPLEMENT_PLAN_PROMPT = "Please implement this plan."; + +export interface PlanImplementationHost { + stateStore: ChatStateStore; + connectedClient(): Promise; + sendTurnText(text: string): Promise; + requestDefaultCollaborationModeForNextTurn(): void; +} + +function canImplementPlanItemId(state: ChatState, itemId: string): boolean { return itemId === implementPlanTargetFromState(state)?.itemId; } @@ -18,3 +29,12 @@ export function implementPlanTargetFromState(state: { } return latestImplementablePlanTargetFromItems(messageStreamItems(state.messageStream)); } + +export async function implementPlan(host: PlanImplementationHost, itemId: string): Promise { + if (!canImplementPlanItemId(host.stateStore.getState(), itemId)) return; + if (!(await host.connectedClient()) || !host.stateStore.getState().activeThread.id) return; + + host.requestDefaultCollaborationModeForNextTurn(); + host.stateStore.dispatch({ type: "ui/panel-set", panel: null }); + await host.sendTurnText(IMPLEMENT_PLAN_PROMPT); +} diff --git a/src/features/chat/application/conversation/turn-state.ts b/src/features/chat/application/conversation/turn-state.ts index 4feb3da4..b31d2e36 100644 --- a/src/features/chat/application/conversation/turn-state.ts +++ b/src/features/chat/application/conversation/turn-state.ts @@ -3,6 +3,8 @@ export interface PendingTurnStart { readonly promptSubmitHookItemIds: readonly string[]; } +export const STATUS_TURN_RUNNING = "Turn running..."; + export type ChatTurnLifecycleState = | { readonly kind: "idle" } | { readonly kind: "starting"; readonly pendingTurnStart: PendingTurnStart } diff --git a/src/features/chat/application/conversation/turn-submission-actions.ts b/src/features/chat/application/conversation/turn-submission-actions.ts index 1445516a..f1c91dfd 100644 --- a/src/features/chat/application/conversation/turn-submission-actions.ts +++ b/src/features/chat/application/conversation/turn-submission-actions.ts @@ -2,8 +2,8 @@ import type { AppServerClient } from "../../../../app-server/connection/client"; import type { CodexInput } from "../../../../domain/chat/input"; import type { ReferencedThreadMetadata } from "../../../../domain/threads/reference"; import type { LocalIdSource } from "../../../../shared/id/local-id"; -import { STATUS_TURN_RUNNING } from "../state/status-text"; import type { ChatStateStore } from "../state/store"; +import { STATUS_TURN_RUNNING } from "./turn-state"; import { acknowledgeOptimisticTurnStart, cleanupFailedTurnStart, diff --git a/src/features/chat/application/state/root-reducer.ts b/src/features/chat/application/state/root-reducer.ts index 8dc596af..1d204b20 100644 --- a/src/features/chat/application/state/root-reducer.ts +++ b/src/features/chat/application/state/root-reducer.ts @@ -61,6 +61,7 @@ import { type RequestAction, } from "../pending-requests/state"; import { + STATUS_TURN_RUNNING, initialChatTurnState, transitionChatTurnLifecycleState, type ChatTurnState, @@ -76,7 +77,6 @@ import { type ChatUiState, type UiAction, } from "./ui-state"; -import { STATUS_TURN_RUNNING } from "./status-text"; import { definedPatch, patchObject } from "./patch"; export { activeTurnId, chatTurnBusy, pendingTurnStart, type ChatTurnState, type PendingTurnStart } from "../conversation/turn-state"; diff --git a/src/features/chat/application/state/status-text.ts b/src/features/chat/application/state/status-text.ts deleted file mode 100644 index 34f3df5a..00000000 --- a/src/features/chat/application/state/status-text.ts +++ /dev/null @@ -1 +0,0 @@ -export const STATUS_TURN_RUNNING = "Turn running..."; diff --git a/src/features/chat/application/threads/history-controller.ts b/src/features/chat/application/threads/history-controller.ts index 1d2f86cc..ec3f27d9 100644 --- a/src/features/chat/application/threads/history-controller.ts +++ b/src/features/chat/application/threads/history-controller.ts @@ -1,5 +1,5 @@ import type { AppServerClient } from "../../../../app-server/connection/client"; -import { readChatThreadHistoryPage, type ChatThreadHistoryPage } from "../../app-server/threads/history"; +import { readChatThreadHistoryPage, type ChatThreadHistoryPage } from "../../app-server/threads/projection"; import type { ChatAction, ChatState } from "../state/root-reducer"; import type { ChatStateStore } from "../state/store"; import { messageStreamItems } from "../state/message-stream"; diff --git a/src/features/chat/application/threads/resume-actions.ts b/src/features/chat/application/threads/resume-actions.ts index 77ec2e28..148651ad 100644 --- a/src/features/chat/application/threads/resume-actions.ts +++ b/src/features/chat/application/threads/resume-actions.ts @@ -1,6 +1,6 @@ import type { AppServerClient } from "../../../../app-server/connection/client"; import type { ThreadTokenUsage } from "../../../../domain/runtime/metrics"; -import { resumeChatThread, type ChatThreadResumeSnapshot } from "../../app-server/threads/resume"; +import { resumeChatThread, type ChatThreadResumeSnapshot } from "../../app-server/threads/projection"; import { resumedThreadAction } from "../state/actions"; import type { ChatStateStore } from "../state/store"; import type { RestorationController } from "./restoration-controller"; diff --git a/src/features/chat/domain/message-stream/format/path-labels.ts b/src/features/chat/domain/message-stream/format/path-labels.ts deleted file mode 100644 index c83afd3a..00000000 --- a/src/features/chat/domain/message-stream/format/path-labels.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { pathRelativeToRoot as sharedPathRelativeToRoot } from "../../../../../shared/path/file-paths"; - -export function pathRelativeToRoot(path: string, root?: string | null): string { - return sharedPathRelativeToRoot(path, root); -} - -export function pathsRelativeToRoot(paths: string[], root?: string | null): string[] { - return paths.map((path) => pathRelativeToRoot(path, root)); -} diff --git a/src/features/chat/domain/pending-requests/approval.ts b/src/features/chat/domain/pending-requests/approval.ts deleted file mode 100644 index e10bd851..00000000 --- a/src/features/chat/domain/pending-requests/approval.ts +++ /dev/null @@ -1,17 +0,0 @@ -import type { ApprovalDetailRow, PendingApproval } from "../../../../domain/pending-requests/model"; - -export function approvalTitle(approval: PendingApproval): string { - return approval.title; -} - -export function approvalSummary(approval: PendingApproval): string { - return approval.summary; -} - -export function approvalResultSummary(approval: PendingApproval): string { - return approval.resultSummary; -} - -export function approvalDetails(approval: PendingApproval): ApprovalDetailRow[] { - return [...approval.details]; -} diff --git a/src/features/chat/domain/pending-requests/result-items.ts b/src/features/chat/domain/pending-requests/result-items.ts index 35361b9a..36bb2cbc 100644 --- a/src/features/chat/domain/pending-requests/result-items.ts +++ b/src/features/chat/domain/pending-requests/result-items.ts @@ -7,7 +7,6 @@ import { type PendingMcpElicitation, type PendingUserInput, } from "../../../../domain/pending-requests/model"; -import { approvalDetails, approvalResultSummary, approvalTitle } from "./approval"; import type { MessageStreamItem, MessageStreamUserInputQuestionResult } from "../message-stream/items"; import { definedProp } from "../../../../utils"; @@ -24,8 +23,8 @@ export function createApprovalResultItem(approval: PendingApproval, action: Appr approval: { status: approvalResultStatus(kind), scope: kind === "accept-session" ? "session" : "turn", - request: approvalTitle(approval), - auditFacts: approvalDetails(approval), + request: approval.title, + auditFacts: [...approval.details], }, }; } @@ -74,7 +73,7 @@ export function createMcpElicitationResultItem( } function approvalResultText(approval: PendingApproval, action: ApprovalAction): string { - return `${approvalResultPrefix(approvalActionKind(action))}: ${approvalResultSummary(approval)}`; + return `${approvalResultPrefix(approvalActionKind(action))}: ${approval.resultSummary}`; } function approvalResultPrefix(kind: ReturnType): string { diff --git a/src/features/chat/panel/shell-state.tsx b/src/features/chat/panel/shell-state.tsx index 2cccac0a..a64778a8 100644 --- a/src/features/chat/panel/shell-state.tsx +++ b/src/features/chat/panel/shell-state.tsx @@ -4,6 +4,7 @@ import { batch, computed, signal, type ReadonlySignal, type Signal } from "@prea import type { RuntimeSnapshot } from "../domain/runtime/snapshot"; import { messageItemsHaveThreadTurns, runtimeSnapshotForChatSlices } from "../application/runtime/snapshot"; +import { implementPlanTargetFromState } from "../application/conversation/plan-implementation"; import { activeTurnId, chatTurnBusy, type ChatState } from "../application/state/root-reducer"; import { messageStreamActiveItems, @@ -13,12 +14,7 @@ import { type MessageStreamRollbackCandidate, } from "../application/state/message-stream"; import type { MessageStreamItem } from "../domain/message-stream/items"; -import { - forkCandidatesFromItems, - latestImplementablePlanTargetFromItems, - type ForkCandidate, - type PlanImplementationTarget, -} from "../domain/message-stream/selectors"; +import { forkCandidatesFromItems, type ForkCandidate, type PlanImplementationTarget } from "../domain/message-stream/selectors"; export interface ChatPanelShellState { connection: Signal; @@ -118,10 +114,14 @@ export function createChatPanelShellState(initialState: ChatState): ChatPanelShe messageStreamActiveItems: computed(() => messageStreamActiveItems(messageStream.value)), messageStreamRollbackCandidate: computed(() => (turnBusy.value ? null : messageStreamRollbackCandidateFromItems(messageItems.value))), messageStreamForkCandidates: computed(() => (turnBusy.value ? [] : forkCandidatesFromItems(messageItems.value))), - messageStreamImplementPlanTarget: computed(() => { - if (!activeThreadIdSignal.value || turnBusy.value || runtime.value.selectedCollaborationMode !== "plan") return null; - return latestImplementablePlanTargetFromItems(messageItems.value); - }), + messageStreamImplementPlanTarget: computed(() => + implementPlanTargetFromState({ + activeThread: { id: activeThreadIdSignal.value }, + turn: turn.value, + runtime: { selectedCollaborationMode: runtime.value.selectedCollaborationMode }, + messageStream: messageStream.value, + }), + ), hasThreadTurns, goalEditor: computed(() => ui.value.goalEditor), goalObjectiveExpanded: computed(() => ui.value.disclosures.goalObjectiveExpanded), diff --git a/src/features/chat/presentation/message-stream/detail-view.ts b/src/features/chat/presentation/message-stream/detail-view.ts index 2789c174..657c5804 100644 --- a/src/features/chat/presentation/message-stream/detail-view.ts +++ b/src/features/chat/presentation/message-stream/detail-view.ts @@ -1,6 +1,6 @@ import { shortThreadId, truncate } from "../../../../utils"; import { failedStatusLabel } from "../../domain/message-stream/execution-state"; -import { pathRelativeToRoot } from "../../domain/message-stream/format/path-labels"; +import { pathRelativeToRoot } from "../../../../shared/path/file-paths"; import type { AgentMessageStreamItem, ApprovalResultMessageStreamItem, diff --git a/src/features/chat/presentation/message-stream/layout.ts b/src/features/chat/presentation/message-stream/layout.ts index 5b6712a0..32c14d70 100644 --- a/src/features/chat/presentation/message-stream/layout.ts +++ b/src/features/chat/presentation/message-stream/layout.ts @@ -1,5 +1,5 @@ import type { MessageStreamItem } from "../../domain/message-stream/items"; -import { pathRelativeToRoot } from "../../domain/message-stream/format/path-labels"; +import { pathRelativeToRoot } from "../../../../shared/path/file-paths"; import { messageStreamSemanticClassifications } from "../../domain/message-stream/semantics/classify"; import { messageStreamIsAutoReviewDecision, diff --git a/src/features/chat/presentation/pending-requests/view-model.ts b/src/features/chat/presentation/pending-requests/view-model.ts index e5f2545b..71229bf4 100644 --- a/src/features/chat/presentation/pending-requests/view-model.ts +++ b/src/features/chat/presentation/pending-requests/view-model.ts @@ -1,4 +1,3 @@ -import { approvalDetails, approvalSummary, approvalTitle } from "../../domain/pending-requests/approval"; import { approvalActionOptions, type ApprovalActionOption } from "./approval-view"; import { type PendingApproval, @@ -91,9 +90,9 @@ export interface PendingMcpElicitationViewModel { export function pendingApprovalViewModel(approval: PendingApproval): PendingApprovalViewModel { return { requestId: approval.requestId, - title: approvalTitle(approval), - summary: approvalSummary(approval), - details: approvalDetails(approval), + title: approval.title, + summary: approval.summary, + details: [...approval.details], actions: approvalActionOptions(approval), }; } diff --git a/tests/features/chat/conversation/turns/plan-implementation.test.ts b/tests/features/chat/conversation/turns/plan-implementation.test.ts index 008e57a4..b3c866b0 100644 --- a/tests/features/chat/conversation/turns/plan-implementation.test.ts +++ b/tests/features/chat/conversation/turns/plan-implementation.test.ts @@ -3,8 +3,11 @@ import { describe, expect, it, vi } from "vitest"; import type { AppServerClient } from "../../../../../src/app-server/connection/client"; import { createChatState } from "../../../../../src/features/chat/application/state/root-reducer"; import { createChatStateStore, type ChatStateStore } from "../../../../../src/features/chat/application/state/store"; -import { implementPlan, type PlanImplementationHost } from "../../../../../src/features/chat/application/conversation/composition"; -import { implementPlanTargetFromState } from "../../../../../src/features/chat/application/conversation/plan-implementation-target"; +import { + implementPlan, + implementPlanTargetFromState, + type PlanImplementationHost, +} from "../../../../../src/features/chat/application/conversation/plan-implementation"; import type { MessageStreamItem } from "../../../../../src/features/chat/domain/message-stream/items"; const planItem = (id: string): MessageStreamItem => ({ diff --git a/tests/features/chat/message-stream/model.test.ts b/tests/features/chat/message-stream/model.test.ts index c9f83631..aa0e611f 100644 --- a/tests/features/chat/message-stream/model.test.ts +++ b/tests/features/chat/message-stream/model.test.ts @@ -6,7 +6,7 @@ import { messageStreamLayoutBlocks } from "../../../../src/features/chat/present import { upsertMessageStreamItemById } from "../../../../src/features/chat/domain/message-stream/updates"; import { taskProgressMessageStreamItem } from "../../../../src/features/chat/domain/message-stream/factories/task-progress"; import { normalizeProposedPlanMarkdown } from "../../../../src/features/chat/domain/message-stream/format/proposed-plan"; -import { pathRelativeToRoot } from "../../../../src/features/chat/domain/message-stream/format/path-labels"; +import { pathRelativeToRoot } from "../../../../src/shared/path/file-paths"; import { permissionRows } from "../../../../src/features/chat/domain/message-stream/format/permission-rows"; import { createAutoReviewResultItem, diff --git a/tests/features/chat/protocol/server-requests/approval.test.ts b/tests/features/chat/protocol/server-requests/approval.test.ts index 7a0da7b0..7137dadf 100644 --- a/tests/features/chat/protocol/server-requests/approval.test.ts +++ b/tests/features/chat/protocol/server-requests/approval.test.ts @@ -4,7 +4,6 @@ import { appServerApprovalRequest as toPendingApproval, appServerApprovalResponse as approvalResponse, } from "../../../../../src/app-server/protocol/server-requests"; -import { approvalDetails, approvalSummary, approvalTitle } from "../../../../../src/features/chat/domain/pending-requests/approval"; import { createApprovalResultItem } from "../../../../../src/features/chat/domain/pending-requests/result-items"; import { approvalActionOptions } from "../../../../../src/features/chat/presentation/pending-requests/approval-view"; import type { ServerRequest } from "../../../../../src/app-server/connection/rpc-messages"; @@ -35,8 +34,8 @@ describe("approval model", () => { }; const approval = expectPresent(toPendingApproval(request)); - expect(approvalTitle(approval)).toBe("Command approval"); - expect(approvalSummary(approval)).toBe("npm run build"); + expect(approval.title).toBe("Command approval"); + expect(approval.summary).toBe("npm run build"); expect(approvalActionOptions(approval).map((option) => option.label)).toEqual(["Allow", "Allow session", "Deny", "Cancel"]); expect(approvalResponse(approval, expectPresent(approvalActionOptions(approval)[1]).action)).toEqual({ decision: "acceptForSession" }); }); @@ -80,8 +79,8 @@ describe("approval model", () => { ); expect(approval).toMatchObject({ kind: "fileChange", title: "File change approval" }); - expect(approvalSummary(approval)).toBe("Allow file changes?"); - expect(approvalDetails(approval)).toEqual([]); + expect(approval.summary).toBe("Allow file changes?"); + expect(approval.details).toEqual([]); }); it("uses command approval decisions supplied by app-server", () => { @@ -233,9 +232,9 @@ describe("approval model", () => { }), ); - expect(approvalSummary(command)).toBe("Needs unsandboxed access\nnpm run build"); - expect(approvalSummary(fileChange)).toBe("Write outside workspace\ngrant root: /tmp/project"); - expect(approvalSummary(permissions).startsWith("Need network\ncwd: /tmp/project")).toBe(true); + expect(command.summary).toBe("Needs unsandboxed access\nnpm run build"); + expect(fileChange.summary).toBe("Write outside workspace\ngrant root: /tmp/project"); + expect(permissions.summary.startsWith("Need network\ncwd: /tmp/project")).toBe(true); }); it("keeps approval details semantic and omits raw payloads", () => { @@ -256,7 +255,7 @@ describe("approval model", () => { }), ); - expect(approvalDetails(approval)).toEqual([ + expect(approval.details).toEqual([ { key: "reason", value: "Need network" }, { key: "cwd", value: "/tmp/project" }, { key: "network", value: "enabled" }, @@ -299,7 +298,7 @@ describe("approval model", () => { }), ); - expect(approvalDetails(approval)).toEqual([ + expect(approval.details).toEqual([ { key: "reason", value: "Needs network access" }, { key: "command", value: "rg TODO src && sed -n '1,20p' src/main.ts" }, { key: "cwd", value: "/tmp/project" }, @@ -332,7 +331,7 @@ describe("approval model", () => { } as unknown as ServerRequest), ); - expect(approvalDetails(approval)).toEqual([ + expect(approval.details).toEqual([ { key: "cwd", value: "/tmp/project" }, { key: "actions", value: '{\n "path": "/tmp/project/src/main.ts"\n}\nlegacy action' }, { key: "future network rules", value: "rule api.github.com\nallow (unknown host)\nlegacy rule" }, @@ -359,7 +358,7 @@ describe("approval model", () => { }), ); - expect(approvalDetails(approval)).toEqual([ + expect(approval.details).toEqual([ { key: "command", value: "npm test" }, { key: "cwd", value: "/tmp/project" }, ]); diff --git a/tests/features/chat/threads/history-controller.test.ts b/tests/features/chat/threads/history-controller.test.ts index 21de36c1..1d728094 100644 --- a/tests/features/chat/threads/history-controller.test.ts +++ b/tests/features/chat/threads/history-controller.test.ts @@ -3,7 +3,7 @@ import { describe, expect, it, vi } from "vitest"; import { createChatStateStore } from "../../../../src/features/chat/application/state/store"; import { HistoryController, type HistoryControllerHost } from "../../../../src/features/chat/application/threads/history-controller"; import type { AppServerClient } from "../../../../src/app-server/connection/client"; -import type { ChatThreadHistoryPage } from "../../../../src/features/chat/app-server/threads/history"; +import type { ChatThreadHistoryPage } from "../../../../src/features/chat/app-server/threads/projection"; import type { MessageStreamItem } from "../../../../src/features/chat/domain/message-stream/items"; import { deferred } from "../../../support/async"; import { chatStateMessageStreamItems } from "../support/message-stream"; diff --git a/tests/features/chat/threads/resume-actions.test.ts b/tests/features/chat/threads/resume-actions.test.ts index 4d5b09e3..50503944 100644 --- a/tests/features/chat/threads/resume-actions.test.ts +++ b/tests/features/chat/threads/resume-actions.test.ts @@ -9,8 +9,7 @@ import type { HistoryController } from "../../../../src/features/chat/applicatio import { ChatResumeWorkTracker } from "../../../../src/features/chat/application/lifecycle"; import type { Thread as PanelThread } from "../../../../src/domain/threads/model"; import type { ThreadTokenUsage } from "../../../../src/domain/runtime/metrics"; -import type { ChatThreadHistoryPage } from "../../../../src/features/chat/app-server/threads/history"; -import type { ChatThreadResumeSnapshot } from "../../../../src/features/chat/app-server/threads/resume"; +import type { ChatThreadHistoryPage, ChatThreadResumeSnapshot } from "../../../../src/features/chat/app-server/threads/projection"; import type { MessageStreamItem } from "../../../../src/features/chat/domain/message-stream/items"; function activation(threadId: string, overrides: Partial = {}): ChatThreadResumeSnapshot { diff --git a/tests/features/chat/ui/message-stream/blocks-and-messages.test.tsx b/tests/features/chat/ui/message-stream/blocks-and-messages.test.tsx index 00914f29..573d1e7c 100644 --- a/tests/features/chat/ui/message-stream/blocks-and-messages.test.tsx +++ b/tests/features/chat/ui/message-stream/blocks-and-messages.test.tsx @@ -5,7 +5,7 @@ import { act } from "preact/test-utils"; import { MarkdownRenderer } from "obsidian"; import type { MessageStreamItem } from "../../../../../src/features/chat/domain/message-stream/items"; -import { implementPlanTargetFromState } from "../../../../../src/features/chat/application/conversation/plan-implementation-target"; +import { implementPlanTargetFromState } from "../../../../../src/features/chat/application/conversation/plan-implementation"; import { MESSAGE_CONTENT_RENDERED_EVENT } from "../../../../../src/features/chat/ui/message-stream/content-events"; import { MarkdownMessageRenderer } from "../../../../../src/features/chat/ui/message-stream/markdown-renderer"; import { deferred } from "../../../../support/async";