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