Unified · Split
| @@ -0,0 +1,33 @@ | ||
| 1 | +import { | |
| 2 | + EventId, | |
| 3 | + ProviderDriverKind, | |
| 4 | + RuntimeRequestId, | |
| 5 | + ThreadId, | |
| 6 | + type ProviderRuntimeEvent, | |
| 7 | +} from "@t3tools/contracts"; | |
| 8 | +import { describe, expect, it } from "vite-plus/test"; | |
| 9 | + | |
| 10 | +import { runtimeEventToActivities } from "./ProviderRuntimeIngestion.ts"; | |
| 11 | + | |
| 12 | +describe("runtimeEventToActivities approval details", () => { | |
| 13 | + it("preserves complete multiline command details", () => { | |
| 14 | + const detail = `bun run release -- ${"long-argument ".repeat(20)}\nsecond line`; | |
| 15 | + const event = { | |
| 16 | + type: "request.opened", | |
| 17 | + eventId: EventId.make("evt-request-opened"), | |
| 18 | + provider: ProviderDriverKind.make("codex"), | |
| 19 | + createdAt: "2026-07-18T00:00:00.000Z", | |
| 20 | + threadId: ThreadId.make("thread-1"), | |
| 21 | + requestId: RuntimeRequestId.make("approval-1"), | |
| 22 | + payload: { | |
| 23 | + requestType: "command_execution_approval", | |
| 24 | + detail, | |
| 25 | + }, | |
| 26 | + } satisfies ProviderRuntimeEvent; | |
| 27 | + | |
| 28 | + const [activity] = runtimeEventToActivities(event); | |
| 29 | + | |
| 30 | + expect(activity?.kind).toBe("approval.requested"); | |
| 31 | + expect((activity?.payload as Record<string, unknown> | undefined)?.detail).toBe(detail); | |
| 32 | + }); | |
| 33 | +}); | |
| @@ -306,7 +306,7 @@ function requestKindFromCanonicalRequestType( · 305 unmodified lines | ||
| 306 | 306 | } |
| 307 | 307 | } |
| 308 | 308 | |
| 309 | −function runtimeEventToActivities( | |
| 309 | +export function runtimeEventToActivities( | |
| 310 | 310 | event: ProviderRuntimeEvent, |
| 311 | 311 | taskTitle?: string, |
| 312 | 312 | ): ReadonlyArray<OrchestrationThreadActivity> { |
| @@ -340,7 +340,7 @@ function runtimeEventToActivities( · 27 unmodified lines | ||
| 340 | 340 | requestId: toApprovalRequestId(event.requestId), |
| 341 | 341 | ...(requestKind ? { requestKind } : {}), |
| 342 | 342 | requestType: event.payload.requestType, |
| 343 | − ...(event.payload.detail ? { detail: truncateDetail(event.payload.detail) } : {}), | |
| 343 | + ...(event.payload.detail ? { detail: event.payload.detail } : {}), | |
| 344 | 344 | }, |
| 345 | 345 | turnId: toTurnId(event.turnId) ?? null, |
| 346 | 346 | ...maybeSequence, |
| @@ -0,0 +1,28 @@ | ||
| 1 | +import { ApprovalRequestId } from "@t3tools/contracts"; | |
| 2 | +import { renderToStaticMarkup } from "react-dom/server"; | |
| 3 | +import { describe, expect, it } from "vite-plus/test"; | |
| 4 | + | |
| 5 | +import { ComposerPendingApprovalPanel } from "./ComposerPendingApprovalPanel"; | |
| 6 | + | |
| 7 | +describe("ComposerPendingApprovalPanel", () => { | |
| 8 | + it("renders complete multiline command details without hover or truncation", () => { | |
| 9 | + const detail = `bun run release -- ${"long-argument ".repeat(20)}\nsecond line`; | |
| 10 | + const markup = renderToStaticMarkup( | |
| 11 | + <ComposerPendingApprovalPanel | |
| 12 | + approval={{ | |
| 13 | + requestId: ApprovalRequestId.make("approval-1"), | |
| 14 | + requestKind: "command", | |
| 15 | + createdAt: "2026-07-18T00:00:00.000Z", | |
| 16 | + detail, | |
| 17 | + }} | |
| 18 | + pendingCount={1} | |
| 19 | + />, | |
| 20 | + ); | |
| 21 | + | |
| 22 | + expect(markup).toContain('data-approval-detail="complete"'); | |
| 23 | + expect(markup).toContain('aria-label="Command"'); | |
| 24 | + expect(markup).toContain(detail); | |
| 25 | + expect(markup).not.toContain("truncate"); | |
| 26 | + expect(markup).not.toContain("line-clamp"); | |
| 27 | + }); | |
| 28 | +}); | |
| @@ -16,6 +16,12 @@ export const ComposerPendingApprovalPanel = memo(function ComposerPendingApprova · 15 unmodified lines | ||
| 16 | 16 | : approval.requestKind === "file-read" |
| 17 | 17 | ? "File-read approval requested" |
| 18 | 18 | : "File-change approval requested"; |
| 19 | + const detailLabel = | |
| 20 | + approval.requestKind === "command" | |
| 21 | + ? "Command" | |
| 22 | + : approval.requestKind === "file-read" | |
| 23 | + ? "File to read" | |
| 24 | + : "File change"; | |
| 19 | 25 | |
| 20 | 26 | return ( |
| 21 | 27 | <div className="px-4 py-3.5 sm:px-5 sm:py-4"> |
| @@ -26,6 +32,18 @@ export const ComposerPendingApprovalPanel = memo(function ComposerPendingApprova · 4 unmodified lines | ||
| 26 | 32 | <span className="text-xs text-muted-foreground">1/{pendingCount}</span> |
| 27 | 33 | ) : null} |
| 28 | 34 | </div> |
| 35 | + {approval.detail ? ( | |
| 36 | + <div className="mt-3 rounded-lg border border-border/65 bg-background/70 p-3"> | |
| 37 | + <p className="text-xs font-medium text-muted-foreground">{detailLabel}</p> | |
| 38 | + <pre | |
| 39 | + aria-label={detailLabel} | |
| 40 | + className="mt-2 max-h-40 overflow-auto whitespace-pre-wrap break-words font-mono text-xs leading-relaxed text-foreground" | |
| 41 | + data-approval-detail="complete" | |
| 42 | + > | |
| 43 | + {approval.detail} | |
| 44 | + </pre> | |
| 45 | + </div> | |
| 46 | + ) : null} | |
| 29 | 47 | </div> |
| 30 | 48 | ); |
| 31 | 49 | }); |