diff --git a/.github/scripts/brand-matrix.test.mjs b/.github/scripts/brand-matrix.test.mjs
index beee7d5b9..340ceba67 100644
--- a/.github/scripts/brand-matrix.test.mjs
+++ b/.github/scripts/brand-matrix.test.mjs
@@ -26,7 +26,7 @@ const RE_MISMATCHED_PUBLICATION_SOURCE = /must equal sourceGitSha/;
const RE_LEGACY_DESKTOP_UPLOAD = /"s3:\/\/\$\{R2_BUCKET\}\/\$\{R2_PREFIX\}\/"/;
const RE_LEGACY_RELEASE_TAG = /must equal refs\/tags\/v1\.2\.3/;
const RE_SECRETS_EXPRESSION = /secrets(?:\.|\[)/;
-const ACTIONS_EXPRESSION = String.fromCodePoint(36);
+const ACTIONS_EXPRESSION = String.fromCharCode(36);
function sha(character) {
return character.repeat(64);
diff --git a/.github/scripts/release-inputs.test.mjs b/.github/scripts/release-inputs.test.mjs
index 7be355065..dd2e1946d 100644
--- a/.github/scripts/release-inputs.test.mjs
+++ b/.github/scripts/release-inputs.test.mjs
@@ -110,25 +110,26 @@ describe('validateReleaseInputs', () => {
});
it('rejects malformed desktop notarization key material', () => {
- const env = Object.fromEntries(
- [
- 'APPLE_API_KEY_BASE64',
- 'APPLE_API_KEY_ID',
- 'APPLE_API_ISSUER',
- 'APPLE_TEAM_ID',
- 'AZURE_CERTIFICATE_PROFILE',
- 'AZURE_CLIENT_ID',
- 'AZURE_CODE_SIGNING_ACCOUNT',
- 'AZURE_PUBLISHER_NAME',
- 'AZURE_SIGN_ENDPOINT',
- 'AZURE_TENANT_ID',
- 'MACOS_CSC_KEY_PASSWORD',
- 'MACOS_CSC_LINK',
- 'POSTHOG_HOST',
- 'POSTHOG_PROJECT_TOKEN',
- 'SENTRY_DSN_DESKTOP',
- ].map((name) => [name, 'set']),
- );
+ const env = [
+ 'APPLE_API_KEY_BASE64',
+ 'APPLE_API_KEY_ID',
+ 'APPLE_API_ISSUER',
+ 'APPLE_TEAM_ID',
+ 'AZURE_CERTIFICATE_PROFILE',
+ 'AZURE_CLIENT_ID',
+ 'AZURE_CODE_SIGNING_ACCOUNT',
+ 'AZURE_PUBLISHER_NAME',
+ 'AZURE_SIGN_ENDPOINT',
+ 'AZURE_TENANT_ID',
+ 'MACOS_CSC_KEY_PASSWORD',
+ 'MACOS_CSC_LINK',
+ 'POSTHOG_HOST',
+ 'POSTHOG_PROJECT_TOKEN',
+ 'SENTRY_DSN_DESKTOP',
+ ].reduce((acc, name) => {
+ acc[name] = 'set';
+ return acc;
+ }, {});
expect(() => validateReleaseInputs({ env, phase: 'sign', platform: 'desktop' })).toThrow(
RE_INVALID_KEY,
);
diff --git a/apps/daemon/e2e/startup.e2e.ts b/apps/daemon/e2e/startup.e2e.ts
index a843129d0..98181341b 100644
--- a/apps/daemon/e2e/startup.e2e.ts
+++ b/apps/daemon/e2e/startup.e2e.ts
@@ -120,7 +120,9 @@ async function main(): Promise {
.map((row) => (row as { name: string }).name),
);
sqlite.close();
- for (const table of ['__drizzle_migrations', 'sessions', 'workspaces', 'schedules', 'loops']) {
+ const expectedTables = ['__drizzle_migrations', 'sessions', 'workspaces', 'schedules', 'loops'];
+ for (let i = 0, len = expectedTables.length; i < len; i++) {
+ const table = expectedTables[i];
assert(tables.has(table), `missing migrated table ${table}`);
}
diff --git a/apps/daemon/package.json b/apps/daemon/package.json
index 0a8331bf4..dbfb14bd9 100644
--- a/apps/daemon/package.json
+++ b/apps/daemon/package.json
@@ -32,7 +32,7 @@
"@sentry/node": "10.62.0",
"@sentry/profiling-node": "10.62.0",
"better-sqlite3": "^13.0.2",
- "foxts": "^5.8.0",
+ "foxts": "^5.9.1",
"pino": "^10.3.1",
"zod": "catalog:"
},
diff --git a/apps/daemon/scripts/dev-clean.mts b/apps/daemon/scripts/dev-clean.mts
index ae90d3fe3..49f6deb68 100644
--- a/apps/daemon/scripts/dev-clean.mts
+++ b/apps/daemon/scripts/dev-clean.mts
@@ -3,4 +3,8 @@ import { databasePath, runtimeFilePath } from '../src/config';
// Resolves through config.ts so a fork's renamed state dir, the resolved channel, or an active
// LINKCODE_PROFILE cleans the same universe the dev daemon will actually use.
-for (const path of [databasePath(), runtimeFilePath()]) rmSync(path, { force: true });
+const devStatePaths = [databasePath(), runtimeFilePath()];
+for (let i = 0, len = devStatePaths.length; i < len; i++) {
+ const path = devStatePaths[i];
+ rmSync(path, { force: true });
+}
diff --git a/apps/daemon/scripts/package-daemon.mts b/apps/daemon/scripts/package-daemon.mts
index e9b773282..01af52413 100644
--- a/apps/daemon/scripts/package-daemon.mts
+++ b/apps/daemon/scripts/package-daemon.mts
@@ -61,10 +61,13 @@ run('pnpm', ['--filter', '@linkcode/daemon', '--prod', 'deploy', '--legacy', out
rmSync(join(outDir, 'dist'), { recursive: true, force: true });
cpSync(join(daemonDir, 'dist'), join(outDir, 'dist'), { recursive: true });
-for (const [scope, prefix] of PRUNE) {
+for (let i = 0, len = PRUNE.length; i < len; i++) {
+ const [scope, prefix] = PRUNE[i];
const scopeDir = join(outDir, 'node_modules', scope);
if (!existsSync(scopeDir)) continue;
- for (const entry of readdirSync(scopeDir)) {
+ const scopeEntries = readdirSync(scopeDir);
+ for (let j = 0, entryCount = scopeEntries.length; j < entryCount; j++) {
+ const entry = scopeEntries[j];
if (entry.startsWith(prefix)) rmSync(join(scopeDir, entry), { recursive: true, force: true });
}
}
@@ -74,7 +77,9 @@ console.log(`daemon packaged at ${outDir} (${Math.round(bytes / 1e6)} MB)`);
function dirSize(dir: string): number {
let total = 0;
- for (const entry of readdirSync(dir, { withFileTypes: true })) {
+ const entries = readdirSync(dir, { withFileTypes: true });
+ for (let i = 0, len = entries.length; i < len; i++) {
+ const entry = entries[i];
const full = join(dir, entry.name);
if (entry.isDirectory()) total += dirSize(full);
else if (entry.isFile()) total += statSync(full).size;
diff --git a/apps/daemon/src/__tests__/ai-gateway.test.ts b/apps/daemon/src/__tests__/ai-gateway.test.ts
index c84e917a0..73ffe2119 100644
--- a/apps/daemon/src/__tests__/ai-gateway.test.ts
+++ b/apps/daemon/src/__tests__/ai-gateway.test.ts
@@ -47,13 +47,22 @@ class FakeChild implements SidecarChildProcess {
this.signal = signal;
}
emitStdout(text: string): void {
- for (const listener of this.dataListeners) listener(text);
+ for (let i = 0, len = this.dataListeners.length; i < len; i++) {
+ const listener = this.dataListeners[i];
+ listener(text);
+ }
}
emitError(error: Error): void {
- for (const listener of this.errorListeners) listener(error);
+ for (let i = 0, len = this.errorListeners.length; i < len; i++) {
+ const listener = this.errorListeners[i];
+ listener(error);
+ }
}
emitExit(code: number | null): void {
- for (const listener of this.exitListeners) listener(code);
+ for (let i = 0, len = this.exitListeners.length; i < len; i++) {
+ const listener = this.exitListeners[i];
+ listener(code);
+ }
}
}
diff --git a/apps/daemon/src/__tests__/secrets-vault.test.ts b/apps/daemon/src/__tests__/secrets-vault.test.ts
index c3a0d655a..de4219912 100644
--- a/apps/daemon/src/__tests__/secrets-vault.test.ts
+++ b/apps/daemon/src/__tests__/secrets-vault.test.ts
@@ -25,8 +25,9 @@ const noKeyring = (): MasterKey | null => null;
* Secrets are only reachable through a namespace, so every case goes through one. On disk the same
* entry is the full `cloud:session` ref — asserting against that is what proves the prefixing.
*/
-const open = (file: string, loadKey: () => MasterKey | null): SecretStore =>
- createSecretVault(file, loadKey).namespace('cloud');
+function open(file: string, loadKey: () => MasterKey | null): SecretStore {
+ return createSecretVault(file, loadKey).namespace('cloud');
+}
let file: string;
const realPlatform = process.platform;
diff --git a/apps/daemon/src/ai-gateway.ts b/apps/daemon/src/ai-gateway.ts
index 7267a6777..6c43714bc 100644
--- a/apps/daemon/src/ai-gateway.ts
+++ b/apps/daemon/src/ai-gateway.ts
@@ -172,7 +172,10 @@ export function createAiGatewaySidecar(options: AiGatewaySidecarOptions = {}): T
},
async closeAll() {
const entries = [...running.values()];
- for (const entry of entries) entry.close();
+ for (let i = 0, len = entries.length; i < len; i++) {
+ const entry = entries[i];
+ entry.close();
+ }
await Promise.allSettled(entries.map((entry) => entry.ready));
},
};
diff --git a/apps/daemon/src/config.ts b/apps/daemon/src/config.ts
index 56ad8aa0d..d7bc8b2ed 100644
--- a/apps/daemon/src/config.ts
+++ b/apps/daemon/src/config.ts
@@ -206,7 +206,8 @@ function parseAccounts(store: SecretStore, raw: unknown): Parsed {
}
const accounts: Accounts = [];
let migrated = false;
- for (const value of raw) {
+ for (let i = 0, len = raw.length; i < len; i++) {
+ const value = raw[i];
// The credential secret lives in the vault (CODE-371); merge it back before validating, so a
// secret that is gone fails the schema and lands in the same drop-and-log path as a malformed one.
const attached = withAccountSecret(store, value);
@@ -267,7 +268,8 @@ function parseCustomMcpServers(store: SecretStore, raw: unknown): Parsed): unknown
seen.set(value, sanitized);
const cause = Reflect.get(value, 'cause');
if (cause !== undefined) sanitized.cause = sanitizeValue(cause, seen);
- for (const [key, entry] of Object.entries(value)) {
+ const errorEntries = Object.entries(value);
+ for (let i = 0, len = errorEntries.length; i < len; i++) {
+ const [key, entry] = errorEntries[i];
sanitized[key] = SENSITIVE_KEY.test(key) ? REDACTED : sanitizeValue(entry, seen);
}
return sanitized;
@@ -49,7 +51,10 @@ function sanitizeValue(value: unknown, seen: WeakMap
) : null}
- {error !== undefined ? {error}
: null}
+ {error === undefined ? null : {error}
}
{listed.length > 0 ? (
{listed.map((model) => {
diff --git a/packages/client/workbench/src/settings/providers/view.ts b/packages/client/workbench/src/settings/providers/view.ts
index 0019f2d3b..7bb01a4b4 100644
--- a/packages/client/workbench/src/settings/providers/view.ts
+++ b/packages/client/workbench/src/settings/providers/view.ts
@@ -54,7 +54,10 @@ export function accountConfigSnippet(
): string {
const bound = boundAgentKinds(account, providers);
const slice: Record
= {};
- for (const kind of bound) slice[kind] = providers?.[kind];
+ for (let i = 0, len = bound.length; i < len; i++) {
+ const kind = bound[i];
+ slice[kind] = providers?.[kind];
+ }
return JSON.stringify({ providers: slice }, null, 2);
}
@@ -241,7 +244,8 @@ export function withEnabled(
export function withoutAccount(providers: ProvidersConfig, accountId: string): ProvidersConfig {
let changed = false;
const next: ProvidersConfig = {};
- for (const kind of AGENT_KINDS) {
+ for (let i = 0, len = AGENT_KINDS.length; i < len; i++) {
+ const kind = AGENT_KINDS[i];
const entry = providers[kind];
if (entry === undefined) continue;
if (entry.enabledAccountIds?.includes(accountId)) {
diff --git a/packages/client/workbench/src/sidebar/ordering.ts b/packages/client/workbench/src/sidebar/ordering.ts
index 83c3a4c0e..30e3f3d95 100644
--- a/packages/client/workbench/src/sidebar/ordering.ts
+++ b/packages/client/workbench/src/sidebar/ordering.ts
@@ -15,7 +15,8 @@ export function orderGroups(
const unlisted: ThreadGroup[] = [];
const fixed: ThreadGroup[] = [];
- for (const group of groups) {
+ for (let i = 0, len = groups.length; i < len; i++) {
+ const group = groups[i];
if (group.isChat || group.workspace === null) fixed.push(group);
else if (rank.has(group.collapseKey)) listed.push(group);
else unlisted.push(group);
diff --git a/packages/client/workbench/src/simulator/__tests__/agent-activity.test.ts b/packages/client/workbench/src/simulator/__tests__/agent-activity.test.ts
index 8898f4013..5a2222f11 100644
--- a/packages/client/workbench/src/simulator/__tests__/agent-activity.test.ts
+++ b/packages/client/workbench/src/simulator/__tests__/agent-activity.test.ts
@@ -23,13 +23,16 @@ function fakeClient(): SimulatorActivityClient & { emit: (activity: Activity) =>
};
}
-const started = (
+function started(
udid: string | undefined,
tool = 'sim_tap',
at?: { x: number; y: number },
-): Activity => ({ sessionId: 's1', udid, tool, phase: 'started', ...at }) as Activity;
-const settled = (udid: string | undefined, tool = 'sim_tap'): Activity =>
- ({ sessionId: 's1', udid, tool, phase: 'settled' }) as Activity;
+): Activity {
+ return { sessionId: 's1', udid, tool, phase: 'started', ...at } as Activity;
+}
+function settled(udid: string | undefined, tool = 'sim_tap'): Activity {
+ return { sessionId: 's1', udid, tool, phase: 'settled' } as Activity;
+}
describe('useSimulatorAgentActivity', () => {
beforeEach(() => {
diff --git a/packages/client/workbench/src/simulator/__tests__/panel-store.test.ts b/packages/client/workbench/src/simulator/__tests__/panel-store.test.ts
index cd4f8bbcf..ac4e91f26 100644
--- a/packages/client/workbench/src/simulator/__tests__/panel-store.test.ts
+++ b/packages/client/workbench/src/simulator/__tests__/panel-store.test.ts
@@ -5,10 +5,12 @@ import { selectDeviceTabs, useSimulatorPanelStore } from '../panel-store';
const A = 'thread-a';
const B = 'thread-b';
-const tabsOf = (key: string): { udids: readonly string[]; activeUdid: string | null } =>
- selectDeviceTabs(useSimulatorPanelStore.getState(), key);
-const open = (key: string, udid: string): void =>
- useSimulatorPanelStore.getState().openDevice(key, udid);
+function tabsOf(key: string): { udids: readonly string[]; activeUdid: string | null } {
+ return selectDeviceTabs(useSimulatorPanelStore.getState(), key);
+}
+function open(key: string, udid: string): void {
+ return useSimulatorPanelStore.getState().openDevice(key, udid);
+}
describe('simulator device tabs', () => {
beforeEach(() => {
diff --git a/packages/client/workbench/src/simulator/agent-activity.ts b/packages/client/workbench/src/simulator/agent-activity.ts
index c02e4e371..996c1c472 100644
--- a/packages/client/workbench/src/simulator/agent-activity.ts
+++ b/packages/client/workbench/src/simulator/agent-activity.ts
@@ -50,7 +50,7 @@ export function useSimulatorAgentActivity(
const at = { x: activity.x, y: activity.y };
setPoint(at);
clearTimeout(pointTimer);
- pointTimer = setTimeout(() => setPoint(null), POINTER_MS);
+ pointTimer = setTimeout(setPoint, POINTER_MS, null);
}
if (activity.phase === 'started') {
inflight += 1;
@@ -61,7 +61,7 @@ export function useSimulatorAgentActivity(
inflight = Math.max(0, inflight - 1);
if (inflight === 0) {
clearTimeout(clearTimer);
- clearTimer = setTimeout(() => setActive(false), LINGER_MS);
+ clearTimer = setTimeout(setActive, LINGER_MS, false);
}
});
return () => {
diff --git a/packages/client/workbench/src/simulator/background-streams.ts b/packages/client/workbench/src/simulator/background-streams.ts
index ac7a276a9..bb889e221 100644
--- a/packages/client/workbench/src/simulator/background-streams.ts
+++ b/packages/client/workbench/src/simulator/background-streams.ts
@@ -36,7 +36,10 @@ export function useBackgroundSimulatorStreams(
return lease;
});
return () => {
- for (const lease of leases) lease.release();
+ for (let i = 0, len = leases.length; i < len; i++) {
+ const lease = leases[i];
+ lease.release();
+ }
};
}, [client, sessionId, key, scale, codec]);
}
diff --git a/packages/client/workbench/src/simulator/panel.tsx b/packages/client/workbench/src/simulator/panel.tsx
index 8e5666254..a6e4b9382 100644
--- a/packages/client/workbench/src/simulator/panel.tsx
+++ b/packages/client/workbench/src/simulator/panel.tsx
@@ -237,6 +237,7 @@ export function SimulatorPanel({ sessionId }: { sessionId: SessionId | null }):
const openUdids =
tabs.udids.length > 0 ? tabs.udids : defaultUdid === null ? EMPTY_UDIDS : [defaultUdid];
const activeUdid = tabs.activeUdid ?? defaultUdid;
+ // eslint-disable-next-line vibe-proof/react-no-performance-impacting-array-find -- a host exposes a handful of simulators at most; a lookup Map would outweigh the scan
const device = devices?.find((item) => item.udid === activeUdid) ?? null;
const udid = device?.udid ?? null;
const booted = device?.state === 'Booted';
@@ -249,14 +250,18 @@ export function SimulatorPanel({ sessionId }: { sessionId: SessionId | null }):
// Fetch bookkeeping lives in a ref (not `masks`) so the effect never loops on its own writes;
// the cache write itself is deliberately not abort-gated — a udid switch mid-fetch must still
- // land the result for the next switch back.
+ // land the result for the next switch back. No stale-overwrite race exists: writes are keyed
+ // by the udid captured at fetch time (never clobbering another udid's entry), and the ref
+ // guarantees at most one fetch per udid, so no two writes ever target the same key.
const maskFetchedRef = useRef(new Set());
useEffect(() => {
if (udid === null || maskFetchedRef.current.has(udid)) return;
maskFetchedRef.current.add(udid);
void client
.simulatorScreenMask(udid)
+ // eslint-disable-next-line vibe-proof/react-detect-potential-race-condition -- see above
.then((data) => setMasks((prev) => ({ ...prev, [udid]: data })))
+ // eslint-disable-next-line vibe-proof/react-detect-potential-race-condition -- see above
.catch(() => setMasks((prev) => ({ ...prev, [udid]: null })));
}, [client, udid]);
@@ -325,7 +330,7 @@ export function SimulatorPanel({ sessionId }: { sessionId: SessionId | null }):
const flagBusy = useCallback(() => {
setBusy(true);
clearTimeout(busyTimerRef.current);
- busyTimerRef.current = setTimeout(() => setBusy(false), BUSY_BANNER_MS);
+ busyTimerRef.current = setTimeout(setBusy, BUSY_BANNER_MS, false);
}, []);
const handleTouch = (phase: SimulatorScreenTouchPhase, point: SimulatorScreenPoint): void => {
diff --git a/packages/client/workbench/src/surface/startup-selection.ts b/packages/client/workbench/src/surface/startup-selection.ts
index 853d6c858..729d969ab 100644
--- a/packages/client/workbench/src/surface/startup-selection.ts
+++ b/packages/client/workbench/src/surface/startup-selection.ts
@@ -19,7 +19,8 @@ export function reflectedStartupSelection(
): NewSessionSelection {
let reflectedModel: string | undefined;
let reflectedEffort: EffortLevel | undefined;
- for (const { event } of events) {
+ for (let i = 0, len = events.length; i < len; i++) {
+ const { event } = events[i];
if (event.type === 'model-update') reflectedModel = event.model;
else if (event.type === 'effort-update') reflectedEffort = event.effort;
}
diff --git a/packages/client/workbench/src/surface/task-resources-panel.tsx b/packages/client/workbench/src/surface/task-resources-panel.tsx
index 8a4fbb9dc..44f413677 100644
--- a/packages/client/workbench/src/surface/task-resources-panel.tsx
+++ b/packages/client/workbench/src/surface/task-resources-panel.tsx
@@ -18,11 +18,11 @@ function resourceItem(resource: SessionResource, localFileLabel: string): Resour
kind: resource.kind,
status: resource.status,
source:
- resource.direction !== 'source'
- ? undefined
- : resource.locator.type === 'url'
+ resource.direction === 'source'
+ ? resource.locator.type === 'url'
? new URL(resource.locator.url).hostname
- : localFileLabel,
+ : localFileLabel
+ : undefined,
updatedAt: resource.updatedAt,
error: resource.error,
canOpen: resource.status === 'ready',
@@ -70,7 +70,8 @@ export function RuntimeTaskResourcesPanel({
}
async function addSources(files: File[]): Promise {
- for (const file of files) {
+ for (let i = 0, len = files.length; i < len; i++) {
+ const file = files[i];
if (file.size > MAX_ATTACHMENT_BYTES) {
toastManager.add({ title: t('tooLarge', { name: file.name }), type: 'error' });
continue;
diff --git a/packages/client/workbench/src/surface/use-seeded-conversation.ts b/packages/client/workbench/src/surface/use-seeded-conversation.ts
index c2318c1ab..396248f30 100644
--- a/packages/client/workbench/src/surface/use-seeded-conversation.ts
+++ b/packages/client/workbench/src/surface/use-seeded-conversation.ts
@@ -33,7 +33,10 @@ async function readConversationSeed(
cursor,
forceRefresh: page === 0,
});
- for (const entry of data.events) events.push({ event: entry.event, ts: entry.ts });
+ for (let i = 0, len = data.events.length; i < len; i++) {
+ const entry = data.events[i];
+ events.push({ event: entry.event, ts: entry.ts });
+ }
cursor = data.cursor;
if (cursor === undefined) break;
}
diff --git a/packages/client/workbench/src/surface/use-workbench-sessions.ts b/packages/client/workbench/src/surface/use-workbench-sessions.ts
index 8be491468..fef42d39c 100644
--- a/packages/client/workbench/src/surface/use-workbench-sessions.ts
+++ b/packages/client/workbench/src/surface/use-workbench-sessions.ts
@@ -288,7 +288,8 @@ function sessionById(
sessionId: SessionId | null,
): SessionInfo | null {
if (!sessionId) return null;
- for (const session of sessions) {
+ for (let i = 0, len = sessions.length; i < len; i++) {
+ const session = sessions[i];
if (session.sessionId === sessionId) return session;
}
return null;
diff --git a/packages/client/workbench/src/surface/workbench.tsx b/packages/client/workbench/src/surface/workbench.tsx
index 74ac1e892..ca4183bc3 100644
--- a/packages/client/workbench/src/surface/workbench.tsx
+++ b/packages/client/workbench/src/surface/workbench.tsx
@@ -234,11 +234,13 @@ function WorkbenchSessionSurface({
const [respondingRequestIds, addRespondingRequest, removeRespondingRequest] = useSet();
const [responseErrors, setResponseErrors] = useState(() => new Map());
const visibleResponseErrors = new Map();
- for (const requestId of conversation.pendingPermissionIds) {
+ for (let i = 0, len = conversation.pendingPermissionIds.length; i < len; i++) {
+ const requestId = conversation.pendingPermissionIds[i];
const message = responseErrors.get(requestId);
if (message) visibleResponseErrors.set(requestId, message);
}
- for (const requestId of conversation.pendingQuestionIds) {
+ for (let i = 0, len = conversation.pendingQuestionIds.length; i < len; i++) {
+ const requestId = conversation.pendingQuestionIds[i];
const message = responseErrors.get(requestId);
if (message) visibleResponseErrors.set(requestId, message);
}
@@ -558,7 +560,8 @@ function WorkbenchSessionSurface({
const workspacesById = new Map();
let chatWorkspace: WorkspaceRecord | null = null;
const projectWorkspaces: WorkspaceRecord[] = [];
- for (const workspace of allWorkspaces) {
+ for (let i = 0, len = allWorkspaces.length; i < len; i++) {
+ const workspace = allWorkspaces[i];
const kind = workspaceKind(workspace);
if (kind === 'worktree') continue;
workspacesById.set(workspace.workspaceId, workspace);
diff --git a/packages/client/workbench/src/terminal/panel.tsx b/packages/client/workbench/src/terminal/panel.tsx
index f7c7563bc..22d619a88 100644
--- a/packages/client/workbench/src/terminal/panel.tsx
+++ b/packages/client/workbench/src/terminal/panel.tsx
@@ -67,7 +67,7 @@ export function TerminalPanel({
const unsubscribe = client.subscribeTerminalError(terminalId, () => {
setInputLost(true);
clearTimeout(inputLostTimerRef.current);
- inputLostTimerRef.current = setTimeout(() => setInputLost(false), INPUT_LOST_BANNER_MS);
+ inputLostTimerRef.current = setTimeout(setInputLost, INPUT_LOST_BANNER_MS, false);
});
return () => {
clearTimeout(inputLostTimerRef.current);
@@ -100,7 +100,7 @@ export function TerminalPanel({
void client.takeTerminalControl(terminalId).catch(() => {
setInputLost(true);
clearTimeout(inputLostTimerRef.current);
- inputLostTimerRef.current = setTimeout(() => setInputLost(false), INPUT_LOST_BANNER_MS);
+ inputLostTimerRef.current = setTimeout(setInputLost, INPUT_LOST_BANNER_MS, false);
});
};
return (
diff --git a/packages/client/workbench/tests/integration/dev-mock-transport.test.ts b/packages/client/workbench/tests/integration/dev-mock-transport.test.ts
index 524b3f050..3febd842e 100644
--- a/packages/client/workbench/tests/integration/dev-mock-transport.test.ts
+++ b/packages/client/workbench/tests/integration/dev-mock-transport.test.ts
@@ -579,7 +579,8 @@ describe('dev mock transport', () => {
},
source: 'user',
});
- for (const request of permissionRequests) {
+ for (let i = 0, len = permissionRequests.length; i < len; i++) {
+ const request = permissionRequests[i];
// eslint-disable-next-line no-await-in-loop -- answer the asks in order, like a user would.
await expect(
client.respondPermission(showcase.sessionId, request.requestId, {
@@ -711,7 +712,8 @@ describe('dev mock transport', () => {
}),
).rejects.toThrow('Unknown question request');
- for (const request of permissionRequests) {
+ for (let i = 0, len = permissionRequests.length; i < len; i++) {
+ const request = permissionRequests[i];
// eslint-disable-next-line no-await-in-loop -- assert each cancelled ask reaches its own tool snapshot.
const cancelledTool = await eventually(() =>
toolCalls(events).find(
diff --git a/packages/foundation/common/package.json b/packages/foundation/common/package.json
index 2593004be..d2187b9a8 100644
--- a/packages/foundation/common/package.json
+++ b/packages/foundation/common/package.json
@@ -19,7 +19,7 @@
"@noble/ed25519": "3.0.0",
"@noble/hashes": "2.2.0",
"canonicalize": "3.0.0",
- "foxts": "^5.8.1",
+ "foxts": "^5.9.1",
"zod": "catalog:",
"zustand": "catalog:"
},
diff --git a/packages/foundation/common/src/config/__tests__/brand-identity.test.ts b/packages/foundation/common/src/config/__tests__/brand-identity.test.ts
index c471fa57b..f44b5e26c 100644
--- a/packages/foundation/common/src/config/__tests__/brand-identity.test.ts
+++ b/packages/foundation/common/src/config/__tests__/brand-identity.test.ts
@@ -20,7 +20,10 @@ const FIXTURE_SHA256 = {
function toHex(bytes: Uint8Array): string {
let hex = '';
- for (const byte of bytes) hex += byte.toString(16).padStart(2, '0');
+ for (let i = 0, len = bytes.length; i < len; i++) {
+ const byte = bytes[i];
+ hex += byte.toString(16).padStart(2, '0');
+ }
return hex;
}
@@ -34,7 +37,9 @@ function mutate(change: (artifact: Record) => void): unknown {
describe('brand identity v1 vendored fixtures', () => {
it('matches the frozen publisher bytes exactly', async () => {
- for (const [name, digest] of Object.entries(FIXTURE_SHA256)) {
+ const fixtureDigests = Object.entries(FIXTURE_SHA256);
+ for (let i = 0, len = fixtureDigests.length; i < len; i++) {
+ const [name, digest] = fixtureDigests[i];
const suffix = name === 'desktop' ? '' : `-${name}`;
// eslint-disable-next-line no-await-in-loop -- four small reads
const bytes = await readFile(
@@ -45,12 +50,14 @@ describe('brand identity v1 vendored fixtures', () => {
});
it('validates every fixture and preserves its target', () => {
- for (const [raw, brandId, platform, channel] of [
+ const fixtureCases = [
[fixture, 'acme', 'desktop', 'stable'],
[fixtureIos, 'acme', 'ios', 'stable'],
[fixtureAndroid, 'acme', 'android', 'stable'],
[fixtureZenithCanary, 'zenith', 'desktop', 'canary'],
- ] as const) {
+ ] as const;
+ for (let i = 0, len = fixtureCases.length; i < len; i++) {
+ const [raw, brandId, platform, channel] = fixtureCases[i];
const identity = parseBrandIdentityArtifact(structuredClone(raw));
expect(identity.brandId).toBe(brandId);
expect(identity.platform).toBe(platform);
@@ -196,7 +203,8 @@ describe('parseBrandIdentityArtifact', () => {
'manifestSchemaVersion',
],
];
- for (const [change, message] of cases) {
+ for (let i = 0, len = cases.length; i < len; i++) {
+ const [change, message] = cases[i];
expect(() => parseBrandIdentityArtifact(mutate(change))).toThrow(message);
}
});
diff --git a/packages/foundation/common/src/config/__tests__/build-bundle.test.ts b/packages/foundation/common/src/config/__tests__/build-bundle.test.ts
index 0853ab329..c88d111ed 100644
--- a/packages/foundation/common/src/config/__tests__/build-bundle.test.ts
+++ b/packages/foundation/common/src/config/__tests__/build-bundle.test.ts
@@ -25,7 +25,10 @@ const RE_SOURCE_GIT_SHA = /^[0-9a-f]{40}$/;
function toHex(bytes: Uint8Array): string {
let hex = '';
- for (const byte of bytes) hex += byte.toString(16).padStart(2, '0');
+ for (let i = 0, len = bytes.length; i < len; i++) {
+ const byte = bytes[i];
+ hex += byte.toString(16).padStart(2, '0');
+ }
return hex;
}
@@ -39,7 +42,9 @@ function mutate(change: (bundle: Record) => void): unknown {
describe('build bundle v1 vendored fixtures', () => {
it('matches the frozen publisher bytes exactly', async () => {
- for (const [name, digest] of Object.entries(FIXTURE_SHA256)) {
+ const fixtureDigests = Object.entries(FIXTURE_SHA256);
+ for (let i = 0, len = fixtureDigests.length; i < len; i++) {
+ const [name, digest] = fixtureDigests[i];
const suffix = name === 'desktop' ? '' : `-${name}`;
// eslint-disable-next-line no-await-in-loop -- three small reads
const bytes = await readFile(
@@ -50,11 +55,13 @@ describe('build bundle v1 vendored fixtures', () => {
});
it('validates every platform fixture and extracts its defaults', () => {
- for (const [raw, platform] of [
+ const fixtureCases = [
[fixture, 'desktop'],
[fixtureIos, 'ios'],
[fixtureAndroid, 'android'],
- ] as const) {
+ ] as const;
+ for (let i = 0, len = fixtureCases.length; i < len; i++) {
+ const [raw, platform] = fixtureCases[i];
const bundle = parseConfigBuildBundle(structuredClone(raw));
expect(bundle.platform).toBe(platform);
expect(bundle.brandId).toBe('acme');
diff --git a/packages/foundation/common/src/config/__tests__/contract.test.ts b/packages/foundation/common/src/config/__tests__/contract.test.ts
index 5685f5a42..8bf4cefa5 100644
--- a/packages/foundation/common/src/config/__tests__/contract.test.ts
+++ b/packages/foundation/common/src/config/__tests__/contract.test.ts
@@ -48,7 +48,9 @@ const crypto: ConfigCrypto = {
describe('configuration contract v1 golden fixture', () => {
it('locks exact snapshot bytes, size, and SHA-256', () => {
- for (const name of ['current', 'previous'] as const) {
+ const snapshotNames = ['current', 'previous'] as const;
+ for (let i = 0, len = snapshotNames.length; i < len; i++) {
+ const name = snapshotNames[i];
const vector = snapshots[name];
const document = parseIJson(encoder.encode(vector.canonicalPayload));
assertConfigSnapshot(document);
@@ -188,14 +190,16 @@ describe('configuration contract v1 golden fixture', () => {
});
it('verifies pointer signatures while retaining additive root fields', () => {
- for (const name of [
+ const pointerNames = [
'normal',
'rollback',
'rotationWithoutBump',
'rotation',
'additive',
'schemaTooNew',
- ]) {
+ ];
+ for (let i = 0, len = pointerNames.length; i < len; i++) {
+ const name = pointerNames[i];
const vector = pointers[name];
assertConfigPointer(vector.document);
const payload = canonicalSignedPayloadBytes(vector.document);
@@ -220,7 +224,9 @@ describe('configuration contract v1 golden fixture', () => {
});
it('verifies independent emergency signatures and rejects tampering', () => {
- for (const name of ['active', 'clear', 'equivocation']) {
+ const emergencyNames = ['active', 'clear', 'equivocation'];
+ for (let i = 0, len = emergencyNames.length; i < len; i++) {
+ const name = emergencyNames[i];
const vector = emergencies[name];
assertEmergencyDocument(vector.document);
const payload = canonicalSignedPayloadBytes(vector.document);
@@ -244,7 +250,8 @@ describe('configuration contract v1 golden fixture', () => {
});
it('freezes normal and emergency anti-replay decisions', () => {
- for (const entry of fixture.cases.pointerAntiReplay) {
+ for (let i = 0, len = fixture.cases.pointerAntiReplay.length; i < len; i++) {
+ const entry = fixture.cases.pointerAntiReplay[i];
const candidate = pointers[entry.candidate];
const accepted = entry.accepted === null ? null : pointers[entry.accepted];
expect(
@@ -255,7 +262,8 @@ describe('configuration contract v1 golden fixture', () => {
entry.name,
).toBe(entry.expectedDecision);
}
- for (const entry of fixture.cases.emergencyAntiReplay) {
+ for (let i = 0, len = fixture.cases.emergencyAntiReplay.length; i < len; i++) {
+ const entry = fixture.cases.emergencyAntiReplay[i];
const candidate = emergencies[entry.candidate];
const accepted = entry.accepted === null ? null : emergencies[entry.accepted];
expect(
@@ -267,7 +275,8 @@ describe('configuration contract v1 golden fixture', () => {
).toBe(entry.expectedDecision);
}
expect(compareMonotonicVersions('9007199254740993', '9007199254740992')).toBeGreaterThan(0);
- for (const value of fixture.cases.monotonicVersionErrors) {
+ for (let i = 0, len = fixture.cases.monotonicVersionErrors.length; i < len; i++) {
+ const value = fixture.cases.monotonicVersionErrors[i];
expect(() => assertMonotonicVersion(value), value).toThrow('uint64');
}
});
@@ -291,7 +300,8 @@ describe('configuration contract v1 golden fixture', () => {
expect(
conditionMatches({ locale: 'k' }, { appVersion: '2.4.0', locale: 'K', os: 'windows' }),
).toBe(false);
- for (const entry of fixture.cases.conditions) {
+ for (let i = 0, len = fixture.cases.conditions.length; i < len; i++) {
+ const entry = fixture.cases.conditions[i];
expect(
conditionMatches(
entry.condition as OverrideCondition,
@@ -309,7 +319,8 @@ describe('configuration contract v1 golden fixture', () => {
it('pins MurmurHash3 x86_32 UTF-8 and rollout boundaries', () => {
expect(murmur3X86_32('', 0)).toBe(0);
expect(murmur3X86_32('foo', 0)).toBe(4_138_058_784);
- for (const entry of fixture.cases.rollouts) {
+ for (let i = 0, len = fixture.cases.rollouts.length; i < len; i++) {
+ const entry = fixture.cases.rollouts[i];
expect(rolloutBucket(entry.salt, entry.deviceId), entry.salt).toBe(entry.expectedBucket);
expect(rolloutMatches(entry.salt, entry.deviceId, entry.expectedBucket)).toBe(
entry.expectedHitAtBoundary,
diff --git a/packages/foundation/common/src/config/__tests__/emergency-handoff.test.ts b/packages/foundation/common/src/config/__tests__/emergency-handoff.test.ts
index 4ecb79400..6a31604c8 100644
--- a/packages/foundation/common/src/config/__tests__/emergency-handoff.test.ts
+++ b/packages/foundation/common/src/config/__tests__/emergency-handoff.test.ts
@@ -169,7 +169,8 @@ describe('emergency handoff signed documents', () => {
describe('emergency handoff anti-replay decision table', () => {
it('reproduces every frozen decision', () => {
expect(fixture.antiReplayCases).toHaveLength(5);
- for (const testCase of fixture.antiReplayCases) {
+ for (let i = 0, len = fixture.antiReplayCases.length; i < len; i++) {
+ const testCase = fixture.antiReplayCases[i];
expect(
decideAntiReplay(
replayState(testCase.candidate as DocumentName),
@@ -183,7 +184,8 @@ describe('emergency handoff anti-replay decision table', () => {
it.each(PLATFORMS)('holds decision-table parity on re-signed %s documents', async (platform) => {
const target: ConfigTarget = { ...TARGET, platform };
const states: Record = {};
- for (const name of DOCUMENT_NAMES) {
+ for (let i = 0, len = DOCUMENT_NAMES.length; i < len; i++) {
+ const name = DOCUMENT_NAMES[i];
const signed = resignForPlatform(name, platform);
// eslint-disable-next-line no-await-in-loop -- documents verify sequentially by design
const verified = await verifyEmergencyBytes(encoder.encode(JSON.stringify(signed)), {
@@ -201,7 +203,8 @@ describe('emergency handoff anti-replay decision table', () => {
expect(verified.payloadSha256).toBe(fixture.documents[name].payloadSha256);
}
}
- for (const testCase of fixture.antiReplayCases) {
+ for (let i = 0, len = fixture.antiReplayCases.length; i < len; i++) {
+ const testCase = fixture.antiReplayCases[i];
expect(
decideAntiReplay(
states[testCase.candidate],
@@ -258,7 +261,7 @@ function hex(bytes: Uint8Array): string {
function hexBytes(text: string): Uint8Array {
const bytes = new Uint8Array(text.length / 2);
- for (let index = 0; index < bytes.length; index += 1) {
+ for (let index = 0, len = bytes.length; index < len; index += 1) {
bytes[index] = Number.parseInt(text.slice(index * 2, index * 2 + 2), 16);
}
return bytes;
diff --git a/packages/foundation/common/src/config/__tests__/telemetry.test.ts b/packages/foundation/common/src/config/__tests__/telemetry.test.ts
index 639f9daf5..b3ce86793 100644
--- a/packages/foundation/common/src/config/__tests__/telemetry.test.ts
+++ b/packages/foundation/common/src/config/__tests__/telemetry.test.ts
@@ -122,7 +122,9 @@ describe('classification', () => {
'unsupported-contract': 'parse_failure',
'unsupported-schema': 'parse_failure',
};
- for (const [code, failureType] of Object.entries(expected)) {
+ const expectedEntries = Object.entries(expected);
+ for (let i = 0, len = expectedEntries.length; i < len; i++) {
+ const [code, failureType] = expectedEntries[i];
expect(configTelemetryFailureType(code as ConfigErrorCode)).toBe(failureType);
}
});
@@ -238,7 +240,9 @@ describe('ConfigTelemetryReporter', () => {
});
it('dequeues on 202 duplicate replay and terminal 400/409 rejections without retrying', async () => {
- for (const outcome of ['accepted', 'rejected'] as const) {
+ const outcomes = ['accepted', 'rejected'] as const;
+ for (let i = 0, len = outcomes.length; i < len; i++) {
+ const outcome = outcomes[i];
const sent: ConfigTelemetryRequest[] = [];
const reporter = makeReporter({
send(request) {
@@ -379,7 +383,8 @@ describe('ConfigTelemetryReporter', () => {
{ ...valid, rollout: { channel: 'canary', email: 'user@example.com' } },
{ ...valid, clientEventId: 'not-a-uuid' },
];
- for (const event of tampered) {
+ for (let i = 0, len = tampered.length; i < len; i++) {
+ const event = tampered[i];
storage.values.set(key, JSON.stringify({ version: 1, events: [event] }));
// eslint-disable-next-line no-await-in-loop -- each corrupted queue is checked in isolation
await expect(probe.snapshotQueue()).resolves.toEqual([]);
@@ -412,7 +417,8 @@ describe('ConfigTelemetryReporter', () => {
];
const storage = new MemoryStorage();
const probe = makeReporter({ storage });
- for (const event of tampered) {
+ for (let i = 0, len = tampered.length; i < len; i++) {
+ const event = tampered[i];
storage.values.set(key, JSON.stringify({ version: 1, events: [event] }));
// eslint-disable-next-line no-await-in-loop -- each corrupted queue is checked in isolation
await expect(probe.snapshotQueue()).resolves.toEqual([]);
diff --git a/packages/foundation/common/src/config/brand-identity.ts b/packages/foundation/common/src/config/brand-identity.ts
index c73ecc596..cb04bc02c 100644
--- a/packages/foundation/common/src/config/brand-identity.ts
+++ b/packages/foundation/common/src/config/brand-identity.ts
@@ -66,7 +66,9 @@ function requireExactKeys(
allowed: ReadonlySet,
label: string,
): void {
- for (const key of Object.keys(value)) {
+ const keys = Object.keys(value);
+ for (let i = 0, len = keys.length; i < len; i++) {
+ const key = keys[i];
if (!allowed.has(key)) fail(`${label} contains unsupported field ${key}`);
}
for (const key of allowed) {
@@ -79,7 +81,8 @@ function assertApplicationId(value: string, platform: ConfigPlatform, label: str
const segments = value.split('.');
if (segments.length < 2) fail(`${label} must contain at least two segments`);
const segmentRule = platform === 'android' ? RE_ANDROID_ID_SEGMENT : RE_APPLE_ID_SEGMENT;
- for (const segment of segments) {
+ for (let i = 0, len = segments.length; i < len; i++) {
+ const segment = segments[i];
if (!segmentRule.test(segment)) {
fail(`${label} segment ${segment || '(empty)'} is invalid for ${platform}`);
}
@@ -108,7 +111,9 @@ function assertAssetsPath(value: string, label: string): void {
if (value[0] === '/' || value.includes('\\')) {
fail(`${label} must be a forward-slash relative path`);
}
- for (const segment of value.split('/')) {
+ const segments = value.split('/');
+ for (let i = 0, len = segments.length; i < len; i++) {
+ const segment = segments[i];
if (segment === '' || segment === '.' || segment === '..') {
fail(`${label} must not contain empty, dot, or parent segments`);
}
diff --git a/packages/foundation/common/src/config/build-bundle.ts b/packages/foundation/common/src/config/build-bundle.ts
index 986d65743..c9f59a7fb 100644
--- a/packages/foundation/common/src/config/build-bundle.ts
+++ b/packages/foundation/common/src/config/build-bundle.ts
@@ -30,7 +30,9 @@ function fail(message: string): never {
}
function assertKeyring(keyring: Readonly>, label: string): void {
- for (const [keyId, publicKey] of Object.entries(keyring)) {
+ const entries = Object.entries(keyring);
+ for (let i = 0, len = entries.length; i < len; i++) {
+ const [keyId, publicKey] = entries[i];
let decoded: Uint8Array;
try {
decoded = decodeBase64Url(publicKey);
@@ -45,7 +47,11 @@ function assertKeyring(keyring: Readonly>, label: string)
function sha256Hex(bytes: Uint8Array): string {
let hex = '';
- for (const byte of sha256(bytes)) hex += byte.toString(16).padStart(2, '0');
+ const digest = sha256(bytes);
+ for (let i = 0, len = digest.length; i < len; i++) {
+ const byte = digest[i];
+ hex += byte.toString(16).padStart(2, '0');
+ }
return hex;
}
@@ -126,14 +132,21 @@ export function configBuildBundleDefaults(
export function definitionsFromDefaults(
defaults: Readonly>,
): ConfigDefinitions {
- return Object.fromEntries(
- Object.entries(defaults).map(([key, defaultValue]) => [
- key,
- {
- defaultValue,
- parse: (value: ConfigValue) => parseLikeDefault(value, defaultValue),
- } satisfies ConfigValueDefinition,
- ]),
+ return Object.entries(defaults).reduce>(
+ (acc, [key, defaultValue]) => {
+ // Own data property, never a prototype write: `acc.__proto__ = …` would mutate the object.
+ Object.defineProperty(acc, key, {
+ configurable: true,
+ enumerable: true,
+ value: {
+ defaultValue,
+ parse: (value: ConfigValue) => parseLikeDefault(value, defaultValue),
+ } satisfies ConfigValueDefinition,
+ writable: true,
+ });
+ return acc;
+ },
+ {},
);
}
diff --git a/packages/foundation/common/src/config/core.ts b/packages/foundation/common/src/config/core.ts
index ccd418d23..fb119dcb1 100644
--- a/packages/foundation/common/src/config/core.ts
+++ b/packages/foundation/common/src/config/core.ts
@@ -22,6 +22,7 @@ import type {
ConfigStorage,
ConfigTarget,
ConfigValue,
+ ConfigValueDefinition,
ConfigValues,
EvaluationContext,
} from './types';
@@ -70,12 +71,18 @@ export class ConfigCore {
throw new TypeError('maximumSchemaVersion must be a positive safe integer');
}
configPointerPath(options.target);
- const definitions = Object.fromEntries(
- Object.entries(options.definitions).map(([key, definition]) => [
- key,
- { defaultValue: cloneJson(definition.defaultValue), parse: definition.parse },
- ]),
- ) as Definitions;
+ const definitions = Object.entries(options.definitions).reduce<
+ Record
+ >((acc, [key, definition]) => {
+ // Own data property, never a prototype write: `acc.__proto__ = …` would mutate the object.
+ Object.defineProperty(acc, key, {
+ configurable: true,
+ enumerable: true,
+ value: { defaultValue: cloneJson(definition.defaultValue), parse: definition.parse },
+ writable: true,
+ });
+ return acc;
+ }, {}) as Definitions;
this.#options = {
...options,
context: { ...options.context },
@@ -323,7 +330,9 @@ export class ConfigCore {
const before = this.#stateIdentity();
const next: Record = {};
const staged: Array> = [];
- for (const key of Object.keys(this.#options.definitions)) {
+ const definitionKeys = Object.keys(this.#options.definitions);
+ for (let i = 0, len = definitionKeys.length; i < len; i++) {
+ const key = definitionKeys[i];
const candidate = lkg.values[key];
const pinned = this.#coldPinned[key];
if (lkg.snapshot.document.applyModes[key] === 'hot') {
diff --git a/packages/foundation/common/src/config/evaluation.ts b/packages/foundation/common/src/config/evaluation.ts
index b70164f13..dd2d56d42 100644
--- a/packages/foundation/common/src/config/evaluation.ts
+++ b/packages/foundation/common/src/config/evaluation.ts
@@ -27,7 +27,9 @@ export function defaultValues(
definitions: Definitions,
): ConfigValues {
const values: Record = {};
- for (const [key, definition] of Object.entries(definitions)) {
+ const definitionEntries = Object.entries(definitions);
+ for (let i = 0, len = definitionEntries.length; i < len; i++) {
+ const [key, definition] = definitionEntries[i];
if (!isConfigKey(key)) throw new ConfigCoreError('schema-invalid', `Invalid known key ${key}`);
values[key] = parseDefinitionValue(definition, key, definition.defaultValue, `default.${key}`);
if (key.startsWith('feature.')) {
@@ -56,19 +58,24 @@ export function evaluateSnapshot(
}
let evaluated: Record = { ...snapshot.values };
- for (const override of snapshot.overrides) {
+ for (let i = 0, len = snapshot.overrides.length; i < len; i++) {
+ const override = snapshot.overrides[i];
if (conditionMatches(override.when, context)) {
evaluated = applyConfigPatch(evaluated, override.set);
}
}
- for (const [key, rollout] of Object.entries(snapshot.rollouts)) {
+ const rolloutEntries = Object.entries(snapshot.rollouts);
+ for (let i = 0, len = rolloutEntries.length; i < len; i++) {
+ const [key, rollout] = rolloutEntries[i];
if (rolloutMatches(rollout.salt, deviceId, rollout.basisPoints)) {
evaluated[key] = rollout.value;
}
}
const values = defaultValues(definitions) as Record;
- for (const [key, value] of Object.entries(evaluated)) {
+ const evaluatedEntries = Object.entries(evaluated);
+ for (let i = 0, len = evaluatedEntries.length; i < len; i++) {
+ const [key, value] = evaluatedEntries[i];
const definition = definitionFor(definitions, key);
if (!definition) continue;
values[key] = parseDefinitionValue(definition, key, value, key);
@@ -82,7 +89,9 @@ export function applyEmergency(
definitions: Definitions,
): ConfigValues {
const projected = cloneJson(values as ConfigValue) as Record;
- for (const key of emergency?.disabledFeatures ?? []) {
+ const disabledFeatures = emergency?.disabledFeatures ?? [];
+ for (let i = 0, len = disabledFeatures.length; i < len; i++) {
+ const key = disabledFeatures[i];
if (definitionFor(definitions, key)) projected[key] = false;
}
return projected as ConfigValues;
@@ -120,7 +129,9 @@ function validateKnownValues(
definitions: ConfigDefinitions,
label: string,
): void {
- for (const [key, value] of Object.entries(values)) {
+ const valueEntries = Object.entries(values);
+ for (let i = 0, len = valueEntries.length; i < len; i++) {
+ const [key, value] = valueEntries[i];
const definition = definitionFor(definitions, key);
if (definition) parseDefinitionValue(definition, key, value, `${label}.${key}`);
}
diff --git a/packages/foundation/common/src/config/i-json.ts b/packages/foundation/common/src/config/i-json.ts
index 216934b74..cef119c5e 100644
--- a/packages/foundation/common/src/config/i-json.ts
+++ b/packages/foundation/common/src/config/i-json.ts
@@ -7,7 +7,7 @@ const NUMBER_PATTERN = /-?(?:0|[1-9]\d*)(?:\.\d+)?(?:e[+-]?\d+)?/iy;
export function encodeBase64Url(bytes: Uint8Array): string {
let encoded = '';
- for (let index = 0; index < bytes.length; index += 3) {
+ for (let index = 0, len = bytes.length; index < len; index += 3) {
const remaining = bytes.length - index;
const first = bytes[index];
const second = remaining > 1 ? bytes[index + 1] : 0;
@@ -47,7 +47,16 @@ export function cloneJson(value: Value): Value;
export function cloneJson(value: JsonValue): JsonValue {
if (Array.isArray(value)) return value.map((entry) => cloneJson(entry));
if (typeof value === 'object' && value !== null) {
- return Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, cloneJson(entry)]));
+ return Object.entries(value).reduce>((acc, [key, entry]) => {
+ // Own data property, never a prototype write: `acc.__proto__ = …` would mutate the clone.
+ Object.defineProperty(acc, key, {
+ configurable: true,
+ enumerable: true,
+ value: cloneJson(entry),
+ writable: true,
+ });
+ return acc;
+ }, {});
}
return value;
}
@@ -63,11 +72,16 @@ function assertIJsonValue(value: unknown, label: string): asserts value is JsonV
}
if (value === null || typeof value === 'boolean') return;
if (Array.isArray(value)) {
- for (const [index, entry] of value.entries()) assertIJsonValue(entry, `${label}[${index}]`);
+ for (let index = 0, len = value.length; index < len; index++) {
+ const entry = value[index];
+ assertIJsonValue(entry, `${label}[${index}]`);
+ }
return;
}
if (typeof value !== 'object') throw new TypeError(`${label} is not an I-JSON value`);
- for (const [key, entry] of Object.entries(value)) {
+ const entries = Object.entries(value);
+ for (let i = 0, len = entries.length; i < len; i++) {
+ const [key, entry] = entries[i];
assertUnicodeScalarString(key, `${label} key`);
assertIJsonValue(entry, `${label}.${key}`);
}
diff --git a/packages/foundation/common/src/config/rules.ts b/packages/foundation/common/src/config/rules.ts
index 429f2a7aa..f97f85836 100644
--- a/packages/foundation/common/src/config/rules.ts
+++ b/packages/foundation/common/src/config/rules.ts
@@ -30,7 +30,9 @@ export function applyMergePatch(
typeof target === 'object' && target !== null && !Array.isArray(target)
? cloneJson(target)
: {};
- for (const [key, patchValue] of Object.entries(patch)) {
+ const patchEntries = Object.entries(patch);
+ for (let i = 0, len = patchEntries.length; i < len; i++) {
+ const [key, patchValue] = patchEntries[i];
const merged = applyMergePatch(result[key], patchValue);
if (merged === undefined) Reflect.deleteProperty(result, key);
else result[key] = merged;
@@ -42,10 +44,19 @@ export function applyConfigPatch(
values: Readonly>,
patch: Readonly>,
): Record {
- const result = Object.fromEntries(
- Object.entries(values).map(([key, value]) => [key, cloneJson(value)]),
- );
- for (const [key, patchValue] of Object.entries(patch)) {
+ const result = Object.entries(values).reduce>((acc, [key, value]) => {
+ // Own data property, never a prototype write: `acc.__proto__ = …` would mutate the result.
+ Object.defineProperty(acc, key, {
+ configurable: true,
+ enumerable: true,
+ value: cloneJson(value),
+ writable: true,
+ });
+ return acc;
+ }, {});
+ const patchEntries = Object.entries(patch);
+ for (let i = 0, len = patchEntries.length; i < len; i++) {
+ const [key, patchValue] = patchEntries[i];
if (!isConfigKey(key)) fail(`patch key ${key} is invalid`);
const merged = applyMergePatch(result[key], patchValue);
if (merged === undefined) Reflect.deleteProperty(result, key);
diff --git a/packages/foundation/common/src/config/semver.ts b/packages/foundation/common/src/config/semver.ts
index a23d14463..aaa761e8d 100644
--- a/packages/foundation/common/src/config/semver.ts
+++ b/packages/foundation/common/src/config/semver.ts
@@ -56,7 +56,9 @@ function parseSemver(value: string): ParsedSemver | null {
}
function compareSemver(left: ParsedSemver, right: ParsedSemver): number {
- for (const field of ['major', 'minor', 'patch'] as const) {
+ const coreFields = ['major', 'minor', 'patch'] as const;
+ for (let i = 0, len = coreFields.length; i < len; i++) {
+ const field = coreFields[i];
const comparison = compareDecimal(left[field], right[field]);
if (comparison !== 0) return comparison;
}
@@ -84,7 +86,8 @@ function parseVersionRange(value: string): readonly VersionComparator[] | null {
const parts = value.split(' ');
if (parts.some((part) => part.length === 0)) return null;
const comparators: VersionComparator[] = [];
- for (const part of parts) {
+ for (let i = 0, len = parts.length; i < len; i++) {
+ const part = parts[i];
const operator = (['>=', '<=', '>', '<', '='] as const).find((candidate) =>
part.startsWith(candidate),
);
diff --git a/packages/foundation/common/src/node/__tests__/executable-locations.test.ts b/packages/foundation/common/src/node/__tests__/executable-locations.test.ts
index 32171e8f1..e242dec26 100644
--- a/packages/foundation/common/src/node/__tests__/executable-locations.test.ts
+++ b/packages/foundation/common/src/node/__tests__/executable-locations.test.ts
@@ -32,7 +32,9 @@ describe('executableSearchLocations', () => {
it('yields only absolute candidate paths', () => {
vi.stubEnv('PATH', ['relative/bin', ''].join(delimiter));
- for (const location of executableSearchLocations('tool')) {
+ const locations = executableSearchLocations('tool');
+ for (let i = 0, len = locations.length; i < len; i++) {
+ const location = locations[i];
expect(isAbsolute(location)).toBe(true);
}
});
diff --git a/packages/foundation/common/src/node/__tests__/release-artifact.test.ts b/packages/foundation/common/src/node/__tests__/release-artifact.test.ts
index 67950baa7..f337fa7a0 100644
--- a/packages/foundation/common/src/node/__tests__/release-artifact.test.ts
+++ b/packages/foundation/common/src/node/__tests__/release-artifact.test.ts
@@ -110,13 +110,15 @@ describe('release artifact provenance', () => {
'executable code',
);
- for (const [key, value, expected] of [
+ const bypassCases = [
['content.pluginUrl', 'https://example.invalid/content', 'executable'],
['modules.wasmLoader', false, 'must exactly match'],
['content.scriptPath', '/content/banner', 'executable'],
['content.inline', '', 'executable'],
['content.source', 'data:text/javascript,alert(1)', 'executable'],
- ] as const) {
+ ] as const;
+ for (let i = 0, len = bypassCases.length; i < len; i++) {
+ const [key, value, expected] = bypassCases[i];
const bypass = structuredClone(bundleFixture);
const bypassSnapshot = JSON.parse(
Buffer.from(bypass.snapshot.base64Url, 'base64url').toString(),
diff --git a/packages/foundation/common/src/node/brand-assets.ts b/packages/foundation/common/src/node/brand-assets.ts
index f4312ef24..3f0c7c603 100644
--- a/packages/foundation/common/src/node/brand-assets.ts
+++ b/packages/foundation/common/src/node/brand-assets.ts
@@ -27,10 +27,11 @@ function validatedAssetDir(structuralDir: string, assetsPath: string): string {
}
let dirStats;
try {
- dirStats = lstatSync(dir);
+ dirStats = lstatSync(dir, { throwIfNoEntry: false });
} catch {
fail(`brand assets directory ${assetsPath} does not exist`);
}
+ if (dirStats === undefined) fail(`brand assets directory ${assetsPath} does not exist`);
if (dirStats.isSymbolicLink()) fail(`brand assets directory ${assetsPath} must not be a symlink`);
if (!dirStats.isDirectory()) fail(`brand assets path ${assetsPath} is not a directory`);
return dir;
@@ -51,7 +52,8 @@ export function stageBrandAssets(options: {
const dir = validatedAssetDir(options.structuralDir, options.assetsPath);
const entries = readdirSync(dir).sort();
const label = options.assetsPath;
- for (const entry of entries) {
+ for (let i = 0, len = entries.length; i < len; i++) {
+ const entry = entries[i];
const stats = lstatSync(join(dir, entry));
if (stats.isSymbolicLink()) fail(`brand asset ${label}/${entry} must not be a symlink`);
if (!stats.isFile()) {
diff --git a/packages/foundation/common/src/node/config-build-render.ts b/packages/foundation/common/src/node/config-build-render.ts
index 3744d5a87..fc95636d1 100644
--- a/packages/foundation/common/src/node/config-build-render.ts
+++ b/packages/foundation/common/src/node/config-build-render.ts
@@ -144,12 +144,15 @@ function parseReleaseManifest(value: unknown, path: string): ConfigBuildReleaseM
throw new TypeError(`Release manifest at ${path} has an unsupported format version`);
}
const keys = version === 1 ? RELEASE_MANIFEST_V1_KEY_SET : RELEASE_MANIFEST_V2_KEY_SET;
- for (const key of Object.keys(manifest)) {
+ const manifestKeys = Object.keys(manifest);
+ for (let i = 0, len = manifestKeys.length; i < len; i++) {
+ const key = manifestKeys[i];
if (!keys.has(key)) {
throw new TypeError(`Release manifest at ${path} contains unsupported field ${key}`);
}
}
- for (const key of RELEASE_MANIFEST_BASE_KEYS) {
+ for (let i = 0, len = RELEASE_MANIFEST_BASE_KEYS.length; i < len; i++) {
+ const key = RELEASE_MANIFEST_BASE_KEYS[i];
if (!(key in manifest)) {
throw new TypeError(`Release manifest at ${path} is missing field ${key}`);
}
@@ -244,11 +247,13 @@ export async function assertReleaseManifestBinding(
}
const manifest = parseReleaseManifest(JSON.parse(manifestText), manifestPath);
const digest = (bytes: Buffer): string => createHash('sha256').update(bytes).digest('hex');
- for (const field of [
+ const hexDigestFields = [
'expectedSnapshotSha256',
'publicKeyringsSha256',
'revisionSha256',
- ] as const) {
+ ] as const;
+ for (let i = 0, len = hexDigestFields.length; i < len; i++) {
+ const field = hexDigestFields[i];
if (!RE_HEX_SHA256.test(manifest[field])) {
throw new TypeError(`Release manifest ${field} must be a lowercase 64-hex SHA-256`);
}
@@ -269,7 +274,8 @@ export async function assertReleaseManifestBinding(
['telemetryEndpoint', manifest.telemetryEndpoint, bundle.endpoints.telemetry],
['expectedSnapshotSha256', manifest.expectedSnapshotSha256, bundle.snapshot.sha256],
];
- for (const [field, expected, got] of checks) {
+ for (let i = 0, len = checks.length; i < len; i++) {
+ const [field, expected, got] = checks[i];
if (expected !== got) {
throw new Error(
`Release manifest binding failed: ${field} is pinned to ${expected} but this build used ${got}`,
diff --git a/packages/foundation/common/src/node/executable-locations.ts b/packages/foundation/common/src/node/executable-locations.ts
index 3080f8107..0482eb21f 100644
--- a/packages/foundation/common/src/node/executable-locations.ts
+++ b/packages/foundation/common/src/node/executable-locations.ts
@@ -17,7 +17,9 @@ import { delimiter, isAbsolute, join } from 'node:path';
*/
function pathInstallLocations(binary: string): string[] {
const locations: string[] = [];
- for (const entry of (process.env.PATH ?? '').split(delimiter)) {
+ const pathEntries = (process.env.PATH ?? '').split(delimiter);
+ for (let i = 0, len = pathEntries.length; i < len; i++) {
+ const entry = pathEntries[i];
// Windows PATH entries with spaces are conventionally double-quoted.
const dir = entry.replaceAll('"', '');
if (dir.length > 0 && isAbsolute(dir)) locations.push(join(dir, binary));
diff --git a/packages/foundation/common/src/node/release-artifact-cli.mts b/packages/foundation/common/src/node/release-artifact-cli.mts
index e1285ef75..7167cd8a1 100644
--- a/packages/foundation/common/src/node/release-artifact-cli.mts
+++ b/packages/foundation/common/src/node/release-artifact-cli.mts
@@ -85,11 +85,19 @@ function compliance(value: unknown): StoreComplianceDeclaration {
bail('compliance declaration must contain checklist and disclosedFeatures');
}
return {
- checklist: Object.fromEntries(
- Object.entries(declaration.checklist).map(([key, entry]) => {
+ checklist: Object.entries(declaration.checklist).reduce>(
+ (acc, [key, entry]) => {
if (typeof entry !== 'boolean') bail(`compliance checklist field ${key} must be boolean`);
- return [key, entry];
- }),
+ // Own data property, never a prototype write: `acc.__proto__ = …` would mutate the object.
+ Object.defineProperty(acc, key, {
+ configurable: true,
+ enumerable: true,
+ value: entry,
+ writable: true,
+ });
+ return acc;
+ },
+ {},
),
disclosedFeatures: declaration.disclosedFeatures.filter(
(entry): entry is string => typeof entry === 'string',
diff --git a/packages/foundation/common/src/node/release-artifact.ts b/packages/foundation/common/src/node/release-artifact.ts
index 025c13145..6fc3fed84 100644
--- a/packages/foundation/common/src/node/release-artifact.ts
+++ b/packages/foundation/common/src/node/release-artifact.ts
@@ -76,7 +76,8 @@ function assertReleaseBinding(bundle: ConfigBuildBundle, manifest: ReleaseManife
['configRevisionId', bundle.provenance.configRevisionId, manifest.configRevisionId],
['expectedSnapshotSha256', bundle.snapshot.sha256, manifest.expectedSnapshotSha256],
] as const;
- for (const [field, actual, expected] of checks) {
+ for (let i = 0, len = checks.length; i < len; i++) {
+ const [field, actual, expected] = checks[i];
if (actual !== expected) {
throw new Error(`release manifest ${field} does not match the rendered bundle`);
}
@@ -88,7 +89,9 @@ async function artifactFile(
path: string,
): Promise<{ path: string; sha256: string; sizeBytes: number }> {
const absoluteRoot = await realpath(root);
- const absolutePath = resolve(root, path);
+ // Resolve against the canonicalized root: a symlinked root (macOS /var → /private/var)
+ // would otherwise make every legitimate path read as an escape.
+ const absolutePath = resolve(absoluteRoot, path);
const relativePath = relative(absoluteRoot, absolutePath);
if (relativePath === '' || relativePath.startsWith('..') || relativePath.includes('\\')) {
throw new TypeError(`artifact path escapes its isolated root: ${path}`);
@@ -217,7 +220,8 @@ function releaseArtifactProvenance(value: unknown): ReleaseArtifactProvenance {
}
const paths = new Set();
const artifacts: Array = [];
- for (const [index, value] of provenance.artifacts.entries()) {
+ for (let index = 0, len = provenance.artifacts.length; index < len; index++) {
+ const value = provenance.artifacts[index];
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
throw new TypeError(`release provenance artifact ${index} must be an object`);
}
@@ -354,7 +358,9 @@ export async function writeReleaseArtifactProvenance(
artifactRoot: string,
): Promise {
const absoluteRoot = await realpath(artifactRoot);
- const absolutePath = resolve(artifactRoot, path);
+ // Resolve against the canonicalized root: a symlinked root (macOS /var → /private/var)
+ // would otherwise make every legitimate path read as an escape.
+ const absolutePath = resolve(absoluteRoot, path);
const relativePath = relative(absoluteRoot, absolutePath);
if (relativePath === '' || relativePath.startsWith('..') || relativePath.includes('\\')) {
throw new TypeError(`provenance path escapes its isolated root: ${path}`);
@@ -380,7 +386,16 @@ function jsonValue(value: unknown): JsonValue {
if (typeof value !== 'object') {
throw new TypeError('release provenance must contain only JSON values');
}
- return Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, jsonValue(entry)]));
+ return Object.entries(value).reduce>((acc, [key, entry]) => {
+ // Own data property, never a prototype write: `acc.__proto__ = …` would mutate the clone.
+ Object.defineProperty(acc, key, {
+ configurable: true,
+ enumerable: true,
+ value: jsonValue(entry),
+ writable: true,
+ });
+ return acc;
+ }, {});
}
export function parseReleaseArtifactInputs(
diff --git a/packages/foundation/common/src/node/release-compliance.ts b/packages/foundation/common/src/node/release-compliance.ts
index 21c893b52..90178bea9 100644
--- a/packages/foundation/common/src/node/release-compliance.ts
+++ b/packages/foundation/common/src/node/release-compliance.ts
@@ -57,13 +57,16 @@ function assertSafeConfigurationValue(
throw new TypeError(`${path} looks like executable code or an executable-code URL`);
}
if (Array.isArray(value)) {
- for (const [index, entry] of value.entries()) {
+ for (let index = 0, len = value.length; index < len; index++) {
+ const entry = value[index];
assertSafeConfigurationValue(entry, `${path}[${index}]`, disclosedFeatures);
}
return;
}
if (typeof value !== 'object' || value === null) return;
- for (const [key, entry] of Object.entries(value)) {
+ const entries = Object.entries(value);
+ for (let i = 0, len = entries.length; i < len; i++) {
+ const [key, entry] = entries[i];
const tokens = configurationKeyTokens(key);
if (tokens.some((token) => EXECUTABLE_KEY_TOKENS.has(token))) {
throw new TypeError(`${path}.${key} declares an executable-code surface`);
@@ -90,7 +93,8 @@ export function assertStoreCompliance(
`compliance checklist must contain exactly: ${STORE_CHECKLIST_KEYS.join(', ')}`,
);
}
- for (const key of STORE_CHECKLIST_KEYS) {
+ for (let i = 0, len = STORE_CHECKLIST_KEYS.length; i < len; i++) {
+ const key = STORE_CHECKLIST_KEYS[i];
if (!declaration.checklist[key]) {
throw new TypeError(`compliance checklist ${key} must be true`);
}
@@ -104,7 +108,8 @@ export function assertStoreCompliance(
`disclosedFeatures must exactly match configurable feature/module keys: ${configurableFeatures.join(', ')}`,
);
}
- for (const key of keys) {
+ for (let i = 0, len = keys.length; i < len; i++) {
+ const key = keys[i];
if (configurationKeyTokens(key).some((token) => EXECUTABLE_KEY_TOKENS.has(token))) {
throw new TypeError(`configuration key ${key} declares an executable-code surface`);
}
diff --git a/packages/foundation/common/src/sentry/index.ts b/packages/foundation/common/src/sentry/index.ts
index 1ffe29925..5ceb992d5 100644
--- a/packages/foundation/common/src/sentry/index.ts
+++ b/packages/foundation/common/src/sentry/index.ts
@@ -105,9 +105,12 @@ export function sanitizeSentryTransaction(
? sanitizeProfileContext(event.contexts.profile)
: undefined;
const spans: TransactionSpan[] = [];
- for (const span of event.spans ?? []) {
- const sanitizedSpan = sanitizeSpan(span, options);
- if (sanitizedSpan) spans.push(sanitizedSpan);
+ if (event.spans != null) {
+ for (let i = 0, len = event.spans.length; i < len; i++) {
+ const span = event.spans[i];
+ const sanitizedSpan = sanitizeSpan(span, options);
+ if (sanitizedSpan) spans.push(sanitizedSpan);
+ }
}
const sanitized: SentryTransactionEvent = {
event_id: validId(event.event_id, RE_EVENT_ID),
@@ -122,7 +125,11 @@ export function sanitizeSentryTransaction(
measurements: sanitizeMeasurements(event.measurements, options.safeMeasurementNames),
};
- for (const key of Object.keys(event)) Reflect.deleteProperty(event, key);
+ const eventKeys = Object.keys(event);
+ for (let i = 0, len = eventKeys.length; i < len; i++) {
+ const key = eventKeys[i];
+ Reflect.deleteProperty(event, key);
+ }
Object.assign(event, sanitized);
return event;
}
@@ -187,7 +194,8 @@ function sanitizeMeasurements(
if (!isRecord(measurements) || !safeNames?.length) return undefined;
const sanitized: Record = {};
let hasMeasurement = false;
- for (const name of safeNames) {
+ for (let i = 0, len = safeNames.length; i < len; i++) {
+ const name = safeNames[i];
const measurement = measurements[name];
if (!isRecord(measurement)) continue;
const value = finiteNumber(measurement.value);
diff --git a/packages/foundation/common/src/telemetry-config/index.ts b/packages/foundation/common/src/telemetry-config/index.ts
index bf256cab9..07eac769e 100644
--- a/packages/foundation/common/src/telemetry-config/index.ts
+++ b/packages/foundation/common/src/telemetry-config/index.ts
@@ -57,7 +57,7 @@ export function parseTelemetryConfig(value: unknown): TelemetryConfig | null {
/** Fetches public sampling configuration without delaying telemetry initialization on failure. */
export async function fetchTelemetryConfig(
- fetcher: TelemetryConfigFetcher = globalThis.fetch,
+ fetcher: TelemetryConfigFetcher = fetch,
): Promise {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 3000);
diff --git a/packages/foundation/providers/package.json b/packages/foundation/providers/package.json
index ddb3b1805..1e7e58a3b 100644
--- a/packages/foundation/providers/package.json
+++ b/packages/foundation/providers/package.json
@@ -11,6 +11,6 @@
},
"dependencies": {
"@linkcode/schema": "workspace:*",
- "foxts": "^5.8.1"
+ "foxts": "^5.9.1"
}
}
diff --git a/packages/foundation/providers/src/__tests__/resolve.test.ts b/packages/foundation/providers/src/__tests__/resolve.test.ts
index c06c3c48d..eb2e0af89 100644
--- a/packages/foundation/providers/src/__tests__/resolve.test.ts
+++ b/packages/foundation/providers/src/__tests__/resolve.test.ts
@@ -31,7 +31,9 @@ describe('resolveBinding: one explicit endpoint', () => {
it('routes an anthropic endpoint natively to claude-code, opencode and pi', () => {
const endpoint = { baseUrl: 'https://api.anthropic.com', protocol: 'anthropic' } as const;
const anthropic = account({ endpoint });
- for (const kind of ['claude-code', 'opencode', 'pi'] as const) {
+ const nativeKinds = ['claude-code', 'opencode', 'pi'] as const;
+ for (let i = 0, len = nativeKinds.length; i < len; i++) {
+ const kind = nativeKinds[i];
expect(resolveBinding(anthropic, kind)).toEqual({ tier: 'native', ...endpoint });
}
expect(resolveBinding(anthropic, 'codex')).toEqual({
@@ -68,7 +70,8 @@ describe('resolveBinding: one explicit endpoint', () => {
it('keeps a pre-catalog bare key bindable everywhere', () => {
const legacy = account({});
- for (const kind of ALL_KINDS) {
+ for (let i = 0, len = ALL_KINDS.length; i < len; i++) {
+ const kind = ALL_KINDS[i];
expect(resolveBinding(legacy, kind)).toEqual({ tier: 'native' });
}
});
@@ -120,7 +123,9 @@ describe('resolveBinding: variant chosen per agent', () => {
protocol: 'openai-responses',
baseUrl: 'https://api.deepseek.com',
});
- for (const kind of ['opencode', 'pi'] as const) {
+ const providerRoutedKinds = ['opencode', 'pi'] as const;
+ for (let i = 0, len = providerRoutedKinds.length; i < len; i++) {
+ const kind = providerRoutedKinds[i];
expect(resolveBinding(deepseek, kind)).toEqual({
tier: 'native',
protocol: 'openai-chat',
@@ -172,7 +177,8 @@ describe('resolveBinding: variant chosen per agent', () => {
['openrouter', 'https://openrouter.ai/api/v1'],
['vercel-gateway', 'https://ai-gateway.vercel.sh/v1'],
] as const;
- for (const [service, baseUrl] of cases) {
+ for (let i = 0, len = cases.length; i < len; i++) {
+ const [service, baseUrl] = cases[i];
expect(resolveBinding(account({ service }), 'codex')).toEqual({
tier: 'native',
protocol: 'openai-responses',
@@ -201,7 +207,9 @@ describe('resolveBinding: variant chosen per agent', () => {
protocol: 'openai-chat',
baseUrl: 'https://gateway.linkcode.ai/v1',
});
- for (const kind of ['opencode', 'pi'] as const) {
+ const providerRoutedKinds = ['opencode', 'pi'] as const;
+ for (let i = 0, len = providerRoutedKinds.length; i < len; i++) {
+ const kind = providerRoutedKinds[i];
expect(resolveBinding(gateway, kind)).toEqual({
tier: 'native',
protocol: 'openai-chat',
@@ -217,7 +225,9 @@ describe('resolveBinding: variant chosen per agent', () => {
it('leaves opencode and pi on their known-provider variant when a responses one exists', () => {
// The added responses variants carry no knownProvider, so provider-routed agents are untouched.
- for (const kind of ['opencode', 'pi'] as const) {
+ const providerRoutedKinds = ['opencode', 'pi'] as const;
+ for (let i = 0, len = providerRoutedKinds.length; i < len; i++) {
+ const kind = providerRoutedKinds[i];
expect(resolveBinding(account({ service: 'xai' }), kind)).toMatchObject({
protocol: 'openai-chat',
knownProvider: 'xai',
@@ -355,7 +365,9 @@ describe('catalog helpers', () => {
it('keeps the model-list url independent of the variant an agent resolves to', () => {
// Deriving from the resolved variant is what this replaced: the anthropic variants of these two
// sit on different paths, so appending would ask a route that does not exist.
- for (const id of ['deepseek', 'vercel-gateway']) {
+ const serviceIds = ['deepseek', 'vercel-gateway'];
+ for (let i = 0, len = serviceIds.length; i < len; i++) {
+ const id = serviceIds[i];
const service = nullthrow(endpointServiceById(id), `${id} missing`);
const anthropic = nullthrow(service.variants.anthropic, `${id} anthropic variant missing`);
expect(nullthrow(service.models, `${id} model list missing`).url).not.toBe(
diff --git a/packages/foundation/providers/src/detected-logins.ts b/packages/foundation/providers/src/detected-logins.ts
index 12ba1d40d..823a0d4c1 100644
--- a/packages/foundation/providers/src/detected-logins.ts
+++ b/packages/foundation/providers/src/detected-logins.ts
@@ -17,7 +17,8 @@ export function detectedLogins(
runtimes: AgentRuntimes | undefined,
): DetectedLogin[] {
const suggestions: DetectedLogin[] = [];
- for (const service of SERVICE_CATALOG) {
+ for (let i = 0, len = SERVICE_CATALOG.length; i < len; i++) {
+ const service = SERVICE_CATALOG[i];
if (service.kind !== 'oauth') continue;
const auth = runtimes?.[service.agent]?.auth;
if (auth?.loggedIn !== true) continue;
diff --git a/packages/foundation/providers/src/resolve.ts b/packages/foundation/providers/src/resolve.ts
index 945c37eaf..fc956ff74 100644
--- a/packages/foundation/providers/src/resolve.ts
+++ b/packages/foundation/providers/src/resolve.ts
@@ -96,7 +96,9 @@ function resolveService(
account: Account,
kind: AgentKind,
): ResolvedBinding {
- for (const protocol of preferredProtocols(service, kind)) {
+ const protocols = preferredProtocols(service, kind);
+ for (let i = 0, len = protocols.length; i < len; i++) {
+ const protocol = protocols[i];
const variant = service.variants[protocol];
if (!variant) continue;
const params = account.endpointParams ?? {};
@@ -191,7 +193,9 @@ function providerEnvFor(
const mapping = variant.endpointEnv?.[kind];
if (!mapping) return undefined;
const env: Record = {};
- for (const [param, name] of Object.entries(mapping)) {
+ const mappingEntries = Object.entries(mapping);
+ for (let i = 0, len = mappingEntries.length; i < len; i++) {
+ const [param, name] = mappingEntries[i];
const value = params[param];
if (value !== undefined) env[name] = value;
}
diff --git a/packages/foundation/providers/src/template.ts b/packages/foundation/providers/src/template.ts
index e05f07401..0e5e25b64 100644
--- a/packages/foundation/providers/src/template.ts
+++ b/packages/foundation/providers/src/template.ts
@@ -2,7 +2,7 @@ const PLACEHOLDER_PATTERN = /\{([a-z_]+)\}/g;
/** Account-specific `{placeholder}` fields a templated endpoint URL needs filled. */
export function templatePlaceholders(baseUrl: string): string[] {
- return [...baseUrl.matchAll(PLACEHOLDER_PATTERN)].map((match) => match[1]);
+ return Array.from(baseUrl.matchAll(PLACEHOLDER_PATTERN), (match) => match[1]);
}
export function fillTemplate(baseUrl: string, values: Record): string {
diff --git a/packages/foundation/schema/package.json b/packages/foundation/schema/package.json
index dc0f951be..d9806d165 100644
--- a/packages/foundation/schema/package.json
+++ b/packages/foundation/schema/package.json
@@ -16,6 +16,7 @@
},
"dependencies": {
"canonicalize": "3.0.0",
+ "foxts": "^5.9.1",
"zod": "catalog:"
},
"devDependencies": {
diff --git a/packages/foundation/schema/src/model/__tests__/linkcode-marketplace.test.ts b/packages/foundation/schema/src/model/__tests__/linkcode-marketplace.test.ts
index 5c760c50d..360c7646a 100644
--- a/packages/foundation/schema/src/model/__tests__/linkcode-marketplace.test.ts
+++ b/packages/foundation/schema/src/model/__tests__/linkcode-marketplace.test.ts
@@ -58,7 +58,9 @@ describe('LinkCode marketplace contracts', () => {
});
it('rejects insecure remote sources and duplicate local marketplace ids', () => {
- for (const url of ['http://example.com/index.json', 'https:example.com/index.json']) {
+ const insecureUrls = ['http://example.com/index.json', 'https:example.com/index.json'];
+ for (let i = 0, len = insecureUrls.length; i < len; i++) {
+ const url = insecureUrls[i];
expect(
LinkCodeMarketplaceConfigListSchema.safeParse([marketplace('community', url)]).success,
).toBe(false);
diff --git a/packages/foundation/schema/src/model/__tests__/plugin.test.ts b/packages/foundation/schema/src/model/__tests__/plugin.test.ts
index 795b9a435..a36d61cb5 100644
--- a/packages/foundation/schema/src/model/__tests__/plugin.test.ts
+++ b/packages/foundation/schema/src/model/__tests__/plugin.test.ts
@@ -219,7 +219,7 @@ describe('LinkCode plugin package contracts', () => {
String.raw`skills\latex\SKILL.md`,
'C:/SKILL.md',
'skills/latex./SKILL.md',
- `skills/latex${String.fromCodePoint(0)}/SKILL.md`,
+ `skills/latex${String.fromCharCode(0)}/SKILL.md`,
'skills/latex/readme.md',
'skills/latex/skill.md',
])('rejects invalid skill package entry %s', (entry) => {
diff --git a/packages/foundation/schema/src/model/linkcode-marketplace.ts b/packages/foundation/schema/src/model/linkcode-marketplace.ts
index f4ab15267..eff08fea1 100644
--- a/packages/foundation/schema/src/model/linkcode-marketplace.ts
+++ b/packages/foundation/schema/src/model/linkcode-marketplace.ts
@@ -1,3 +1,4 @@
+import { split0th } from 'foxts/split-nth';
import { z } from 'zod';
import {
LinkCodeMarketplaceIdSchema,
@@ -38,7 +39,8 @@ export const LinkCodeMarketplaceConfigListSchema = z
.array(LinkCodeMarketplaceConfigSchema)
.superRefine((marketplaces, ctx) => {
const ids = new Set();
- for (const [index, marketplace] of marketplaces.entries()) {
+ for (let index = 0, len = marketplaces.length; index < len; index++) {
+ const marketplace = marketplaces[index];
if (ids.has(marketplace.id)) {
ctx.addIssue({
code: 'custom',
@@ -56,7 +58,8 @@ function validateMarketplacePlugin(
ctx: z.RefinementCtx,
): void {
const versions = new Set();
- for (const [index, release] of plugin.releases.entries()) {
+ for (let index = 0, len = plugin.releases.length; index < len; index++) {
+ const release = plugin.releases[index];
if (release.manifest.id !== plugin.id) {
ctx.addIssue({
code: 'custom',
@@ -64,7 +67,7 @@ function validateMarketplacePlugin(
path: ['releases', index, 'manifest', 'id'],
});
}
- const precedenceVersion = release.manifest.version.split('+', 1)[0];
+ const precedenceVersion = split0th(release.manifest.version, '+');
if (versions.has(precedenceVersion)) {
ctx.addIssue({
code: 'custom',
@@ -108,7 +111,8 @@ function rejectDuplicatePlugins(
ctx: z.RefinementCtx,
): void {
const pluginIds = new Set();
- for (const [pluginIndex, plugin] of index.plugins.entries()) {
+ for (let pluginIndex = 0, len = index.plugins.length; pluginIndex < len; pluginIndex++) {
+ const plugin = index.plugins[pluginIndex];
if (pluginIds.has(plugin.id)) {
ctx.addIssue({
code: 'custom',
diff --git a/packages/foundation/schema/src/model/linkcode-plugin.ts b/packages/foundation/schema/src/model/linkcode-plugin.ts
index 4155f625c..157ac4f84 100644
--- a/packages/foundation/schema/src/model/linkcode-plugin.ts
+++ b/packages/foundation/schema/src/model/linkcode-plugin.ts
@@ -126,7 +126,8 @@ function rejectDuplicateComponents(
ctx: z.RefinementCtx,
): void {
const names = new Set();
- for (const component of manifest.components) {
+ for (let i = 0, len = manifest.components.length; i < len; i++) {
+ const component = manifest.components[i];
const key = `${component.kind}:${component.name}`;
if (names.has(key)) {
ctx.addIssue({
diff --git a/packages/foundation/schema/src/model/permission.ts b/packages/foundation/schema/src/model/permission.ts
index 293e72f42..3e5d72baa 100644
--- a/packages/foundation/schema/src/model/permission.ts
+++ b/packages/foundation/schema/src/model/permission.ts
@@ -24,7 +24,8 @@ const PermissionOptionsSchema = z
.min(1)
.superRefine((options, ctx) => {
const optionIds = new Set();
- for (const [index, option] of options.entries()) {
+ for (let index = 0, len = options.length; index < len; index++) {
+ const option = options[index];
if (optionIds.has(option.optionId)) {
ctx.addIssue({
code: 'custom',
diff --git a/packages/foundation/schema/src/model/question.ts b/packages/foundation/schema/src/model/question.ts
index 53ce878aa..8ebbe4276 100644
--- a/packages/foundation/schema/src/model/question.ts
+++ b/packages/foundation/schema/src/model/question.ts
@@ -18,7 +18,8 @@ const QuestionOptionsSchema = z
.min(1)
.superRefine((options, ctx) => {
const optionIds = new Set();
- for (const [index, option] of options.entries()) {
+ for (let index = 0, len = options.length; index < len; index++) {
+ const option = options[index];
if (optionIds.has(option.optionId)) {
ctx.addIssue({
code: 'custom',
@@ -48,7 +49,8 @@ const QuestionsSchema = z
.min(1)
.superRefine((questions, ctx) => {
const questionIds = new Set();
- for (const [index, question] of questions.entries()) {
+ for (let index = 0, len = questions.length; index < len; index++) {
+ const question = questions[index];
if (questionIds.has(question.questionId)) {
ctx.addIssue({
code: 'custom',
diff --git a/packages/foundation/schema/src/remote-config.ts b/packages/foundation/schema/src/remote-config.ts
index d496fc747..af25705d8 100644
--- a/packages/foundation/schema/src/remote-config.ts
+++ b/packages/foundation/schema/src/remote-config.ts
@@ -139,7 +139,9 @@ export const ConfigSnapshotSchema = z
.catchall(JsonValueSchema)
.superRefine((snapshot, context) => {
const coveredKeys = new Set();
- for (const [key, value] of Object.entries(snapshot.values)) {
+ const valueEntries = Object.entries(snapshot.values);
+ for (let i = 0, len = valueEntries.length; i < len; i++) {
+ const [key, value] = valueEntries[i];
if (!isConfigKey(key)) {
context.addIssue({
code: 'custom',
@@ -156,8 +158,11 @@ export const ConfigSnapshotSchema = z
}
coveredKeys.add(key);
}
- for (const [index, override] of snapshot.overrides.entries()) {
- for (const [key, value] of Object.entries(override.set)) {
+ for (let index = 0, len = snapshot.overrides.length; index < len; index++) {
+ const override = snapshot.overrides[index];
+ const overrideEntries = Object.entries(override.set);
+ for (let j = 0, entryCount = overrideEntries.length; j < entryCount; j++) {
+ const [key, value] = overrideEntries[j];
if (!isConfigKey(key)) {
context.addIssue({
code: 'custom',
@@ -175,7 +180,9 @@ export const ConfigSnapshotSchema = z
coveredKeys.add(key);
}
}
- for (const key of Object.keys(snapshot.rollouts)) {
+ const rolloutKeys = Object.keys(snapshot.rollouts);
+ for (let i = 0, len = rolloutKeys.length; i < len; i++) {
+ const key = rolloutKeys[i];
if (!key.startsWith('feature.') || !isConfigKey(key)) {
context.addIssue({
code: 'custom',
@@ -185,7 +192,9 @@ export const ConfigSnapshotSchema = z
}
coveredKeys.add(key);
}
- for (const key of Object.keys(snapshot.applyModes)) {
+ const applyModeKeys = Object.keys(snapshot.applyModes);
+ for (let i = 0, len = applyModeKeys.length; i < len; i++) {
+ const key = applyModeKeys[i];
if (!isConfigKey(key)) {
context.addIssue({
code: 'custom',
@@ -271,7 +280,9 @@ export const ConfigBuildBundleSchema = z
snapshot: ConfigBuildBundleSnapshotEnvelopeSchema,
})
.superRefine((bundle, context) => {
- for (const kind of ['normal', 'emergency'] as const) {
+ const keyringKinds = ['normal', 'emergency'] as const;
+ for (let i = 0, len = keyringKinds.length; i < len; i++) {
+ const kind = keyringKinds[i];
if (
(bundle.endpoints[kind] === null) !==
(Object.keys(bundle.keyrings[kind]).at(0) === undefined)
@@ -338,7 +349,8 @@ export const EmergencyDocumentSchema = z
})
.catchall(SignedJsonValueSchema)
.superRefine((document, context) => {
- for (const [index, key] of document.disabledFeatures.entries()) {
+ for (let index = 0, len = document.disabledFeatures.length; index < len; index++) {
+ const key = document.disabledFeatures[index];
if (!key.startsWith('feature.') || !isConfigKey(key)) {
context.addIssue({
code: 'custom',
@@ -390,7 +402,19 @@ export function compareMonotonicVersions(left: string, right: string): number {
export function canonicalSignedPayload(document: unknown): string {
if (!isRecord(document)) throw new TypeError('signed envelope must be an object');
SignatureSchema.parse(document.sig);
- const unsigned = Object.fromEntries(Object.entries(document).filter(([key]) => key !== 'sig'));
+ const unsigned = Object.entries(document).reduce>((acc, [key, value]) => {
+ if (key !== 'sig') {
+ // Own data property, never a prototype write: a JSON `__proto__` member must survive
+ // into the canonical payload instead of silently mutating (and vanishing from) it.
+ Object.defineProperty(acc, key, {
+ configurable: true,
+ enumerable: true,
+ value,
+ writable: true,
+ });
+ }
+ return acc;
+ }, {});
assertSignedEnvelopeValue(unsigned, 'signed envelope payload');
return canonicalizeJson(unsigned);
}
@@ -486,13 +510,16 @@ function assertSignedEnvelopeValue(value: unknown, label: string): asserts value
}
if (value === null || typeof value === 'boolean' || typeof value === 'string') return;
if (Array.isArray(value)) {
- for (const [index, entry] of value.entries()) {
+ for (let index = 0, len = value.length; index < len; index++) {
+ const entry = value[index];
assertSignedEnvelopeValue(entry, `${label}[${index}]`);
}
return;
}
if (!isRecord(value)) throw new TypeError(`${label} must contain only JSON values`);
- for (const [key, entry] of Object.entries(value)) {
+ const entries = Object.entries(value);
+ for (let i = 0, len = entries.length; i < len; i++) {
+ const [key, entry] = entries[i];
assertSignedEnvelopeValue(entry, `${label}.${key}`);
}
}
diff --git a/packages/foundation/schema/tests/contract/wire/loop.test.ts b/packages/foundation/schema/tests/contract/wire/loop.test.ts
index 83142a4d9..45314c260 100644
--- a/packages/foundation/schema/tests/contract/wire/loop.test.ts
+++ b/packages/foundation/schema/tests/contract/wire/loop.test.ts
@@ -63,7 +63,8 @@ describe('loop wire variants', () => {
{ kind: 'loop.iteration', iteration },
{ kind: 'loop.log', loopId: loop.loopId, entry: log },
];
- for (const payload of payloads) {
+ for (let i = 0, len = payloads.length; i < len; i++) {
+ const payload = payloads[i];
expect(parseWireMessage(envelope(payload)).ok, payload.kind).toBe(true);
}
});
diff --git a/packages/foundation/schema/tests/contract/wire/plugin.test.ts b/packages/foundation/schema/tests/contract/wire/plugin.test.ts
index 66380b264..dc8649e33 100644
--- a/packages/foundation/schema/tests/contract/wire/plugin.test.ts
+++ b/packages/foundation/schema/tests/contract/wire/plugin.test.ts
@@ -134,7 +134,9 @@ describe('plugin wire schema', () => {
});
it('round-trips install and uninstall requests', () => {
- for (const kind of ['plugin.install', 'plugin.uninstall']) {
+ const kinds = ['plugin.install', 'plugin.uninstall'];
+ for (let i = 0, len = kinds.length; i < len; i++) {
+ const kind = kinds[i];
expect(
parseWireMessage(
envelope({
diff --git a/packages/foundation/schema/tests/contract/wire/schedule.test.ts b/packages/foundation/schema/tests/contract/wire/schedule.test.ts
index 4f9db92fb..6853462d4 100644
--- a/packages/foundation/schema/tests/contract/wire/schedule.test.ts
+++ b/packages/foundation/schema/tests/contract/wire/schedule.test.ts
@@ -54,7 +54,8 @@ describe('schedule wire variants', () => {
{ kind: 'schedule.removed', scheduleId: schedule.scheduleId },
{ kind: 'schedule.run', run },
];
- for (const payload of payloads) {
+ for (let i = 0, len = payloads.length; i < len; i++) {
+ const payload = payloads[i];
expect(parseWireMessage(envelope(payload)).ok, payload.kind).toBe(true);
}
});
diff --git a/packages/foundation/transport/package.json b/packages/foundation/transport/package.json
index 7c6a76a3f..8469f8f13 100644
--- a/packages/foundation/transport/package.json
+++ b/packages/foundation/transport/package.json
@@ -15,7 +15,7 @@
"dependencies": {
"@linkcode/schema": "workspace:*",
"@linkcode/tunnel": "^0.1.0",
- "foxts": "^5.8.0",
+ "foxts": "^5.9.1",
"socket.io": "catalog:",
"socket.io-client": "catalog:",
"ws": "catalog:",
diff --git a/packages/foundation/transport/src/__tests__/tunnel.test.ts b/packages/foundation/transport/src/__tests__/tunnel.test.ts
index f9dbab2af..2f04a685f 100644
--- a/packages/foundation/transport/src/__tests__/tunnel.test.ts
+++ b/packages/foundation/transport/src/__tests__/tunnel.test.ts
@@ -53,7 +53,10 @@ class FakeWebSocket {
}
emit(type: string, event: unknown): void {
- for (const cb of this.listeners.get(type) ?? []) cb(event);
+ const callbacks = this.listeners.get(type);
+ if (callbacks != null) {
+ for (const cb of callbacks) cb(event);
+ }
}
}
@@ -106,7 +109,9 @@ describe('TunnelTransportServer', () => {
const inbound: unknown[] = [];
connection.onMessage((message) => inbound.push(message));
const request = createWireMessage({ kind: 'session.list', clientReqId: 'r1' });
- for (const data of new TunnelChunkEncoder(1).encode(JSON.stringify(request))) {
+ const chunks = new TunnelChunkEncoder(1).encode(JSON.stringify(request));
+ for (let i = 0, len = chunks.length; i < len; i++) {
+ const data = chunks[i];
socket.emit('message', {
data: encodeTunnelPeerFrame({ kind: 'peer.data', peerId: 'peer-1', data }),
});
@@ -115,7 +120,8 @@ describe('TunnelTransportServer', () => {
connection.send(createWireMessage({ kind: 'request.succeeded', replyTo: 'r1' }));
let outbound: TunnelPeerFrame | null = null;
- for (const sent of socket.sent) {
+ for (let i = 0, len = socket.sent.length; i < len; i++) {
+ const sent = socket.sent[i];
if (!(sent instanceof ArrayBuffer)) continue;
const frame = decodeTunnelPeerFrame(sent);
if (frame?.kind === 'peer.data') {
@@ -330,7 +336,9 @@ describe('TunnelTransportServer', () => {
candidate.emit('message', {
data: encodeTunnelPeerFrame({ kind: 'peer.join', peerId: 'peer-1' }),
});
- for (const data of chunks.slice(1)) {
+ const remainingChunks = chunks.slice(1);
+ for (let i = 0, len = remainingChunks.length; i < len; i++) {
+ const data = remainingChunks[i];
candidate.emit('message', {
data: encodeTunnelPeerFrame({ kind: 'peer.data', peerId: 'peer-1', data }),
});
@@ -346,7 +354,8 @@ describe('TunnelTransportServer', () => {
expect(inbound).toEqual([request]);
await vi.waitFor(() => {
let outbound = 0;
- for (const sent of candidate.sent) {
+ for (let i = 0, len = candidate.sent.length; i < len; i++) {
+ const sent = candidate.sent[i];
if (!(sent instanceof ArrayBuffer)) continue;
if (decodeTunnelPeerFrame(sent)?.kind === 'peer.data') outbound += 1;
}
@@ -401,7 +410,9 @@ describe('TunnelTransportServer', () => {
candidate.emit('message', {
data: encodeTunnelPeerFrame({ kind: 'peer.join', peerId: 'peer-1' }),
});
- for (const data of new TunnelChunkEncoder(2).encode(JSON.stringify(request))) {
+ const chunks = new TunnelChunkEncoder(2).encode(JSON.stringify(request));
+ for (let i = 0, len = chunks.length; i < len; i++) {
+ const data = chunks[i];
candidate.emit('message', {
data: encodeTunnelPeerFrame({ kind: 'peer.data', peerId: 'peer-1', data }),
});
diff --git a/packages/foundation/transport/src/http-server.ts b/packages/foundation/transport/src/http-server.ts
index 9b68f5a0f..f7c81f3ed 100644
--- a/packages/foundation/transport/src/http-server.ts
+++ b/packages/foundation/transport/src/http-server.ts
@@ -2,6 +2,7 @@ import { once } from 'node:events';
import type { Server as HttpServer, IncomingMessage, ServerResponse } from 'node:http';
import type { DaemonIdentity } from '@linkcode/schema';
import { DAEMON_IDENTITY_PATH } from '@linkcode/schema';
+import { split0th } from 'foxts/split-nth';
/** Shared HTTP plumbing for the Node server transports: the `GET /linkcode` identity endpoint
* (tells a linkcode daemon apart from a foreign process holding the port) and a `listen` that
@@ -11,7 +12,7 @@ export function createIdentityRequestHandler(
identity: DaemonIdentity | undefined,
): (req: IncomingMessage, res: ServerResponse) => void {
return (req, res) => {
- if (identity && req.method === 'GET' && req.url?.split('?', 1)[0] === DAEMON_IDENTITY_PATH) {
+ if (identity && req.method === 'GET' && split0th(req.url ?? '', '?') === DAEMON_IDENTITY_PATH) {
res.writeHead(200, { 'content-type': 'application/json' });
res.end(JSON.stringify(identity));
return;
diff --git a/packages/foundation/transport/src/hub.ts b/packages/foundation/transport/src/hub.ts
index 8a6923d92..5c4fc3e96 100644
--- a/packages/foundation/transport/src/hub.ts
+++ b/packages/foundation/transport/src/hub.ts
@@ -126,14 +126,25 @@ export class Hub implements Transport {
bestEffort(() => conn.send(createWireMessage(pong())));
return;
}
- if (p.kind === 'session.attach') subscription.attached.add(p.sessionId);
- else if (p.kind === 'session.detach') subscription.attached.delete(p.sessionId);
- else if (p.kind === 'terminal.detach') {
- const attachments = subscription.terminals.get(p.terminalId);
- if (attachments?.get(p.attachmentId) === p.attachmentSecret) {
- attachments.delete(p.attachmentId);
- if (attachments.size === 0) subscription.terminals.delete(p.terminalId);
+ switch (p.kind) {
+ case 'session.attach': {
+ subscription.attached.add(p.sessionId);
+ break;
}
+ case 'session.detach': {
+ subscription.attached.delete(p.sessionId);
+ break;
+ }
+ case 'terminal.detach': {
+ const attachments = subscription.terminals.get(p.terminalId);
+ if (attachments?.get(p.attachmentId) === p.attachmentSecret) {
+ attachments.delete(p.attachmentId);
+ if (attachments.size === 0) subscription.terminals.delete(p.terminalId);
+ }
+
+ break;
+ }
+ // no default
}
}
if ('clientReqId' in p) {
diff --git a/packages/foundation/transport/src/preview-proxy.ts b/packages/foundation/transport/src/preview-proxy.ts
index 3cadff99c..7356be535 100644
--- a/packages/foundation/transport/src/preview-proxy.ts
+++ b/packages/foundation/transport/src/preview-proxy.ts
@@ -198,7 +198,7 @@ function proxyUpgrade(req: IncomingMessage, socket: Duplex, head: Buffer, port:
upstream.on('connect', () => {
const lines = [`${req.method ?? 'GET'} ${req.url ?? '/'} HTTP/1.1`];
- for (let i = 0; i < req.rawHeaders.length; i += 2) {
+ for (let i = 0, len = req.rawHeaders.length; i < len; i += 2) {
lines.push(`${req.rawHeaders[i]}: ${req.rawHeaders[i + 1]}`);
}
upstream.write(`${lines.join('\r\n')}\r\n\r\n`);
diff --git a/packages/foundation/transport/src/tunnel-client.ts b/packages/foundation/transport/src/tunnel-client.ts
index bc98d2118..e17a378c7 100644
--- a/packages/foundation/transport/src/tunnel-client.ts
+++ b/packages/foundation/transport/src/tunnel-client.ts
@@ -101,7 +101,11 @@ export class TunnelClient {
throw new Error('TunnelClient: host messages must be sent through a peer');
}
const ws = this.openSocket();
- for (const frame of this.encoder.encode(message)) ws.send(frame);
+ const frames = this.encoder.encode(message);
+ for (let i = 0, len = frames.length; i < len; i++) {
+ const frame = frames[i];
+ ws.send(frame);
+ }
}
onMessage(cb: (message: string) => void): () => void {
@@ -155,7 +159,10 @@ export class TunnelClient {
throw failure;
}
if (this.candidateWs === ws) this.candidateWs = null;
- for (const event of prepared.buffered) this.handleMessage(ws, event);
+ for (let i = 0, len = prepared.buffered.length; i < len; i++) {
+ const event = prepared.buffered[i];
+ this.handleMessage(ws, event);
+ }
if (this.closedByUser || ws !== this.ws) {
throw new Error('TunnelClient: socket closed during handshake');
}
@@ -277,7 +284,12 @@ export class TunnelClient {
this.adopt(ws, prepared);
const queued = this.handoffQueue;
this.handoffQueue = null;
- if (queued) for (const frame of queued) ws.send(frame);
+ if (queued) {
+ for (let i = 0, len = queued.length; i < len; i++) {
+ const frame = queued[i];
+ ws.send(frame);
+ }
+ }
if (current !== ws && current.readyState === current.OPEN) current.close(1000);
} catch {
if (handoffStarted) this.abortHandoff(current);
@@ -288,7 +300,9 @@ export class TunnelClient {
private sendToPeer(peerId: string, message: string): void {
if (!this.peers.has(peerId)) throw new Error('TunnelPeer: connection closed');
const ws = this.handoffQueue ? null : this.openSocket();
- for (const data of this.encoder.encode(message)) {
+ const chunks = this.encoder.encode(message);
+ for (let i = 0, len = chunks.length; i < len; i++) {
+ const data = chunks[i];
const frame = encodeTunnelPeerFrame({ kind: 'peer.data', peerId, data });
if (this.handoffQueue) this.handoffQueue.push(frame);
else ws?.send(frame);
@@ -358,7 +372,8 @@ export class TunnelClient {
private reconcilePeers(buffered: readonly MessageEvent[]): void {
const successorPeers = new Set();
- for (const event of buffered) {
+ for (let i = 0, len = buffered.length; i < len; i++) {
+ const event = buffered[i];
if (!(event.data instanceof ArrayBuffer)) continue;
const frame = decodeTunnelPeerFrame(event.data);
if (frame?.kind === 'peer.join') successorPeers.add(frame.peerId);
diff --git a/packages/foundation/transport/tests/integration/preview-proxy.test.ts b/packages/foundation/transport/tests/integration/preview-proxy.test.ts
index c24f6ae6f..5806f0aca 100644
--- a/packages/foundation/transport/tests/integration/preview-proxy.test.ts
+++ b/packages/foundation/transport/tests/integration/preview-proxy.test.ts
@@ -9,8 +9,12 @@ import { WebSocket, WebSocketServer } from 'ws';
const cleanups: Array<() => Promise | void> = [];
afterAll(async () => {
- // eslint-disable-next-line no-await-in-loop -- teardown must run one at a time in LIFO order
- for (const cleanup of cleanups.reverse()) await cleanup();
+ const pending = cleanups.reverse();
+ for (let i = 0, len = pending.length; i < len; i++) {
+ const cleanup = pending[i];
+ // eslint-disable-next-line no-await-in-loop -- teardown must run one at a time in LIFO order
+ await cleanup();
+ }
});
/** An upstream dev-server stand-in: echoes the request path and Host, and echoes WS frames. */
diff --git a/packages/host/agent-adapter/package.json b/packages/host/agent-adapter/package.json
index d364c6128..2e69f9ea9 100644
--- a/packages/host/agent-adapter/package.json
+++ b/packages/host/agent-adapter/package.json
@@ -20,7 +20,7 @@
"@openai/codex": "^0.144.6",
"@opencode-ai/sdk": "^1.18.3",
"cross-spawn": "^7.0.6",
- "foxts": "^5.8.0",
+ "foxts": "^5.9.1",
"smol-toml": "^1.7.1",
"zod": "catalog:"
},
diff --git a/packages/host/agent-adapter/src/__tests__/claude-code-approval.test.ts b/packages/host/agent-adapter/src/__tests__/claude-code-approval.test.ts
index d1a6d5ee2..9b6569d15 100644
--- a/packages/host/agent-adapter/src/__tests__/claude-code-approval.test.ts
+++ b/packages/host/agent-adapter/src/__tests__/claude-code-approval.test.ts
@@ -262,7 +262,6 @@ describe('ClaudeCodeAdapter approval policy', () => {
],
},
});
- // eslint-disable-next-line sukka/unicorn/prefer-single-call -- AsyncMessageQueue.push takes ONE message; the merge autofix silently drops this one
q.push({
type: 'system',
subtype: 'permission_denied',
diff --git a/packages/host/agent-adapter/src/__tests__/claude-code-commands.test.ts b/packages/host/agent-adapter/src/__tests__/claude-code-commands.test.ts
index d908d8a6a..f9eb86032 100644
--- a/packages/host/agent-adapter/src/__tests__/claude-code-commands.test.ts
+++ b/packages/host/agent-adapter/src/__tests__/claude-code-commands.test.ts
@@ -186,7 +186,10 @@ class FakeQuery {
}
function pushMessages(query: FakeQuery, ...messages: WireMessage[]): void {
- for (const message of messages) query.push(message);
+ for (let i = 0, len = messages.length; i < len; i++) {
+ const message = messages[i];
+ query.push(message);
+ }
}
const queries: FakeQuery[] = [];
diff --git a/packages/host/agent-adapter/src/__tests__/claude-code-effort.test.ts b/packages/host/agent-adapter/src/__tests__/claude-code-effort.test.ts
index 683e46232..3e8331601 100644
--- a/packages/host/agent-adapter/src/__tests__/claude-code-effort.test.ts
+++ b/packages/host/agent-adapter/src/__tests__/claude-code-effort.test.ts
@@ -172,7 +172,6 @@ describe('ClaudeCodeAdapter session titles', () => {
await adapter.start({ kind: 'claude-code', cwd: '/tmp/repo' });
await prompt(adapter);
queries[0].push({ type: 'system', session_id: 'sess-title' });
- // eslint-disable-next-line sukka/unicorn/prefer-single-call -- FakeQuery.push accepts one provider message at a time
queries[0].push({
type: 'result',
subtype: 'success',
@@ -224,7 +223,6 @@ describe('ClaudeCodeAdapter session titles', () => {
await adapter.start({ kind: 'claude-code', cwd: '/tmp/repo' });
await prompt(adapter);
queries[0].push({ type: 'system', session_id: 'sess-rename' });
- // eslint-disable-next-line sukka/unicorn/prefer-single-call -- FakeQuery.push accepts one provider message at a time
queries[0].push({
type: 'result',
subtype: 'success',
@@ -280,7 +278,6 @@ describe('ClaudeCodeAdapter session titles', () => {
await adapter.start({ kind: 'claude-code', cwd: '/tmp/repo' });
await prompt(adapter);
queries[0].push({ type: 'system', session_id: 'sess-delayed' });
- // eslint-disable-next-line sukka/unicorn/prefer-single-call -- FakeQuery.push accepts one provider message at a time
queries[0].push({
type: 'result',
subtype: 'success',
diff --git a/packages/host/agent-adapter/src/__tests__/claude-code-subagent.test.ts b/packages/host/agent-adapter/src/__tests__/claude-code-subagent.test.ts
index 2914aaa67..2443eb807 100644
--- a/packages/host/agent-adapter/src/__tests__/claude-code-subagent.test.ts
+++ b/packages/host/agent-adapter/src/__tests__/claude-code-subagent.test.ts
@@ -317,7 +317,8 @@ describe('ClaudeCodeAdapter readHistory subagent splice', () => {
'agent-message:u3',
]);
- for (const e of result.events) {
+ for (let i = 0, len = result.events.length; i < len; i++) {
+ const e = result.events[i];
if (e.event.type === 'tool-call' && e.event.toolCall.toolCallId === 'toolu_sub') {
expect(e.event.toolCall.parentToolCallId).toBe(TASK_ID);
}
@@ -416,10 +417,7 @@ describe('ClaudeCodeAdapter readHistory subagent splice', () => {
message: { content: [{ type: 'tool_result', tool_use_id: EDIT_ID, content: 'updated' }] },
tool_use_result: toolUseResult,
});
- const liveSettle = live
- .tools()
- .filter((t) => t.toolCallId === EDIT_ID)
- .at(-1);
+ const liveSettle = live.tools().findLast((t) => t.toolCallId === EDIT_ID);
// History: `getSubagentMessages` strips `tool_use_result`, so the patch has to come from the
// subagent's own raw transcript instead.
@@ -444,7 +442,7 @@ describe('ClaudeCodeAdapter readHistory subagent splice', () => {
}),
);
const result = await adapter.readHistory({ historyId: asHistoryId(SESSION) });
- const replayedSettle = result.events.filter((e) => e.itemId === EDIT_ID).at(-1)?.event;
+ const replayedSettle = result.events.findLast((e) => e.itemId === EDIT_ID)?.event;
expect(replayedSettle?.type).toBe('tool-call');
if (replayedSettle?.type !== 'tool-call') return;
diff --git a/packages/host/agent-adapter/src/__tests__/codex-app-server.test.ts b/packages/host/agent-adapter/src/__tests__/codex-app-server.test.ts
index c123de333..4eac6f0ba 100644
--- a/packages/host/agent-adapter/src/__tests__/codex-app-server.test.ts
+++ b/packages/host/agent-adapter/src/__tests__/codex-app-server.test.ts
@@ -31,7 +31,10 @@ function fakeChild() {
buffered += chunk.toString();
const lines = buffered.split('\n');
buffered = lines.pop() ?? '';
- for (const line of lines) writes.push(JSON.parse(line) as Record);
+ for (let i = 0, len = lines.length; i < len; i++) {
+ const line = lines[i];
+ writes.push(JSON.parse(line) as Record);
+ }
});
return {
child,
diff --git a/packages/host/agent-adapter/src/__tests__/codex-auth.test.ts b/packages/host/agent-adapter/src/__tests__/codex-auth.test.ts
index 348b3f878..3a0d4ce14 100644
--- a/packages/host/agent-adapter/src/__tests__/codex-auth.test.ts
+++ b/packages/host/agent-adapter/src/__tests__/codex-auth.test.ts
@@ -34,7 +34,8 @@ class FakeCodexServer {
close(): void {
this.closed = true;
const rejections = this.held.splice(0);
- for (const reject of rejections) {
+ for (let i = 0, len = rejections.length; i < len; i++) {
+ const reject = rejections[i];
reject(new Error('codex: app-server connection is closed'));
}
}
diff --git a/packages/host/agent-adapter/src/__tests__/opencode.test.ts b/packages/host/agent-adapter/src/__tests__/opencode.test.ts
index 5fe9a70be..4a5faacd1 100644
--- a/packages/host/agent-adapter/src/__tests__/opencode.test.ts
+++ b/packages/host/agent-adapter/src/__tests__/opencode.test.ts
@@ -284,7 +284,6 @@ describe('OpenCodeAdapter.consumeEvents', () => {
},
},
});
- // eslint-disable-next-line sukka/unicorn/prefer-single-call -- FakeEventStream.push accepts one provider event at a time
client.stream.push({
id: 'e-tool-completed',
type: 'message.part.updated',
@@ -354,19 +353,16 @@ describe('OpenCodeAdapter.consumeEvents', () => {
type: 'message.part.updated',
properties: { sessionID: 'sess-1', time: 0, part: part('prt-mcp-1', 'linear_list_issues') },
});
- // eslint-disable-next-line sukka/unicorn/prefer-single-call -- FakeEventStream.push accepts one provider event at a time
client.stream.push({
id: 'e-mcp-configured',
type: 'message.part.updated',
properties: { sessionID: 'sess-1', time: 1, part: part('prt-mcp-2', 'notion_search_pages') },
});
- // eslint-disable-next-line sukka/unicorn/prefer-single-call -- FakeEventStream.push accepts one provider event at a time
client.stream.push({
id: 'e-builtin',
type: 'message.part.updated',
properties: { sessionID: 'sess-1', time: 2, part: part('prt-builtin', 'bash') },
});
- // eslint-disable-next-line sukka/unicorn/prefer-single-call -- FakeEventStream.push accepts one provider event at a time
client.stream.push({
id: 'e-mcp-ambiguous',
type: 'message.part.updated',
@@ -412,7 +408,6 @@ describe('OpenCodeAdapter.consumeEvents', () => {
},
},
});
- // eslint-disable-next-line sukka/unicorn/prefer-single-call -- not array
client.stream.push({
id: 'e-user-part',
type: 'message.part.updated',
@@ -428,7 +423,6 @@ describe('OpenCodeAdapter.consumeEvents', () => {
},
},
});
- // eslint-disable-next-line sukka/unicorn/prefer-single-call -- not array
client.stream.push({
id: 'e-assistant-msg',
type: 'message.updated',
@@ -450,7 +444,6 @@ describe('OpenCodeAdapter.consumeEvents', () => {
},
},
});
- // eslint-disable-next-line sukka/unicorn/prefer-single-call -- not array
client.stream.push({
id: 'e-assistant-part',
type: 'message.part.updated',
diff --git a/packages/host/agent-adapter/src/__tests__/pi-approval.test.ts b/packages/host/agent-adapter/src/__tests__/pi-approval.test.ts
index 0ad3d505d..b7fcb188b 100644
--- a/packages/host/agent-adapter/src/__tests__/pi-approval.test.ts
+++ b/packages/host/agent-adapter/src/__tests__/pi-approval.test.ts
@@ -38,15 +38,18 @@ vi.mock('@earendil-works/pi-coding-agent', async () => {
private readonly options: { extensionFactories?: Array<(api: unknown) => void> },
) {}
reload() {
- for (const factory of this.options.extensionFactories ?? []) {
- factory({
- on(_name: string, handler: Gate) {
- sdk.gate = handler;
- },
- registerTool(tool: RegisteredTool) {
- sdk.tool = tool;
- },
- });
+ if (this.options.extensionFactories != null) {
+ for (let i = 0, len = this.options.extensionFactories.length; i < len; i++) {
+ const factory = this.options.extensionFactories[i];
+ factory({
+ on(_name: string, handler: Gate) {
+ sdk.gate = handler;
+ },
+ registerTool(tool: RegisteredTool) {
+ sdk.tool = tool;
+ },
+ });
+ }
}
return Promise.resolve();
}
diff --git a/packages/host/agent-adapter/src/__tests__/pi-model-registry.test.ts b/packages/host/agent-adapter/src/__tests__/pi-model-registry.test.ts
index 6e08b0164..ac9188b38 100644
--- a/packages/host/agent-adapter/src/__tests__/pi-model-registry.test.ts
+++ b/packages/host/agent-adapter/src/__tests__/pi-model-registry.test.ts
@@ -9,7 +9,11 @@ const roots: string[] = [];
afterEach(() => {
vi.unstubAllEnvs();
- for (const root of roots.splice(0)) rmSync(root, { force: true, recursive: true });
+ const staleRoots = roots.splice(0);
+ for (let i = 0, len = staleRoots.length; i < len; i++) {
+ const root = staleRoots[i];
+ rmSync(root, { force: true, recursive: true });
+ }
});
describe('Pi model registry integration', () => {
diff --git a/packages/host/agent-adapter/src/credential.ts b/packages/host/agent-adapter/src/credential.ts
index c79d34672..4b5285be9 100644
--- a/packages/host/agent-adapter/src/credential.ts
+++ b/packages/host/agent-adapter/src/credential.ts
@@ -96,6 +96,10 @@ function readString(value: unknown): string | undefined {
function readStringRecord(value: unknown): Record | undefined {
if (typeof value !== 'object' || value === null || Array.isArray(value)) return undefined;
const out: Record = {};
- for (const [key, val] of Object.entries(value)) if (typeof val === 'string') out[key] = val;
+ const entries = Object.entries(value);
+ for (let i = 0, len = entries.length; i < len; i++) {
+ const [key, val] = entries[i];
+ if (typeof val === 'string') out[key] = val;
+ }
return isObjectEmpty(out) ? undefined : out;
}
diff --git a/packages/host/agent-adapter/src/history-util.ts b/packages/host/agent-adapter/src/history-util.ts
index 48c9267c2..4799c05e5 100644
--- a/packages/host/agent-adapter/src/history-util.ts
+++ b/packages/host/agent-adapter/src/history-util.ts
@@ -52,7 +52,8 @@ export function sliceHistoryEventPage(
): { events: AgentHistoryEvent[]; cursor: string | undefined } {
const page: AgentHistoryEvent[] = [];
let pageByteLength = 0;
- for (let index = offset; index < events.length && page.length < limit; index += 1) {
+ for (let index = offset, len = events.length; index < len; index += 1) {
+ if (page.length >= limit) break;
const eventLength = eventSerializedByteLength(events[index]);
if (page.length > 0 && pageByteLength + eventLength > MAX_ATTACHMENT_TOTAL_BASE64_LENGTH) {
break;
diff --git a/packages/host/agent-adapter/src/native/claude-code.ts b/packages/host/agent-adapter/src/native/claude-code.ts
index d913128a7..b77c19372 100644
--- a/packages/host/agent-adapter/src/native/claude-code.ts
+++ b/packages/host/agent-adapter/src/native/claude-code.ts
@@ -171,7 +171,8 @@ async function settingsDefaultMode(cwd: string): Promise | undefined {
if (!servers?.length) return undefined;
const out: Record = {};
- for (const server of servers) {
+ for (let i = 0, len = servers.length; i < len; i++) {
+ const server = servers[i];
out[server.name] =
server.type === 'http'
? { type: 'http', url: server.url, ...(server.headers && { headers: server.headers }) }
@@ -322,13 +324,16 @@ function usageWindows(
push('seven_day_oauth_apps', 10080, limits.seven_day_oauth_apps);
push('seven_day_opus', 10080, limits.seven_day_opus);
push('seven_day_sonnet', 10080, limits.seven_day_sonnet);
- for (const bucket of limits.model_scoped ?? []) {
- windows.push({
- label: bucket.display_name,
- utilization: bucket.utilization,
- resetsAt: bucket.resets_at,
- durationMins: 10080,
- });
+ if (limits.model_scoped != null) {
+ for (let i = 0, len = limits.model_scoped.length; i < len; i++) {
+ const bucket = limits.model_scoped[i];
+ windows.push({
+ label: bucket.display_name,
+ utilization: bucket.utilization,
+ resetsAt: bucket.resets_at,
+ durationMins: 10080,
+ });
+ }
}
return windows;
}
@@ -364,18 +369,34 @@ export function mapClaudeUsageReport(raw: SDKControlGetUsageResponse): UsageRepo
totalDurationMs: raw.session.total_duration_ms,
totalLinesAdded: raw.session.total_lines_added,
totalLinesRemoved: raw.session.total_lines_removed,
- modelUsage: Object.fromEntries(
- Object.entries(raw.session.model_usage).map(([model, usage]) => [
- model,
+ modelUsage: Object.entries(raw.session.model_usage).reduce<
+ Record<
+ string,
{
+ inputTokens: number;
+ outputTokens: number;
+ cacheReadTokens: number;
+ cacheCreationTokens: number;
+ totalCostUsd: number;
+ }
+ >
+ >((acc, [model, usage]) => {
+ // Own data property, never a prototype write: model ids come from the CLI subprocess.
+ // (The `UsageReportSchema.parse` below drops a `__proto__` key from the output either way.)
+ Object.defineProperty(acc, model, {
+ configurable: true,
+ enumerable: true,
+ value: {
inputTokens: usage.inputTokens,
outputTokens: usage.outputTokens,
cacheReadTokens: usage.cacheReadInputTokens,
cacheCreationTokens: usage.cacheCreationInputTokens,
totalCostUsd: usage.costUSD,
},
- ]),
- ),
+ writable: true,
+ });
+ return acc;
+ }, {}),
},
subscriptionType: raw.subscription_type,
rateLimits: limits
@@ -719,7 +740,10 @@ export class ClaudeCodeAdapter extends BaseAgentAdapter {
const children = subagentEvents.get(event.event.toolCall.toolCallId);
if (children) {
subagentEvents.delete(event.event.toolCall.toolCallId);
- for (const child of children) pushWithSubagents(child);
+ for (let i = 0, len = children.length; i < len; i++) {
+ const child = children[i];
+ pushWithSubagents(child);
+ }
}
}
};
@@ -731,8 +755,13 @@ export class ClaudeCodeAdapter extends BaseAgentAdapter {
const returned = new Set(page.map((message) => message.uuid));
const dropped =
offset === 0 ? supplement.droppedRows.filter((row) => !returned.has(row.uuid)) : [];
- for (const message of [...dropped, ...page]) {
- for (const event of mapper(message)) pushWithSubagents(event);
+ const rows = [...dropped, ...page];
+ for (let i = 0, len = rows.length; i < len; i++) {
+ const message = rows[i];
+ const mapped = mapper(message);
+ for (let j = 0, eventCount = mapped.length; j < eventCount; j++) {
+ pushWithSubagents(mapped[j]);
+ }
}
return {
session: info
@@ -1195,12 +1224,14 @@ export class ClaudeCodeAdapter extends BaseAgentAdapter {
}
const byQuestionId = new Map(outcome.answers.map((answer) => [answer.questionId, answer]));
const answers: Record = {};
- for (const [qi, question] of questions.entries()) {
+ for (let qi = 0, len = questions.length; qi < len; qi++) {
+ const question = questions[qi];
const answer = byQuestionId.get(`q${qi}`);
if (!answer) continue;
const selected = new Set(answer.selectedOptionIds);
const labels: string[] = [];
- for (const [oi, option] of question.options.entries()) {
+ for (let oi = 0, optionCount = question.options.length; oi < optionCount; oi++) {
+ const option = question.options[oi];
if (selected.has(`o${oi}`)) labels.push(option.label);
}
const value = answer.customText?.trim() || labels.join(', ');
@@ -1349,7 +1380,8 @@ export class ClaudeCodeAdapter extends BaseAgentAdapter {
// (`init` fires only at Query creation, so it can't catch a live `setModel`).
this.syncModel(message.model);
let calledTool = false;
- for (const block of message.content) {
+ for (let i = 0, len = message.content.length; i < len; i++) {
+ const block = message.content[i];
if (block.type === 'tool_use') {
const content = toolInputContent(block.name, block.input);
// Announce the tool the moment Claude requests it; the matching tool_result settles it.
@@ -1377,7 +1409,8 @@ export class ClaudeCodeAdapter extends BaseAgentAdapter {
* cursors or calls `freshSegment()`, so a mid-turn subagent can't break the main streaming bubble.
*/
private handleSubagentAssistant(message: AssistantMessage, parent: string): void {
- for (const block of message.content) {
+ for (let i = 0, len = message.content.length; i < len; i++) {
+ const block = message.content[i];
// eslint-disable-next-line sukka/unicorn/prefer-switch -- deliberately non-exhaustive (other block variants are ignored); the switch autofix then trips the error-level default-case rule
if (block.type === 'tool_use') {
const content = toolInputContent(block.name, block.input);
@@ -1410,14 +1443,16 @@ export class ClaudeCodeAdapter extends BaseAgentAdapter {
const results = content.filter((block) => block.type === 'tool_result');
const envelope = results.length === 1 ? toolUseResultEnvelope(msg.tool_use_result) : undefined;
const patched = results.length === 1 ? editResultDiffContent(msg.tool_use_result) : undefined;
- for (const block of content) {
+ for (let i = 0, len = content.length; i < len; i++) {
+ const block = content[i];
if (block.type !== 'tool_result') continue;
// Replace (not append) so the patch-bearing diff supersedes the announce-time fragment
// instead of stacking a second card, and do it before the settle below: a completed tool is
// terminal, so any content emitted after it is silently dropped.
if (patched) this.emitTool({ toolCallId: block.tool_use_id, content: patched });
- for (const result of toolResultContent(block.content)) {
- this.appendToolContent(block.tool_use_id, result);
+ const resultContent = toolResultContent(block.content);
+ for (let j = 0, resultCount = resultContent.length; j < resultCount; j++) {
+ this.appendToolContent(block.tool_use_id, resultContent[j]);
}
this.emitTool({
toolCallId: block.tool_use_id,
@@ -1681,7 +1716,9 @@ export function toolUseResultEnvelope(value: unknown): Record |
if (!isRecord(value)) return undefined;
const envelope: Record = {};
let fields = 0;
- for (const [key, field] of Object.entries(value)) {
+ const valueEntries = Object.entries(value);
+ for (let i = 0, len = valueEntries.length; i < len; i++) {
+ const [key, field] = valueEntries[i];
const scalar =
typeof field === 'string'
? field.length > 0 && field.length <= TOOL_USE_RESULT_SCALAR_MAX
@@ -1857,7 +1894,9 @@ export function buildClaudeTranscriptSupplement(
function harvestToolUses(toolUses: Map, row: Record): void {
const parentToolCallId = stringField(row, 'parent_tool_use_id');
- for (const block of messageContentBlocks(row.message)) {
+ const blocks = messageContentBlocks(row.message);
+ for (let i = 0, len = blocks.length; i < len; i++) {
+ const block = blocks[i];
if (isToolUseBlock(block)) {
toolUses.set(block.id, claudeToolCallFromUse(block, parentToolCallId));
}
@@ -2062,7 +2101,8 @@ export function createClaudeHistoryEventMapper(
lastModel = model;
events.push({ historyId, ts, event: { type: 'model-update', model } });
}
- for (const block of blocks) {
+ for (let i = 0, len = blocks.length; i < len; i++) {
+ const block = blocks[i];
if (!isThinkingBlock(block)) continue;
const thought = thoughtHistoryEvent(
historyId,
@@ -2075,7 +2115,8 @@ export function createClaudeHistoryEventMapper(
}
const text = textHistoryEvent(historyId, 'assistant', messageId, message.message, ts, parent);
if (text) events.push(text);
- for (const block of blocks) {
+ for (let i = 0, len = blocks.length; i < len; i++) {
+ const block = blocks[i];
if (!isToolUseBlock(block)) continue;
events.push(toolEvent(claudeToolCallFromUse(block, parent)));
}
@@ -2083,7 +2124,8 @@ export function createClaudeHistoryEventMapper(
}
const results = blocks.filter((block) => isToolResultBlock(block));
- for (const block of results) {
+ for (let i = 0, len = results.length; i < len; i++) {
+ const block = results[i];
const existing = announced.get(block.tool_use_id) ?? toolUses?.get(block.tool_use_id);
events.push(
toolEvent({
diff --git a/packages/host/agent-adapter/src/native/codex/adapter.ts b/packages/host/agent-adapter/src/native/codex/adapter.ts
index d8b0ef9c3..f7c82d4a8 100644
--- a/packages/host/agent-adapter/src/native/codex/adapter.ts
+++ b/packages/host/agent-adapter/src/native/codex/adapter.ts
@@ -93,15 +93,20 @@ function resolveCodexEnvironment(cwd?: string): Promise {
const BRAND_COLOR_RE = /^#[0-9A-F]{6}$/i;
+const collator = new Intl.Collator();
+
/** Map the app-server's `skills/list` response onto the normalized command catalog: only enabled
* skills are invokable, and duplicate names resolve to the first provider result, like the TUI's
* name-based mention lookup. */
export function codexSkillCommands(response: unknown): CodexSkillCommand[] {
if (!isRecord(response) || !Array.isArray(response.data)) return [];
const commands = new Map();
- for (const entry of response.data) {
+ for (let i = 0, len = response.data.length; i < len; i++) {
+ const entry = response.data[i];
if (!isRecord(entry) || !Array.isArray(entry.skills)) continue;
- for (const skill of entry.skills) {
+ const skills = entry.skills;
+ for (let j = 0, skillCount = skills.length; j < skillCount; j++) {
+ const skill = skills[j];
if (!isRecord(skill) || skill.enabled !== true) continue;
const name = stringField(skill, 'name');
const path = stringField(skill, 'path');
@@ -123,7 +128,7 @@ export function codexSkillCommands(response: unknown): CodexSkillCommand[] {
});
}
}
- return [...commands.values()].sort((a, b) => a.name.localeCompare(b.name));
+ return [...commands.values()].sort((a, b) => collator.compare(a.name, b.name));
}
/** Composer icons render at chip size; anything bigger than this is not an icon. */
@@ -235,7 +240,8 @@ interface CodexModelCatalog {
function codexModelCatalog(response: unknown): CodexModelCatalog {
const catalog: CodexModelCatalog = { defaultModel: undefined, models: [] };
if (!isRecord(response) || !Array.isArray(response.data)) return catalog;
- for (const candidate of response.data) {
+ for (let i = 0, len = response.data.length; i < len; i++) {
+ const candidate = response.data[i];
if (!isRecord(candidate)) continue;
const model = stringField(candidate, 'model') ?? stringField(candidate, 'id');
if (!model) continue;
@@ -243,7 +249,8 @@ function codexModelCatalog(response: unknown): CodexModelCatalog {
let effortLevels: EffortLevel[] | undefined;
if (Array.isArray(advertised)) {
const supported = new Set();
- for (const option of advertised) {
+ for (let j = 0, optionCount = advertised.length; j < optionCount; j++) {
+ const option = advertised[j];
if (!isRecord(option)) continue;
const effort = EffortLevelSchema.safeParse(stringField(option, 'reasoningEffort'));
if (effort.success && effort.data !== 'ultracode') supported.add(effort.data);
@@ -354,7 +361,8 @@ export function codexMcpConfigOverrides(
const out: Record = {};
if (!servers?.length) return out;
let hasHttp = false;
- for (const server of servers) {
+ for (let i = 0, len = servers.length; i < len; i++) {
+ const server = servers[i];
const prefix = `mcp_servers.${server.name}`;
if (server.type === 'http') {
if (server.headers && !isObjectEmpty(server.headers)) {
@@ -642,7 +650,8 @@ export class CodexAdapter extends BaseAgentAdapter {
// live-verified against codex app-server 0.144.1; nothing documents it (codex has no .d.ts) —
// verify again if the app-server pin moves.
const input: CodexTurnInput[] = [];
- for (const block of content) {
+ for (let i = 0, len = content.length; i < len; i++) {
+ const block = content[i];
if (block.type === 'text') {
const previous = input.at(-1);
if (previous?.type === 'text') previous.text += `\n${block.text}`;
@@ -1049,7 +1058,10 @@ export class CodexAdapter extends BaseAgentAdapter {
// The icon reads awaited — re-check that a newer refresh or server didn't win meanwhile.
if (this.server !== server || generation !== this.skillsRefreshGeneration) return;
this.skillCommands.clear();
- for (const skill of skills) this.skillCommands.set(skill.name, skill);
+ for (let i = 0, len = skills.length; i < len; i++) {
+ const skill = skills[i];
+ this.skillCommands.set(skill.name, skill);
+ }
this.emitCommands([COMPACT_COMMAND, ...capSkillIconPayload(catalog)]);
} catch {
if (this.server === server && generation === this.skillsRefreshGeneration) {
@@ -1360,7 +1372,10 @@ export class CodexAdapter extends BaseAgentAdapter {
this.emitTool(toolCall);
break;
}
- for (const content of toolCall.content) this.appendToolContent(id, content);
+ for (let i = 0, len = toolCall.content.length; i < len; i++) {
+ const content = toolCall.content[i];
+ this.appendToolContent(id, content);
+ }
this.emitTool({ ...toolCall, content: undefined });
break;
}
@@ -1368,7 +1383,8 @@ export class CodexAdapter extends BaseAgentAdapter {
const changes = Array.isArray(item.changes) ? item.changes.filter(isRecord) : [];
const locations: Array<{ path: string }> = [];
const content: ToolCallContent[] = [];
- for (const change of changes) {
+ for (let i = 0, len = changes.length; i < len; i++) {
+ const change = changes[i];
const path = stringField(change, 'path');
if (!path) continue;
// An update kind can carry a rename: `kind: {type:'update', move_path}` with `path`
diff --git a/packages/host/agent-adapter/src/native/codex/history-tools.ts b/packages/host/agent-adapter/src/native/codex/history-tools.ts
index d3f6ec155..cc433f4e7 100644
--- a/packages/host/agent-adapter/src/native/codex/history-tools.ts
+++ b/packages/host/agent-adapter/src/native/codex/history-tools.ts
@@ -377,7 +377,8 @@ export function applyPatchToolView(input: string): ApplyPatchView | null {
const lines = input.split('\n');
// An envelope ending in '\n' splits into a trailing '' that is not a content line.
if (lines.at(-1) === '') lines.pop();
- for (const line of lines) {
+ for (let i = 0, len = lines.length; i < len; i++) {
+ const line = lines[i];
if (line.startsWith('*** Update File: ')) {
flush();
updatePath = line.slice('*** Update File: '.length);
diff --git a/packages/host/agent-adapter/src/native/codex/history.ts b/packages/host/agent-adapter/src/native/codex/history.ts
index 0f5b51f03..5e194fc9d 100644
--- a/packages/host/agent-adapter/src/native/codex/history.ts
+++ b/packages/host/agent-adapter/src/native/codex/history.ts
@@ -62,7 +62,7 @@ function isBase64(data: string): boolean {
if (!isDigit && !isUppercase && !isLowercase && code !== 43 && code !== 47) return false;
}
for (let index = payloadLength; index < data.length; index += 1) {
- if (data.codePointAt(index) !== 61) return false;
+ if (data.charCodeAt(index) !== 61) return false;
}
return true;
}
@@ -176,7 +176,8 @@ function isSyntheticCodexUserDigest(
* of the rollout. */
export function collectCodexPromptFingerprints(rows: JsonRecord[]): Set {
const prints = new Set();
- for (const row of rows) {
+ for (let i = 0, len = rows.length; i < len; i++) {
+ const row = rows[i];
if (stringField(row, 'type') !== 'event_msg') continue;
const payload = recordField(row, 'payload');
if (!payload || stringField(payload, 'type') !== 'user_message') continue;
@@ -279,7 +280,8 @@ export function codexHome(environment: NodeJS.ProcessEnv = env): string {
export async function readCodexIndex(home = codexHome()): Promise