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
107 if (value === undefined) {107 if (value === undefined) {
108 return undefined;108 return undefined;
109 }109 }
110 const normalized = JSON.parse(110 return JSON.parse(
111 JSON.stringify(value, (_key, nestedValue) => (nestedValue === undefined ? null : nestedValue)),111 JSON.stringify(value, (_key, nestedValue) => (nestedValue === undefined ? null : nestedValue)),
112 ) as CanonicalJsonValue | null;112 ) as CanonicalJsonValue;
113 return normalized ?? undefined;
114}113}
115 114
116function buildContextWindowActivityPayload(115function buildContextWindowActivityPayload(
apps/server/src/provider/Layers/ClaudeAdapter.ts+25−65
@@ -75,6 +75,7 @@ import { · 74 unmodified lines
75 type ProviderAdapterError,75 type ProviderAdapterError,
76} from "../Errors.ts";76} from "../Errors.ts";
77import { ClaudeAdapter, type ClaudeAdapterShape } from "../Services/ClaudeAdapter.ts";77import { ClaudeAdapter, type ClaudeAdapterShape } from "../Services/ClaudeAdapter.ts";
78import { normalizeCommandValue, pushChangedFile } from "../toolDataUtils.ts";
78import { type EventNdjsonLogger, makeEventNdjsonLogger } from "./EventNdjsonLogger.ts";79import { type EventNdjsonLogger, makeEventNdjsonLogger } from "./EventNdjsonLogger.ts";
79 80
80const PROVIDER = "claudeAgent" as const;81const PROVIDER = "claudeAgent" as const;
@@ -747,32 +748,6 @@ function tryParseJsonRecord(value: string): Record<string, unknown> | undefined · 666 unmodified lines
747 }748 }
748}749}
749 750
750function 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
764function 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
776function collectChangedFiles(value: unknown, target: string[], seen: Set<string>, depth: number) {751function collectChangedFiles(value: unknown, target: string[], seen: Set<string>, depth: number) {
777 if (depth > 4 || target.length >= 12) {752 if (depth > 4 || target.length >= 12) {
778 return;753 return;
@@ -1540,19 +1515,14 @@ function makeClaudeAdapter(options?: ClaudeAdapterLiveOptions) { · 761 unmodified lines
1540 status: status === "completed" ? "completed" : "failed",1515 status: status === "completed" ? "completed" : "failed",
1541 title: tool.title,1516 title: tool.title,
1542 ...(tool.detail ? { detail: tool.detail } : {}),1517 ...(tool.detail ? { detail: tool.detail } : {}),
1543 ...(canonicalToolLifecycleData({1518 ...(() => {
1544 itemType: tool.itemType,1519 const toolData = canonicalToolLifecycleData({
1545 toolName: tool.toolName,1520 itemType: tool.itemType,
1546 input: tool.input,1521 toolName: tool.toolName,
1547 })1522 input: tool.input,
1548 ? {1523 });
1549 data: canonicalToolLifecycleData({1524 return toolData ? { data: toolData } : {};
1550 itemType: tool.itemType,1525 })(),
1551 toolName: tool.toolName,
1552 input: tool.input,
1553 }),
1554 }
1555 : {}),
1556 },1526 },
1557 providerRefs: nativeProviderRefs(context, { providerItemId: tool.itemId }),1527 providerRefs: nativeProviderRefs(context, { providerItemId: tool.itemId }),
1558 raw: {1528 raw: {
@@ -1744,19 +1714,14 @@ function makeClaudeAdapter(options?: ClaudeAdapterLiveOptions) { · 185 unmodified lines
1744 status: "inProgress",1714 status: "inProgress",
1745 title: nextTool.title,1715 title: nextTool.title,
1746 ...(nextTool.detail ? { detail: nextTool.detail } : {}),1716 ...(nextTool.detail ? { detail: nextTool.detail } : {}),
1747 ...(canonicalToolLifecycleData({1717 ...(() => {
1748 itemType: nextTool.itemType,1718 const toolData = canonicalToolLifecycleData({
1749 toolName: nextTool.toolName,1719 itemType: nextTool.itemType,
1750 input: nextTool.input,1720 toolName: nextTool.toolName,
1751 })1721 input: nextTool.input,
1752 ? {1722 });
1753 data: canonicalToolLifecycleData({1723 return toolData ? { data: toolData } : {};
1754 itemType: nextTool.itemType,1724 })(),
1755 toolName: nextTool.toolName,
1756 input: nextTool.input,
1757 }),
1758 }
1759 : {}),
1760 },1725 },
1761 providerRefs: nativeProviderRefs(context, { providerItemId: nextTool.itemId }),1726 providerRefs: nativeProviderRefs(context, { providerItemId: nextTool.itemId }),
1762 raw: {1727 raw: {
@@ -1822,19 +1787,14 @@ function makeClaudeAdapter(options?: ClaudeAdapterLiveOptions) { · 59 unmodified lines
1822 status: "inProgress",1787 status: "inProgress",
1823 title: tool.title,1788 title: tool.title,
1824 ...(tool.detail ? { detail: tool.detail } : {}),1789 ...(tool.detail ? { detail: tool.detail } : {}),
1825 ...(canonicalToolLifecycleData({1790 ...(() => {
1826 itemType: tool.itemType,1791 const toolData = canonicalToolLifecycleData({
1827 toolName: tool.toolName,1792 itemType: tool.itemType,
1828 input: toolInput,1793 toolName: tool.toolName,
1829 })1794 input: toolInput,
1830 ? {1795 });
1831 data: canonicalToolLifecycleData({1796 return toolData ? { data: toolData } : {};
1832 itemType: tool.itemType,1797 })(),
1833 toolName: tool.toolName,
1834 input: toolInput,
1835 }),
1836 }
1837 : {}),
1838 },1798 },
1839 providerRefs: nativeProviderRefs(context, { providerItemId: tool.itemId }),1799 providerRefs: nativeProviderRefs(context, { providerItemId: tool.itemId }),
1840 raw: {1800 raw: {
apps/server/src/provider/Layers/CodexAdapter.ts+5−29
@@ -33,6 +33,7 @@ import { · 32 unmodified lines
33 type ProviderAdapterError,33 type ProviderAdapterError,
34} from "../Errors.ts";34} from "../Errors.ts";
35import { CodexAdapter, type CodexAdapterShape } from "../Services/CodexAdapter.ts";35import { CodexAdapter, type CodexAdapterShape } from "../Services/CodexAdapter.ts";
36import { normalizeCommandValue, pushChangedFile } from "../toolDataUtils.ts";
36import {37import {
37 CodexAppServerManager,38 CodexAppServerManager,
38 type CodexAppServerStartSessionInput,39 type CodexAppServerStartSessionInput,
@@ -111,32 +112,6 @@ function asNumber(value: unknown): number | undefined { · 72 unmodified lines
111 return typeof value === "number" && Number.isFinite(value) ? value : undefined;112 return typeof value === "number" && Number.isFinite(value) ? value : undefined;
112}113}
113 114
114function 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
128function 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
140function collectChangedFiles(value: unknown, target: string[], seen: Set<string>, depth: number) {115function collectChangedFiles(value: unknown, target: string[], seen: Set<string>, depth: number) {
141 if (depth > 4 || target.length >= 12) {116 if (depth > 4 || target.length >= 12) {
142 return;117 return;
@@ -670,9 +645,10 @@ function mapItemLifecycle( · 527 unmodified lines
670 ...(status ? { status } : {}),645 ...(status ? { status } : {}),
671 ...(itemTitle(itemType) ? { title: itemTitle(itemType) } : {}),646 ...(itemTitle(itemType) ? { title: itemTitle(itemType) } : {}),
672 ...(detail ? { detail } : {}),647 ...(detail ? { detail } : {}),
673 ...(buildCanonicalToolLifecycleData(itemType, source, payload ?? {})648 ...(() => {
674 ? { data: buildCanonicalToolLifecycleData(itemType, source, payload ?? {}) }649 const toolData = buildCanonicalToolLifecycleData(itemType, source, payload ?? {});
675 : {}),650 return toolData ? { data: toolData } : {};
651 })(),
676 },652 },
677 };653 };
678}654}
apps/server/src/provider/toolDataUtils.ts+25−0
@@ -0,0 +1,25 @@
1export 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
15export 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