diff --git a/eslint.config.mjs b/eslint.config.mjs index c6777712..0704a0e1 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -98,7 +98,6 @@ const nonUiEventListenerFiles = ["src/shared/lifecycle/abortable.ts"]; const generatedAppServerImportLegacyFiles = [ "src/features/chat/chat-state-actions.ts", "src/features/chat/chat-state.ts", - "src/features/chat/composer/controller.ts", "src/features/chat/display/agent.ts", "src/features/chat/display/goal-messages.ts", "src/features/chat/display/hooks.ts", @@ -117,11 +116,8 @@ const generatedAppServerImportLegacyFiles = [ "src/features/chat/threads/thread-history-controller.ts", "src/features/chat/threads/thread-rename-controller.ts", "src/features/chat/threads/thread-resume.ts", - "src/features/chat/turns/composer-submission-actions.ts", "src/features/chat/turns/slash-command-actions.ts", "src/features/chat/turns/slash-command-execution.ts", - "src/features/chat/turns/turn-submission-controller.ts", - "src/features/chat/turns/turn-submission.ts", "src/features/chat/ui/goal-banner.tsx", "src/features/chat/ui/pending-request-message.tsx", ]; diff --git a/src/app-server/client.ts b/src/app-server/client.ts index 404946bc..2e21ab79 100644 --- a/src/app-server/client.ts +++ b/src/app-server/client.ts @@ -44,6 +44,7 @@ import type { ClientRequestMethod, ClientRequestParams, PendingRequest, RpcError import type { ServerNotification } from "../generated/app-server/ServerNotification"; import type { ServerRequest } from "../generated/app-server/ServerRequest"; import type { JsonValue } from "../generated/app-server/serde_json/JsonValue"; +import { toAppServerUserInput, type CodexInput } from "./request-input"; import type { ServiceTierRequest, ThreadSettingsUpdate } from "./thread-settings"; const DEFAULT_REQUEST_TIMEOUT_MS = 120_000; @@ -122,9 +123,9 @@ type AppServerClientLifecycleState = | { kind: "starting"; transport: AppServerTransport } | { kind: "initialized"; transport: AppServerTransport; initializeResponse: InitializeResponse }; -function toUserInput(input: string | UserInput[]): UserInput[] { - if (typeof input !== "string") return input; - return [{ type: "text", text: input, text_elements: [] }]; +function toUserInput(input: string | CodexInput): UserInput[] { + if (typeof input !== "string") return toAppServerUserInput(input); + return toAppServerUserInput([{ type: "text", text: input }]); } export class AppServerClient { @@ -366,7 +367,7 @@ export class AppServerClient { startTurn( threadId: string, cwd: string, - input: string | UserInput[], + input: string | CodexInput, clientUserMessageId?: string | null, serviceTier?: ServiceTierRequest, collaborationMode?: CollaborationMode | null, @@ -416,7 +417,7 @@ export class AppServerClient { steerTurn( threadId: string, expectedTurnId: string, - input: string | UserInput[], + input: string | CodexInput, clientUserMessageId?: string | null, ): Promise { return this.request("turn/steer", { diff --git a/src/app-server/request-input.ts b/src/app-server/request-input.ts index 771e86e3..d7a4f9de 100644 --- a/src/app-server/request-input.ts +++ b/src/app-server/request-input.ts @@ -5,22 +5,39 @@ export interface RequestMention { path: string; } -export function appServerTextInputWithMentions( +export type CodexInputItem = + | { type: "text"; text: string } + | { type: "image"; url: string; detail?: UserInputImageDetail } + | { type: "localImage"; path: string; detail?: UserInputImageDetail } + | { type: "skill"; name: string; path: string } + | { type: "mention"; name: string; path: string }; + +export type CodexInput = CodexInputItem[]; +type UserInputImageDetail = "auto" | "low" | "high" | "original"; + +export function codexTextInputWithMentions( text: string, mentions: readonly RequestMention[], skills: readonly RequestMention[] = [], -): UserInput[] { +): CodexInput { return [ - ...appServerTextInput(text), + ...codexTextInput(text), ...mentions.map((mention) => ({ type: "mention" as const, name: mention.name, path: mention.path })), ...skills.map((skill) => ({ type: "skill" as const, name: skill.name, path: skill.path })), ]; } -export function appServerTextInputWithAttachments(text: string, input: readonly UserInput[]): UserInput[] { - return [...appServerTextInput(text), ...input.filter((item) => item.type !== "text")]; +export function codexTextInputWithAttachments(text: string, input: readonly CodexInputItem[]): CodexInput { + return [...codexTextInput(text), ...input.filter((item) => item.type !== "text")]; } -function appServerTextInput(text: string): UserInput[] { - return [{ type: "text", text, text_elements: [] }]; +export function toAppServerUserInput(input: readonly CodexInputItem[]): UserInput[] { + return input.map((item) => { + if (item.type === "text") return { type: "text", text: item.text, text_elements: [] }; + return { ...item }; + }); +} + +function codexTextInput(text: string): CodexInput { + return [{ type: "text", text }]; } diff --git a/src/features/chat/composer/controller.ts b/src/features/chat/composer/controller.ts index 9109e9ab..0126623f 100644 --- a/src/features/chat/composer/controller.ts +++ b/src/features/chat/composer/controller.ts @@ -1,6 +1,6 @@ import type { App, EventRef } from "obsidian"; -import type { UserInput } from "../../../generated/app-server/v2/UserInput"; +import type { CodexInput } from "../../../app-server/request-input"; import { isComposerSendKey, type SendShortcut } from "../../../shared/ui/keyboard"; import { textareaCursorAtVisualBoundary } from "../../../shared/ui/textarea-caret"; import { chatTurnBusy, type ChatAction, type ChatState, type ChatStateStore } from "../chat-state"; @@ -172,7 +172,7 @@ export class ChatComposerController { this.render(parent, options); } - codexInput(text: string): UserInput[] { + codexInput(text: string): CodexInput { return userInputWithWikiLinkMentionsAndSkills( text, (target) => resolveAppWikiLinkMention(this.options.app, target), diff --git a/src/features/chat/composer/wikilink-context.ts b/src/features/chat/composer/wikilink-context.ts index 96c7827a..c919115e 100644 --- a/src/features/chat/composer/wikilink-context.ts +++ b/src/features/chat/composer/wikilink-context.ts @@ -1,4 +1,4 @@ -import { appServerTextInputWithMentions, type RequestMention } from "../../../app-server/request-input"; +import { codexTextInputWithMentions, type RequestMention } from "../../../app-server/request-input"; import type { SkillMetadata } from "../../../domain/catalog/metadata"; import { parseObsidianWikiLink } from "../../../shared/obsidian/wikilinks"; @@ -60,7 +60,7 @@ export function userInputWithWikiLinkMentionsAndSkills( resolvedSkills.push({ name: skill.name, path: skill.path }); } - return appServerTextInputWithMentions(text, mentions, resolvedSkills); + return codexTextInputWithMentions(text, mentions, resolvedSkills); } export function parsedSkillReferences(text: string): string[] { diff --git a/src/features/chat/display/thread-items.ts b/src/features/chat/display/thread-items.ts index 70708938..87680310 100644 --- a/src/features/chat/display/thread-items.ts +++ b/src/features/chat/display/thread-items.ts @@ -1,8 +1,8 @@ import type { DisplayDetailSection, DisplayFileChange, DisplayFileMention, DisplayItem } from "./types"; +import type { CodexInput, CodexInputItem } from "../../../app-server/request-input"; import type { FileUpdateChange } from "../../../generated/app-server/v2/FileUpdateChange"; import type { ThreadItem } from "../../../generated/app-server/v2/ThreadItem"; import type { Turn } from "../../../generated/app-server/v2/Turn"; -import type { UserInput } from "../../../generated/app-server/v2/UserInput"; import { definedProp, truncate } from "../../../utils"; import { referencedThreadDisplayFromPrompt } from "../../../domain/threads/reference"; import { appServerUserItemText } from "../../../app-server/turn-model"; @@ -127,7 +127,7 @@ function userMessageDisplayItem(item: UserMessageItem, turnId?: string): Display }; } -export function fileMentionsFromInput(input: UserInput[]): DisplayFileMention[] { +export function fileMentionsFromInput(input: readonly CodexInputItem[]): DisplayFileMention[] { const seen = new Set(); const mentions: DisplayFileMention[] = []; for (const item of input) { @@ -138,7 +138,7 @@ export function fileMentionsFromInput(input: UserInput[]): DisplayFileMention[] return mentions; } -export function userMessageDisplayText(text: string, input: readonly UserInput[]): string { +export function userMessageDisplayText(text: string, input: CodexInput): string { const names = resolvedSkillNames(input); if (names.length === 0) return text; @@ -150,7 +150,7 @@ export function userMessageDisplayText(text: string, input: readonly UserInput[] }); } -function resolvedSkillNames(input: readonly UserInput[]): string[] { +function resolvedSkillNames(input: readonly CodexInputItem[]): string[] { const seen = new Set(); const names: string[] = []; for (const item of input) { diff --git a/src/features/chat/turns/composer-submission-actions.ts b/src/features/chat/turns/composer-submission-actions.ts index 3074b37a..7513e213 100644 --- a/src/features/chat/turns/composer-submission-actions.ts +++ b/src/features/chat/turns/composer-submission-actions.ts @@ -1,11 +1,11 @@ import type { AppServerClient } from "../../../app-server/client"; +import type { CodexInput } from "../../../app-server/request-input"; import { submissionStateSnapshot } from "../chat-state-selectors"; import type { ChatStateStore } from "../chat-state"; import { parseSlashCommand } from "../composer/suggestions"; import type { SlashCommandExecutionResult } from "./slash-command-execution"; import type { SlashCommandName } from "../composer/slash-commands"; import type { ReferencedThreadDisplay } from "../../../domain/threads/reference"; -import type { UserInput } from "../../../generated/app-server/v2/UserInput"; interface ComposerDraftPort { readonly trimmedDraft: string; @@ -17,7 +17,7 @@ interface ComposerSlashCommandPort { } interface ComposerTurnSubmissionPort { - sendTurnText(text: string, codexInputOverride?: UserInput[], referencedThread?: ReferencedThreadDisplay): Promise; + sendTurnText(text: string, codexInputOverride?: CodexInput, referencedThread?: ReferencedThreadDisplay): Promise; } interface ComposerConnectionPort { @@ -91,13 +91,13 @@ function sendComposerTurn(host: ComposerSubmissionActionsHost, text: string): Pr function sendComposerTurn( host: ComposerSubmissionActionsHost, text: string, - codexInputOverride: UserInput[] | undefined, + codexInputOverride: CodexInput | undefined, referencedThread: ReferencedThreadDisplay | undefined, ): Promise; async function sendComposerTurn( host: ComposerSubmissionActionsHost, text: string, - codexInputOverride?: UserInput[], + codexInputOverride?: CodexInput, referencedThread?: ReferencedThreadDisplay, ): Promise { host.scroll.forceBottom(); diff --git a/src/features/chat/turns/slash-command-actions.ts b/src/features/chat/turns/slash-command-actions.ts index 7a0221f3..e196f6da 100644 --- a/src/features/chat/turns/slash-command-actions.ts +++ b/src/features/chat/turns/slash-command-actions.ts @@ -1,5 +1,5 @@ import type { AppServerClient } from "../../../app-server/client"; -import { appServerTextInputWithAttachments } from "../../../app-server/request-input"; +import { codexTextInputWithAttachments, type CodexInput } from "../../../app-server/request-input"; import { chronologicalConversationSummariesFromAppServerTurns } from "../../../app-server/turn-model"; import { referencedThreadPromptBundle, referencedThreadTurns, REFERENCED_THREAD_TURN_LIMIT } from "../../../domain/threads/reference"; import type { Thread } from "../../../domain/threads/model"; @@ -13,7 +13,6 @@ import type { DisplayDetailSection } from "../display/types"; import type { ReasoningEffort } from "../../../domain/catalog/metadata"; import type { ThreadGoal } from "../../../generated/app-server/v2/ThreadGoal"; import type { ThreadGoalStatus } from "../../../generated/app-server/v2/ThreadGoalStatus"; -import type { UserInput } from "../../../generated/app-server/v2/UserInput"; import { submissionStateSnapshot } from "../chat-state-selectors"; import type { ChatStateStore } from "../chat-state"; @@ -58,7 +57,7 @@ export interface SlashCommandGoalPort { export interface SlashCommandActionsHost { stateStore: ChatStateStore; currentClient: () => AppServerClient | null; - codexInput: (text: string) => UserInput[]; + codexInput: (text: string) => CodexInput; threads: SlashCommandThreadPort; runtime: SlashCommandRuntimePort; goals: SlashCommandGoalPort; @@ -140,7 +139,7 @@ async function referencedThreadInput( const messageInput = host.codexInput(message); host.status.setStatus(reference.status); return { - input: appServerTextInputWithAttachments(reference.prompt, messageInput), + input: codexTextInputWithAttachments(reference.prompt, messageInput), referencedThread: reference.referencedThread, }; } catch (error) { diff --git a/src/features/chat/turns/slash-command-execution.ts b/src/features/chat/turns/slash-command-execution.ts index 656ef256..c2005e5e 100644 --- a/src/features/chat/turns/slash-command-execution.ts +++ b/src/features/chat/turns/slash-command-execution.ts @@ -1,8 +1,8 @@ +import type { CodexInput } from "../../../app-server/request-input"; import type { ReasoningEffort } from "../../../domain/catalog/metadata"; import type { Thread } from "../../../domain/threads/model"; import type { ThreadGoal } from "../../../generated/app-server/v2/ThreadGoal"; import type { ThreadGoalStatus } from "../../../generated/app-server/v2/ThreadGoalStatus"; -import type { UserInput } from "../../../generated/app-server/v2/UserInput"; import { getThreadTitle } from "../../../domain/threads/model"; import type { ReferencedThreadDisplay } from "../../../domain/threads/reference"; import { @@ -55,13 +55,13 @@ export interface SlashCommandExecutionContext { export interface SlashCommandExecutionResult { sendText?: string; - sendInput?: UserInput[]; + sendInput?: CodexInput; referencedThread?: ReferencedThreadDisplay; composerDraft?: string; } export interface ThreadReferenceInput { - input: UserInput[]; + input: CodexInput; referencedThread: ReferencedThreadDisplay; } diff --git a/src/features/chat/turns/turn-submission-controller.ts b/src/features/chat/turns/turn-submission-controller.ts index 8b506461..f8de3fcb 100644 --- a/src/features/chat/turns/turn-submission-controller.ts +++ b/src/features/chat/turns/turn-submission-controller.ts @@ -1,5 +1,5 @@ import type { AppServerClient } from "../../../app-server/client"; -import type { UserInput } from "../../../generated/app-server/v2/UserInput"; +import type { CodexInput } from "../../../app-server/request-input"; import type { ReferencedThreadDisplay } from "../../../domain/threads/reference"; import { addTranscriptItemAction, @@ -37,7 +37,7 @@ export interface TurnSubmissionRuntimePort { } export interface TurnSubmissionComposerPort { - codexInput: (text: string) => UserInput[]; + codexInput: (text: string) => CodexInput; setDraft: (text: string, options?: { focus?: boolean; clearSuggestions?: boolean }) => void; } @@ -65,7 +65,7 @@ export interface TurnSubmissionControllerHost { export class TurnSubmissionController { constructor(private readonly host: TurnSubmissionControllerHost) {} - async sendTurnText(text: string, codexInputOverride?: UserInput[], referencedThread?: ReferencedThreadDisplay): Promise { + async sendTurnText(text: string, codexInputOverride?: CodexInput, referencedThread?: ReferencedThreadDisplay): Promise { if (!(await this.host.restoredThread.ensureRestoredThreadLoaded())) return; const client = this.host.connection.currentClient(); if (!client) return; @@ -137,7 +137,7 @@ export class TurnSubmissionController { private async steerCurrentTurn( client: AppServerClient, text: string, - codexInputOverride?: UserInput[], + codexInputOverride?: CodexInput, referencedThread?: ReferencedThreadDisplay, ): Promise { const state = submissionStateSnapshot(this.host.stateStore.getState()); diff --git a/src/features/chat/turns/turn-submission.ts b/src/features/chat/turns/turn-submission.ts index 2d24d6f8..3afbb64b 100644 --- a/src/features/chat/turns/turn-submission.ts +++ b/src/features/chat/turns/turn-submission.ts @@ -2,7 +2,7 @@ import type { PendingTurnStart } from "../chat-state"; import type { DisplayFileMention, DisplayItem, MessageDisplayItem } from "../display/types"; import { fileMentionsFromInput, userMessageDisplayText } from "../display/thread-items"; import { attachHookRunsToTurn } from "../display/hooks"; -import type { UserInput } from "../../../generated/app-server/v2/UserInput"; +import type { CodexInput } from "../../../app-server/request-input"; export interface LocalUserMessageParams { id: string; @@ -21,7 +21,7 @@ export interface OptimisticTurnStartAckParams { } export interface LocalUserMessageFromInputParams extends Omit { - codexInput: readonly UserInput[]; + codexInput: CodexInput; } export type OptimisticTurnStartParams = LocalUserMessageFromInputParams; diff --git a/tests/app-server/request-input.test.ts b/tests/app-server/request-input.test.ts index bb49a54f..0ce632d9 100644 --- a/tests/app-server/request-input.test.ts +++ b/tests/app-server/request-input.test.ts @@ -1,31 +1,42 @@ import { describe, expect, it } from "vitest"; -import { appServerTextInputWithAttachments, appServerTextInputWithMentions } from "../../src/app-server/request-input"; -import type { UserInput } from "../../src/generated/app-server/v2/UserInput"; +import { + codexTextInputWithAttachments, + codexTextInputWithMentions, + toAppServerUserInput, + type CodexInput, +} from "../../src/app-server/request-input"; describe("app-server request input", () => { it("builds text input with mentions and skills", () => { expect( - appServerTextInputWithMentions( + codexTextInputWithMentions( "Use [[Note]] and $Skill", [{ name: "Note", path: "Note.md" }], [{ name: "Skill", path: ".codex/skills/skill/SKILL.md" }], ), ).toEqual([ - { type: "text", text: "Use [[Note]] and $Skill", text_elements: [] }, + { type: "text", text: "Use [[Note]] and $Skill" }, { type: "mention", name: "Note", path: "Note.md" }, { type: "skill", name: "Skill", path: ".codex/skills/skill/SKILL.md" }, ]); }); it("replaces text input while preserving non-text attachments", () => { - const input: UserInput[] = [ - { type: "text", text: "visible request", text_elements: [] }, + const input: CodexInput = [ + { type: "text", text: "visible request" }, { type: "mention", name: "Note", path: "Note.md" }, ]; - expect(appServerTextInputWithAttachments("rewritten prompt", input)).toEqual([ - { type: "text", text: "rewritten prompt", text_elements: [] }, + expect(codexTextInputWithAttachments("rewritten prompt", input)).toEqual([ + { type: "text", text: "rewritten prompt" }, + { type: "mention", name: "Note", path: "Note.md" }, + ]); + }); + + it("serializes text input for app-server requests", () => { + expect(toAppServerUserInput(codexTextInputWithMentions("Use [[Note]]", [{ name: "Note", path: "Note.md" }]))).toEqual([ + { type: "text", text: "Use [[Note]]", text_elements: [] }, { type: "mention", name: "Note", path: "Note.md" }, ]); }); diff --git a/tests/features/chat/composer/composer-suggestions.test.ts b/tests/features/chat/composer/composer-suggestions.test.ts index 11e76884..1660e314 100644 --- a/tests/features/chat/composer/composer-suggestions.test.ts +++ b/tests/features/chat/composer/composer-suggestions.test.ts @@ -190,7 +190,7 @@ describe("composer suggestions", () => { replacement: "[[Assets/Diagram.png]]", }); expect(input).toEqual([ - { type: "text", text, text_elements: [] }, + { type: "text", text }, { type: "mention", name: "Diagram", path: "Assets/Diagram.png" }, ]); }); diff --git a/tests/features/chat/composer/wikilink-context.test.ts b/tests/features/chat/composer/wikilink-context.test.ts index 9ff0c5de..50c3bfa5 100644 --- a/tests/features/chat/composer/wikilink-context.test.ts +++ b/tests/features/chat/composer/wikilink-context.test.ts @@ -23,7 +23,7 @@ describe("wikilink context", () => { ); expect(input).toEqual([ - { type: "text", text, text_elements: [] }, + { type: "text", text }, { type: "mention", name: "Alpha", path: "thoughts/Alpha.md" }, ]); expect(input).toHaveLength(2); @@ -46,7 +46,7 @@ describe("wikilink context", () => { { raw: "Assets/Diagram.png#crop|Diagram", target: "Assets/Diagram.png", subpath: "#crop", display: "Diagram" }, ]); expect(input).toEqual([ - { type: "text", text, text_elements: [] }, + { type: "text", text }, { type: "mention", name: "Projects", path: "Bases/Projects.base" }, { type: "mention", name: "Paper", path: "References/Paper.pdf" }, { type: "mention", name: "Diagram", path: "Assets/Diagram.png" }, @@ -60,7 +60,7 @@ describe("wikilink context", () => { ); expect(input).toEqual([ - { type: "text", text, text_elements: [] }, + { type: "text", text }, { type: "mention", name: "Alpha", path: "thoughts/Alpha.md" }, ]); }); @@ -90,7 +90,7 @@ describe("wikilink context", () => { ); expect(input).toEqual([ - { type: "text", text, text_elements: [] }, + { type: "text", text }, { type: "mention", name: "Alpha", path: "thoughts/Alpha.md" }, { type: "skill", @@ -134,7 +134,7 @@ describe("wikilink context", () => { ]); expect(input).toEqual([ - { type: "text", text, text_elements: [] }, + { type: "text", text }, { type: "skill", name: "First", path: "/skills/first/SKILL.md" }, ]); }); diff --git a/tests/features/chat/turns/slash-command-actions.test.ts b/tests/features/chat/turns/slash-command-actions.test.ts index d05d5e5d..a0381c53 100644 --- a/tests/features/chat/turns/slash-command-actions.test.ts +++ b/tests/features/chat/turns/slash-command-actions.test.ts @@ -1,6 +1,7 @@ import { describe, expect, it, vi } from "vitest"; import type { AppServerClient } from "../../../../src/app-server/client"; +import type { CodexInput } from "../../../../src/app-server/request-input"; import { createChatState, createChatStateStore } from "../../../../src/features/chat/chat-state"; import { createSlashCommandActions, @@ -11,9 +12,8 @@ import { type SlashCommandThreadPort, } from "../../../../src/features/chat/turns/slash-command-actions"; import type { Thread } from "../../../../src/generated/app-server/v2/Thread"; -import type { UserInput } from "../../../../src/generated/app-server/v2/UserInput"; -const textInput = (text: string): UserInput[] => [{ type: "text", text, text_elements: [] }]; +const textInput = (text: string): CodexInput => [{ type: "text", text }]; function thread(id: string, name: string | null = null): Thread & { archived: boolean } { return { diff --git a/tests/features/chat/turns/slash-command-execution.test.ts b/tests/features/chat/turns/slash-command-execution.test.ts index 2992840a..9e6114fd 100644 --- a/tests/features/chat/turns/slash-command-execution.test.ts +++ b/tests/features/chat/turns/slash-command-execution.test.ts @@ -14,7 +14,7 @@ function context(overrides: Partial = {}): SlashCo startThreadForGoal: vi.fn().mockResolvedValue("thread-new"), resumeThread: vi.fn().mockResolvedValue(undefined), referThread: vi.fn().mockResolvedValue({ - input: [{ type: "text", text: "referenced", text_elements: [] }], + input: [{ type: "text", text: "referenced" }], referencedThread: { threadId: "thread-2", title: "Referenced", includedTurns: 1, turnLimit: 20 }, }), forkThread: vi.fn().mockResolvedValue(undefined), @@ -143,7 +143,7 @@ describe("slash commands", () => { it("returns referenced input for /refer", async () => { const target = thread({ id: "thread-alpha", name: "Alpha" }); - const input = [{ type: "text" as const, text: "context\n質問です", text_elements: [] }]; + const input = [{ type: "text" as const, text: "context\n質問です" }]; const referencedThread = { threadId: "thread-alpha", title: "Alpha", includedTurns: 2, turnLimit: 20 }; const ctx = context({ listedThreads: [thread({ id: "thread-current", name: "Current" }), target], diff --git a/tests/features/chat/turns/turn-submission-controller.test.ts b/tests/features/chat/turns/turn-submission-controller.test.ts index 1bbb7824..8d7dd4d3 100644 --- a/tests/features/chat/turns/turn-submission-controller.test.ts +++ b/tests/features/chat/turns/turn-submission-controller.test.ts @@ -1,6 +1,7 @@ import { describe, expect, it, vi } from "vitest"; import type { AppServerClient } from "../../../../src/app-server/client"; +import type { CodexInput } from "../../../../src/app-server/request-input"; import { createChatState, createChatStateStore } from "../../../../src/features/chat/chat-state"; import { TurnSubmissionController, @@ -14,9 +15,8 @@ import { type TurnSubmissionViewPort, } from "../../../../src/features/chat/turns/turn-submission-controller"; import type { Thread } from "../../../../src/generated/app-server/v2/Thread"; -import type { UserInput } from "../../../../src/generated/app-server/v2/UserInput"; -const textInput = (text: string): UserInput[] => [{ type: "text", text, text_elements: [] }]; +const textInput = (text: string): CodexInput => [{ type: "text", text }]; function thread(id: string): Thread & { archived: boolean } { return { diff --git a/tests/features/chat/turns/turn-submission.test.ts b/tests/features/chat/turns/turn-submission.test.ts index ccd0ae61..c7bbaf7b 100644 --- a/tests/features/chat/turns/turn-submission.test.ts +++ b/tests/features/chat/turns/turn-submission.test.ts @@ -23,7 +23,7 @@ describe("chat turn submission helpers", () => { it("builds optimistic turn starts from immutable input snapshots", () => { const input = [ - { type: "text" as const, text: "hello [[Note]]", text_elements: [] }, + { type: "text" as const, text: "hello [[Note]]" }, { type: "mention" as const, name: "Note", path: "Note.md" }, ]; @@ -49,7 +49,7 @@ describe("chat turn submission helpers", () => { it("formats resolved skill references in optimistic user messages only for display", () => { const text = "Use $obsidian-codex-panel-maintain and $missing."; const input = [ - { type: "text" as const, text, text_elements: [] }, + { type: "text" as const, text }, { type: "skill" as const, name: "obsidian-codex-panel-maintain", diff --git a/tests/features/chat/view-connection.test.ts b/tests/features/chat/view-connection.test.ts index 2ebb3c3d..5c93f342 100644 --- a/tests/features/chat/view-connection.test.ts +++ b/tests/features/chat/view-connection.test.ts @@ -491,7 +491,7 @@ describe("CodexChatView connection lifecycle", () => { expect(client.startTurn).toHaveBeenCalledWith( "thread-1", "/vault", - [{ type: "text", text: "hello", text_elements: [] }], + [{ type: "text", text: "hello" }], expect.stringMatching(/^local-user-\d+$/), ); });