mirror of
https://github.com/murashit/codex-panel.git
synced 2026-07-22 06:57:10 +00:00
Model chat Codex input boundary
This commit is contained in:
parent
0b22597a36
commit
edf95e8244
19 changed files with 88 additions and 64 deletions
|
|
@ -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",
|
||||
];
|
||||
|
|
|
|||
|
|
@ -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<TurnSteerResponse> {
|
||||
return this.request("turn/steer", {
|
||||
|
|
|
|||
|
|
@ -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 }];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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[] {
|
||||
|
|
|
|||
|
|
@ -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<string>();
|
||||
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<string>();
|
||||
const names: string[] = [];
|
||||
for (const item of input) {
|
||||
|
|
|
|||
|
|
@ -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<void>;
|
||||
sendTurnText(text: string, codexInputOverride?: CodexInput, referencedThread?: ReferencedThreadDisplay): Promise<void>;
|
||||
}
|
||||
|
||||
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<void>;
|
||||
async function sendComposerTurn(
|
||||
host: ComposerSubmissionActionsHost,
|
||||
text: string,
|
||||
codexInputOverride?: UserInput[],
|
||||
codexInputOverride?: CodexInput,
|
||||
referencedThread?: ReferencedThreadDisplay,
|
||||
): Promise<void> {
|
||||
host.scroll.forceBottom();
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<void> {
|
||||
async sendTurnText(text: string, codexInputOverride?: CodexInput, referencedThread?: ReferencedThreadDisplay): Promise<void> {
|
||||
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<void> {
|
||||
const state = submissionStateSnapshot(this.host.stateStore.getState());
|
||||
|
|
|
|||
|
|
@ -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<LocalUserMessageParams, "mentionedFiles"> {
|
||||
codexInput: readonly UserInput[];
|
||||
codexInput: CodexInput;
|
||||
}
|
||||
|
||||
export type OptimisticTurnStartParams = LocalUserMessageFromInputParams;
|
||||
|
|
|
|||
|
|
@ -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" },
|
||||
]);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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" },
|
||||
]);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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" },
|
||||
]);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ function context(overrides: Partial<SlashCommandExecutionContext> = {}): 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],
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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+$/),
|
||||
);
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue