feat: support explicit live demo launch
This commit is contained in:
@@ -109,6 +109,37 @@ describe("useTimelineAgent", () => {
|
|||||||
expect(start).toHaveBeenCalledWith("replay");
|
expect(start).toHaveBeenCalledWith("replay");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("starts live explicitly from a direct replay view when the target is ready", async () => {
|
||||||
|
const start = vi.fn();
|
||||||
|
const demo = demoController({
|
||||||
|
state: { ...initialDemoTimelineState, mode: "replay" },
|
||||||
|
start,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { result } = renderHook(() => useTimelineAgent(demo, {
|
||||||
|
mode: "live",
|
||||||
|
status: replayStatus,
|
||||||
|
liveTargetReady: true,
|
||||||
|
}));
|
||||||
|
|
||||||
|
expect(result.current.canRunLive).toBe(true);
|
||||||
|
await act(async () => result.current.runPreparedWorkflow("live"));
|
||||||
|
|
||||||
|
expect(start).toHaveBeenCalledWith("live");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not offer an explicit live launch when health has failed", () => {
|
||||||
|
const start = vi.fn();
|
||||||
|
const demo = demoController({ start });
|
||||||
|
const { result } = renderHook(() => useTimelineAgent(demo, {
|
||||||
|
mode: "live",
|
||||||
|
status: failedStatus,
|
||||||
|
liveTargetReady: false,
|
||||||
|
}));
|
||||||
|
|
||||||
|
expect(result.current.canRunLive).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
it("submits selected issues from the current interrupt payload", async () => {
|
it("submits selected issues from the current interrupt payload", async () => {
|
||||||
const submitSelectedIssues = vi.fn(async () => {});
|
const submitSelectedIssues = vi.fn(async () => {});
|
||||||
const demo = demoController({
|
const demo = demoController({
|
||||||
|
|||||||
@@ -14,13 +14,15 @@ export type TimelineAgentMode = "live" | "replay";
|
|||||||
export type TimelineAgentOptions = {
|
export type TimelineAgentOptions = {
|
||||||
readonly mode: TimelineAgentMode;
|
readonly mode: TimelineAgentMode;
|
||||||
readonly status: PresentationTargetHealth;
|
readonly status: PresentationTargetHealth;
|
||||||
|
readonly liveTargetReady?: boolean | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TimelineAgentController = {
|
export type TimelineAgentController = {
|
||||||
readonly messages: ReadonlyArray<AgentMessage>;
|
readonly messages: ReadonlyArray<AgentMessage>;
|
||||||
readonly canRun: boolean;
|
readonly canRun: boolean;
|
||||||
|
readonly canRunLive: boolean;
|
||||||
readonly runLabel: string;
|
readonly runLabel: string;
|
||||||
readonly runPreparedWorkflow: () => Promise<void>;
|
readonly runPreparedWorkflow: (mode?: TimelineAgentMode) => Promise<void>;
|
||||||
readonly submitSelectedIssues: () => Promise<void>;
|
readonly submitSelectedIssues: () => Promise<void>;
|
||||||
readonly requestRevision: () => Promise<void>;
|
readonly requestRevision: () => Promise<void>;
|
||||||
};
|
};
|
||||||
@@ -89,18 +91,23 @@ export const useTimelineAgent = (
|
|||||||
|
|
||||||
const runLabel = modeLabel === "live" ? "Run prepared workflow" : "Run replay walkthrough";
|
const runLabel = modeLabel === "live" ? "Run prepared workflow" : "Run replay walkthrough";
|
||||||
const canRun = demo.canStart && !demo.inFlight && demo.state.phase !== "running";
|
const canRun = demo.canStart && !demo.inFlight && demo.state.phase !== "running";
|
||||||
|
const canRunLive = (options.liveTargetReady ?? (options.mode === "live" && (
|
||||||
|
options.status.kind === "ready" || options.status.kind === "active"
|
||||||
|
))) && !demo.inFlight && demo.state.phase !== "running";
|
||||||
|
|
||||||
const runPreparedWorkflow = useCallback(async () => {
|
const runPreparedWorkflow = useCallback(async (requestedMode?: TimelineAgentMode) => {
|
||||||
|
const mode = requestedMode ?? modeLabel;
|
||||||
|
if (mode === "live" && !canRunLive) return;
|
||||||
if (!demo.canStart || demo.inFlight || demo.state.phase === "running") return;
|
if (!demo.canStart || demo.inFlight || demo.state.phase === "running") return;
|
||||||
demo.start(modeLabel);
|
demo.start(mode);
|
||||||
setMessages((current) => appendToolMessage(
|
setMessages((current) => appendToolMessage(
|
||||||
current,
|
current,
|
||||||
"timeline-agent-start",
|
"timeline-agent-start",
|
||||||
"startRun",
|
"startRun",
|
||||||
{ mode: modeLabel },
|
{ mode },
|
||||||
{ phase: "started" },
|
{ phase: "started" },
|
||||||
));
|
));
|
||||||
}, [demo, modeLabel]);
|
}, [canRunLive, demo, modeLabel]);
|
||||||
|
|
||||||
const selectedIssueIds = useMemo(
|
const selectedIssueIds = useMemo(
|
||||||
() => demo.interruptPayload?.proposed_issues.map((issue) => issue.id) ?? [],
|
() => demo.interruptPayload?.proposed_issues.map((issue) => issue.id) ?? [],
|
||||||
@@ -135,6 +142,7 @@ export const useTimelineAgent = (
|
|||||||
return {
|
return {
|
||||||
messages,
|
messages,
|
||||||
canRun,
|
canRun,
|
||||||
|
canRunLive,
|
||||||
runLabel,
|
runLabel,
|
||||||
runPreparedWorkflow,
|
runPreparedWorkflow,
|
||||||
submitSelectedIssues,
|
submitSelectedIssues,
|
||||||
|
|||||||
@@ -191,6 +191,7 @@ describe("OperatorChat", () => {
|
|||||||
timelineAgent={{
|
timelineAgent={{
|
||||||
messages: [],
|
messages: [],
|
||||||
canRun: true,
|
canRun: true,
|
||||||
|
canRunLive: true,
|
||||||
runLabel: "Run prepared workflow",
|
runLabel: "Run prepared workflow",
|
||||||
runPreparedWorkflow,
|
runPreparedWorkflow,
|
||||||
submitSelectedIssues: vi.fn(async () => {}),
|
submitSelectedIssues: vi.fn(async () => {}),
|
||||||
@@ -236,6 +237,7 @@ describe("OperatorChat", () => {
|
|||||||
timelineAgent={{
|
timelineAgent={{
|
||||||
messages: [],
|
messages: [],
|
||||||
canRun: false,
|
canRun: false,
|
||||||
|
canRunLive: false,
|
||||||
runLabel: "Run prepared workflow",
|
runLabel: "Run prepared workflow",
|
||||||
runPreparedWorkflow: vi.fn(async () => {}),
|
runPreparedWorkflow: vi.fn(async () => {}),
|
||||||
submitSelectedIssues,
|
submitSelectedIssues,
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ export const PresentationRoute = () => {
|
|||||||
const timelineAgent = useTimelineAgent(demo, {
|
const timelineAgent = useTimelineAgent(demo, {
|
||||||
mode: presentationTarget.mode === "live" ? "live" : "replay",
|
mode: presentationTarget.mode === "live" ? "live" : "replay",
|
||||||
status: targetStatus,
|
status: targetStatus,
|
||||||
|
liveTargetReady: targetStatusController.liveTargetReady,
|
||||||
});
|
});
|
||||||
const requiredDemoStage = state.location.kind === "main"
|
const requiredDemoStage = state.location.kind === "main"
|
||||||
? requirementForDemoBeat(state.location.sceneId, state.location.beatId).requiredStage
|
? requirementForDemoBeat(state.location.sceneId, state.location.beatId).requiredStage
|
||||||
|
|||||||
Reference in New Issue
Block a user