History

[codex] show complete approval details (#4111)

Maxwell Young committed 3 months ago

Commit a135f2c1 parent 946b8674 files · +81 −2Browse files

Showing the changes to apps (4 of 4 files). Show all

Unified · Split

4 files
  1. apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.approval.test.ts +33 −0
  2. apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts +2 −2
  3. apps/web/src/components/chat/ComposerPendingApprovalPanel.test.tsx +28 −0
  4. apps/web/src/components/chat/ComposerPendingApprovalPanel.tsx +18 −0
apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.approval.test.ts+33−0
@@ -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+});
apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts+2−2
@@ -306,7 +306,7 @@ function requestKindFromCanonicalRequestType( · 305 unmodified lines
306306 }
307307 }
308308
309−function runtimeEventToActivities(
309+export function runtimeEventToActivities(
310310 event: ProviderRuntimeEvent,
311311 taskTitle?: string,
312312 ): ReadonlyArray<OrchestrationThreadActivity> {
@@ -340,7 +340,7 @@ function runtimeEventToActivities( · 27 unmodified lines
340340 requestId: toApprovalRequestId(event.requestId),
341341 ...(requestKind ? { requestKind } : {}),
342342 requestType: event.payload.requestType,
343− ...(event.payload.detail ? { detail: truncateDetail(event.payload.detail) } : {}),
343+ ...(event.payload.detail ? { detail: event.payload.detail } : {}),
344344 },
345345 turnId: toTurnId(event.turnId) ?? null,
346346 ...maybeSequence,
apps/web/src/components/chat/ComposerPendingApprovalPanel.test.tsx+28−0
@@ -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+});
apps/web/src/components/chat/ComposerPendingApprovalPanel.tsx+18−0
@@ -16,6 +16,12 @@ export const ComposerPendingApprovalPanel = memo(function ComposerPendingApprova · 15 unmodified lines
1616 : approval.requestKind === "file-read"
1717 ? "File-read approval requested"
1818 : "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";
1925
2026 return (
2127 <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
2632 <span className="text-xs text-muted-foreground">1/{pendingCount}</span>
2733 ) : null}
2834 </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}
2947 </div>
3048 );
3149 });