89332e1cbb
* fix(backend): start agent immediately for passthrough quick-chat sessions Passthrough profiles need a running PTY for the terminal to attach. When launchPrepare is called with a passthrough profile, upgrade the request to launchStart so the PTY exists by the time the frontend renders the terminal. * fix(web): render passthrough terminal in quick chat for passthrough profiles Quick chat used MessageList + ChatInputArea unconditionally, so passthrough profiles showed an empty chat with no agent output (PTY never attached). Render PassthroughTerminal when the active session is passthrough, mirroring the task-page branch. Passthrough is detected via session.is_passthrough or, before WS hydrates, via the agent profile's cli_passthrough flag — looked up through a new agentProfileId stored on quickChat session entries (set by the picker on selection and seeded by SSR for restored sessions). * style(web): apply prettier formatting * fix(web): address review feedback on quick-chat passthrough rendering - Narrow `metadata.agent_profile_id` to string at runtime via a `readAgentProfileId` helper instead of a blind type cast. - Collapse `useIsQuickChatPassthrough` into a single `useShallow` selector to avoid three separate store subscriptions. * fix(backend): prevent launchPrepare ↔ launchStart recursion for passthrough When passthrough quick-chat sessions hit a workflow step that blocks auto-start, launchStart's downgrade to launchPrepare would have re-upgraded back into launchStart and looped forever. Skip the passthrough upgrade when AutoStart=true, since that flag uniquely identifies the downgrade path. Adds tests for `isPassthroughProfile` and a regression test that runs the launchStart→launchPrepare path under a deadline so future recursions surface. * test(web): cover existing agentProfileId preservation on reopen * style(web): apply prettier formatting * test(backend): seed task with blocked workflow step in recursion regression Without persisted task and step, shouldBlockAutoStart short-circuited to false, the test bypassed the launchStart→launchPrepare downgrade entirely, and removing the recursion guard would still pass. Seed the task with a WorkflowStepID mapping to a step lacking auto_start_agent so the guard is the only thing preventing the bounce. Verified by temporarily removing the guard — the test now times out as expected. * test(backend): cover ResolveAgentProfile error path in isPassthroughProfile
51 lines
2.0 KiB
TypeScript
51 lines
2.0 KiB
TypeScript
import { describe, it, expect } from "vitest";
|
|
import { create } from "zustand";
|
|
import { immer } from "zustand/middleware/immer";
|
|
import { createUISlice } from "@/lib/state/slices/ui/ui-slice";
|
|
import type { UISlice } from "@/lib/state/slices/ui/types";
|
|
|
|
const SESSION_ID = "sess-1";
|
|
const WORKSPACE_ID = "ws-1";
|
|
const PROFILE_ID = "profile-pass";
|
|
|
|
function makeStore() {
|
|
return create<UISlice>()(immer(createUISlice));
|
|
}
|
|
|
|
function findSession(store: ReturnType<typeof makeStore>) {
|
|
return store.getState().quickChat.sessions.find((s) => s.sessionId === SESSION_ID);
|
|
}
|
|
|
|
describe("openQuickChat agentProfileId persistence", () => {
|
|
it("stores agentProfileId on a new session entry", () => {
|
|
const store = makeStore();
|
|
store.getState().openQuickChat(SESSION_ID, WORKSPACE_ID, PROFILE_ID);
|
|
expect(findSession(store)?.agentProfileId).toBe(PROFILE_ID);
|
|
expect(store.getState().quickChat.activeSessionId).toBe(SESSION_ID);
|
|
});
|
|
|
|
it("backfills agentProfileId on an existing session entry without one", () => {
|
|
const store = makeStore();
|
|
store.getState().openQuickChat(SESSION_ID, WORKSPACE_ID);
|
|
expect(findSession(store)?.agentProfileId).toBeUndefined();
|
|
store.getState().openQuickChat(SESSION_ID, WORKSPACE_ID, PROFILE_ID);
|
|
expect(findSession(store)?.agentProfileId).toBe(PROFILE_ID);
|
|
});
|
|
|
|
it("does not duplicate sessions when reopening with the same id", () => {
|
|
const store = makeStore();
|
|
store.getState().openQuickChat(SESSION_ID, WORKSPACE_ID, "profile-a");
|
|
store.getState().openQuickChat(SESSION_ID, WORKSPACE_ID, "profile-a");
|
|
expect(
|
|
store.getState().quickChat.sessions.filter((s) => s.sessionId === SESSION_ID),
|
|
).toHaveLength(1);
|
|
});
|
|
|
|
it("keeps existing agentProfileId when reopened without one", () => {
|
|
const store = makeStore();
|
|
store.getState().openQuickChat(SESSION_ID, WORKSPACE_ID, PROFILE_ID);
|
|
store.getState().openQuickChat(SESSION_ID, WORKSPACE_ID); // no profile
|
|
expect(findSession(store)?.agentProfileId).toBe(PROFILE_ID);
|
|
});
|
|
});
|