# Files changed · PR \#21643 · pingdotgg/t3code

[View on GitCafe](https://git.cafe/pingdotgg/t3code/pull/21643/diffs)

Repository: [pingdotgg/t3code](https://git.cafe/pingdotgg/t3code)

Visibility: public

[feat\(clients\): display optional subagent context snapshots](https://git.cafe/pingdotgg/t3code/pull/21643.md)

Version: 5

Head commit: 90df6f1425d462c234423abeab9b51783340ed0e

Base commit: 1451e7cb23034d29774338608368a15d8845df36

Comparison base: 1451e7cb23034d29774338608368a15d8845df36

Showing up to 20 entries from this revision. If the pull request changes, reopen the first page before continuing.

## apps/mobile/src/features/threads/SubagentRow.tsx

Status: modified; +41; −32

```diff
@@ -1,6 +1,10 @@
 import { scopeProjectRef, scopeThreadRef } from "@t3tools/client-runtime/environment";
 import { resolveProviderInstanceDisplayName } from "@t3tools/client-runtime/state/provider-instance-display";
+import {
+  formatSubagentUsage,
+  resolveSubagentContextUsage,
-import { resolveSubagentMetadata } from "@t3tools/client-runtime/state/subagent-display";
+  resolveSubagentMetadata,
+} from "@t3tools/client-runtime/state/subagent-display";
 import type { EnvironmentId, OrchestrationV2Subagent } from "@t3tools/contracts";
 import type { ReactNode } from "react";
 import { View } from "react-native";
@@ -26,6 +30,7 @@
   | "status"
   | "progress"
   | "result"
+  | "usage"
 >;
 
 export function SubagentRow(props: {
@@ -112,38 +117,42 @@
     parentProject,
     childProject,
   });
+  const usage = formatSubagentUsage(resolveSubagentContextUsage(subagent, child));
   return (
+    <View className="gap-1">
+      <View className="min-w-0 flex-row items-center gap-1.5">
+        <ProviderIcon
+          provider={provider?.driver ?? subagent.driver}
+          iconUrl={provider?.iconUrl}
+          size={12}
+        />
+        <Text className="min-w-0 shrink text-xs text-foreground-muted" numberOfLines={1}>
+          {provider ? `${resolveProviderInstanceDisplayName(provider)} · ` : ""}
+          {modelLabel}
+        </Text>
+        {workspace.map(({ label, value }) => (
+          // The row reads this label in place of the icon. collapsable keeps the
+          // view (and label) from being flattened away without making it a
+          // separate accessibility stop.
+          <View
+            key={label}
+            collapsable={false}
+            accessibilityLabel={`${label}: ${value}`}
+            className="min-w-0 shrink flex-row items-center gap-1.5"
+          >
+            <Text className="text-xs text-foreground-muted">·</Text>
+            <SymbolView
+              name={label === "Branch" ? "arrow.triangle.branch" : "folder"}
+              size={11}
+              tintColorClassName="accent-icon-muted"
+            />
+            <Text className="min-w-0 shrink text-xs text-foreground-muted" numberOfLines={1}>
+              {value}
-    <View className="min-w-0 flex-row items-center gap-1.5">
-      <ProviderIcon
-        provider={provider?.driver ?? subagent.driver}
-        iconUrl={provider?.iconUrl}
-        size={12}
-      />
-      <Text className="min-w-0 shrink text-xs text-foreground-muted" numberOfLines={1}>
-        {provider ? `${resolveProviderInstanceDisplayName(provider)} · ` : ""}
-        {modelLabel}
-      </Text>
-      {workspace.map(({ label, value }) => (
-        // The row reads this label in place of the icon. collapsable keeps the
-        // view (and label) from being flattened away without making it a
-        // separate accessibility stop.
-        <View
-          key={label}
-          collapsable={false}
-          accessibilityLabel={`${label}: ${value}`}
-          className="min-w-0 shrink flex-row items-center gap-1.5"
-        >
-          <Text className="text-xs text-foreground-muted">·</Text>
-          <SymbolView
-            name={label === "Branch" ? "arrow.triangle.branch" : "folder"}
-            size={11}
-            tintColorClassName="accent-icon-muted"
-          />
-          <Text className="min-w-0 shrink text-xs text-foreground-muted" numberOfLines={1}>
+            </Text>
+          </View>
+        ))}
+      </View>
+      {usage ? <Text className="text-xs tabular-nums text-foreground-muted">{usage}</Text> : null}
-            {value}
-          </Text>
-        </View>
-      ))}
     </View>
   );
 }
```

## apps/mobile/src/features/threads/thread-subagent-group.tsx

Status: modified; +1; −0

```diff
@@ -89,6 +89,7 @@
       result: live?.result ?? item.result,
       progress: live?.progress ?? item.progress,
       model: live?.model ?? null,
+      usage: live?.usage,
     };
   });
   const grouped = agents.length > 1;
```

## apps/web/src/components/chat/SubagentTooltipContent.tsx

Status: modified; +10; −0

```diff
@@ -8,6 +8,7 @@
   OrchestrationV2Subagent,
 } from "@t3tools/contracts";
 import {
+  formatSubagentUsage,
   resolveSubagentMetadata,
   subagentDetailPreview,
 } from "@t3tools/client-runtime/state/subagent-display";
@@ -20,6 +21,7 @@
   CircleDashedIcon,
   CircleXIcon,
   FolderIcon,
+  GaugeIcon,
   GitBranchIcon,
   TerminalIcon,
 } from "lucide-react";
@@ -49,9 +51,11 @@
   status: OrchestrationV2TurnItemStatus;
   result?: string | null | undefined;
   progress?: string | null | undefined;
+  usage?: OrchestrationV2Subagent["usage"] | null;
 }) {
   const { modelLabel, workspace: metadata } = resolveSubagentMetadata(props);
   const preview = subagentDetailPreview(props);
+  const usage = formatSubagentUsage(props.usage ?? null);
   const model = props.model?.trim();
   const modelSlug = props.provider
     ? resolveSelectableModel(props.provider.driver, model, props.provider.models)
@@ -178,6 +182,12 @@
           </div>
         );
       })}
+      {usage ? (
+        <div className="flex min-w-0 items-center gap-2">
+          <GaugeIcon aria-hidden className="size-3 shrink-0" />
+          <span className="tabular-nums">{usage}</span>
+        </div>
+      ) : null}
       {preview ? (
         <div className="flex min-w-0 items-center gap-2">
           <TerminalIcon aria-hidden className="size-3 shrink-0" />
```

## apps/web/src/components/chat/ThreadRelationshipsControl.agents.browser.test.tsx

Status: modified; +70; −0

```diff
@@ -832,6 +832,76 @@
   expect(text()).not.toContain("Worktree");
   expect(text()).not.toContain("Workspace");
 
+  expect(text()).not.toContain("context");
+  expect(text()).not.toContain("tool uses");
+  const contextAgent = {
+    ...projection.subagents[0],
+    origin: "provider_native",
+    driver: "claudeAgent",
+    providerInstanceId: "claude",
+    status: "running",
+    usage: { contextTokens: 41_100, toolUses: 2 },
+  };
+  state.projection = { ...projection, subagents: [contextAgent] };
+  await update();
+  expect(text()).toContain("41.1K context · 2 tool uses");
+
+  state.projection = {
+    ...projection,
+    subagents: [{ ...contextAgent, status: "completed" }],
+  };
+  state.shells = [{ environmentId: "test", source: { ...child, activityRunStatus: "running" } }];
+  await update();
+  expect(text()).not.toContain("context");
+  expect(text()).not.toContain("tool uses");
+
+  state.projection = { ...projection, subagents: [contextAgent] };
+  await update();
+  expect(text()).toContain("41.1K context · 2 tool uses");
+  state.projection = {
+    ...projection,
+    subagents: [{ ...contextAgent, usage: { contextTokens: 12_000, toolUses: 3 } }],
+  };
+  await update();
+  expect(text()).toContain("12K context · 3 tool uses");
+  expect(text()).not.toContain("41.1K context");
+
+  state.projection = {
+    ...projection,
+    subagents: [
+      {
+        ...contextAgent,
+        status: "completed",
+        completedAt: DateTime.makeUnsafe("2026-09-16T12:01:00Z"),
+      },
+    ],
+  };
+  const completedChild = {
+    ...child,
+    status: "completed",
+    activityRunStatus: null,
+    latestRunRequestedAt: DateTime.makeUnsafe("2026-09-16T12:00:00Z"),
+    latestRunCompletedAt: DateTime.makeUnsafe("2026-09-16T12:01:00Z"),
+  };
+  await rerender(completedChild);
+  await click(sectionToggle(false));
+  expect(text()).toContain("41.1K context · 2 tool uses");
+  const newerCompletedChild = {
+    ...completedChild,
+    latestRunRequestedAt: DateTime.makeUnsafe("2026-09-16T12:02:00Z"),
+    latestRunCompletedAt: DateTime.makeUnsafe("2026-09-16T12:03:00Z"),
+  };
+  await rerender(newerCompletedChild);
+  expect(text()).toContain("Worker");
+  expect(text()).not.toContain("context");
+  expect(text()).not.toContain("tool uses");
+  await click(sectionToggle(true));
+
+  state.projection = projection;
+  await rerender();
+  expect(text()).not.toContain("context");
+  expect(text()).not.toContain("tool uses");
+
   for (const [model, expected] of [
     [null, "Not reported"],
     ["", "Not reported"],
```

## apps/web/src/components/chat/ThreadRelationshipsControl.tsx

Status: modified; +9; −1

```diff
@@ -11,7 +11,10 @@
   projectedSubagentsToRuntime,
   type RuntimeSubagent,
 } from "@t3tools/client-runtime/state/subagentRuntime";
+import {
+  formatSubagentDisplayTitle,
+  resolveSubagentContextUsage,
-import { formatSubagentDisplayTitle } from "@t3tools/client-runtime/state/subagent-display";
+} from "@t3tools/client-runtime/state/subagent-display";
 import {
   deriveThreadRelationshipGraph,
   immediateThreadRelationships,
@@ -198,6 +201,7 @@
           : status,
     startedAt: startedAt ? DateTime.formatIso(startedAt) : null,
     completedAt: completedAt ? DateTime.formatIso(completedAt) : null,
+    contextUsage: newerRun ? null : agent.contextUsage,
     progress: null,
     result: null,
     error: null,
@@ -487,6 +491,10 @@
                   status={agent.status}
                   result={agent.result}
                   progress={agent.progress}
+                  usage={resolveSubagentContextUsage(
+                    { status: agent.nativeStatus, usage: agent.contextUsage },
+                    node?.thread,
+                  )}
                   parentThread={currentThread ?? undefined}
                   childThread={node?.thread ?? undefined}
                   parentProject={currentProject}
```

## apps/web/src/components/chat/V2LifecycleRow.tsx

Status: modified; +9; −1

```diff
@@ -16,7 +16,10 @@
   type HandoffEndpoints,
   type HandoffTimelineRun,
 } from "@t3tools/client-runtime/handoff";
+import {
+  formatSubagentDisplayTitle,
+  resolveSubagentContextUsage,
-import { formatSubagentDisplayTitle } from "@t3tools/client-runtime/state/subagent-display";
+} from "@t3tools/client-runtime/state/subagent-display";
 import {
   ProviderDriverKind,
   type OrchestrationV2Notification,
@@ -517,6 +520,8 @@
           status={liveStatus}
           result={agent?.result ?? props.result}
           progress={agent?.progress ?? props.progress}
+          usage={agent?.usage}
+          taskStatus={agent?.status ?? props.status}
         />
       </ThreadHoverCardPopup>
     </Tooltip>
@@ -526,6 +531,8 @@
 function SubagentTimelineTooltip(
   props: Parameters<typeof SubagentTimelineLink>[0] & {
     model: string | null;
+    usage: OrchestrationV2Subagent["usage"];
+    taskStatus: OrchestrationV2Subagent["status"];
     modelSelection: OrchestrationV2Subagent["modelSelection"];
     elapsed: ReactNode;
   },
@@ -557,6 +564,7 @@
       status={props.status}
       result={props.result}
       progress={props.progress}
+      usage={resolveSubagentContextUsage({ status: props.taskStatus, usage: props.usage }, child)}
       parentThread={parent}
       childThread={child}
       parentProject={parentProject ?? undefined}
```

## packages/client-runtime/src/state/subagentDisplay.ts

Status: modified; +25; −0

```diff
@@ -1,10 +1,12 @@
 import type {
+  OrchestrationV2Subagent,
   OrchestrationV2TurnItemStatus,
   OrchestrationV2ThreadShell,
   OrchestrationProjectShell,
   ServerProvider,
 } from "@t3tools/contracts";
 import { formatModelSlugName, resolveSelectableModel } from "@t3tools/shared/model";
+import { formatTokens } from "@t3tools/shared/usageFormat";
 import { fileBasename } from "@t3tools/shared/path";
 import { isTerminalSubagentStatus } from "./subagentRuntime.ts";
 
@@ -122,3 +124,26 @@
   const compact = detail.replace(/\s+/gu, " ");
   return compact.length > 280 ? `${compact.slice(0, 280).trimEnd()}…` : compact || null;
 }
+
+export function resolveSubagentContextUsage(
+  subagent: {
+    readonly status: OrchestrationV2Subagent["status"];
+    readonly usage?: OrchestrationV2Subagent["usage"] | null;
+  },
+  childThread?: Pick<OrchestrationV2ThreadShell, "activityRunStatus" | "status"> | null,
+): OrchestrationV2Subagent["usage"] | null {
+  // A follow-up runs in the child thread without updating the settled task.
+  if (
+    isTerminalSubagentStatus(subagent.status) &&
+    (childThread?.activityRunStatus || childThread?.status === "queued")
+  ) {
+    return null;
+  }
+  return subagent.usage ?? null;
+}
+
+export function formatSubagentUsage(usage: OrchestrationV2Subagent["usage"] | null): string | null {
+  if (!usage) return null;
+  const tools = `${usage.toolUses} ${usage.toolUses === 1 ? "tool use" : "tool uses"}`;
+  return `${formatTokens(usage.contextTokens)} context · ${tools}`;
+}
```

## packages/client-runtime/src/state/subagentRuntime.test.ts

Status: modified; +52; −0

```diff
@@ -1,5 +1,6 @@
 import { describe, expect, it } from "vite-plus/test";
 import {
+  OrchestrationV2SubagentJson,
   ProviderDriverKind,
   ProviderInstanceId,
   ThreadId,
@@ -7,6 +8,7 @@
   type OrchestrationV2SubagentWorkflow,
 } from "@t3tools/contracts";
 import * as DateTime from "effect/DateTime";
+import * as Schema from "effect/Schema";
 
 import {
   deriveWorkflowGroups,
@@ -58,6 +60,56 @@
     expect(agent?.phases).toEqual([]);
   });
 
+  it("keeps wire context snapshots separate from cumulative workflow usage", () => {
+    const decode = Schema.decodeUnknownSync(OrchestrationV2SubagentJson);
+    const encode = Schema.encodeSync(OrchestrationV2SubagentJson);
+    const wire = {
+      ...subagent(),
+      threadId: "parent",
+      runId: "run",
+      parentNodeId: "root",
+      origin: "provider_native",
+      createdBy: "agent",
+      driver: "claudeAgent",
+      providerInstanceId: "claude",
+      providerThreadId: null,
+      childThreadId: null,
+      nativeTaskRef: null,
+      startedAt: "2026-08-01T10:00:00.000Z",
+      updatedAt: "2026-08-01T10:00:05.000Z",
+    };
+    for (const usage of [
+      { contextTokens: 41_100, toolUses: 2 },
+      { contextTokens: 12_000, toolUses: 3 },
+      { contextTokens: 0, toolUses: 0 },
+      undefined,
+    ]) {
+      const projected = decode({ ...wire, ...(usage === undefined ? {} : { usage }) });
+      const roundTripped = decode(encode(projected));
+      const [plain] = projectedSubagentsToRuntime([roundTripped]);
+      expect(plain).toMatchObject({ contextUsage: usage ?? null, usage: null });
+
+      const [coordinator, child] = projectedSubagentsToRuntime([
+        {
+          ...roundTripped,
+          workflow: {
+            phases: [],
+            agents: [member({ totalTokens: 500, toolCalls: 1 })],
+            totalTokens: 1000,
+          },
+        },
+      ]);
+      expect(coordinator).toMatchObject({
+        contextUsage: usage ?? null,
+        usage: { totalTokens: 1000 },
+      });
+      expect(child).toMatchObject({
+        contextUsage: null,
+        usage: { totalTokens: 500, toolUses: 1 },
+      });
+    }
+  });
+
   it("projects a coordinator plus one row per member", () => {
     const rows = projectedSubagentsToRuntime([
       subagent({
```

## packages/client-runtime/src/state/subagentRuntime.ts

Status: modified; +4; −0

```diff
@@ -55,6 +55,7 @@
   readonly status: RuntimeSubagentStatus;
   readonly activationCount: number;
   readonly usage: SubagentUsage | null;
+  readonly contextUsage?: OrchestrationV2Subagent["usage"] | null;
   readonly progress: string | null;
   readonly lastToolName: string | null;
   readonly result: string | null;
@@ -152,6 +153,7 @@
       status,
       activationCount: agent.attempt ?? 1,
       usage: workflowUsage(agent),
+      contextUsage: null,
       progress: agent.prompt ?? null,
       result: failed ? null : (agent.result ?? null),
       error: failed ? (agent.result ?? null) : null,
@@ -179,6 +181,7 @@
     readonly model: string | null;
     readonly status: OrchestrationV2Subagent["status"];
     readonly progress?: string | undefined;
+    readonly usage?: OrchestrationV2Subagent["usage"];
     readonly childThreadId?: string | null | undefined;
     readonly workflow?: OrchestrationV2SubagentWorkflow | undefined;
     readonly result: string | null;
@@ -205,6 +208,7 @@
       status: subagent.status,
       activationCount: 1,
       usage: workflow === undefined ? null : workflowUsage(workflow),
+      contextUsage: subagent.usage ?? null,
       progress: subagent.progress ?? null,
       lastToolName: null,
       result: subagent.result,
```

## packages/contracts/src/orchestrationV2.ts

Status: modified; +7; −0

```diff
@@ -707,6 +707,13 @@
     "interrupted",
   ]),
   progress: Schema.optional(Schema.String),
+  /** Latest context snapshot; may decrease after compaction and must not be summed. */
+  usage: Schema.optional(
+    Schema.Struct({
+      contextTokens: NonNegativeInt,
+      toolUses: NonNegativeInt,
+    }),
+  ),
   workflow: Schema.optional(OrchestrationV2SubagentWorkflow),
   result: Schema.NullOr(Schema.String),
   startedAt: Schema.NullOr(Schema.DateTimeUtc),
```
