mirror of
https://github.com/murashit/codex-panel.git
synced 2026-07-22 06:57:10 +00:00
Refresh lint boundaries and validation cleanup
This commit is contained in:
parent
552e53681b
commit
7ebe802e0b
38 changed files with 180 additions and 187 deletions
|
|
@ -29,7 +29,7 @@ The source tree is organized by responsibility rather than by the original singl
|
|||
- `src/domain/threads/` contains thread title, reference, transcript, naming, and archive export helpers shared outside the chat feature. Some helpers still read app-server turn/item types directly when they are pure protocol interpreters, but new shared UI/workspace/settings-facing behavior should prefer panel-owned models or small adapters at the app-server boundary.
|
||||
- `src/styles/` contains the authored CSS modules and `order.json` concatenation order. `scripts/build-styles.mjs` concatenates them into the ignored root `styles.css`, which remains the Obsidian release asset.
|
||||
|
||||
Keep new code near the state or API it owns. Feature code should not import from another feature directly; move shared behavior to `src/shared/`, `src/domain/`, or `src/app-server/` when more than one feature needs it.
|
||||
Keep new code near the state or API it owns. Feature-to-feature imports are allowed when the imported feature owns a concrete capability or integration point, such as thread export, thread title generation, or an Obsidian view. Do not use another feature as a generic utility bucket; move truly feature-neutral behavior to `src/shared/`, `src/domain/`, or `src/app-server/`. Lower-level modules in `src/app-server/`, `src/domain/`, and `src/shared/` must remain independent from `src/features/`; ESLint enforces that dependency direction.
|
||||
|
||||
Codex Panel's runtime UI is Preact-owned through the direct Preact APIs. Keep chat panel UI, the Threads view, and the selection rewrite popover in Preact components. The chat panel shell is a single Preact tree; toolbar, goal, message stream, and composer content should be composed as Preact nodes rather than mounted through HTMLElement region renderers. Use `preact/hooks` for hooks, `preact` for component types and fragments, and the shared root adapter for mounting and unmounting Preact subtrees. Imperative DOM writes are limited to explicit bridge modules or Obsidian-owned API boundaries such as `MarkdownRenderer`, TanStack Virtual measurement/lifecycle glue, diff rendering, icon rendering, `SuggestModal`, and the Obsidian `Setting`-based settings tab. ESLint enforces this boundary with allowlisted bridge files; add a new allowlist entry only when the code is genuinely bridging an external DOM API.
|
||||
|
||||
|
|
|
|||
|
|
@ -21,24 +21,9 @@ const removedChatStateEscapeHatchRestrictions = [
|
|||
message: "Use a named ChatAction instead of reintroducing the generic state patch escape hatch.",
|
||||
},
|
||||
];
|
||||
const generatedAppServerSourceImportPatterns = [
|
||||
"src/generated/app-server/**",
|
||||
"../generated/app-server/**",
|
||||
"../../generated/app-server/**",
|
||||
"../../../generated/app-server/**",
|
||||
"../../../../generated/app-server/**",
|
||||
"../../../../../generated/app-server/**",
|
||||
"../../../../../../generated/app-server/**",
|
||||
];
|
||||
const generatedAppServerTestImportPatterns = [
|
||||
"src/generated/app-server/**",
|
||||
"../src/generated/app-server/**",
|
||||
"../../src/generated/app-server/**",
|
||||
"../../../src/generated/app-server/**",
|
||||
"../../../../src/generated/app-server/**",
|
||||
"../../../../../src/generated/app-server/**",
|
||||
"../../../../../../src/generated/app-server/**",
|
||||
];
|
||||
const generatedAppServerSourceImportPatterns = importBoundaryPatterns("generated/app-server", "src/generated/app-server", 6);
|
||||
const generatedAppServerTestImportPatterns = importBoundaryPatterns("src/generated/app-server", "src/generated/app-server", 6);
|
||||
const lowerLevelFeatureImportPatterns = importBoundaryPatterns("features", "src/features", 6);
|
||||
const generatedAppServerThreadImportRestrictions = [
|
||||
{
|
||||
selector:
|
||||
|
|
@ -99,17 +84,16 @@ const pureChatModelRestrictions = [
|
|||
];
|
||||
const chatExternalDomBridgeFiles = [
|
||||
"src/features/chat/ui/message-stream/markdown-renderer.ts",
|
||||
"src/features/chat/ui/message-stream/rendered-markdown-links.ts",
|
||||
"src/features/chat/ui/message-virtualizer.ts",
|
||||
"src/features/chat/ui/message-stream/virtualizer.ts",
|
||||
];
|
||||
const chatPreactDomBridgeFiles = [
|
||||
"src/features/chat/ui/goal.tsx",
|
||||
"src/features/chat/ui/message-stream/text-item-actions.tsx",
|
||||
"src/features/chat/ui/message-stream/text-item.tsx",
|
||||
"src/features/chat/ui/message-stream/render.tsx",
|
||||
"src/features/chat/ui/message-stream/tool-result.tsx",
|
||||
"src/features/chat/ui/message-stream/viewport.tsx",
|
||||
"src/features/chat/ui/composer.tsx",
|
||||
"src/features/chat/ui/goal-banner.tsx",
|
||||
"src/features/chat/ui/shell.tsx",
|
||||
"src/features/chat/ui/tool-result.tsx",
|
||||
"src/features/chat/turn-diff/render.tsx",
|
||||
];
|
||||
const chatImperativeDomBridgeFiles = [...chatExternalDomBridgeFiles, ...chatPreactDomBridgeFiles];
|
||||
|
|
@ -125,6 +109,16 @@ const nonChatImperativeDomBridgeFiles = [
|
|||
"src/shared/ui/ui-root.tsx",
|
||||
];
|
||||
const nonUiEventListenerFiles = ["src/shared/lifecycle/abortable.ts"];
|
||||
const baseSourceSyntaxRestrictions = [
|
||||
...removedChatStateEscapeHatchRestrictions,
|
||||
...generatedAppServerThreadImportRestrictions,
|
||||
...unsafeIteratorRestrictions,
|
||||
...preactFormRestrictions,
|
||||
];
|
||||
const sourceSyntaxRestrictions = [...baseSourceSyntaxRestrictions, ...imperativeDomRestrictions];
|
||||
const domBridgeSyntaxRestrictions = baseSourceSyntaxRestrictions;
|
||||
const eventBridgeSyntaxRestrictions = [...baseSourceSyntaxRestrictions, ...imperativeDomWriteRestrictions];
|
||||
const pureChatModelSyntaxRestrictions = [...sourceSyntaxRestrictions, ...pureChatModelRestrictions];
|
||||
const codexPanelEslintPlugin = {
|
||||
rules: {
|
||||
"no-self-referential-initializer-callback": {
|
||||
|
|
@ -179,6 +173,17 @@ const codexPanelEslintPlugin = {
|
|||
},
|
||||
};
|
||||
|
||||
function importBoundaryPatterns(relativeTarget, absoluteTarget, maxParentDepth) {
|
||||
const targets = [absoluteTarget, ...Array.from({ length: maxParentDepth }, (_, index) => `${"../".repeat(index + 1)}${relativeTarget}`)];
|
||||
return targets.flatMap((target) => [target, `${target}/**`]);
|
||||
}
|
||||
|
||||
function restrictedSyntaxRule(restrictions) {
|
||||
return {
|
||||
"no-restricted-syntax": ["error", ...restrictions],
|
||||
};
|
||||
}
|
||||
|
||||
function isChatStateMember(node) {
|
||||
if (!isMemberExpression(node)) return false;
|
||||
|
||||
|
|
@ -328,7 +333,7 @@ export default defineConfig([
|
|||
"@typescript-eslint/no-unsafe-member-access": "off",
|
||||
"@typescript-eslint/no-unnecessary-type-assertion": "off",
|
||||
"@typescript-eslint/require-await": "off",
|
||||
"no-restricted-syntax": ["error", ...generatedAppServerThreadImportRestrictions],
|
||||
...restrictedSyntaxRule(generatedAppServerThreadImportRestrictions),
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -351,107 +356,48 @@ export default defineConfig([
|
|||
{
|
||||
files: ["src/**/*.{ts,tsx}"],
|
||||
ignores: ["src/features/chat/**/*.{ts,tsx}", ...nonChatImperativeDomBridgeFiles, ...nonUiEventListenerFiles],
|
||||
rules: {
|
||||
"no-restricted-syntax": [
|
||||
"error",
|
||||
...removedChatStateEscapeHatchRestrictions,
|
||||
...generatedAppServerThreadImportRestrictions,
|
||||
...unsafeIteratorRestrictions,
|
||||
...imperativeDomRestrictions,
|
||||
...preactFormRestrictions,
|
||||
],
|
||||
},
|
||||
rules: restrictedSyntaxRule(sourceSyntaxRestrictions),
|
||||
},
|
||||
{
|
||||
files: ["src/features/chat/**/*.{ts,tsx}"],
|
||||
ignores: chatImperativeDomBridgeFiles,
|
||||
rules: {
|
||||
"no-restricted-syntax": [
|
||||
"error",
|
||||
...removedChatStateEscapeHatchRestrictions,
|
||||
...generatedAppServerThreadImportRestrictions,
|
||||
...unsafeIteratorRestrictions,
|
||||
...imperativeDomRestrictions,
|
||||
...preactFormRestrictions,
|
||||
],
|
||||
...restrictedSyntaxRule(sourceSyntaxRestrictions),
|
||||
"codex-panel/no-chat-state-direct-mutation": "error",
|
||||
},
|
||||
},
|
||||
{
|
||||
files: chatImperativeDomBridgeFiles,
|
||||
rules: {
|
||||
"no-restricted-syntax": [
|
||||
"error",
|
||||
...removedChatStateEscapeHatchRestrictions,
|
||||
...generatedAppServerThreadImportRestrictions,
|
||||
...unsafeIteratorRestrictions,
|
||||
...preactFormRestrictions,
|
||||
],
|
||||
...restrictedSyntaxRule(domBridgeSyntaxRestrictions),
|
||||
"codex-panel/no-chat-state-direct-mutation": "error",
|
||||
},
|
||||
},
|
||||
{
|
||||
files: nonChatImperativeDomBridgeFiles,
|
||||
rules: {
|
||||
"no-restricted-syntax": [
|
||||
"error",
|
||||
...removedChatStateEscapeHatchRestrictions,
|
||||
...generatedAppServerThreadImportRestrictions,
|
||||
...unsafeIteratorRestrictions,
|
||||
...preactFormRestrictions,
|
||||
],
|
||||
},
|
||||
rules: restrictedSyntaxRule(domBridgeSyntaxRestrictions),
|
||||
},
|
||||
{
|
||||
files: nonUiEventListenerFiles,
|
||||
rules: {
|
||||
"no-restricted-syntax": [
|
||||
"error",
|
||||
...removedChatStateEscapeHatchRestrictions,
|
||||
...generatedAppServerThreadImportRestrictions,
|
||||
...unsafeIteratorRestrictions,
|
||||
...imperativeDomWriteRestrictions,
|
||||
...preactFormRestrictions,
|
||||
],
|
||||
},
|
||||
rules: restrictedSyntaxRule(eventBridgeSyntaxRestrictions),
|
||||
},
|
||||
{
|
||||
files: ["src/features/chat/state/reducer.ts", "src/features/chat/display/**/*.{ts,tsx}"],
|
||||
rules: {
|
||||
"no-restricted-syntax": [
|
||||
"error",
|
||||
...removedChatStateEscapeHatchRestrictions,
|
||||
...generatedAppServerThreadImportRestrictions,
|
||||
...unsafeIteratorRestrictions,
|
||||
...imperativeDomRestrictions,
|
||||
...preactFormRestrictions,
|
||||
...pureChatModelRestrictions,
|
||||
],
|
||||
...restrictedSyntaxRule(pureChatModelSyntaxRestrictions),
|
||||
"codex-panel/no-chat-state-direct-mutation": "error",
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ["src/app-server/**/*.{ts,tsx}", "src/domain/**/*.{ts,tsx}", "src/runtime/**/*.{ts,tsx}", "src/shared/**/*.{ts,tsx}"],
|
||||
files: ["src/app-server/**/*.{ts,tsx}", "src/domain/**/*.{ts,tsx}", "src/shared/**/*.{ts,tsx}"],
|
||||
rules: {
|
||||
"no-restricted-imports": [
|
||||
"error",
|
||||
{
|
||||
patterns: [
|
||||
{
|
||||
group: [
|
||||
"src/features",
|
||||
"src/features/**",
|
||||
"../features",
|
||||
"../features/**",
|
||||
"../../features",
|
||||
"../../features/**",
|
||||
"../../../features",
|
||||
"../../../features/**",
|
||||
"../../../../features",
|
||||
"../../../../features/**",
|
||||
],
|
||||
message:
|
||||
"Lower-level modules must not import feature modules. Move shared behavior to shared, domain, runtime, or app-server.",
|
||||
group: lowerLevelFeatureImportPatterns,
|
||||
message: "Lower-level modules must not import feature modules. Move shared behavior to shared, domain, or app-server.",
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"protocolAdapterFiles": [
|
||||
"src/features/chat/protocol/display-items.ts",
|
||||
"src/features/chat/protocol/inbound/notification-plan.ts"
|
||||
]
|
||||
}
|
||||
|
|
@ -39,7 +39,14 @@ import type { TurnSteerResponse } from "../../generated/app-server/v2/TurnSteerR
|
|||
import type { UserInput } from "../../generated/app-server/v2/UserInput";
|
||||
import { CLIENT_VERSION } from "../../constants";
|
||||
import { StdioAppServerTransport, type AppServerTransport, type AppServerTransportHandlers } from "./transport";
|
||||
import type { ClientRequestMethod, ClientRequestParams, PendingRequest, RpcError, RpcInboundMessage, RpcOutboundMessage } from "./rpc-messages";
|
||||
import type {
|
||||
ClientRequestMethod,
|
||||
ClientRequestParams,
|
||||
PendingRequest,
|
||||
RpcError,
|
||||
RpcInboundMessage,
|
||||
RpcOutboundMessage,
|
||||
} from "./rpc-messages";
|
||||
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";
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ export const DIAGNOSTIC_PROBE_METHODS = [
|
|||
|
||||
export type DiagnosticProbeMethod = (typeof DIAGNOSTIC_PROBE_METHODS)[number];
|
||||
type DiagnosticProbeStatus = "unknown" | "ok" | "failed";
|
||||
export type McpAuthStatus = "unsupported" | "notLoggedIn" | "bearerToken" | "oAuth";
|
||||
type McpAuthStatus = "unsupported" | "notLoggedIn" | "bearerToken" | "oAuth";
|
||||
export type McpServerStartupStatus = "starting" | "ready" | "failed" | "cancelled";
|
||||
|
||||
export interface McpServerStatus {
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@ function asRecordOrNull(value: unknown): Record<string, unknown> | null {
|
|||
}
|
||||
|
||||
function asConfigRecord(value: unknown): ConfigProjectionRecord {
|
||||
return asRecord(value) as ConfigProjectionRecord;
|
||||
return asRecord(value);
|
||||
}
|
||||
|
||||
function selectedConfigProfile(layers: ConfigReadResult["layers"]): string | null {
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ export interface TokenUsageBreakdown {
|
|||
reasoningOutputTokens: number;
|
||||
}
|
||||
|
||||
export function rateLimitSnapshotFromAppServerSnapshot(snapshot: AppServerRateLimitSnapshot): RateLimitSnapshot {
|
||||
function rateLimitSnapshotFromAppServerSnapshot(snapshot: AppServerRateLimitSnapshot): RateLimitSnapshot {
|
||||
return {
|
||||
limitId: snapshot.limitId,
|
||||
limitName: snapshot.limitName,
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ export function transcriptEntriesFromTurnRecords(turns: readonly TurnRecord[]):
|
|||
return turns.flatMap(transcriptEntriesFromTurnRecord);
|
||||
}
|
||||
|
||||
export function conversationSummaryFromTurnRecord(turn: TurnRecord): ThreadConversationSummary {
|
||||
function conversationSummaryFromTurnRecord(turn: TurnRecord): ThreadConversationSummary {
|
||||
return conversationSummaryFromTranscriptEntries(transcriptEntriesFromTurnRecord(turn));
|
||||
}
|
||||
|
||||
|
|
@ -42,7 +42,7 @@ export function completedConversationSummariesFromTurnRecords(turns: readonly Tu
|
|||
});
|
||||
}
|
||||
|
||||
export function conversationSummariesFromTurnRecords(turns: readonly TurnRecord[]): ThreadConversationSummary[] {
|
||||
function conversationSummariesFromTurnRecords(turns: readonly TurnRecord[]): ThreadConversationSummary[] {
|
||||
return nonEmptyConversationSummaries(turns.map(conversationSummaryFromTurnRecord));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -73,11 +73,7 @@ async function refreshDiagnosticProbes(
|
|||
return `${String(count)} skills`;
|
||||
},
|
||||
),
|
||||
probeDiagnostic(
|
||||
"account/rateLimits/read",
|
||||
() => client.readAccountRateLimits(),
|
||||
accountRateLimitsSummaryFromResponse,
|
||||
),
|
||||
probeDiagnostic("account/rateLimits/read", () => client.readAccountRateLimits(), accountRateLimitsSummaryFromResponse),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -148,10 +144,7 @@ async function mcpStatusLines(host: ChatServerDiagnosticsActionsHost): Promise<s
|
|||
try {
|
||||
const state = host.stateStore.getState();
|
||||
const response = await client.listMcpServerStatus(mcpServerStatusParams(state.activeThread.id));
|
||||
return buildMcpStatusLines(
|
||||
mcpServerStatusSummariesFromStatuses(response.data),
|
||||
state.connection.appServerDiagnostics.mcpServers,
|
||||
);
|
||||
return buildMcpStatusLines(mcpServerStatusSummariesFromStatuses(response.data), state.connection.appServerDiagnostics.mcpServers);
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
return ["MCP servers", `Could not load MCP servers: ${message}`];
|
||||
|
|
|
|||
|
|
@ -15,15 +15,15 @@ import {
|
|||
} from "../../protocol/server-requests/user-input";
|
||||
|
||||
export type PendingRequestId = RequestId;
|
||||
export type PendingRequestApprovalAction = ApprovalAction;
|
||||
type PendingRequestApprovalAction = ApprovalAction;
|
||||
|
||||
export interface PendingRequestApprovalOption {
|
||||
interface PendingRequestApprovalOption {
|
||||
label: string;
|
||||
className: string;
|
||||
action: PendingRequestApprovalAction;
|
||||
}
|
||||
|
||||
export interface PendingRequestDetailRow {
|
||||
interface PendingRequestDetailRow {
|
||||
key: string;
|
||||
value: string;
|
||||
}
|
||||
|
|
@ -36,7 +36,7 @@ export interface PendingApprovalViewModel {
|
|||
actions: PendingRequestApprovalOption[];
|
||||
}
|
||||
|
||||
export interface PendingUserInputOptionViewModel {
|
||||
interface PendingUserInputOptionViewModel {
|
||||
label: string;
|
||||
description?: string | null;
|
||||
}
|
||||
|
|
@ -96,4 +96,3 @@ export function pendingUserInputViewModel(input: PendingUserInput): PendingUserI
|
|||
})),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -47,11 +47,7 @@ export function rollbackCandidateFromItems(items: readonly DisplayItem[]): Rollb
|
|||
|
||||
export function isRollbackCandidateItem(item: DisplayItem, candidate: RollbackCandidate | null): boolean {
|
||||
return Boolean(
|
||||
candidate &&
|
||||
item.kind === "message" &&
|
||||
item.role === "user" &&
|
||||
item.id === candidate.displayItemId &&
|
||||
item.turnId === candidate.turnId,
|
||||
candidate && item.kind === "message" && item.role === "user" && item.id === candidate.displayItemId && item.turnId === candidate.turnId,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ export function agentDisplayItem(item: DisplayCollabAgentToolCall, turnId?: stri
|
|||
};
|
||||
}
|
||||
|
||||
export function agentActivitySummaryLabel(tool: string): string {
|
||||
function agentActivitySummaryLabel(tool: string): string {
|
||||
if (tool === "spawnAgent") return "Spawn agent";
|
||||
if (tool === "sendInput") return "Send input to agent";
|
||||
if (tool === "resumeAgent") return "Resume agent";
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ const TASK_STATES = {
|
|||
completed: "completed",
|
||||
} as const satisfies ExecutionStateByStatus;
|
||||
|
||||
export type TaskStepStatus = "pending" | "inProgress" | "completed";
|
||||
type TaskStepStatus = "pending" | "inProgress" | "completed";
|
||||
|
||||
export interface TaskPlanStep {
|
||||
step: string;
|
||||
|
|
|
|||
|
|
@ -31,9 +31,7 @@ type DynamicToolCallItem = Extract<TurnItem, { type: "dynamicToolCall" }>;
|
|||
type WebSearchItem = Extract<TurnItem, { type: "webSearch" }>;
|
||||
type ImageViewItem = Extract<TurnItem, { type: "imageView" }>;
|
||||
type ImageGenerationItem = Extract<TurnItem, { type: "imageGeneration" }>;
|
||||
type ReviewModeItem =
|
||||
| Extract<TurnItem, { type: "enteredReviewMode" }>
|
||||
| Extract<TurnItem, { type: "exitedReviewMode" }>;
|
||||
type ReviewModeItem = Extract<TurnItem, { type: "enteredReviewMode" }> | Extract<TurnItem, { type: "exitedReviewMode" }>;
|
||||
type ContextCompactionItem = Extract<TurnItem, { type: "contextCompaction" }>;
|
||||
type DisplayExecutionState = Exclude<ExecutionState, null>;
|
||||
type ExecutionStateByStatus = Readonly<Record<string, DisplayExecutionState>>;
|
||||
|
|
@ -693,7 +691,7 @@ export function dynamicToolCallExecutionState(status: string, success?: boolean
|
|||
return success === true ? "completed" : null;
|
||||
}
|
||||
|
||||
export function imageGenerationExecutionState(status: string): ExecutionState {
|
||||
function imageGenerationExecutionState(status: string): ExecutionState {
|
||||
return standardToolCallExecutionState(status);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ interface ChatPanelGoalActions {
|
|||
setEditingOpen: (open: boolean) => void;
|
||||
}
|
||||
|
||||
export interface ChatPanelStatePort {
|
||||
interface ChatPanelStatePort {
|
||||
state: {
|
||||
chat: () => ChatState;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -111,7 +111,10 @@ export class ChatInboundController {
|
|||
}
|
||||
|
||||
addStructuredSystemMessage(text: string, details: DisplayDetailSection[]): void {
|
||||
this.dispatch({ type: "message-stream/system-item-added", item: createStructuredSystemItem(this.localItemId("system"), text, details) });
|
||||
this.dispatch({
|
||||
type: "message-stream/system-item-added",
|
||||
item: createStructuredSystemItem(this.localItemId("system"), text, details),
|
||||
});
|
||||
}
|
||||
|
||||
addDedupedSystemMessage(text: string): void {
|
||||
|
|
|
|||
|
|
@ -21,7 +21,12 @@ import {
|
|||
completeReasoningItems,
|
||||
upsertDisplayItem,
|
||||
} from "../../state/message-stream-updates";
|
||||
import { displayItemFromTurnItem, displayItemsFromTurns, normalizeFileChanges, shouldSuppressLifecycleItem } from "../../display/turn-items";
|
||||
import {
|
||||
displayItemFromTurnItem,
|
||||
displayItemsFromTurns,
|
||||
normalizeFileChanges,
|
||||
shouldSuppressLifecycleItem,
|
||||
} from "../../display/turn-items";
|
||||
import { taskProgressDisplayItem } from "../../display/items/task-progress";
|
||||
import { createSystemItem } from "../../display/items/system";
|
||||
import type { DisplayItem, DisplayKind, MessageDisplayItem } from "../../display/types";
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
import { cloneRuntimeConfigSnapshot, emptyRuntimeConfigSnapshot, type RuntimeConfigSnapshot } from "../../../app-server/protocol/runtime-config";
|
||||
import {
|
||||
cloneRuntimeConfigSnapshot,
|
||||
emptyRuntimeConfigSnapshot,
|
||||
type RuntimeConfigSnapshot,
|
||||
} from "../../../app-server/protocol/runtime-config";
|
||||
import { findModelMetadataByIdOrName, type ModelMetadata } from "../../../domain/catalog/metadata";
|
||||
import type { ApprovalPolicy, ApprovalsReviewer } from "../../../app-server/protocol/runtime-policy";
|
||||
import { supportedEffortsForModelMetadata, type ReasoningEffort } from "../../../domain/catalog/metadata";
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
import { applyThreadSettingsValue, appServerCollaborationMode, type ThreadSettingsUpdate } from "../../../app-server/protocol/thread-settings";
|
||||
import {
|
||||
applyThreadSettingsValue,
|
||||
appServerCollaborationMode,
|
||||
type ThreadSettingsUpdate,
|
||||
} from "../../../app-server/protocol/thread-settings";
|
||||
import type { ServiceTierRequest } from "../../../app-server/protocol/thread-settings";
|
||||
import { currentModel, currentReasoningEffort, fastServiceTierRequestValue } from "./effective";
|
||||
import type { RuntimeConfigSnapshot } from "../../../app-server/protocol/runtime-config";
|
||||
|
|
|
|||
|
|
@ -30,7 +30,9 @@ function mergeChanges(previous: DisplayItem, next: DisplayItem): DisplayFileChan
|
|||
}
|
||||
|
||||
export function appendAssistantDelta(items: readonly DisplayItem[], sourceItemId: string, turnId: string, delta: string): DisplayItem[] {
|
||||
const index = items.findIndex((item) => item.sourceItemId === sourceItemId && item.kind === "message" && item.messageKind === "assistantResponse");
|
||||
const index = items.findIndex(
|
||||
(item) => item.sourceItemId === sourceItemId && item.kind === "message" && item.messageKind === "assistantResponse",
|
||||
);
|
||||
if (index !== -1) {
|
||||
return items.map((item, itemIndex) =>
|
||||
itemIndex === index && item.kind === "message" && item.messageKind === "assistantResponse"
|
||||
|
|
|
|||
|
|
@ -46,7 +46,12 @@ import type {
|
|||
TurnStartFailedAction,
|
||||
UserInputDraftSetAction,
|
||||
} from "./actions";
|
||||
import { initialChatMessageStreamState, reduceMessageStreamSlice, type ChatMessageStreamState, type MessageStreamAction } from "./message-stream";
|
||||
import {
|
||||
initialChatMessageStreamState,
|
||||
reduceMessageStreamSlice,
|
||||
type ChatMessageStreamState,
|
||||
type MessageStreamAction,
|
||||
} from "./message-stream";
|
||||
import {
|
||||
initialChatRequestState,
|
||||
reduceRequestSlice,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,12 @@
|
|||
import { activeTurnId as selectActiveTurnId, chatTurnBusy, pendingTurnStart } from "./reducer";
|
||||
import type { ChatActiveThreadState, ChatRuntimeState, ChatState, ChatMessageStreamState, ChatTurnState, PendingTurnStart } from "./reducer";
|
||||
import type {
|
||||
ChatActiveThreadState,
|
||||
ChatRuntimeState,
|
||||
ChatState,
|
||||
ChatMessageStreamState,
|
||||
ChatTurnState,
|
||||
PendingTurnStart,
|
||||
} from "./reducer";
|
||||
import type { Thread } from "../../../domain/threads/model";
|
||||
import type { DisplayItem } from "../display/types";
|
||||
|
||||
|
|
|
|||
|
|
@ -49,12 +49,7 @@ async function syncThreadGoal(host: GoalActionsHost, threadId: string): Promise<
|
|||
}
|
||||
}
|
||||
|
||||
async function setObjective(
|
||||
host: GoalActionsHost,
|
||||
threadId: string,
|
||||
objective: string,
|
||||
tokenBudget: number | null,
|
||||
): Promise<boolean> {
|
||||
async function setObjective(host: GoalActionsHost, threadId: string, objective: string, tokenBudget: number | null): Promise<boolean> {
|
||||
const trimmed = objective.trim();
|
||||
if (!trimmed) {
|
||||
host.addSystemMessage("Goal objective cannot be empty.");
|
||||
|
|
@ -104,12 +99,7 @@ async function setGoal(host: GoalActionsHost, threadId: string, params: ThreadGo
|
|||
}
|
||||
}
|
||||
|
||||
function applyGoalIfActive(
|
||||
host: GoalActionsHost,
|
||||
threadId: string,
|
||||
goal: ThreadGoal | null,
|
||||
options: { reportChange: boolean },
|
||||
): boolean {
|
||||
function applyGoalIfActive(host: GoalActionsHost, threadId: string, goal: ThreadGoal | null, options: { reportChange: boolean }): boolean {
|
||||
const state = host.stateStore.getState();
|
||||
if (state.activeThread.id !== threadId) return false;
|
||||
const item = options.reportChange ? goalChangeItem(goalEventId(), state.activeThread.goal, goal) : null;
|
||||
|
|
|
|||
|
|
@ -58,7 +58,11 @@ export class HistoryController {
|
|||
if (this.state.activeThread.id !== threadId) return false;
|
||||
this.host.setThreadTurnPresence(response.data.length > 0);
|
||||
this.host.showLatestPageAtBottom();
|
||||
this.dispatch({ type: "message-stream/items-replaced", items: displayItemsFromTurns(response.data), historyCursor: response.nextCursor });
|
||||
this.dispatch({
|
||||
type: "message-stream/items-replaced",
|
||||
items: displayItemsFromTurns(response.data),
|
||||
historyCursor: response.nextCursor,
|
||||
});
|
||||
this.host.render();
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,11 +4,7 @@ import { getThreadTitle } from "../../../domain/threads/model";
|
|||
import type { Thread } from "../../../domain/threads/model";
|
||||
import type { CodexPanelSettings } from "../../../settings/model";
|
||||
import { generateThreadTitleWithCodex } from "../../thread-title/generation";
|
||||
import {
|
||||
findThreadTitleContext,
|
||||
THREAD_TITLE_CONTEXT_UNAVAILABLE_MESSAGE,
|
||||
type ThreadTitleContext,
|
||||
} from "../../thread-title/model";
|
||||
import { findThreadTitleContext, THREAD_TITLE_CONTEXT_UNAVAILABLE_MESSAGE, type ThreadTitleContext } from "../../thread-title/model";
|
||||
import type { ChatState, ChatStateStore } from "../state/reducer";
|
||||
import { renameConnectedThread } from "./rename-actions";
|
||||
import { firstThreadTitleContextFromDisplayItems } from "./title-context";
|
||||
|
|
|
|||
|
|
@ -2,7 +2,10 @@ import { parseServiceTier } from "../../../app-server/protocol/runtime-policy";
|
|||
import { upsertThread } from "../../../domain/threads/model";
|
||||
import { normalizeReasoningEffort } from "../../../domain/catalog/metadata";
|
||||
import type { Thread } from "../../../domain/threads/model";
|
||||
import { threadActivationSnapshotFromAppServerResponse, type ThreadActivationSnapshot } from "../../../app-server/services/thread-activation";
|
||||
import {
|
||||
threadActivationSnapshotFromAppServerResponse,
|
||||
type ThreadActivationSnapshot,
|
||||
} from "../../../app-server/services/thread-activation";
|
||||
import type { ChatRuntimeState } from "../runtime/state";
|
||||
import type { DisplayItem } from "../display/types";
|
||||
import type { ActiveThreadResumedAction } from "../state/actions";
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ export function bindRenderedWikiLinks(parent: HTMLElement, sourcePath: string, c
|
|||
});
|
||||
}
|
||||
|
||||
export function bindRenderedMarkdownFileLinks(parent: HTMLElement, sourcePath: string, context: RenderedMarkdownLinkContext): void {
|
||||
function bindRenderedMarkdownFileLinks(parent: HTMLElement, sourcePath: string, context: RenderedMarkdownLinkContext): void {
|
||||
parent.querySelectorAll<HTMLAnchorElement>("a[href]:not(.internal-link)").forEach((link) => {
|
||||
const href = link.getAttribute("href") ?? "";
|
||||
const target = vaultFileLinkTarget(context.app, context.vaultPath, href);
|
||||
|
|
|
|||
|
|
@ -93,7 +93,13 @@ function CollapsibleTextItemContent({ item, context }: { item: TextDisplayItem;
|
|||
.filter(Boolean)
|
||||
.join(" ")}
|
||||
>
|
||||
<TextContent key={textItemContentKey(item)} item={item} context={context} contentRef={contentRef} collapsed={overflows && !expanded} />
|
||||
<TextContent
|
||||
key={textItemContentKey(item)}
|
||||
item={item}
|
||||
context={context}
|
||||
contentRef={contentRef}
|
||||
collapsed={overflows && !expanded}
|
||||
/>
|
||||
<details
|
||||
className="codex-panel__message-collapse-details"
|
||||
hidden={!overflows || expanded}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,12 @@
|
|||
import type { ComponentChild as UiNode } from "preact";
|
||||
import { useLayoutEffect, useState } from "preact/hooks";
|
||||
|
||||
import { activeAgentRunSummary, agentActivityMetaLabel, agentMessagePreview, agentRunSummaryLabel } from "../../display/stream/agent-summary";
|
||||
import {
|
||||
activeAgentRunSummary,
|
||||
agentActivityMetaLabel,
|
||||
agentMessagePreview,
|
||||
agentRunSummaryLabel,
|
||||
} from "../../display/stream/agent-summary";
|
||||
import type {
|
||||
AgentDisplayItem,
|
||||
AgentRunSummary,
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ interface ToolbarDiagnosticRow {
|
|||
level?: "normal" | "warning" | "error";
|
||||
}
|
||||
|
||||
export interface ToolbarDiagnosticSection {
|
||||
interface ToolbarDiagnosticSection {
|
||||
title: string;
|
||||
rows: ToolbarDiagnosticRow[];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { AppServerClient } from "../../src/app-server/connection/client";
|
||||
import type { AppServerRpcError, AppServerStartStructuredTurnOptions, AppServerStartTurnOptions } from "../../src/app-server/connection/client";
|
||||
import type {
|
||||
AppServerRpcError,
|
||||
AppServerStartStructuredTurnOptions,
|
||||
AppServerStartTurnOptions,
|
||||
} from "../../src/app-server/connection/client";
|
||||
import type { AppServerTransport, AppServerTransportHandlers } from "../../src/app-server/connection/transport";
|
||||
import type { RpcOutboundMessage } from "../../src/app-server/connection/rpc-messages";
|
||||
import type { InitializeResponse } from "../../src/generated/app-server/InitializeResponse";
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { appServerCollaborationMode, applyThreadSettingsValue, type ThreadSettingsUpdate } from "../../src/app-server/protocol/thread-settings";
|
||||
import {
|
||||
appServerCollaborationMode,
|
||||
applyThreadSettingsValue,
|
||||
type ThreadSettingsUpdate,
|
||||
} from "../../src/app-server/protocol/thread-settings";
|
||||
import { appServerApprovalsReviewerOrNull, parseServiceTier } from "../../src/app-server/protocol/runtime-policy";
|
||||
|
||||
describe("app-server thread settings", () => {
|
||||
|
|
|
|||
|
|
@ -11,14 +11,15 @@ import {
|
|||
appendToolOutput,
|
||||
upsertDisplayItem,
|
||||
} from "../../../../src/features/chat/state/message-stream-updates";
|
||||
import {
|
||||
taskProgressDisplayItem,
|
||||
taskProgressExecutionState,
|
||||
} from "../../../../src/features/chat/display/items/task-progress";
|
||||
import { taskProgressDisplayItem, taskProgressExecutionState } from "../../../../src/features/chat/display/items/task-progress";
|
||||
import { normalizeProposedPlanMarkdown } from "../../../../src/features/chat/display/items/proposed-plan";
|
||||
import { pathRelativeToRoot } from "../../../../src/features/chat/display/details/path-labels";
|
||||
import { permissionRows } from "../../../../src/features/chat/display/details/permission-rows";
|
||||
import { autoReviewExecutionState, createAutoReviewResultItem, createReviewResultItem } from "../../../../src/features/chat/display/items/review-result";
|
||||
import {
|
||||
autoReviewExecutionState,
|
||||
createAutoReviewResultItem,
|
||||
createReviewResultItem,
|
||||
} from "../../../../src/features/chat/display/items/review-result";
|
||||
import {
|
||||
commandExecutionState,
|
||||
dynamicToolCallExecutionState,
|
||||
|
|
|
|||
|
|
@ -1231,7 +1231,9 @@ describe("ChatInboundController", () => {
|
|||
expect(fallbackStateWithoutClientId.messageStream.displayItems).toEqual([
|
||||
expect.objectContaining({ id: "server-u1", text: "fallback text" }),
|
||||
]);
|
||||
expect(fallbackStateWithoutClientId.messageStream.displayItems.some((item) => item.id === "local-user-without-client-id")).toBe(false);
|
||||
expect(fallbackStateWithoutClientId.messageStream.displayItems.some((item) => item.id === "local-user-without-client-id")).toBe(
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
it("keeps the observed steer message order when completed turns reconcile by client id", () => {
|
||||
|
|
@ -1483,7 +1485,11 @@ describe("ChatInboundController", () => {
|
|||
method: "thread/goal/updated",
|
||||
params: { threadId: "thread-active", turnId: null, goal: updatedGoal },
|
||||
} satisfies Extract<ServerNotification, { method: "thread/goal/updated" }>);
|
||||
expect(state.messageStream.displayItems.at(-1)).toMatchObject({ kind: "goal", text: "updated: Finish well", objective: "Finish well" });
|
||||
expect(state.messageStream.displayItems.at(-1)).toMatchObject({
|
||||
kind: "goal",
|
||||
text: "updated: Finish well",
|
||||
objective: "Finish well",
|
||||
});
|
||||
|
||||
const pausedGoal = { ...updatedGoal, status: "paused", updatedAt: 3 } satisfies Extract<
|
||||
ServerNotification,
|
||||
|
|
@ -1493,7 +1499,11 @@ describe("ChatInboundController", () => {
|
|||
method: "thread/goal/updated",
|
||||
params: { threadId: "thread-active", turnId: null, goal: pausedGoal },
|
||||
} satisfies Extract<ServerNotification, { method: "thread/goal/updated" }>);
|
||||
expect(state.messageStream.displayItems.at(-1)).toMatchObject({ kind: "goal", text: "paused: Finish well", objective: "Finish well" });
|
||||
expect(state.messageStream.displayItems.at(-1)).toMatchObject({
|
||||
kind: "goal",
|
||||
text: "paused: Finish well",
|
||||
objective: "Finish well",
|
||||
});
|
||||
|
||||
const resumedGoal = { ...pausedGoal, status: "active", updatedAt: 4 } satisfies Extract<
|
||||
ServerNotification,
|
||||
|
|
@ -1503,7 +1513,11 @@ describe("ChatInboundController", () => {
|
|||
method: "thread/goal/updated",
|
||||
params: { threadId: "thread-active", turnId: null, goal: resumedGoal },
|
||||
} satisfies Extract<ServerNotification, { method: "thread/goal/updated" }>);
|
||||
expect(state.messageStream.displayItems.at(-1)).toMatchObject({ kind: "goal", text: "resumed: Finish well", objective: "Finish well" });
|
||||
expect(state.messageStream.displayItems.at(-1)).toMatchObject({
|
||||
kind: "goal",
|
||||
text: "resumed: Finish well",
|
||||
objective: "Finish well",
|
||||
});
|
||||
|
||||
const messageCount = state.messageStream.displayItems.length;
|
||||
controller.handleNotification({
|
||||
|
|
@ -1518,7 +1532,11 @@ describe("ChatInboundController", () => {
|
|||
} satisfies Extract<ServerNotification, { method: "thread/goal/cleared" }>);
|
||||
|
||||
expect(state.activeThread.goal).toBeNull();
|
||||
expect(state.messageStream.displayItems.at(-1)).toMatchObject({ kind: "goal", text: "cleared: Finish well", objective: "Finish well" });
|
||||
expect(state.messageStream.displayItems.at(-1)).toMatchObject({
|
||||
kind: "goal",
|
||||
text: "cleared: Finish well",
|
||||
objective: "Finish well",
|
||||
});
|
||||
});
|
||||
|
||||
it("adds a goal event when a goal completes", () => {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,7 @@
|
|||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { createChatState, createChatStateStore, type ChatStateStore } from "../../../../src/features/chat/state/reducer";
|
||||
import {
|
||||
createSelectionActions,
|
||||
type SelectionActionsHost,
|
||||
} from "../../../../src/features/chat/threads/selection-actions";
|
||||
import { createSelectionActions, type SelectionActionsHost } from "../../../../src/features/chat/threads/selection-actions";
|
||||
|
||||
function resumeThreadState(stateStore: ChatStateStore, threadId: string): void {
|
||||
stateStore.dispatch({
|
||||
|
|
|
|||
|
|
@ -25,7 +25,11 @@ import {
|
|||
type SelectionRewriteClient,
|
||||
type SelectionRewriteClientFactory,
|
||||
} from "../../../src/features/selection-rewrite/runner";
|
||||
import type { AppServerClient, AppServerClientHandlers, AppServerStartStructuredTurnOptions } from "../../../src/app-server/connection/client";
|
||||
import type {
|
||||
AppServerClient,
|
||||
AppServerClientHandlers,
|
||||
AppServerStartStructuredTurnOptions,
|
||||
} from "../../../src/app-server/connection/client";
|
||||
import type { AppServerInitialization } from "../../../src/app-server/protocol/initialization";
|
||||
import type { RequestId, ServerNotification } from "../../../src/app-server/connection/rpc-messages";
|
||||
import type { TurnItem, TurnRecord } from "../../../src/app-server/protocol/turn";
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import type { AppServerClient, AppServerClientHandlers, AppServerStartStructuredTurnOptions } from "../../../src/app-server/connection/client";
|
||||
import type {
|
||||
AppServerClient,
|
||||
AppServerClientHandlers,
|
||||
AppServerStartStructuredTurnOptions,
|
||||
} from "../../../src/app-server/connection/client";
|
||||
import { modelMetadataFromCatalogModels, type CatalogModel } from "../../../src/app-server/protocol/catalog";
|
||||
import type { AppServerInitialization } from "../../../src/app-server/protocol/initialization";
|
||||
import type { TurnItem, TurnRecord } from "../../../src/app-server/protocol/turn";
|
||||
|
|
|
|||
|
|
@ -12,10 +12,7 @@ import {
|
|||
modelOverrideMessage,
|
||||
reasoningEffortOverrideMessage,
|
||||
} from "../../src/features/chat/runtime/settings-copy";
|
||||
import {
|
||||
parseModelOverride,
|
||||
parseReasoningEffortOverride,
|
||||
} from "../../src/features/chat/conversation/turns/runtime-setting-commands";
|
||||
import { parseModelOverride, parseReasoningEffortOverride } from "../../src/features/chat/conversation/turns/runtime-setting-commands";
|
||||
import {
|
||||
autoReviewActive,
|
||||
currentApprovalsReviewer,
|
||||
|
|
@ -764,10 +761,7 @@ function snapshotConfig(snapshot: RuntimeSnapshot): RuntimeConfigSnapshot {
|
|||
return runtimeConfigOrDefault(snapshot.runtimeConfig);
|
||||
}
|
||||
|
||||
function runtimeConfigFixture(
|
||||
config: Record<string, unknown>,
|
||||
layers: ConfigReadResult["layers"] = null,
|
||||
): RuntimeConfigSnapshot {
|
||||
function runtimeConfigFixture(config: Record<string, unknown>, layers: ConfigReadResult["layers"] = null): RuntimeConfigSnapshot {
|
||||
return runtimeConfigSnapshotFromAppServerConfig({
|
||||
config: config as ConfigReadResult["config"],
|
||||
origins: {},
|
||||
|
|
|
|||
Loading…
Reference in a new issue