| author | |
| committer | |
| log | 3bcde91f8215ec24eeffdd4abdee8fcb8a5e94f9 |
| tree | ebf2617ec9c603e85aabbe1c9a8212836267efcf |
| parent | da1bebbb11701b58ebdde5ee019f3aeef3d42b27 |
| signature | Signed by PGP key B5690EEEBB952194 |
2 files changed, 0 insertions(+), 117 deletions(-)
packages/client-runtime/src/state/subagentRuntime.test.ts-62| ... | ... | @@ -5,10 +5,6 @@ import { |
| 5 | 5 | foldSubagentActivities, |
| 6 | 6 | formatSubagentModelLabel, |
| 7 | 7 | formatSubagentTokenCount, |
| 8 | isAgentAttributedToolActivity, | |
| 9 | isSubagentActivityKind, | |
| 10 | isTimelineBypassActivity, | |
| 11 | workflowCardMembers, | |
| 12 | 8 | } from "./subagentRuntime.ts"; |
| 13 | 9 | |
| 14 | 10 | let sequence = 0; |
| ... | ... | @@ -557,64 +553,6 @@ describe("deriveAgentPanelModel", () => { |
| 557 | 553 | }); |
| 558 | 554 | }); |
| 559 | 555 | |
| 560 | describe("workflowCardMembers", () => { | |
| 561 | it("orders by urgency (failed, running, waiting) and reports overflow", () => { | |
| 562 | const roster = fold([ | |
| 563 | activity("task.started", { taskId: "wf-1", taskType: "local_workflow" }), | |
| 564 | ...[..."abcdefghij"].map((letter, index) => | |
| 565 | activity("task.progress", { | |
| 566 | taskId: `wf-1:wf:${index}`, | |
| 567 | title: `agent-${letter}`, | |
| 568 | status: index === 3 ? "failed" : index < 3 ? "completed" : "running", | |
| 569 | ...(index === 3 ? { error: "died" } : {}), | |
| 570 | parentAgentId: "wf-1", | |
| 571 | agentIndex: index, | |
| 572 | phaseIndex: 0, | |
| 573 | phaseTitle: "Work", | |
| 574 | }), | |
| 575 | ), | |
| 576 | ]); | |
| 577 | const model = deriveAgentPanelModel({ agents: roster }); | |
| 578 | const { visible, overflow } = workflowCardMembers(model.workflows[0]!, 8); | |
| 579 | expect(visible).toHaveLength(8); | |
| 580 | expect(overflow).toBe(2); | |
| 581 | expect(visible[0]!.status).toBe("failed"); | |
| 582 | expect(visible.filter((agent) => agent.status === "completed").length).toBeLessThanOrEqual(2); | |
| 583 | }); | |
| 584 | }); | |
| 585 | ||
| 586 | describe("timeline predicates", () => { | |
| 587 | it("recognizes subagent activity kinds as fold input", () => { | |
| 588 | for (const kind of [ | |
| 589 | "task.started", | |
| 590 | "task.progress", | |
| 591 | "task.updated", | |
| 592 | "task.completed", | |
| 593 | "tool.progress", | |
| 594 | ]) { | |
| 595 | expect(isSubagentActivityKind(kind)).toBe(true); | |
| 596 | } | |
| 597 | expect(isSubagentActivityKind("tool.completed")).toBe(false); | |
| 598 | }); | |
| 599 | ||
| 600 | it("attributed tool rows are re-homed; unattributed rows stay in the timeline", () => { | |
| 601 | expect(isAgentAttributedToolActivity(activity("tool.completed", { agentId: "task-1" }))).toBe( | |
| 602 | true, | |
| 603 | ); | |
| 604 | expect(isAgentAttributedToolActivity(activity("tool.completed", {}))).toBe(false); | |
| 605 | expect(isAgentAttributedToolActivity(activity("tool.completed", { agentId: " " }))).toBe( | |
| 606 | false, | |
| 607 | ); | |
| 608 | }); | |
| 609 | ||
| 610 | it("timelineBypass rows never render in the parent chat", () => { | |
| 611 | expect(isTimelineBypassActivity(activity("task.progress", { timelineBypass: true }))).toBe( | |
| 612 | true, | |
| 613 | ); | |
| 614 | expect(isTimelineBypassActivity(activity("task.progress", {}))).toBe(false); | |
| 615 | }); | |
| 616 | }); | |
| 617 | ||
| 618 | 556 | describe("formatSubagentTokenCount", () => { |
| 619 | 557 | it("formats plain counters", () => { |
| 620 | 558 | expect(formatSubagentTokenCount(950)).toBe("950"); |
packages/client-runtime/src/state/subagentRuntime.ts-55| ... | ... | @@ -861,61 +861,6 @@ export function deriveAgentPanelModel({ |
| 861 | 861 | }; |
| 862 | 862 | } |
| 863 | 863 | |
| 864 | /** | |
| 865 | * Members ordered by urgency for the capped inline workflow card: running and | |
| 866 | * failed first, then waiting, then most recently updated. | |
| 867 | */ | |
| 868 | export function workflowCardMembers( | |
| 869 | group: AgentPanelWorkflowGroup, | |
| 870 | limit: number, | |
| 871 | ): { readonly visible: ReadonlyArray<RuntimeSubagent>; readonly overflow: number } { | |
| 872 | const all = [...group.phases.flatMap((phase) => phase.members), ...group.unphasedMembers]; | |
| 873 | const urgency = (agent: RuntimeSubagent): number => { | |
| 874 | if (agent.status === "failed") return 0; | |
| 875 | if (agent.status === "running") return 1; | |
| 876 | if (agent.status === "waiting") return 2; | |
| 877 | return 3; | |
| 878 | }; | |
| 879 | const ordered = all | |
| 880 | .slice() | |
| 881 | .sort((a, b) => urgency(a) - urgency(b) || b.updatedAt.localeCompare(a.updatedAt)); | |
| 882 | return { | |
| 883 | visible: ordered.slice(0, limit), | |
| 884 | overflow: Math.max(0, ordered.length - limit), | |
| 885 | }; | |
| 886 | } | |
| 887 | ||
| 888 | /** Kinds the timeline should not render as generic rows (fold input only). */ | |
| 889 | export function isSubagentActivityKind(kind: string): boolean { | |
| 890 | return ( | |
| 891 | kind === "task.started" || | |
| 892 | kind === "task.progress" || | |
| 893 | kind === "task.updated" || | |
| 894 | kind === "task.completed" || | |
| 895 | kind === "tool.progress" | |
| 896 | ); | |
| 897 | } | |
| 898 | ||
| 899 | /** | |
| 900 | * Quiet-timeline guarantee: tool rows attributed to an owning agent belong in | |
| 901 | * the Agents surface, not the parent chat. Unattributed rows must stay. | |
| 902 | */ | |
| 903 | export function isAgentAttributedToolActivity(activity: OrchestrationThreadActivity): boolean { | |
| 904 | if (typeof activity.payload !== "object" || activity.payload === null) { | |
| 905 | return false; | |
| 906 | } | |
| 907 | const payload = activity.payload as Record<string, unknown>; | |
| 908 | return typeof payload.agentId === "string" && payload.agentId.trim().length > 0; | |
| 909 | } | |
| 910 | ||
| 911 | /** Timeline-bypassing synthesized rows (Codex children, workflow members). */ | |
| 912 | export function isTimelineBypassActivity(activity: OrchestrationThreadActivity): boolean { | |
| 913 | if (typeof activity.payload !== "object" || activity.payload === null) { | |
| 914 | return false; | |
| 915 | } | |
| 916 | return (activity.payload as Record<string, unknown>).timelineBypass === true; | |
| 917 | } | |
| 918 | ||
| 919 | 864 | /** |
| 920 | 865 | * Compact model chip text: strips vendor prefixes/date-or-context suffixes |
| 921 | 866 | * ("claude-sonnet-5[1m]" → "sonnet-5[1m]", "claude-opus-4-20250514" → |