History

Fix duplicate tool lifecycle computations, extract shared utils, preserve null in toCanonicalJsonValue

- Cache canonicalToolLifecycleData/buildCanonicalToolLifecycleData results in local variables instead of calling twice with identical arguments (ClaudeAdapter x3, CodexAdapter x1) - Extract identical normalizeCommandValue and pushChangedFile helpers into apps/server/src/provider/toolDataUtils.ts, imported by both adapters - Fix toCanonicalJsonValue to preserve null as a valid CanonicalJsonValue instead of silently converting it to undefined via the nullish coalescing operator

Cursor Agent committed 7 months ago

Commit 5b3e7101 parent ff581d64 files · +57 −97Browse files

Unified · Split

4 files
  1. apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts +2 −3
  2. apps/server/src/provider/Layers/ClaudeAdapter.ts +25 −65
  3. apps/server/src/provider/Layers/CodexAdapter.ts +5 −29
  4. apps/server/src/provider/toolDataUtils.ts +25 −0
apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts+2−3
@@ -107,10 +107,9 @@ function toCanonicalJsonValue(value: unknown): CanonicalJsonValue | undefined { · 106 unmodified lines
107107 if (value === undefined) {
108108 return undefined;
109109 }
110− const normalized = JSON.parse(
110+ return JSON.parse(
111111 JSON.stringify(value, (_key, nestedValue) => (nestedValue === undefined ? null : nestedValue)),
112− ) as CanonicalJsonValue | null;
113− return normalized ?? undefined;
112+ ) as CanonicalJsonValue;
114113 }
115114
116115 function buildContextWindowActivityPayload(
apps/server/src/provider/Layers/ClaudeAdapter.ts+25−65
@@ -75,6 +75,7 @@ import { · 74 unmodified lines
7575 type ProviderAdapterError,
7676 } from "../Errors.ts";
7777 import { ClaudeAdapter, type ClaudeAdapterShape } from "../Services/ClaudeAdapter.ts";
78+import { normalizeCommandValue, pushChangedFile } from "../toolDataUtils.ts";
7879 import { type EventNdjsonLogger, makeEventNdjsonLogger } from "./EventNdjsonLogger.ts";
7980
8081 const PROVIDER = "claudeAgent" as const;
@@ -747,32 +748,6 @@ function tryParseJsonRecord(value: string): Record<string, unknown> | undefined · 666 unmodified lines
747748 }
748749 }
749750
750−function normalizeCommandValue(value: unknown): string | undefined {
751− if (typeof value === "string") {
752− const trimmed = value.trim();
753− return trimmed.length > 0 ? trimmed : undefined;
754− }
755− if (!Array.isArray(value)) {
756− return undefined;
757− }
758− const parts = value
759− .map((entry) => (typeof entry === "string" ? entry.trim() : ""))
760− .filter((entry) => entry.length > 0);
761− return parts.length > 0 ? parts.join(" ") : undefined;
762−}
763−
764−function pushChangedFile(target: string[], seen: Set<string>, value: unknown) {
765− if (typeof value !== "string") {
766− return;
767− }
768− const normalized = value.trim();
769− if (normalized.length === 0 || seen.has(normalized)) {
770− return;
771− }
772− seen.add(normalized);
773− target.push(normalized);
774−}
775−
776751 function collectChangedFiles(value: unknown, target: string[], seen: Set<string>, depth: number) {
777752 if (depth > 4 || target.length >= 12) {
778753 return;
@@ -1540,19 +1515,14 @@ function makeClaudeAdapter(options?: ClaudeAdapterLiveOptions) { · 761 unmodified lines
15401515 status: status === "completed" ? "completed" : "failed",
15411516 title: tool.title,
15421517 ...(tool.detail ? { detail: tool.detail } : {}),
1543− ...(canonicalToolLifecycleData({
1544− itemType: tool.itemType,
1545− toolName: tool.toolName,
1546− input: tool.input,
1547− })
1548− ? {
1549− data: canonicalToolLifecycleData({
1550− itemType: tool.itemType,
1551− toolName: tool.toolName,
1552− input: tool.input,
1553− }),
1554− }
1555− : {}),
1518+ ...(() => {
1519+ const toolData = canonicalToolLifecycleData({
1520+ itemType: tool.itemType,
1521+ toolName: tool.toolName,
1522+ input: tool.input,
1523+ });
1524+ return toolData ? { data: toolData } : {};
1525+ })(),
15561526 },
15571527 providerRefs: nativeProviderRefs(context, { providerItemId: tool.itemId }),
15581528 raw: {
@@ -1744,19 +1714,14 @@ function makeClaudeAdapter(options?: ClaudeAdapterLiveOptions) { · 185 unmodified lines
17441714 status: "inProgress",
17451715 title: nextTool.title,
17461716 ...(nextTool.detail ? { detail: nextTool.detail } : {}),
1747− ...(canonicalToolLifecycleData({
1748− itemType: nextTool.itemType,
1749− toolName: nextTool.toolName,
1750− input: nextTool.input,
1751− })
1752− ? {
1753− data: canonicalToolLifecycleData({
1754− itemType: nextTool.itemType,
1755− toolName: nextTool.toolName,
1756− input: nextTool.input,
1757− }),
1758− }
1759− : {}),
1717+ ...(() => {
1718+ const toolData = canonicalToolLifecycleData({
1719+ itemType: nextTool.itemType,
1720+ toolName: nextTool.toolName,
1721+ input: nextTool.input,
1722+ });
1723+ return toolData ? { data: toolData } : {};
1724+ })(),
17601725 },
17611726 providerRefs: nativeProviderRefs(context, { providerItemId: nextTool.itemId }),
17621727 raw: {
@@ -1822,19 +1787,14 @@ function makeClaudeAdapter(options?: ClaudeAdapterLiveOptions) { · 59 unmodified lines
18221787 status: "inProgress",
18231788 title: tool.title,
18241789 ...(tool.detail ? { detail: tool.detail } : {}),
1825− ...(canonicalToolLifecycleData({
1826− itemType: tool.itemType,
1827− toolName: tool.toolName,
1828− input: toolInput,
1829− })
1830− ? {
1831− data: canonicalToolLifecycleData({
1832− itemType: tool.itemType,
1833− toolName: tool.toolName,
1834− input: toolInput,
1835− }),
1836− }
1837− : {}),
1790+ ...(() => {
1791+ const toolData = canonicalToolLifecycleData({
1792+ itemType: tool.itemType,
1793+ toolName: tool.toolName,
1794+ input: toolInput,
1795+ });
1796+ return toolData ? { data: toolData } : {};
1797+ })(),
18381798 },
18391799 providerRefs: nativeProviderRefs(context, { providerItemId: tool.itemId }),
18401800 raw: {
apps/server/src/provider/Layers/CodexAdapter.ts+5−29
@@ -33,6 +33,7 @@ import { · 32 unmodified lines
3333 type ProviderAdapterError,
3434 } from "../Errors.ts";
3535 import { CodexAdapter, type CodexAdapterShape } from "../Services/CodexAdapter.ts";
36+import { normalizeCommandValue, pushChangedFile } from "../toolDataUtils.ts";
3637 import {
3738 CodexAppServerManager,
3839 type CodexAppServerStartSessionInput,
@@ -111,32 +112,6 @@ function asNumber(value: unknown): number | undefined { · 72 unmodified lines
111112 return typeof value === "number" && Number.isFinite(value) ? value : undefined;
112113 }
113114
114−function normalizeCommandValue(value: unknown): string | undefined {
115− if (typeof value === "string") {
116− const trimmed = value.trim();
117− return trimmed.length > 0 ? trimmed : undefined;
118− }
119− if (!Array.isArray(value)) {
120− return undefined;
121− }
122− const parts = value
123− .map((entry) => (typeof entry === "string" ? entry.trim() : ""))
124− .filter((entry) => entry.length > 0);
125− return parts.length > 0 ? parts.join(" ") : undefined;
126−}
127−
128−function pushChangedFile(target: string[], seen: Set<string>, value: unknown) {
129− if (typeof value !== "string") {
130− return;
131− }
132− const normalized = value.trim();
133− if (normalized.length === 0 || seen.has(normalized)) {
134− return;
135− }
136− seen.add(normalized);
137− target.push(normalized);
138−}
139−
140115 function collectChangedFiles(value: unknown, target: string[], seen: Set<string>, depth: number) {
141116 if (depth > 4 || target.length >= 12) {
142117 return;
@@ -670,9 +645,10 @@ function mapItemLifecycle( · 527 unmodified lines
670645 ...(status ? { status } : {}),
671646 ...(itemTitle(itemType) ? { title: itemTitle(itemType) } : {}),
672647 ...(detail ? { detail } : {}),
673− ...(buildCanonicalToolLifecycleData(itemType, source, payload ?? {})
674− ? { data: buildCanonicalToolLifecycleData(itemType, source, payload ?? {}) }
675− : {}),
648+ ...(() => {
649+ const toolData = buildCanonicalToolLifecycleData(itemType, source, payload ?? {});
650+ return toolData ? { data: toolData } : {};
651+ })(),
676652 },
677653 };
678654 }
apps/server/src/provider/toolDataUtils.ts+25−0
@@ -0,0 +1,25 @@
1+export function normalizeCommandValue(value: unknown): string | undefined {
2+ if (typeof value === "string") {
3+ const trimmed = value.trim();
4+ return trimmed.length > 0 ? trimmed : undefined;
5+ }
6+ if (!Array.isArray(value)) {
7+ return undefined;
8+ }
9+ const parts = value
10+ .map((entry) => (typeof entry === "string" ? entry.trim() : ""))
11+ .filter((entry) => entry.length > 0);
12+ return parts.length > 0 ? parts.join(" ") : undefined;
13+}
14+
15+export function pushChangedFile(target: string[], seen: Set<string>, value: unknown) {
16+ if (typeof value !== "string") {
17+ return;
18+ }
19+ const normalized = value.trim();
20+ if (normalized.length === 0 || seen.has(normalized)) {
21+ return;
22+ }
23+ seen.add(normalized);
24+ target.push(normalized);
25+}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t