mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-21 08:22:20 +02:00
## Thinking Path > - Paperclip helps people manage AI agents and inspect their work. > - Task threads show live activity and saved transcripts from several runners. > - Legacy runs used a separate activity renderer that expanded tool cards as commands arrived. > - A compact live row makes ongoing work easier to follow. > - This PR shares the native runner activity group across live and saved legacy turns. > - Both paths now use the same labels, icon gutter, animation, and history controls. ## Linked Issues or Issue Description Related: Refs #13255, which introduced rolling native-runner activity groups. **What happened?** During a legacy CLI run, each new command added an expanded tool card under an activity heading. The original legacy parity story rendered a completed turn, so it did not exercise this live path. **Expected behavior** Show one current activity line per commentary group. Roll that line forward when a new activity starts. Keep tool icons aligned on the left, use friendly labels, and show history only when expanded. **Steps to reproduce** 1. Start a task with the Codex local adapter using the CLI engine. 2. Ask it to read two files in separate tool calls and run a test command. 3. Watch the activity feed while the run is active, then expand its history. **Paperclip version or commit** The live behavior was reproduced on bdee5ebb21b2d09280e49c88bc329435da1b07c8. This update includes both live and saved rendering fixes. **Deployment mode** Built from source with the local test-drive command and a real Codex CLI agent. ## What Changed - Use `TaskChatRunnerActivityGroup` for both saved legacy phases and `TaskChatLiveTail`. - Align the legacy Working spinner with the shared activity icon gutter. - Cover streamed reasoning updates, successive commands, image labels, hidden details, and explicit history expansion in tests. - Feed raw legacy transcript events through the real adapter and live renderer in Storybook. Add live, expanded, narrow, light, and completed stories with the final reply preserved. ## Verification - Passed 195 targeted tests covering the live tail, status pill, shared activity group, native turn, and full task thread. - Passed UI typechecking, token gates, UI build, and Storybook build before submission. - Observed a real Codex CLI run while it read separate files and ran tests. The current activity stayed at one 32-pixel row without an accumulated tool list; the spinner and activity icon centers aligned. - Compared native and legacy Storybooks: identical rolling animation, fixed height, persistent expansion, truncated long labels, and correct light and completed states. - Full repository `pnpm -r typecheck` and `pnpm build` passed after replaying the branch on current master, including the Rust runner. The full `pnpm test:run` suite is still running. ## Risks - Legacy activity now starts collapsed. Users can expand each group and each row to inspect the same transcript details. - Live runtime request cards must retain their timeline positions. Existing task-thread and native-runner regression tests cover this boundary. - No API, database, or transcript format changes. ## Model Used - OpenAI GPT-6 through Codex for the live-path fix, tests, and browser verification. Capabilities used: reasoning, tool use, local code execution, and image inspection. The exact deployment ID and context-window size are not exposed in this session. - The original saved-turn change recorded OpenAI Codex with GPT-5.6; its exact variant and context size were not recorded. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [ ] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing>
108 lines
3.8 KiB
TypeScript
108 lines
3.8 KiB
TypeScript
import type { ReactElement } from "react";
|
|
import { MarkdownBody } from "@/components/MarkdownBody";
|
|
import type { TaskChatItem, TaskChatRuntimeRequestDecision, TaskChatRuntimeRequestItem } from "./task-chat-model";
|
|
import { TaskChatToolCard } from "./TaskChatToolCard";
|
|
import { TaskChatUsageReadout } from "./TaskChatUsageReadout";
|
|
import { TaskChatRunnerActivityGroup } from "./TaskChatRunnerActivityGroup";
|
|
import { TaskChatThinking } from "./TaskChatThinking";
|
|
import { TaskChatProtocolCard } from "./TaskChatProtocolCard";
|
|
import { buildTurnTimelineRows } from "./transcript-adapter";
|
|
|
|
/**
|
|
* Live body for legacy runner transcripts. The adapter drops debug plumbing;
|
|
* the same activity group as the native runner shows one rolling current row
|
|
* with friendly tool labels and explicitly expandable history. The status pill
|
|
* above this body (`TaskChatLiveRunPill`) owns the run-status affordance.
|
|
*
|
|
* Stable assistant and runtime-request boundaries compact the rows into an
|
|
* ordered turn timeline. Commentary remains readable above the activity group
|
|
* it introduced, and resolved request receipts keep their original slot.
|
|
*/
|
|
export function TaskChatLiveTail({
|
|
items,
|
|
emptyMessage,
|
|
excludeFinal = false,
|
|
onRuntimeRequestDecision,
|
|
}: {
|
|
items: readonly TaskChatItem[];
|
|
/** Shown when nothing renderable has streamed yet (queued / pre-first-token). */
|
|
emptyMessage?: string;
|
|
/** New-runner turn renders final-answer messages in its dedicated response slot. */
|
|
excludeFinal?: boolean;
|
|
onRuntimeRequestDecision?: (
|
|
item: TaskChatRuntimeRequestItem,
|
|
decision: TaskChatRuntimeRequestDecision,
|
|
) => void | Promise<void>;
|
|
}) {
|
|
const visibleItems = excludeFinal
|
|
? items.filter((item) => item.kind !== "message" || item.interstitial)
|
|
: items;
|
|
const rows = buildTurnTimelineRows(visibleItems, true)
|
|
.map((item) => renderTailRow(item, onRuntimeRequestDecision))
|
|
.filter((row): row is ReactElement => row != null);
|
|
|
|
if (rows.length === 0) {
|
|
return emptyMessage ? (
|
|
<div className="px-1 py-1 text-xs text-muted-foreground/70">{emptyMessage}</div>
|
|
) : null;
|
|
}
|
|
|
|
return <div className="flex flex-col gap-2">{rows}</div>;
|
|
}
|
|
|
|
function renderTailRow(
|
|
item: TaskChatItem,
|
|
onRuntimeRequestDecision?: (
|
|
item: TaskChatRuntimeRequestItem,
|
|
decision: TaskChatRuntimeRequestDecision,
|
|
) => void | Promise<void>,
|
|
): ReactElement | null {
|
|
switch (item.kind) {
|
|
case "message": {
|
|
// Streamed reply text (always interstitial from the transcript adapter).
|
|
// Rendered as plain markdown — the settled thread later replaces it with
|
|
// the posted comment bubble, so no author header/bubble chrome here.
|
|
const text = item.text.trim();
|
|
if (!text) return null;
|
|
return (
|
|
<div
|
|
key={item.id}
|
|
className="px-1 text-sm text-foreground/90"
|
|
data-testid="task-chat-live-text"
|
|
>
|
|
<MarkdownBody softBreaks linkIssueReferences>
|
|
{item.text}
|
|
</MarkdownBody>
|
|
</div>
|
|
);
|
|
}
|
|
case "tool":
|
|
return (
|
|
<div key={item.id}>
|
|
<TaskChatToolCard item={item} />
|
|
</div>
|
|
);
|
|
case "usage":
|
|
return (
|
|
<div key={item.id}>
|
|
<TaskChatUsageReadout item={item} />
|
|
</div>
|
|
);
|
|
case "thinking":
|
|
return <TaskChatThinking key={item.id} item={item} />;
|
|
case "activity_phase":
|
|
return (
|
|
<TaskChatRunnerActivityGroup
|
|
key={item.id}
|
|
item={item}
|
|
/>
|
|
);
|
|
case "protocol":
|
|
return <TaskChatProtocolCard key={item.id} item={item} onRuntimeRequestDecision={onRuntimeRequestDecision} />;
|
|
// Markers, interactions, briefs, statuses, turns, and dropped debug kinds
|
|
// cannot appear as direct live-tail rows.
|
|
default:
|
|
return null;
|
|
}
|
|
}
|