| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
Harden test isolation and smoke checks (#1440) * fix(test): isolate provider-related attribution and preconnect tests Remove process-global provider mocks from apiPreconnect tests and exercise real env-based provider resolution with hermetic first-party setup. Reset bootstrap/settings state around attribution tests and reload the attribution module per test so provider and client state cannot leak across suites. Verification: bun test --max-concurrency=1 src/utils/apiPreconnect.test.ts src/utils/attribution.test.ts * Fix full local check failures Add a check script that runs smoke plus the full single-concurrency Bun test suite, and wire it into CONTRIBUTING, the PR template, and PR checks. Fix Windows/full-suite failures by preferring Git Bash over the WSL bash launcher, normalizing settings paths before source matching, making path and warning-glyph tests platform-aware, and restoring persistent Bun module mocks for AgentTool and hook-chain tests. Verified with bun test src\tools\BashTool\BashTool.errorOutput.test.ts --max-concurrency=1 and bun run check. * fix(test): eliminate mock.module() leaks and platform-specific test failures ## Problem The full test suite (bun test --max-concurrency=1) had 10 failing tests on Windows. Investigation revealed 4 distinct root causes, all stemming from bun's mock.module() not being fully reversible by mock.restore(). When a test file replaces a shared module via mock.module(), stale bindings persist in already-imported modules even after mock.restore() is called. This is a known bun limitation. The CI (Ubuntu) only showed 1 consistent failure (the attribution test), but the Windows-local failures exposed real bugs that could surface in CI under different test ordering. ## Changes ### src/utils/hookChains.integration.test.ts (root polluter) This file was the biggest source of test pollution with 9 mock.module() calls replacing shared modules (analytics, growthbook, policyLimits, teammateMailbox, teammate, AgentTool, replBridge, etc.) with partial surfaces. For example, the teammateMailbox mock only exported writeToMailbox but the real module has 20+ exports including isIdleNotification, createIdleNotification, readMailbox, etc. When mock.restore() didn't fully undo these mocks, downstream tests got undefined for missing exports. Fix: Import real modules via cache-busted dynamic imports before setting up mocks, then spread the real module surface into each mock.module() call. This way even if the mock leaks, downstream tests see the full module surface with only the intended overrides. All 9 mock.module calls now spread their real module counterparts. Also fixed: the test was failing in isolation with SyntaxError because attachments.ts transitively imports isIdleNotification from teammateMailbox.js, which was missing from the partial mock. ### src/utils/settings/changeDetector.test.ts (Windows path normalization) 4 tests failed because getSourceForPath() normalizes paths using path.normalize() which converts forward slashes to backslashes on Windows. The test hardcoded Unix-style paths (/tmp/openclaude/user/settings.json) but path.normalize produces \tmp\openclaude\user\settings.json on Windows. The path comparison always failed, so handleChange() returned early without triggering any callbacks or debounce timers. Fix: Import normalize from 'path' and apply it to all test path constants (pathsBySource, getManagedSettingsDropInDir). This matches what the production code does. ### src/utils/exportFormats.test.ts (Windows path separator) resolveExportFilepath() uses path.join() which produces backslash-separated paths on Windows. The test expected forward-slash paths. Fix: Import join from 'path' and use it in the expected value so the assertion is platform-agnostic. ### src/utils/file.test.ts (growthbook mock leak) importFileModuleWithKillswitchEnabled() mocked growthbook.js with only getFeatureValue_CACHED_MAY_BE_STALE: () => killswitchEnabled. When killswitchEnabled was false, this poisoned isAgentSwarmsEnabled() for all downstream tests because agentSwarmsEnabled.ts has a static import of getFeatureValue_CACHED_MAY_BE_STALE that captured the mock binding. Fix: Import the real growthbook module and spread it into the mock, so all exports remain available even if the mock leaks. ### src/utils/plugins/officialMarketplaceStartupCheck.test.ts (same pattern) Same growthbook mock leak pattern. Top-level mock.module with only getFeatureValue_CACHED_MAY_BE_STALE: () => true. Fix: Import real growthbook module and spread into mock. ### src/tools/AgentTool/AgentTool.teammateModel.test.ts (transitive mock binding) 4 tests failed with 'Agent Teams is not yet available on your plan' because isAgentSwarmsEnabled() returned false. The function checks getFeatureValue_CACHED_MAY_BE_STALE('tengu_amber_flint', true) from growthbook.js, but the static import binding in agentSwarmsEnabled.ts was captured from a leaked mock that returned false. Cache-busting the AgentTool.js import doesn't help because agentSwarmsEnabled.ts is a transitive dependency that keeps its already-loaded (mocked) growthbook binding. Fix: Add mock.module for agentSwarmsEnabled.js in importAgentToolWithSpawnMock() to pin isAgentSwarmsEnabled to true, matching the test's intent (it sets CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1). ## Verification - bun run smoke: passes - bun test --max-concurrency=1: 3019 pass, 0 fail (verified twice) - No skipped tests (test.skip/it.skip/describe.skip), no test.todo, no flaky markers, no test exclusions in config ## Known remaining risks 6 test files still have partial mock.module() calls on providers.js (withRetry, officialRegistry, domainCheck, conversationRecovery, fastMode) that don't spread the real module. These don't cause failures under current test ordering but are latent risks if bun changes file execution order. * Fix remaining provider mock leak risks Address the known remaining risks from 7583157 by making provider mocks in withRetry, officialRegistry, and fastMode tests spread and restore the real providers module surface. Verified with the targeted provider-mock test group and bun run check. * Harden smoke test coverage Remove the CI-only skip and unrelated error swallowing from the SDK query lifecycle tests so fork/resume behavior is asserted in CI and local runs. Isolate test-suite global state by disabling built-in SDK agents for the lifecycle test, restoring MACRO presence exactly, clearing the agent cache, restoring axios mocks, and protecting xAI loopback tests from proxy/fetch leakage. Make the provider API test script run serially to match the shared env/proxy mutation surface. Validation: bun run check; CI=1 bun test tests\\sdk\\query-lifecycle.test.ts --max-concurrency=1; bun run test:provider; npm run test:provider-recommendation; bun run security:pr-scan -- --base upstream/main; bun run web:typecheck; bun run web:build; python -m pytest -q -p no:cacheprovider python/tests. * Expose hidden SDK test failures Tighten SDK test drains so they only suppress expected lifecycle abort errors instead of swallowing arbitrary init and bootstrap failures. Replace no-op test assertions with real checks and add V2 lifecycle isolation for MACRO, built-in agents, and agent cache state. Fix SDK V2 sendMessage to fast-exit when the caller-provided AbortController is already aborted, preventing aborted sessions from submitting work and producing result messages. Validation: bun test scripts\\feature-flags-source-guard.test.ts tests\\sdk\\query-concurrency.test.ts tests\\sdk\\sdk-v2-lifecycle.test.ts --max-concurrency=1; bun test tests\\sdk\\query-concurrency.test.ts tests\\sdk\\query-lifecycle.test.ts tests\\sdk\\sdk-v2-lifecycle.test.ts --max-concurrency=1; bun run check. * Fix CI smoke test failures Respect SDK context null session project directories so regenerated SDK sessions do not fall back to global project state. Isolate attribution tests from CI provider/model environment and replace nondeterministic live query permission checks with direct assertions against the SDK permission machinery. Validation: bun test src\\utils\\attribution.test.ts tests\\sdk\\query-lifecycle.test.ts tests\\sdk\\permissions.test.ts --max-concurrency=1; bun test tests\\sdk\\sdk-context-isolation.test.ts tests\\sdk\\query-concurrency.test.ts --max-concurrency=1; bun run check. * Stabilize attribution contract test Assert that includeCoAuthoredBy emits the default co-author trailer without pinning the active provider's model label, which can legitimately differ in CI provider environments. Validation: bun test src\\utils\\attribution.test.ts --max-concurrency=1; ANTHROPIC_MODEL=claude-sonnet-4-5-20250929 CLAUDE_CODE_USE_BEDROCK=1 bun test src\\utils\\attribution.test.ts --max-concurrency=1; bun run check. | 3 个月前 | |
feat: SDK Core — Permission System, Async Context, and Engine Extensions (#951) * feat(sdk): add SDK foundation — type declarations, errors, and utilities Adds standalone SDK building blocks with no SDK source dependencies: - sdk.d.ts: ambient type declarations for SDK bundle - coreSchemas.ts + coreTypes.generated.ts: Zod schemas and generated types - errors.ts: SDK-specific error classes - validation.ts: input validation utilities - messageFilters.ts: extracted message filter logic - handlePromptSubmit.ts: imports from messageFilters - 16 generated-types tests * fix(sdk): narrow assertFunction type from broad Function to callable signature Code review finding: assertFunction used `asserts value is Function` which accepts any function-like value without narrowing. Changed to `(...args: any[]) => any` for better type safety. * fix(sdk): update sdk.d.ts header — manually maintained, not generated Reviewer noted the header said "Generated from index.ts" but no generator produces this file. Updated to "Manually maintained — keep in sync with index.ts". Drift detection added in validate-externals.ts (PR 3). * fix(sdk): align sdk.d.ts types with canonical coreTypes.generated.ts Tighten SDK public type contract to resolve reviewer blockers: - PermissionResult: unknown[] → precise 6-shape discriminated union (addRules/replaceRules/removeRules/setMode/addDirectories/removeDirectories) - SDKSessionInfo: snake_case → camelCase (sessionId, lastModified, etc.) - ForkSessionResult: session_id → sessionId - SDKPermissionRequestMessage: uuid + session_id now required - SDKPermissionTimeoutMessage: added uuid + session_id - SessionMessage: parent_uuid → parentUuid - SDKMessage/SDKUserMessage/SDKResultMessage: replaced loose inline definitions with re-exports from coreTypes.generated.ts * feat(sdk): wire existing code modules + SDK shared utilities Modifies core modules for SDK integration: - QueryEngine, tools, state, commands: SDK type hooks - SDK shared utilities (shared.ts, permissions.ts) - 21 SDK tests (shared-utils, permissions) Stack: main ← pr1-foundation ← pr2-sdk-core * feat(sdk): add snake_case ↔ camelCase key mapping utilities casing.ts provides recursive key transformation for the SDK boundary layer. Internal runtime uses snake_case; public API exposes camelCase. Will be used by shared.ts, sessions.ts, query.ts at export boundaries. * test(sdk): add tests for snake_case ↔ camelCase mapping utilities Covers snakeToCamel, camelToSnake, mapKeysToCamel, mapKeysToSnake including nested objects, arrays, null/undefined, and round-trips. * fix(sdk): prevent permission timeout race condition with once-only resolve wrapper Add createOnceOnlyResolve utility to prevent double-resolution of promises when timeout and host response happen simultaneously. This ensures deterministic behavior in the permission handling flow. * fix(sdk): improve race condition test robustness * fix(sdk): handle consecutive underscores in snakeToCamel conversion Changes: - Use _+([a-z]) regex to match multiple consecutive underscores before letters - Add lookahead (?=. ) to preserve underscore-letter pairs at string end - Handle dunder names (__proto__, __typename) by stripping wrapper and capitalizing - Add tests for consecutive underscores and trailing underscore preservation * fix(sdk): include original error message in permission callback denial When a canUseTool callback throws an error, the catch block now includes the original error message in the denial message, making debugging easier for SDK consumers. * feat(sdk): add optional timeout to env mutex for deadlock prevention Add timeout parameter to acquireEnvMutex() to prevent infinite waits in deadlock scenarios. The timeout is optional and defaults to no timeout (wait forever) for backward compatibility. Returns a MutexAcquireResult object with acquired status and optional timeout reason for failed acquisitions. * fix(sdk): remove timed-out callback from mutex queue to prevent deadlock * test(sdk): add missing error path and timeout scenario tests Add tests for timeout scenarios when host doesn't respond to permission requests, fallback behavior when no onPermissionRequest callback, and MCP connection edge cases for undefined/empty config. * fix(sdk): address code review issues - race conditions, validation, error handling - Add createPermissionTarget() factory that applies onceOnlyResolve at registration time, fixing race condition where timeout and host response could both try to resolve the same promise - Add try-catch to releaseEnvMutex() to prevent permanent lock if callback throws - Extract DEFAULT_PERMISSION_TIMEOUT_MS constant (30 seconds) - Add MCP config validation rejecting null, non-objects, and arrays - Preserve error stack traces in MCP connection failures - Add runtime validation to mapMessageToSDK for null/non-object/invalid type - Update tests to use createPermissionTarget and add validation tests * test(sdk): add sequential timeout-then-host-response race condition tests Adds two tests addressing reviewer request for proof that host response after SDK timeout is safely handled with no double-resolve or leaked listener: 1. Integration test: stale host resolve called after timeout deny — verifies no error, no mutation, map cleanup 2. Unit test: raw resolve called exactly once when timeout wins — directly proves createOnceOnlyResolve prevents second execution * fix: restore openclaude.json comment in REPL.tsx Reviewer caught that the comment was incorrectly changed to ~/.claude.json during merge — project has already migrated to ~/.openclaude.json. * fix(sdk): register pending permission before emitting onPermissionRequest The previous code emitted onPermissionRequest before calling registerPendingPermission, so a host responding synchronously from the callback would find an empty map and its response was lost. Swap the order so registration happens first. Adds a regression test for the synchronous host response path. * fix(sdk): make state setters context-aware for SDK isolation When running inside runWithSdkContext(), setter functions (regenerateSessionId, switchSession, setCwdState, setOriginalCwd) now write to the AsyncLocalStorage context instead of global STATE. This prevents cross-session state leakage in multi-session SDK scenarios. Reads were already context-aware; this completes the isolation by making writes consistent. Outside of SDK context, behavior is unchanged — all writes go to global STATE as before. * test(sdk): add context-aware state isolation tests Tests verify that setters within runWithSdkContext() write to the SDK context (not global STATE) and that parallel async contexts do not leak state between sessions. Covers setCwdState, setOriginalCwd, regenerateSessionId, switchSession, and an end-to-end parallel session scenario. * fix(sdk): selective tool schema cache invalidation for multi-engine isolation Replace global clearToolSchemaCache() in QueryEngine.updateTools() with selective invalidation that only removes cache entries for tools no longer in the tool set. This preserves cached schemas for tools that remain, avoiding unnecessary recomputation for concurrent QueryEngine instances in multi-session SDK scenarios. New function invalidateRemovedToolSchemas() handles both simple tool name keys and schema-variant keys (format: "toolName:{...schemaJSON...}"). * docs(sdk): address PR2 non-blocking documentation and logging issues - Document request_id vs tool_use_id relationship in shared.ts (request_id for response correlation, tool_use_id for tracking) - Add injectable SDKLogger interface to permissions.ts, replacing direct console.warn calls with logger.warn (hosts can control noise) - Document Node.js-only AsyncLocalStorage requirement in state.ts (requires Node.js 12.17.0+ or 14.0.0+) - Clarify env-mutex is host utility (SDK doesn't mutate process.env) * fix(sdk): handle throwing onPermissionRequest and fix permission request shape - Wrap onPermissionRequest in try-catch to clean up pending resolver on throw - Add uuid and session_id to permission_request message to match SDK schema - Add regression tests for throwing callback and message shape validation * fix(sdk): use explicit no-session placeholder for standalone permission prompts - Add NO_SESSION_PLACEHOLDER constant ('no-session') for permission requests - Update SDKPermissionRequestMessage doc to explain session_id semantics - Replace empty string fallback with explicit placeholder - Add test verifying placeholder behavior when sessionId omitted * docs(sdk): add example code to permission denial warning Include canUseTool example in warning message to improve developer experience and make SDK usage more discoverable for new users. * fix(sdk): scope parentSessionId to SDK context for parallel isolation regenerateSessionId({ setCurrentAsParent: true }) was writing to the process-global STATE.parentSessionId even inside runWithSdkContext(), allowing one SDK context to overwrite another's parent-session metadata. Add parentSessionId to the SdkContext type and update both regenerateSessionId and getParentSessionId to read/write from the active context when one exists, using an explicit if-else pattern rather than ?? to avoid undefined fallback leaking across contexts. The non-SDK CLI path (no active context) continues to use STATE directly, preserving existing behavior. --------- Co-authored-by: Ali Alakbarli <ali.alakbarli@users.noreply.github.com> | 4 个月前 | |
Harden test isolation for smoke stability (#1192) * Fix flaky smoke build checks Replace feature-flag build preprocessing with a Bun onLoad transform so smoke/build no longer rewrites tracked src files while tests may be reading them. Keep telemetry stubs ahead of the feature transform in both CLI and SDK builds, preserve non-empty text token counts in hybrid context splitting, and make the corrupted Orama stress test assert against the actual project directory used by the test. Verified with bun run smoke, bun test src/utils/hybridContextStrategy.test.ts, bun test src/utils/knowledgeGraph.stress.test.ts --rerun-each 3, and bun test --max-concurrency=1. * Harden KnowledgeGraph smoke stress isolation Give each KnowledgeGraph stress test its own temporary config directory and remove it during teardown so Orama, SQLite, and corrupted-file state cannot bleed between stress cases or later PR test runs. Reviewed at least 20 open PRs and found the recurring smoke-and-tests failure cluster is the full unit suite, especially KnowledgeGraph corrupted Orama recovery. Verified with bun test src/utils/knowledgeGraph.stress.test.ts --rerun-each 5, bun test --max-concurrency=1, and bun run smoke. * Harden smoke test isolation Audit and harden broad smoke-adjacent test suites for process-global leaks, including env/config restoration, shared registry/module mock cleanup, fetch/axios/mock restoration, and global MACRO/platform/sandbox mutations. Replace fragile render sleeps in interactive tests with output-driven waits, and isolate provider/model/profile tests behind the shared mutation lock so unrelated PRs do not inherit stale process state. Make SQLite knowledge graph cleanup clear closed on-disk databases before best-effort file cleanup, with coverage for the stale database reset path. Verified: bun run smoke; bun test --max-concurrency=1; python -m pytest -q python/tests; bun run security:pr-scan -- --base origin/main; bun run test:provider; npm run test:provider-recommendation. * Harden test isolation across smoke suite Guard process-global test mutations with the shared mutation lock across env, module mock, config cache, and storage tests.\n\nDeep-copy global config snapshots, restore transient globals precisely, and make plugin/LSP mocks expose compatible export surfaces so concurrent test loading does not poison unrelated suites.\n\nReplace fixed SDK cleanup sleeps with call polling to remove timing sensitivity.\n\nVerification:\n- bun test --max-concurrency=1\n- bun run smoke\n- python -m pytest -q python/tests\n- bun run test:provider\n- npm run test:provider-recommendation\n- bun run security:pr-scan -- --base origin/main\n- git diff --check * Close remaining test global-state leaks Guard remaining cache, plugin, console, and VS Code module-mock tests with the shared mutation lock.\n\nThis follow-up audit covers non-env process-global state that can leak across test files: tool schema cache, cache stats tracker state, plugin loader caches, console.error replacement, and VS Code mock.module usage.\n\nVerification:\n- leak-surface scans for env/global/mock.module/cache outliers\n- duplicate top-level mock collision cluster\n- affected tests cluster\n- bun test --max-concurrency=1\n- bun run smoke\n- git diff --check * Guard remaining mock restore cleanup Lock tests that call bun:test mock.restore without installing module mocks themselves.\n\nmock.restore is process-global, so these cleanup hooks can still tear down another test file's active module mocks when files run concurrently.\n\nVerification:\n- expanded leak scans for env, globals, module mocks, mock.restore, timers, argv, and caches\n- bun test src/components/useCodexOAuthFlow.test.tsx src/services/github/deviceFlow.test.ts\n- bun test --max-concurrency=1\n- bun run smoke\n- git diff --check * Harden test isolation for smoke stability Serialize tests that mutate process-global state behind the shared mutation lock, including process.env, transient globals, global config/cache state, storage mocks, and Bun module mocks. Add isolated env mutex instances for SDK mutex tests so timeout coverage no longer manipulates the live process-global mutex. Move top-level mock.module setup behind lock acquisition and restore mocks before releasing locks to prevent cross-file leakage under parallel smoke runs. Verified with: bun test --max-concurrency=1; bun test; bun run smoke. | 4 个月前 | |
feat(cli): add headless heartbeat for print mode (#1789) * feat(cli): add headless heartbeat for print mode * fix(cli): harden heartbeat validation and predicates * fix(cli): align print heartbeat phases * fix(cli): keep heartbeat payloads schema-valid * fix(cli): delay stream-json heartbeat until drain * test(sdk): cover heartbeat placeholder identifiers * fix(cli): clamp heartbeat durations * fix(cli): ignore file persistence final events * test(cli): cover post-turn final filtering * fix(cli): harden headless heartbeat follow-up Export the heartbeat SDK message type from generated core types. Keep heartbeat cleanup paired with setup and streaming failures, and cover timing/count edge cases with focused regression tests. * test(sdk): exercise generated heartbeat types Expose the SDK type generator as a pure helper so tests compare fresh output with the checked-in generated artifact. * fix(scripts): canonicalize sdk type generator entrypoint Compare real paths for direct script execution so symlinked invocations still run the generator. * test(sdk): harden generator import coverage Normalize generated type freshness checks across line endings and keep the SDK type generator import-safe for non-file entrypoints. * test(sdk): assert generator import has no write side effects Snapshot the generated SDK type artifact around the non-file import regression so importing the generator cannot silently rewrite the committed output. | 2 个月前 | |
Harden test isolation for smoke stability (#1192) * Fix flaky smoke build checks Replace feature-flag build preprocessing with a Bun onLoad transform so smoke/build no longer rewrites tracked src files while tests may be reading them. Keep telemetry stubs ahead of the feature transform in both CLI and SDK builds, preserve non-empty text token counts in hybrid context splitting, and make the corrupted Orama stress test assert against the actual project directory used by the test. Verified with bun run smoke, bun test src/utils/hybridContextStrategy.test.ts, bun test src/utils/knowledgeGraph.stress.test.ts --rerun-each 3, and bun test --max-concurrency=1. * Harden KnowledgeGraph smoke stress isolation Give each KnowledgeGraph stress test its own temporary config directory and remove it during teardown so Orama, SQLite, and corrupted-file state cannot bleed between stress cases or later PR test runs. Reviewed at least 20 open PRs and found the recurring smoke-and-tests failure cluster is the full unit suite, especially KnowledgeGraph corrupted Orama recovery. Verified with bun test src/utils/knowledgeGraph.stress.test.ts --rerun-each 5, bun test --max-concurrency=1, and bun run smoke. * Harden smoke test isolation Audit and harden broad smoke-adjacent test suites for process-global leaks, including env/config restoration, shared registry/module mock cleanup, fetch/axios/mock restoration, and global MACRO/platform/sandbox mutations. Replace fragile render sleeps in interactive tests with output-driven waits, and isolate provider/model/profile tests behind the shared mutation lock so unrelated PRs do not inherit stale process state. Make SQLite knowledge graph cleanup clear closed on-disk databases before best-effort file cleanup, with coverage for the stale database reset path. Verified: bun run smoke; bun test --max-concurrency=1; python -m pytest -q python/tests; bun run security:pr-scan -- --base origin/main; bun run test:provider; npm run test:provider-recommendation. * Harden test isolation across smoke suite Guard process-global test mutations with the shared mutation lock across env, module mock, config cache, and storage tests.\n\nDeep-copy global config snapshots, restore transient globals precisely, and make plugin/LSP mocks expose compatible export surfaces so concurrent test loading does not poison unrelated suites.\n\nReplace fixed SDK cleanup sleeps with call polling to remove timing sensitivity.\n\nVerification:\n- bun test --max-concurrency=1\n- bun run smoke\n- python -m pytest -q python/tests\n- bun run test:provider\n- npm run test:provider-recommendation\n- bun run security:pr-scan -- --base origin/main\n- git diff --check * Close remaining test global-state leaks Guard remaining cache, plugin, console, and VS Code module-mock tests with the shared mutation lock.\n\nThis follow-up audit covers non-env process-global state that can leak across test files: tool schema cache, cache stats tracker state, plugin loader caches, console.error replacement, and VS Code mock.module usage.\n\nVerification:\n- leak-surface scans for env/global/mock.module/cache outliers\n- duplicate top-level mock collision cluster\n- affected tests cluster\n- bun test --max-concurrency=1\n- bun run smoke\n- git diff --check * Guard remaining mock restore cleanup Lock tests that call bun:test mock.restore without installing module mocks themselves.\n\nmock.restore is process-global, so these cleanup hooks can still tear down another test file's active module mocks when files run concurrently.\n\nVerification:\n- expanded leak scans for env, globals, module mocks, mock.restore, timers, argv, and caches\n- bun test src/components/useCodexOAuthFlow.test.tsx src/services/github/deviceFlow.test.ts\n- bun test --max-concurrency=1\n- bun run smoke\n- git diff --check * Harden test isolation for smoke stability Serialize tests that mutate process-global state behind the shared mutation lock, including process.env, transient globals, global config/cache state, storage mocks, and Bun module mocks. Add isolated env mutex instances for SDK mutex tests so timeout coverage no longer manipulates the live process-global mutex. Move top-level mock.module setup behind lock acquisition and restore mocks before releasing locks to prevent cross-file leakage under parallel smoke runs. Verified with: bun test --max-concurrency=1; bun test; bun run smoke. | 4 个月前 | |
feat(agents): add per-agent step limits (#1815) * feat(agents): add per-agent step limits Add maxSteps agent configuration for markdown, JSON, plugin, and SDK agent definitions. Enforce the limit in subagent query execution by blocking over-limit tool calls, preserving a no-tool summary turn, and recording an agent_step_limit terminal reason. Add focused coverage for default behavior, invalid values, multi-turn accumulation, plugin parsing, failure-loop interaction, and summary-tool blocking. * test(agents): isolate agent loader fixtures * test(agents): stabilize agent loader config fixtures * fix(agents): harden step-limit summaries * fix(sdk): harden agent injection follow-up * fix(sdk): report invalid agent step limits | 2 个月前 | |
fix(sdk): report a permission timeout as a timeout (#2028) * fix(sdk): report a permission timeout as a timeout On timeout the handler called denyPendingPermission and then fell through to the fallback. The deny resolves the promise registered by registerPendingPermission, but Promise.race has already settled with {timedOut: true}, so nothing is awaiting it and the decision is discarded. The fallback is createDefaultCanUseTool, whose contract is that the host supplied no permission callback at all. A host that wired up onPermissionRequest and simply answered too slowly therefore got the tool result 'no canUseTool or onPermissionRequest callback provided. Pass canUseTool in options', plus the matching warning on stderr -- both false, and both pointing at a configuration problem that does not exist. It also consumed the one-shot warning latch, so a genuinely misconfigured later query in the same process is never warned. Return the timeout decision directly. The permission_timeout event and the existing deny are unchanged. * test(sdk): move the timeout cases into the existing permissions suite tests/sdk/permissions.test.ts pinned the old behavior -- it asserted the timeout result was the fallback's message, with a comment describing the fall-through as intended. It is not: that message claims no permission callback was provided, which is false whenever onPermissionRequest is wired up. Assert the timeout reports itself instead. The new cases live in that suite rather than a new file: a separate test file adds a slot to bun's sequential file ordering, which shifted which suite runs before which and surfaced an unrelated mock leak in CI (taskReport git metadata and the /ads command). * test(sdk): drive the permission-timeout case off a mocked clock The no-callback-fallback-on-timeout test relied on a real 10ms wait, so the deny hinged on scheduling. Use fake timers and advance the clock by the timeout window instead, making the timer the deterministic cause of the denial. | 1 个月前 | |
fix(sdk): preserve async generator session context (#2204) * fix(sdk): preserve async generator session context * fix(sdk): complete async context boundaries * test(sdk): assert queryAsync init isolation and close V2 sessions. Replace the source-text queryAsync check with a behavioral observation of init, and release concurrent V2 sessions so the async-context coverage matches the runtime contract. | 14 天前 | |
feat(agents): add per-agent step limits (#1815) * feat(agents): add per-agent step limits Add maxSteps agent configuration for markdown, JSON, plugin, and SDK agent definitions. Enforce the limit in subagent query execution by blocking over-limit tool calls, preserving a no-tool summary turn, and recording an agent_step_limit terminal reason. Add focused coverage for default behavior, invalid values, multi-turn accumulation, plugin parsing, failure-loop interaction, and summary-tool blocking. * test(agents): isolate agent loader fixtures * test(agents): stabilize agent loader config fixtures * fix(agents): harden step-limit summaries * fix(sdk): harden agent injection follow-up * fix(sdk): report invalid agent step limits | 2 个月前 | |
diagnostics(query): trace interruption causality (#2111) * diagnostics(issue-1830): trace interruption causality * test(issue-1830): lock interruption ownership matrix * fix(codex): preserve stream deadline contract * fix(diagnostics): harden interruption trace lifecycle Refs #1830 * fix(diagnostics): harden interruption trace settlement Refs #1830 * fix(diagnostics): preserve interruption causality * fix(diagnostics): address interruption trace review * fix(diagnostics): preserve tracing observer contracts * fix(diagnostics): preserve interruption trace contracts * test(permissions): cover interactive hook interrupts | 1 个月前 | |
Harden test isolation for smoke stability (#1192) * Fix flaky smoke build checks Replace feature-flag build preprocessing with a Bun onLoad transform so smoke/build no longer rewrites tracked src files while tests may be reading them. Keep telemetry stubs ahead of the feature transform in both CLI and SDK builds, preserve non-empty text token counts in hybrid context splitting, and make the corrupted Orama stress test assert against the actual project directory used by the test. Verified with bun run smoke, bun test src/utils/hybridContextStrategy.test.ts, bun test src/utils/knowledgeGraph.stress.test.ts --rerun-each 3, and bun test --max-concurrency=1. * Harden KnowledgeGraph smoke stress isolation Give each KnowledgeGraph stress test its own temporary config directory and remove it during teardown so Orama, SQLite, and corrupted-file state cannot bleed between stress cases or later PR test runs. Reviewed at least 20 open PRs and found the recurring smoke-and-tests failure cluster is the full unit suite, especially KnowledgeGraph corrupted Orama recovery. Verified with bun test src/utils/knowledgeGraph.stress.test.ts --rerun-each 5, bun test --max-concurrency=1, and bun run smoke. * Harden smoke test isolation Audit and harden broad smoke-adjacent test suites for process-global leaks, including env/config restoration, shared registry/module mock cleanup, fetch/axios/mock restoration, and global MACRO/platform/sandbox mutations. Replace fragile render sleeps in interactive tests with output-driven waits, and isolate provider/model/profile tests behind the shared mutation lock so unrelated PRs do not inherit stale process state. Make SQLite knowledge graph cleanup clear closed on-disk databases before best-effort file cleanup, with coverage for the stale database reset path. Verified: bun run smoke; bun test --max-concurrency=1; python -m pytest -q python/tests; bun run security:pr-scan -- --base origin/main; bun run test:provider; npm run test:provider-recommendation. * Harden test isolation across smoke suite Guard process-global test mutations with the shared mutation lock across env, module mock, config cache, and storage tests.\n\nDeep-copy global config snapshots, restore transient globals precisely, and make plugin/LSP mocks expose compatible export surfaces so concurrent test loading does not poison unrelated suites.\n\nReplace fixed SDK cleanup sleeps with call polling to remove timing sensitivity.\n\nVerification:\n- bun test --max-concurrency=1\n- bun run smoke\n- python -m pytest -q python/tests\n- bun run test:provider\n- npm run test:provider-recommendation\n- bun run security:pr-scan -- --base origin/main\n- git diff --check * Close remaining test global-state leaks Guard remaining cache, plugin, console, and VS Code module-mock tests with the shared mutation lock.\n\nThis follow-up audit covers non-env process-global state that can leak across test files: tool schema cache, cache stats tracker state, plugin loader caches, console.error replacement, and VS Code mock.module usage.\n\nVerification:\n- leak-surface scans for env/global/mock.module/cache outliers\n- duplicate top-level mock collision cluster\n- affected tests cluster\n- bun test --max-concurrency=1\n- bun run smoke\n- git diff --check * Guard remaining mock restore cleanup Lock tests that call bun:test mock.restore without installing module mocks themselves.\n\nmock.restore is process-global, so these cleanup hooks can still tear down another test file's active module mocks when files run concurrently.\n\nVerification:\n- expanded leak scans for env, globals, module mocks, mock.restore, timers, argv, and caches\n- bun test src/components/useCodexOAuthFlow.test.tsx src/services/github/deviceFlow.test.ts\n- bun test --max-concurrency=1\n- bun run smoke\n- git diff --check * Harden test isolation for smoke stability Serialize tests that mutate process-global state behind the shared mutation lock, including process.env, transient globals, global config/cache state, storage mocks, and Bun module mocks. Add isolated env mutex instances for SDK mutex tests so timeout coverage no longer manipulates the live process-global mutex. Move top-level mock.module setup behind lock acquisition and restore mocks before releasing locks to prevent cross-file leakage under parallel smoke runs. Verified with: bun test --max-concurrency=1; bun test; bun run smoke. | 4 个月前 | |
fix(sdk): preserve async generator session context (#2204) * fix(sdk): preserve async generator session context * fix(sdk): complete async context boundaries * test(sdk): assert queryAsync init isolation and close V2 sessions. Replace the source-text queryAsync check with a behavioral observation of init, and release concurrent V2 sessions so the async-context coverage matches the runtime contract. | 14 天前 | |
feat: SDK Runtime — Query Engine, Sessions, and Build Pipeline (#984) * feat(sdk): add SDK foundation — type declarations, errors, and utilities Adds standalone SDK building blocks with no SDK source dependencies: - sdk.d.ts: ambient type declarations for SDK bundle - coreSchemas.ts + coreTypes.generated.ts: Zod schemas and generated types - errors.ts: SDK-specific error classes - validation.ts: input validation utilities - messageFilters.ts: extracted message filter logic - handlePromptSubmit.ts: imports from messageFilters - 16 generated-types tests * fix(sdk): narrow assertFunction type from broad Function to callable signature Code review finding: assertFunction used `asserts value is Function` which accepts any function-like value without narrowing. Changed to `(...args: any[]) => any` for better type safety. * fix(sdk): update sdk.d.ts header — manually maintained, not generated Reviewer noted the header said "Generated from index.ts" but no generator produces this file. Updated to "Manually maintained — keep in sync with index.ts". Drift detection added in validate-externals.ts (PR 3). * fix(sdk): align sdk.d.ts types with canonical coreTypes.generated.ts Tighten SDK public type contract to resolve reviewer blockers: - PermissionResult: unknown[] → precise 6-shape discriminated union (addRules/replaceRules/removeRules/setMode/addDirectories/removeDirectories) - SDKSessionInfo: snake_case → camelCase (sessionId, lastModified, etc.) - ForkSessionResult: session_id → sessionId - SDKPermissionRequestMessage: uuid + session_id now required - SDKPermissionTimeoutMessage: added uuid + session_id - SessionMessage: parent_uuid → parentUuid - SDKMessage/SDKUserMessage/SDKResultMessage: replaced loose inline definitions with re-exports from coreTypes.generated.ts * feat(sdk): wire existing code modules + SDK shared utilities Modifies core modules for SDK integration: - QueryEngine, tools, state, commands: SDK type hooks - SDK shared utilities (shared.ts, permissions.ts) - 21 SDK tests (shared-utils, permissions) Stack: main ← pr1-foundation ← pr2-sdk-core * feat(sdk): add snake_case ↔ camelCase key mapping utilities casing.ts provides recursive key transformation for the SDK boundary layer. Internal runtime uses snake_case; public API exposes camelCase. Will be used by shared.ts, sessions.ts, query.ts at export boundaries. * test(sdk): add tests for snake_case ↔ camelCase mapping utilities Covers snakeToCamel, camelToSnake, mapKeysToCamel, mapKeysToSnake including nested objects, arrays, null/undefined, and round-trips. * feat(sdk): add SDK runtime — query engine, sessions, build pipeline Completes the SDK implementation: - SDK build target (dist/sdk.mjs) with TUI dependency stubbing - External dependency lists (scripts/externals.ts) - SDK type generation from Zod schemas (scripts/generate-sdk-types.ts) - External validation (scripts/validate-externals.ts) - SDK source: index, query, v2, sessions modules - agentSdkTypes: re-exports SDK functions (query, createSession, etc.) - 136 SDK tests + 7 build scanner tests Stack: main ← pr1-foundation ← pr2-sdk-core ← pr3-sdk-runtime * fix(sdk): align internal SDK types with camelCase public contract shared.ts: SDKSessionInfo, ForkSessionResult, SessionMessage fields now use camelCase matching sdk.d.ts. SDKPermissionRequestMessage and SDKPermissionTimeoutMessage gain required uuid + session_id fields. permissions.ts: onPermissionRequest/onTimeout callbacks now include uuid and session_id in emitted messages. * fix(sdk): update runtime modules to use camelCase field names sessions.ts: toSDKSessionInfo outputs camelCase keys, entryToSessionMessage uses parentUuid, forkSession returns sessionId. query.ts: reads sessionId from listSessions/forkSession results instead of snake_case session_id. * fix(test): update session tests to use camelCase field names session_id → sessionId in forkSession result assertions and getSessionMessages calls. * fix(sdk): prevent permission timeout race condition with once-only resolve wrapper Add createOnceOnlyResolve utility to prevent double-resolution of promises when timeout and host response happen simultaneously. This ensures deterministic behavior in the permission handling flow. * fix(sdk): improve race condition test robustness * fix(sdk): handle consecutive underscores in snakeToCamel conversion Changes: - Use _+([a-z]) regex to match multiple consecutive underscores before letters - Add lookahead (?=. ) to preserve underscore-letter pairs at string end - Handle dunder names (__proto__, __typename) by stripping wrapper and capitalizing - Add tests for consecutive underscores and trailing underscore preservation * fix(sdk): include original error message in permission callback denial When a canUseTool callback throws an error, the catch block now includes the original error message in the denial message, making debugging easier for SDK consumers. * feat(sdk): add optional timeout to env mutex for deadlock prevention Add timeout parameter to acquireEnvMutex() to prevent infinite waits in deadlock scenarios. The timeout is optional and defaults to no timeout (wait forever) for backward compatibility. Returns a MutexAcquireResult object with acquired status and optional timeout reason for failed acquisitions. * fix(sdk): remove timed-out callback from mutex queue to prevent deadlock * test(sdk): add missing error path and timeout scenario tests Add tests for timeout scenarios when host doesn't respond to permission requests, fallback behavior when no onPermissionRequest callback, and MCP connection edge cases for undefined/empty config. * fix(sdk): address code review issues - race conditions, validation, error handling - Add createPermissionTarget() factory that applies onceOnlyResolve at registration time, fixing race condition where timeout and host response could both try to resolve the same promise - Add try-catch to releaseEnvMutex() to prevent permanent lock if callback throws - Extract DEFAULT_PERMISSION_TIMEOUT_MS constant (30 seconds) - Add MCP config validation rejecting null, non-objects, and arrays - Preserve error stack traces in MCP connection failures - Add runtime validation to mapMessageToSDK for null/non-object/invalid type - Update tests to use createPermissionTarget and add validation tests * fix(sdk): syntax fixes and MCP connection error handling - Remove extra closing parenthesis in permissions.ts - Remove extra closing braces in shared.ts type definitions - Wrap MCP connection in try/catch to continue without MCP tools on failure * fix(sdk): syntax fixes, MCP error handling, and logic clarity - Remove extra closing parenthesis in permissions.ts - Remove extra closing braces in shared.ts type definitions - Wrap MCP connection in try/catch to continue without MCP tools on failure - Clarify thinkingConfig logic: use ?? true instead of !== false - Add explanatory comment about thinkingEnabled default behavior - Apply createOnceOnlyResolve wrapper in QueryImpl.registerPendingPermission * fix(sdk): comprehensive error handling and resource cleanup - Add try-catch around injectAgents() to gracefully handle plugin agent tool validation failures (prevents test crashes from unknown 'LS' tool) - Add console.warn logging to agent loading/injection catch blocks for debugging visibility (matches v2.ts pattern) - Add pendingPermissionPrompts.clear() to close() and interrupt() methods in both query.ts and v2.ts to prevent memory accumulation - Add close() method to SDKSession interface and SDKSessionImpl - Wrap MCP connection in query.ts with try-catch (matches v2.ts behavior) - Add timeoutQueue cleanup in finally blocks (query.ts + v2.ts) - Remove error.stack from MCP error messages to prevent internal path leak All 208 SDK tests pass. TypeScript errors are pre-existing. * fix(sdk): address code review non-blocking issues - Add SDKAgentLoadFailureMessage type for agent load failure events - Emit agent definition/injection failures to SDK message stream - Add tool name to permission timeout denial message - Replace 'as any' casts with proper typed state access - Fix supportedCommands to use correct mcp.commands/plugins.commands paths - Update test for correct AppState structure * fix(sdk): address code review blocking and non-blocking issues Blocking Issues Fixed: - MCP cleanup missing on session/query close - now disconnects MCP clients to prevent resource leaks in long-running processes with multiple sessions - Engine reference not cleared on close - now sets _engine = null to prevent memory leaks - Added MCP cleanup tests (9 new tests covering cleanup scenarios) Non-Blocking Issues Fixed: - Removed redundant catch block that just rethrew errors (query.ts) - Fixed inconsistent timeout denial message format (permissions.ts) - Fixed hardcoded tool name 'Bash' in test (permissions.test.ts) - Exported PermissionResolveDecision type for SDK consumers (index.ts) All 217 SDK tests pass. * fix(sdk): address code review type consistency issues - Add close() method to SDKSession interface (documented but missing from type) - Fix SDKSessionInfo, ForkSessionResult, SessionMessage field naming: snake_case → camelCase to match sdk.d.ts public contract and implementation - Add uuid and session_id to SDKPermissionTimeoutMessage for correlation - Fix JSDoc comment in forkSession to use sessionId (not session_id) These changes align internal types (shared.ts) with the public SDK contract (sdk.d.ts) and actual implementation output. The merge from origin/main introduced snake_case types that mismatched camelCase implementation and tests. * fix: restore openclaude.json comment in REPL.tsx Merge 0f3aa7a incorrectly took main's side for this comment, reverting PR2 fix c725c48. Project has migrated to ~/.openclaude.json, not ~/.claude.json. This is the only PR2 fix lost during merge - all other PR2 fixes (permissions.ts race conditions, state.ts parentSessionId, etc.) are preserved in PR3 via subsequent fix commits. * fix(sdk): add missing type declarations to sdk.d.ts Add SDKAgentLoadFailureMessage and PermissionResolveDecision to sdk.d.ts to resolve type declaration drift detected by build validation. - SDKAgentLoadFailureMessage: Agent loading failure notification (stage: definitions/injection, error_message) - PermissionResolveDecision: SDK-specific permission resolution result (allow with updatedInput, deny with message + decisionReason) Build validation now passes: 56 exports match between index.ts and sdk.d.ts. * fix(sdk): resource leak and null safety in close/interrupt paths - unstable_v2_prompt: wrap session in try/finally to guarantee session.close() on both success and error paths, preventing MCP connection and engine resource leaks - QueryImpl.interrupt(): add null guard on _engine so calling interrupt() after close() is a safe no-op instead of throwing - SDKSessionImpl.interrupt(): add matching null guard for v2 sessions, consistent with the Query fix - QueryImpl.close(): call this.interrupt() before cleanup to properly stop in-flight engine operations, matching v2's close() pattern and ensuring engine.interrupt() runs before nulling * fix(sdk): abort AbortController in SDKSessionImpl.close() to prevent resource leak SDKSessionImpl.close() was not aborting the AbortController, unlike QueryImpl.close() which does. This meant in-flight HTTP requests and async operations could continue running after session closure. - Store AbortController reference via _abortController field + late-bind setter - Abort and null the controller in close(), mirroring QueryImpl pattern - Also null _appStateStore in close() to release state snapshots - Wire abortController through createEngineFromOptions return value * fix(sdk): index ALL entries in byUuid for compact preserved segment The byUuid map must index system compact_boundary entries, not just user/assistant. When anchorUuid === boundary.uuid, the relink walk needs to find the boundary in byUuid. Changes: - query.ts: Index ALL non-sidechain entries (user, assistant, system) - v2.ts: Same fix — index ALL entries, leaf selection user/assistant only - Add regression test: boundary.uuid as anchorUuid scenario Test verifies preserved messages kept, stale pre-compact dropped, post-boundary chain intact when anchorUuid points to boundary itself. * fix(sdk): complete preserved segment handling for compact resumes Multiple fixes for compact-aware transcript loading: 1. Index ALL entries in byUuid (including system compact_boundary) - Needed when anchorUuid === boundary.uuid 2. Keep anchorUuid when pruning preserved segment entries - The anchor is the parent of preserved head after relink - Deleting it breaks the conversation chain 3. Filter system entries from final messages - compact_boundary is metadata, shouldn't pass to engine 4. Fix test timestamp format (ISO 8601 requires 2-digit hours) - '2025-01-04T0:00:00Z' → '2025-01-04T00:00:00Z' 5. Update test expectations for anchor inclusion - When anchor is a stale entry, it appears in messages - preserved(4) + anchor(1) + post(4) = 9 max All 224 SDK tests pass. * fix(sdk): MCP type:sdk tools properly convert SdkMcpToolDefinition to Tool - Import MCPTool base from tools/MCPTool/MCPTool.js - Spread MCPTool properties for proper Tool interface compliance - Add tools field to SdkMcpSdkConfig type declaration - Add regression tests for type:sdk tools wiring Fix ensures in-process SDK tools match Tool interface expected by QueryEngine and permission handlers. * test(sdk): strengthen preserved segment and MCP tools tests Preserved segment test improvements: - Fix content extraction (access message.content, not message) - Add exact count assert: messages.length === 6 - Add exact content asserts: preserved turn 1/2, post-boundary present - Assert no stale, no system entries in final messages MCP tools test additions: - Direct test of connectSdkMcpServers() function - Assert clients.length === 0 (in-process, no MCP connections) - Assert tools.length === 1 with proper name/description - Verify handler works via direct call (not via Tool.call which needs context) * fix(sdk): published types complete, init errors fatal, permission session IDs Three fixes for SDK production readiness: 1. HIGH: Published SDK types incomplete - Add coreTypes.generated.d.ts to package.json "files" array - sdk.d.ts re-exports from ./sdk/coreTypes.generated.js which was missing - TypeScript consumers would get module resolution errors 2. MEDIUM: query() swallows real init() failures - Add _engineWasInjected field to track pre-injected vs fresh engine - Check _engineWasInjected, not _engine !== null (always true after setEngine) - Auth/config/init errors now properly fatal for normal query() calls 3. MEDIUM: SDK permission events lose real session id - Pass sessionId to createExternalCanUseTool() in both query.ts and v2.ts - Permission_request/timeout messages now have correct session_id - Hosts can correlate permission callbacks to sessions Test result: 225 pass, 0 fail * fix(sdk): complete package types + dynamic permission session_id Two fixes for SDK production readiness: 1. Published SDK types now include actual definitions - Replace 215-byte wrapper with 63KB coreTypes.generated.ts - TypeScript consumers get full type definitions (SDKMessage, etc.) - npm pack now includes real generated types 2. Permission event session_id dynamic for all query() paths - createExternalCanUseTool accepts string | (() => string | undefined) - query.ts passes () => queryImpl.sessionId getter - Fresh/fork/continue queries emit correct session_id at event time - V2 passes static sessionId (stable at creation/resume) - Add 4 tests: static sessionId, getter resolution, undefined fallback, timeout Test result: 229 pass, 0 fail * fix(sdk): fix sdk.d.ts for real TypeScript consumer compilation Two issues prevented external consumers from compiling against packed SDK types: 1. SDKRateLimitError used constructor parameter properties (readonly resetsAt, readonly rateLimitType) which are invalid in .d.ts declarations — moved to class properties with separate constructor signature. 2. Re-exported SDKMessage/SDKUserMessage/SDKResultMessage were not imported into local scope — added import type alongside export type so TypeScript can resolve them for use in other declarations within the same file. Added package-consumer-types.test.ts that compiles a real temp project against the SDK types with skipLibCheck:false, catching both regressions. * fix(sdk): eliminate React/Ink imports from SDK bundle SDK bundle leaked React/Ink imports via tool UI modules, keybindings, react-compiler-runtime, and spawnMultiAgent's static React import. Changes: - Stub root ink.js barrel, tool UI.js, keybindings/, react-compiler-runtime, It2SetupPrompt, and React hook files in SDK build - Add local no-op stub for react/jsx-dev-runtime (jsxDEV returns null) - Convert spawnMultiAgent's static React/It2SetupPrompt imports to dynamic await import() — spawnTeammate logic stays fully intact - Add post-build leakage validation (fails on from "react"/"ink"/jsx-dev-runtime) - Remove react/jsx-dev-runtime from SDK externals (now handled by build plugin) * fix(sdk): wire disallowedTools through permission context QueryOptions.disallowedTools was declared but never used. buildPermissionContext() now passes it to alwaysDenyRules.cliArg so getTools() filters denied tools from the model-visible list. Also added to V2 SDKSessionOptions for API consistency. * fix(sdk): defer permission warning to execution time createDefaultCanUseTool() warned at construction time even when the caller provided canUseTool/onPermissionRequest. Move warning to first actual default denial so valid SDK consumers never see false warnings. Add tests for disallowedTools filtering, tool exclusion, and warning timing. * refactor(sdk): extract transcript helpers + fix permission typing - Extract shared transcript utilities to transcript.ts (parseJsonlEntries, findLastCompactBoundary, applyPreservedSegmentRelinks, buildConversationChain, stripExtraFields) deduplicating query.ts and v2.ts - Add PermissionTarget interface to hide internal pendingPermissionPrompts map from createExternalCanUseTool, with deletePendingPermission and denyPendingPermission methods on QueryImpl and SDKSessionImpl - Fix sessionId stability: preserve constructor UUID for fresh queries when continue:true finds no existing sessions, and when explicit sessionId does not resolve to a valid transcript file - Add getMcpClients/setMcpClients to QueryEngine for SDK cleanup access * fix(sdk): resolve remaining TypeScript errors in SDK modules - Fix PermissionDecision type compatibility: import from types/permissions and cast PermissionResolveDecision to PermissionDecision properly - Fix AsyncIterator/AsyncGenerator: async generators must return AsyncGenerator (which implements AsyncIterable), not AsyncIterator - Fix Map method callable errors: cast additionalWorkingDirectories to Map<string, unknown> before calling .set() and .keys() - Fix ApiKeySource type: map internal ApiKeySource to SDK's narrower type using conversion function, spread info before apiKeySource to avoid override - Fix MCP config scope type: cast 'session' scope to ScopedMcpServerConfig for connectToServer compatibility - Add PermissionMode import and cast for decisionReason.mode - Deny pending permissions in interrupt(): resolve all pending promises with deny before clearing the map (both query.ts and v2.ts) * fix(sdk): correct init skip logic and test mocks - query.ts: skip init() entirely for injected engines (mocks, SDK host overrides) instead of calling init() and swallowing errors. Pass { injected: false } from query() factory to distinguish real engine from test mocks. - mock-engine.ts: add getMcpClients() and setMcpClients() methods to match QueryEngine API added in this PR. - permissions.test.ts: use filterToolsByDenyRules instead of getTools for disallowedTools tests, with proper base tool fixtures. * fix: address code review feedback for exports and build script package.json exports (Breaking Change Mitigation): - Add "./package.json": "./package.json" for tool compatibility - Add "./dist/cli.mjs": "./dist/cli.mjs" for CLI bundle access - Keep ./sdk as sole library entrypoint - Root import intentionally blocked (CLI-first package, no main field) build.ts (Bug Fix): - Add | undefined to result/sdkResult type declarations - Add optional chaining: result?.success, sdkResult?.success - Prevents TypeError masking actual build errors when Bun.build throws tests/sdk/package-consumer-types.test.ts: - Update simulated exports to match real package.json - Add tests verifying exports map structure and file existence --------- Co-authored-by: Ali Alakbarli <ali.alakbarli@users.noreply.github.com> | 4 个月前 | |
feat: SDK Runtime — Query Engine, Sessions, and Build Pipeline (#984) * feat(sdk): add SDK foundation — type declarations, errors, and utilities Adds standalone SDK building blocks with no SDK source dependencies: - sdk.d.ts: ambient type declarations for SDK bundle - coreSchemas.ts + coreTypes.generated.ts: Zod schemas and generated types - errors.ts: SDK-specific error classes - validation.ts: input validation utilities - messageFilters.ts: extracted message filter logic - handlePromptSubmit.ts: imports from messageFilters - 16 generated-types tests * fix(sdk): narrow assertFunction type from broad Function to callable signature Code review finding: assertFunction used `asserts value is Function` which accepts any function-like value without narrowing. Changed to `(...args: any[]) => any` for better type safety. * fix(sdk): update sdk.d.ts header — manually maintained, not generated Reviewer noted the header said "Generated from index.ts" but no generator produces this file. Updated to "Manually maintained — keep in sync with index.ts". Drift detection added in validate-externals.ts (PR 3). * fix(sdk): align sdk.d.ts types with canonical coreTypes.generated.ts Tighten SDK public type contract to resolve reviewer blockers: - PermissionResult: unknown[] → precise 6-shape discriminated union (addRules/replaceRules/removeRules/setMode/addDirectories/removeDirectories) - SDKSessionInfo: snake_case → camelCase (sessionId, lastModified, etc.) - ForkSessionResult: session_id → sessionId - SDKPermissionRequestMessage: uuid + session_id now required - SDKPermissionTimeoutMessage: added uuid + session_id - SessionMessage: parent_uuid → parentUuid - SDKMessage/SDKUserMessage/SDKResultMessage: replaced loose inline definitions with re-exports from coreTypes.generated.ts * feat(sdk): wire existing code modules + SDK shared utilities Modifies core modules for SDK integration: - QueryEngine, tools, state, commands: SDK type hooks - SDK shared utilities (shared.ts, permissions.ts) - 21 SDK tests (shared-utils, permissions) Stack: main ← pr1-foundation ← pr2-sdk-core * feat(sdk): add snake_case ↔ camelCase key mapping utilities casing.ts provides recursive key transformation for the SDK boundary layer. Internal runtime uses snake_case; public API exposes camelCase. Will be used by shared.ts, sessions.ts, query.ts at export boundaries. * test(sdk): add tests for snake_case ↔ camelCase mapping utilities Covers snakeToCamel, camelToSnake, mapKeysToCamel, mapKeysToSnake including nested objects, arrays, null/undefined, and round-trips. * feat(sdk): add SDK runtime — query engine, sessions, build pipeline Completes the SDK implementation: - SDK build target (dist/sdk.mjs) with TUI dependency stubbing - External dependency lists (scripts/externals.ts) - SDK type generation from Zod schemas (scripts/generate-sdk-types.ts) - External validation (scripts/validate-externals.ts) - SDK source: index, query, v2, sessions modules - agentSdkTypes: re-exports SDK functions (query, createSession, etc.) - 136 SDK tests + 7 build scanner tests Stack: main ← pr1-foundation ← pr2-sdk-core ← pr3-sdk-runtime * fix(sdk): align internal SDK types with camelCase public contract shared.ts: SDKSessionInfo, ForkSessionResult, SessionMessage fields now use camelCase matching sdk.d.ts. SDKPermissionRequestMessage and SDKPermissionTimeoutMessage gain required uuid + session_id fields. permissions.ts: onPermissionRequest/onTimeout callbacks now include uuid and session_id in emitted messages. * fix(sdk): update runtime modules to use camelCase field names sessions.ts: toSDKSessionInfo outputs camelCase keys, entryToSessionMessage uses parentUuid, forkSession returns sessionId. query.ts: reads sessionId from listSessions/forkSession results instead of snake_case session_id. * fix(test): update session tests to use camelCase field names session_id → sessionId in forkSession result assertions and getSessionMessages calls. * fix(sdk): prevent permission timeout race condition with once-only resolve wrapper Add createOnceOnlyResolve utility to prevent double-resolution of promises when timeout and host response happen simultaneously. This ensures deterministic behavior in the permission handling flow. * fix(sdk): improve race condition test robustness * fix(sdk): handle consecutive underscores in snakeToCamel conversion Changes: - Use _+([a-z]) regex to match multiple consecutive underscores before letters - Add lookahead (?=. ) to preserve underscore-letter pairs at string end - Handle dunder names (__proto__, __typename) by stripping wrapper and capitalizing - Add tests for consecutive underscores and trailing underscore preservation * fix(sdk): include original error message in permission callback denial When a canUseTool callback throws an error, the catch block now includes the original error message in the denial message, making debugging easier for SDK consumers. * feat(sdk): add optional timeout to env mutex for deadlock prevention Add timeout parameter to acquireEnvMutex() to prevent infinite waits in deadlock scenarios. The timeout is optional and defaults to no timeout (wait forever) for backward compatibility. Returns a MutexAcquireResult object with acquired status and optional timeout reason for failed acquisitions. * fix(sdk): remove timed-out callback from mutex queue to prevent deadlock * test(sdk): add missing error path and timeout scenario tests Add tests for timeout scenarios when host doesn't respond to permission requests, fallback behavior when no onPermissionRequest callback, and MCP connection edge cases for undefined/empty config. * fix(sdk): address code review issues - race conditions, validation, error handling - Add createPermissionTarget() factory that applies onceOnlyResolve at registration time, fixing race condition where timeout and host response could both try to resolve the same promise - Add try-catch to releaseEnvMutex() to prevent permanent lock if callback throws - Extract DEFAULT_PERMISSION_TIMEOUT_MS constant (30 seconds) - Add MCP config validation rejecting null, non-objects, and arrays - Preserve error stack traces in MCP connection failures - Add runtime validation to mapMessageToSDK for null/non-object/invalid type - Update tests to use createPermissionTarget and add validation tests * fix(sdk): syntax fixes and MCP connection error handling - Remove extra closing parenthesis in permissions.ts - Remove extra closing braces in shared.ts type definitions - Wrap MCP connection in try/catch to continue without MCP tools on failure * fix(sdk): syntax fixes, MCP error handling, and logic clarity - Remove extra closing parenthesis in permissions.ts - Remove extra closing braces in shared.ts type definitions - Wrap MCP connection in try/catch to continue without MCP tools on failure - Clarify thinkingConfig logic: use ?? true instead of !== false - Add explanatory comment about thinkingEnabled default behavior - Apply createOnceOnlyResolve wrapper in QueryImpl.registerPendingPermission * fix(sdk): comprehensive error handling and resource cleanup - Add try-catch around injectAgents() to gracefully handle plugin agent tool validation failures (prevents test crashes from unknown 'LS' tool) - Add console.warn logging to agent loading/injection catch blocks for debugging visibility (matches v2.ts pattern) - Add pendingPermissionPrompts.clear() to close() and interrupt() methods in both query.ts and v2.ts to prevent memory accumulation - Add close() method to SDKSession interface and SDKSessionImpl - Wrap MCP connection in query.ts with try-catch (matches v2.ts behavior) - Add timeoutQueue cleanup in finally blocks (query.ts + v2.ts) - Remove error.stack from MCP error messages to prevent internal path leak All 208 SDK tests pass. TypeScript errors are pre-existing. * fix(sdk): address code review non-blocking issues - Add SDKAgentLoadFailureMessage type for agent load failure events - Emit agent definition/injection failures to SDK message stream - Add tool name to permission timeout denial message - Replace 'as any' casts with proper typed state access - Fix supportedCommands to use correct mcp.commands/plugins.commands paths - Update test for correct AppState structure * fix(sdk): address code review blocking and non-blocking issues Blocking Issues Fixed: - MCP cleanup missing on session/query close - now disconnects MCP clients to prevent resource leaks in long-running processes with multiple sessions - Engine reference not cleared on close - now sets _engine = null to prevent memory leaks - Added MCP cleanup tests (9 new tests covering cleanup scenarios) Non-Blocking Issues Fixed: - Removed redundant catch block that just rethrew errors (query.ts) - Fixed inconsistent timeout denial message format (permissions.ts) - Fixed hardcoded tool name 'Bash' in test (permissions.test.ts) - Exported PermissionResolveDecision type for SDK consumers (index.ts) All 217 SDK tests pass. * fix(sdk): address code review type consistency issues - Add close() method to SDKSession interface (documented but missing from type) - Fix SDKSessionInfo, ForkSessionResult, SessionMessage field naming: snake_case → camelCase to match sdk.d.ts public contract and implementation - Add uuid and session_id to SDKPermissionTimeoutMessage for correlation - Fix JSDoc comment in forkSession to use sessionId (not session_id) These changes align internal types (shared.ts) with the public SDK contract (sdk.d.ts) and actual implementation output. The merge from origin/main introduced snake_case types that mismatched camelCase implementation and tests. * fix: restore openclaude.json comment in REPL.tsx Merge 0f3aa7a incorrectly took main's side for this comment, reverting PR2 fix c725c48. Project has migrated to ~/.openclaude.json, not ~/.claude.json. This is the only PR2 fix lost during merge - all other PR2 fixes (permissions.ts race conditions, state.ts parentSessionId, etc.) are preserved in PR3 via subsequent fix commits. * fix(sdk): add missing type declarations to sdk.d.ts Add SDKAgentLoadFailureMessage and PermissionResolveDecision to sdk.d.ts to resolve type declaration drift detected by build validation. - SDKAgentLoadFailureMessage: Agent loading failure notification (stage: definitions/injection, error_message) - PermissionResolveDecision: SDK-specific permission resolution result (allow with updatedInput, deny with message + decisionReason) Build validation now passes: 56 exports match between index.ts and sdk.d.ts. * fix(sdk): resource leak and null safety in close/interrupt paths - unstable_v2_prompt: wrap session in try/finally to guarantee session.close() on both success and error paths, preventing MCP connection and engine resource leaks - QueryImpl.interrupt(): add null guard on _engine so calling interrupt() after close() is a safe no-op instead of throwing - SDKSessionImpl.interrupt(): add matching null guard for v2 sessions, consistent with the Query fix - QueryImpl.close(): call this.interrupt() before cleanup to properly stop in-flight engine operations, matching v2's close() pattern and ensuring engine.interrupt() runs before nulling * fix(sdk): abort AbortController in SDKSessionImpl.close() to prevent resource leak SDKSessionImpl.close() was not aborting the AbortController, unlike QueryImpl.close() which does. This meant in-flight HTTP requests and async operations could continue running after session closure. - Store AbortController reference via _abortController field + late-bind setter - Abort and null the controller in close(), mirroring QueryImpl pattern - Also null _appStateStore in close() to release state snapshots - Wire abortController through createEngineFromOptions return value * fix(sdk): index ALL entries in byUuid for compact preserved segment The byUuid map must index system compact_boundary entries, not just user/assistant. When anchorUuid === boundary.uuid, the relink walk needs to find the boundary in byUuid. Changes: - query.ts: Index ALL non-sidechain entries (user, assistant, system) - v2.ts: Same fix — index ALL entries, leaf selection user/assistant only - Add regression test: boundary.uuid as anchorUuid scenario Test verifies preserved messages kept, stale pre-compact dropped, post-boundary chain intact when anchorUuid points to boundary itself. * fix(sdk): complete preserved segment handling for compact resumes Multiple fixes for compact-aware transcript loading: 1. Index ALL entries in byUuid (including system compact_boundary) - Needed when anchorUuid === boundary.uuid 2. Keep anchorUuid when pruning preserved segment entries - The anchor is the parent of preserved head after relink - Deleting it breaks the conversation chain 3. Filter system entries from final messages - compact_boundary is metadata, shouldn't pass to engine 4. Fix test timestamp format (ISO 8601 requires 2-digit hours) - '2025-01-04T0:00:00Z' → '2025-01-04T00:00:00Z' 5. Update test expectations for anchor inclusion - When anchor is a stale entry, it appears in messages - preserved(4) + anchor(1) + post(4) = 9 max All 224 SDK tests pass. * fix(sdk): MCP type:sdk tools properly convert SdkMcpToolDefinition to Tool - Import MCPTool base from tools/MCPTool/MCPTool.js - Spread MCPTool properties for proper Tool interface compliance - Add tools field to SdkMcpSdkConfig type declaration - Add regression tests for type:sdk tools wiring Fix ensures in-process SDK tools match Tool interface expected by QueryEngine and permission handlers. * test(sdk): strengthen preserved segment and MCP tools tests Preserved segment test improvements: - Fix content extraction (access message.content, not message) - Add exact count assert: messages.length === 6 - Add exact content asserts: preserved turn 1/2, post-boundary present - Assert no stale, no system entries in final messages MCP tools test additions: - Direct test of connectSdkMcpServers() function - Assert clients.length === 0 (in-process, no MCP connections) - Assert tools.length === 1 with proper name/description - Verify handler works via direct call (not via Tool.call which needs context) * fix(sdk): published types complete, init errors fatal, permission session IDs Three fixes for SDK production readiness: 1. HIGH: Published SDK types incomplete - Add coreTypes.generated.d.ts to package.json "files" array - sdk.d.ts re-exports from ./sdk/coreTypes.generated.js which was missing - TypeScript consumers would get module resolution errors 2. MEDIUM: query() swallows real init() failures - Add _engineWasInjected field to track pre-injected vs fresh engine - Check _engineWasInjected, not _engine !== null (always true after setEngine) - Auth/config/init errors now properly fatal for normal query() calls 3. MEDIUM: SDK permission events lose real session id - Pass sessionId to createExternalCanUseTool() in both query.ts and v2.ts - Permission_request/timeout messages now have correct session_id - Hosts can correlate permission callbacks to sessions Test result: 225 pass, 0 fail * fix(sdk): complete package types + dynamic permission session_id Two fixes for SDK production readiness: 1. Published SDK types now include actual definitions - Replace 215-byte wrapper with 63KB coreTypes.generated.ts - TypeScript consumers get full type definitions (SDKMessage, etc.) - npm pack now includes real generated types 2. Permission event session_id dynamic for all query() paths - createExternalCanUseTool accepts string | (() => string | undefined) - query.ts passes () => queryImpl.sessionId getter - Fresh/fork/continue queries emit correct session_id at event time - V2 passes static sessionId (stable at creation/resume) - Add 4 tests: static sessionId, getter resolution, undefined fallback, timeout Test result: 229 pass, 0 fail * fix(sdk): fix sdk.d.ts for real TypeScript consumer compilation Two issues prevented external consumers from compiling against packed SDK types: 1. SDKRateLimitError used constructor parameter properties (readonly resetsAt, readonly rateLimitType) which are invalid in .d.ts declarations — moved to class properties with separate constructor signature. 2. Re-exported SDKMessage/SDKUserMessage/SDKResultMessage were not imported into local scope — added import type alongside export type so TypeScript can resolve them for use in other declarations within the same file. Added package-consumer-types.test.ts that compiles a real temp project against the SDK types with skipLibCheck:false, catching both regressions. * fix(sdk): eliminate React/Ink imports from SDK bundle SDK bundle leaked React/Ink imports via tool UI modules, keybindings, react-compiler-runtime, and spawnMultiAgent's static React import. Changes: - Stub root ink.js barrel, tool UI.js, keybindings/, react-compiler-runtime, It2SetupPrompt, and React hook files in SDK build - Add local no-op stub for react/jsx-dev-runtime (jsxDEV returns null) - Convert spawnMultiAgent's static React/It2SetupPrompt imports to dynamic await import() — spawnTeammate logic stays fully intact - Add post-build leakage validation (fails on from "react"/"ink"/jsx-dev-runtime) - Remove react/jsx-dev-runtime from SDK externals (now handled by build plugin) * fix(sdk): wire disallowedTools through permission context QueryOptions.disallowedTools was declared but never used. buildPermissionContext() now passes it to alwaysDenyRules.cliArg so getTools() filters denied tools from the model-visible list. Also added to V2 SDKSessionOptions for API consistency. * fix(sdk): defer permission warning to execution time createDefaultCanUseTool() warned at construction time even when the caller provided canUseTool/onPermissionRequest. Move warning to first actual default denial so valid SDK consumers never see false warnings. Add tests for disallowedTools filtering, tool exclusion, and warning timing. * refactor(sdk): extract transcript helpers + fix permission typing - Extract shared transcript utilities to transcript.ts (parseJsonlEntries, findLastCompactBoundary, applyPreservedSegmentRelinks, buildConversationChain, stripExtraFields) deduplicating query.ts and v2.ts - Add PermissionTarget interface to hide internal pendingPermissionPrompts map from createExternalCanUseTool, with deletePendingPermission and denyPendingPermission methods on QueryImpl and SDKSessionImpl - Fix sessionId stability: preserve constructor UUID for fresh queries when continue:true finds no existing sessions, and when explicit sessionId does not resolve to a valid transcript file - Add getMcpClients/setMcpClients to QueryEngine for SDK cleanup access * fix(sdk): resolve remaining TypeScript errors in SDK modules - Fix PermissionDecision type compatibility: import from types/permissions and cast PermissionResolveDecision to PermissionDecision properly - Fix AsyncIterator/AsyncGenerator: async generators must return AsyncGenerator (which implements AsyncIterable), not AsyncIterator - Fix Map method callable errors: cast additionalWorkingDirectories to Map<string, unknown> before calling .set() and .keys() - Fix ApiKeySource type: map internal ApiKeySource to SDK's narrower type using conversion function, spread info before apiKeySource to avoid override - Fix MCP config scope type: cast 'session' scope to ScopedMcpServerConfig for connectToServer compatibility - Add PermissionMode import and cast for decisionReason.mode - Deny pending permissions in interrupt(): resolve all pending promises with deny before clearing the map (both query.ts and v2.ts) * fix(sdk): correct init skip logic and test mocks - query.ts: skip init() entirely for injected engines (mocks, SDK host overrides) instead of calling init() and swallowing errors. Pass { injected: false } from query() factory to distinguish real engine from test mocks. - mock-engine.ts: add getMcpClients() and setMcpClients() methods to match QueryEngine API added in this PR. - permissions.test.ts: use filterToolsByDenyRules instead of getTools for disallowedTools tests, with proper base tool fixtures. * fix: address code review feedback for exports and build script package.json exports (Breaking Change Mitigation): - Add "./package.json": "./package.json" for tool compatibility - Add "./dist/cli.mjs": "./dist/cli.mjs" for CLI bundle access - Keep ./sdk as sole library entrypoint - Root import intentionally blocked (CLI-first package, no main field) build.ts (Bug Fix): - Add | undefined to result/sdkResult type declarations - Add optional chaining: result?.success, sdkResult?.success - Prevents TypeError masking actual build errors when Bun.build throws tests/sdk/package-consumer-types.test.ts: - Update simulated exports to match real package.json - Add tests verifying exports map structure and file existence --------- Co-authored-by: Ali Alakbarli <ali.alakbarli@users.noreply.github.com> | 4 个月前 | |
chore: centralize Bun version and refresh CI tool pins (#1171) * chore: centralize Bun version and refresh CI tool pins - add .bun-version as the shared Bun source of truth for workflows and Docker builds - update PR and release workflows to read Bun from bun-version-file - refresh pinned GitHub Actions and Docker action SHAs to newer low-risk releases - align contributor docs with Bun 1.3.13 guidance * test: stabilize reset and provider profile persistence Harden knowledge graph reset behavior across Windows file-lock scenarios by improving SQLite and JSON reset signaling, preserving a safe JSON source of truth when SQLite cannot be cleared, and adding direct storage regression coverage. Also centralize deterministic config-home handling for tests, tighten provider profile persistence path resolution and cleanup semantics, isolate environment-sensitive suites with the env mutex, and remove flaky external npx dependency from the SDK consumer type test. * test: fix Codex OAuth callback flake Investigate the real provider smoke failure from GitHub Actions and fix the root cause instead of patching the symptom. - make Codex OAuth callback host explicit and consistent across redirect URI generation and listener binding - allow safe loopback host overrides for localhost, 127.0.0.1, and ::1 - harden Codex OAuth tests with env/fetch isolation so they do not poison neighboring provider suites - pin the OAuth callback tests to 127.0.0.1 to avoid localhost IPv4/IPv6 family mismatch flakes in CI Validated with bun test src/services/api/codexOAuth.test.ts, bun test src/services/api/providerConfig.codexSecureStorage.test.ts, and bun run test:provider. * test: harden Codex OAuth callback tests Investigate the recurring provider-smoke OAuth failures across multiple PR runs and fix the flaky callback test design at the root. - remove the free-port reservation race from Codex OAuth tests - add bounded callback retry only for loopback listener warm-up during the in-process OAuth test flow - move ephemeral callback port support into an explicit CodexOAuthService test seam instead of widening production env parsing - keep runtime callback-port semantics unchanged while adding regression coverage for callback host and port parsing Validated with targeted Codex OAuth tests and repeated provider-bucket reruns to check for recurring flake. * test: serialize provider shared-state suites Fix the recurring provider smoke flake at the root cause by serializing test suites that mutate process.env or globalThis.fetch. Add a shared test mutation lock and wire it into the provider bucket so Codex OAuth no longer races with unrelated provider/config/openai shim tests under Bun's parallel test execution. Cleanup now releases the lock in finally blocks, and the shared lock waits indefinitely by default to avoid timeout-based CI flakes. * test: fix smoke root causes and noisy suites Replace the Codex OAuth test's live loopback listener dependency with an injected listener seam, avoid module-mock leakage across provider suites, and clean up the auth-code listener test setup. Also harden noisy storage and search tests by asserting expected log output, isolating SQLite masterpiece persistence per test cwd, and removing routine benchmark/stress logging from passing runs. * build: harden Bun version install in Docker Validate the repo-tracked .bun-version value before using it in the Docker build stage, strip line endings, and install Bun through a quoted semver-only variable instead of raw shell expansion. * test: replace flaky conversation arc benchmark Fix the recurring smoke failure caused by an absolute wall-clock assertion in the normal unit suite. Replace the CI-speed-sensitive conversation arc benchmark with deterministic regression coverage that verifies repeated fact extraction, expected entity shapes, bounded graph growth, and populated-summary behavior. * test: isolate shared-state smoke suites * test: restore codex credential mocks between suites * test: fix shared-state and provider init-order flakes * test: isolate remaining shared-state smoke suites Serialize the remaining smoke-sensitive suites that mutate process env, CLAUDE_CONFIG_DIR, fetch, or SDK session globals. Add shared lock coverage to discovery, agent/skills loading, platform storage, and SDK lifecycle/preserved-segment tests. Restore session and cwd state inside the lock boundary so parallel files cannot leak bootstrap state into knowledge graph and SDK isolation tests. Validated with repeated smoke and full-suite passes: - bun run smoke (2x) - bun test - bun test --max-concurrency=1 - bun run test:provider - python -m pytest -q python/tests - npm run test:provider-recommendation | 4 个月前 | |
fix(sdk): preserve async generator session context (#2204) * fix(sdk): preserve async generator session context * fix(sdk): complete async context boundaries * test(sdk): assert queryAsync init isolation and close V2 sessions. Replace the source-text queryAsync check with a behavioral observation of init, and release concurrent V2 sessions so the async-context coverage matches the runtime contract. | 14 天前 | |
feat: SDK Runtime — Query Engine, Sessions, and Build Pipeline (#984) * feat(sdk): add SDK foundation — type declarations, errors, and utilities Adds standalone SDK building blocks with no SDK source dependencies: - sdk.d.ts: ambient type declarations for SDK bundle - coreSchemas.ts + coreTypes.generated.ts: Zod schemas and generated types - errors.ts: SDK-specific error classes - validation.ts: input validation utilities - messageFilters.ts: extracted message filter logic - handlePromptSubmit.ts: imports from messageFilters - 16 generated-types tests * fix(sdk): narrow assertFunction type from broad Function to callable signature Code review finding: assertFunction used `asserts value is Function` which accepts any function-like value without narrowing. Changed to `(...args: any[]) => any` for better type safety. * fix(sdk): update sdk.d.ts header — manually maintained, not generated Reviewer noted the header said "Generated from index.ts" but no generator produces this file. Updated to "Manually maintained — keep in sync with index.ts". Drift detection added in validate-externals.ts (PR 3). * fix(sdk): align sdk.d.ts types with canonical coreTypes.generated.ts Tighten SDK public type contract to resolve reviewer blockers: - PermissionResult: unknown[] → precise 6-shape discriminated union (addRules/replaceRules/removeRules/setMode/addDirectories/removeDirectories) - SDKSessionInfo: snake_case → camelCase (sessionId, lastModified, etc.) - ForkSessionResult: session_id → sessionId - SDKPermissionRequestMessage: uuid + session_id now required - SDKPermissionTimeoutMessage: added uuid + session_id - SessionMessage: parent_uuid → parentUuid - SDKMessage/SDKUserMessage/SDKResultMessage: replaced loose inline definitions with re-exports from coreTypes.generated.ts * feat(sdk): wire existing code modules + SDK shared utilities Modifies core modules for SDK integration: - QueryEngine, tools, state, commands: SDK type hooks - SDK shared utilities (shared.ts, permissions.ts) - 21 SDK tests (shared-utils, permissions) Stack: main ← pr1-foundation ← pr2-sdk-core * feat(sdk): add snake_case ↔ camelCase key mapping utilities casing.ts provides recursive key transformation for the SDK boundary layer. Internal runtime uses snake_case; public API exposes camelCase. Will be used by shared.ts, sessions.ts, query.ts at export boundaries. * test(sdk): add tests for snake_case ↔ camelCase mapping utilities Covers snakeToCamel, camelToSnake, mapKeysToCamel, mapKeysToSnake including nested objects, arrays, null/undefined, and round-trips. * feat(sdk): add SDK runtime — query engine, sessions, build pipeline Completes the SDK implementation: - SDK build target (dist/sdk.mjs) with TUI dependency stubbing - External dependency lists (scripts/externals.ts) - SDK type generation from Zod schemas (scripts/generate-sdk-types.ts) - External validation (scripts/validate-externals.ts) - SDK source: index, query, v2, sessions modules - agentSdkTypes: re-exports SDK functions (query, createSession, etc.) - 136 SDK tests + 7 build scanner tests Stack: main ← pr1-foundation ← pr2-sdk-core ← pr3-sdk-runtime * fix(sdk): align internal SDK types with camelCase public contract shared.ts: SDKSessionInfo, ForkSessionResult, SessionMessage fields now use camelCase matching sdk.d.ts. SDKPermissionRequestMessage and SDKPermissionTimeoutMessage gain required uuid + session_id fields. permissions.ts: onPermissionRequest/onTimeout callbacks now include uuid and session_id in emitted messages. * fix(sdk): update runtime modules to use camelCase field names sessions.ts: toSDKSessionInfo outputs camelCase keys, entryToSessionMessage uses parentUuid, forkSession returns sessionId. query.ts: reads sessionId from listSessions/forkSession results instead of snake_case session_id. * fix(test): update session tests to use camelCase field names session_id → sessionId in forkSession result assertions and getSessionMessages calls. * fix(sdk): prevent permission timeout race condition with once-only resolve wrapper Add createOnceOnlyResolve utility to prevent double-resolution of promises when timeout and host response happen simultaneously. This ensures deterministic behavior in the permission handling flow. * fix(sdk): improve race condition test robustness * fix(sdk): handle consecutive underscores in snakeToCamel conversion Changes: - Use _+([a-z]) regex to match multiple consecutive underscores before letters - Add lookahead (?=. ) to preserve underscore-letter pairs at string end - Handle dunder names (__proto__, __typename) by stripping wrapper and capitalizing - Add tests for consecutive underscores and trailing underscore preservation * fix(sdk): include original error message in permission callback denial When a canUseTool callback throws an error, the catch block now includes the original error message in the denial message, making debugging easier for SDK consumers. * feat(sdk): add optional timeout to env mutex for deadlock prevention Add timeout parameter to acquireEnvMutex() to prevent infinite waits in deadlock scenarios. The timeout is optional and defaults to no timeout (wait forever) for backward compatibility. Returns a MutexAcquireResult object with acquired status and optional timeout reason for failed acquisitions. * fix(sdk): remove timed-out callback from mutex queue to prevent deadlock * test(sdk): add missing error path and timeout scenario tests Add tests for timeout scenarios when host doesn't respond to permission requests, fallback behavior when no onPermissionRequest callback, and MCP connection edge cases for undefined/empty config. * fix(sdk): address code review issues - race conditions, validation, error handling - Add createPermissionTarget() factory that applies onceOnlyResolve at registration time, fixing race condition where timeout and host response could both try to resolve the same promise - Add try-catch to releaseEnvMutex() to prevent permanent lock if callback throws - Extract DEFAULT_PERMISSION_TIMEOUT_MS constant (30 seconds) - Add MCP config validation rejecting null, non-objects, and arrays - Preserve error stack traces in MCP connection failures - Add runtime validation to mapMessageToSDK for null/non-object/invalid type - Update tests to use createPermissionTarget and add validation tests * fix(sdk): syntax fixes and MCP connection error handling - Remove extra closing parenthesis in permissions.ts - Remove extra closing braces in shared.ts type definitions - Wrap MCP connection in try/catch to continue without MCP tools on failure * fix(sdk): syntax fixes, MCP error handling, and logic clarity - Remove extra closing parenthesis in permissions.ts - Remove extra closing braces in shared.ts type definitions - Wrap MCP connection in try/catch to continue without MCP tools on failure - Clarify thinkingConfig logic: use ?? true instead of !== false - Add explanatory comment about thinkingEnabled default behavior - Apply createOnceOnlyResolve wrapper in QueryImpl.registerPendingPermission * fix(sdk): comprehensive error handling and resource cleanup - Add try-catch around injectAgents() to gracefully handle plugin agent tool validation failures (prevents test crashes from unknown 'LS' tool) - Add console.warn logging to agent loading/injection catch blocks for debugging visibility (matches v2.ts pattern) - Add pendingPermissionPrompts.clear() to close() and interrupt() methods in both query.ts and v2.ts to prevent memory accumulation - Add close() method to SDKSession interface and SDKSessionImpl - Wrap MCP connection in query.ts with try-catch (matches v2.ts behavior) - Add timeoutQueue cleanup in finally blocks (query.ts + v2.ts) - Remove error.stack from MCP error messages to prevent internal path leak All 208 SDK tests pass. TypeScript errors are pre-existing. * fix(sdk): address code review non-blocking issues - Add SDKAgentLoadFailureMessage type for agent load failure events - Emit agent definition/injection failures to SDK message stream - Add tool name to permission timeout denial message - Replace 'as any' casts with proper typed state access - Fix supportedCommands to use correct mcp.commands/plugins.commands paths - Update test for correct AppState structure * fix(sdk): address code review blocking and non-blocking issues Blocking Issues Fixed: - MCP cleanup missing on session/query close - now disconnects MCP clients to prevent resource leaks in long-running processes with multiple sessions - Engine reference not cleared on close - now sets _engine = null to prevent memory leaks - Added MCP cleanup tests (9 new tests covering cleanup scenarios) Non-Blocking Issues Fixed: - Removed redundant catch block that just rethrew errors (query.ts) - Fixed inconsistent timeout denial message format (permissions.ts) - Fixed hardcoded tool name 'Bash' in test (permissions.test.ts) - Exported PermissionResolveDecision type for SDK consumers (index.ts) All 217 SDK tests pass. * fix(sdk): address code review type consistency issues - Add close() method to SDKSession interface (documented but missing from type) - Fix SDKSessionInfo, ForkSessionResult, SessionMessage field naming: snake_case → camelCase to match sdk.d.ts public contract and implementation - Add uuid and session_id to SDKPermissionTimeoutMessage for correlation - Fix JSDoc comment in forkSession to use sessionId (not session_id) These changes align internal types (shared.ts) with the public SDK contract (sdk.d.ts) and actual implementation output. The merge from origin/main introduced snake_case types that mismatched camelCase implementation and tests. * fix: restore openclaude.json comment in REPL.tsx Merge 0f3aa7a incorrectly took main's side for this comment, reverting PR2 fix c725c48. Project has migrated to ~/.openclaude.json, not ~/.claude.json. This is the only PR2 fix lost during merge - all other PR2 fixes (permissions.ts race conditions, state.ts parentSessionId, etc.) are preserved in PR3 via subsequent fix commits. * fix(sdk): add missing type declarations to sdk.d.ts Add SDKAgentLoadFailureMessage and PermissionResolveDecision to sdk.d.ts to resolve type declaration drift detected by build validation. - SDKAgentLoadFailureMessage: Agent loading failure notification (stage: definitions/injection, error_message) - PermissionResolveDecision: SDK-specific permission resolution result (allow with updatedInput, deny with message + decisionReason) Build validation now passes: 56 exports match between index.ts and sdk.d.ts. * fix(sdk): resource leak and null safety in close/interrupt paths - unstable_v2_prompt: wrap session in try/finally to guarantee session.close() on both success and error paths, preventing MCP connection and engine resource leaks - QueryImpl.interrupt(): add null guard on _engine so calling interrupt() after close() is a safe no-op instead of throwing - SDKSessionImpl.interrupt(): add matching null guard for v2 sessions, consistent with the Query fix - QueryImpl.close(): call this.interrupt() before cleanup to properly stop in-flight engine operations, matching v2's close() pattern and ensuring engine.interrupt() runs before nulling * fix(sdk): abort AbortController in SDKSessionImpl.close() to prevent resource leak SDKSessionImpl.close() was not aborting the AbortController, unlike QueryImpl.close() which does. This meant in-flight HTTP requests and async operations could continue running after session closure. - Store AbortController reference via _abortController field + late-bind setter - Abort and null the controller in close(), mirroring QueryImpl pattern - Also null _appStateStore in close() to release state snapshots - Wire abortController through createEngineFromOptions return value * fix(sdk): index ALL entries in byUuid for compact preserved segment The byUuid map must index system compact_boundary entries, not just user/assistant. When anchorUuid === boundary.uuid, the relink walk needs to find the boundary in byUuid. Changes: - query.ts: Index ALL non-sidechain entries (user, assistant, system) - v2.ts: Same fix — index ALL entries, leaf selection user/assistant only - Add regression test: boundary.uuid as anchorUuid scenario Test verifies preserved messages kept, stale pre-compact dropped, post-boundary chain intact when anchorUuid points to boundary itself. * fix(sdk): complete preserved segment handling for compact resumes Multiple fixes for compact-aware transcript loading: 1. Index ALL entries in byUuid (including system compact_boundary) - Needed when anchorUuid === boundary.uuid 2. Keep anchorUuid when pruning preserved segment entries - The anchor is the parent of preserved head after relink - Deleting it breaks the conversation chain 3. Filter system entries from final messages - compact_boundary is metadata, shouldn't pass to engine 4. Fix test timestamp format (ISO 8601 requires 2-digit hours) - '2025-01-04T0:00:00Z' → '2025-01-04T00:00:00Z' 5. Update test expectations for anchor inclusion - When anchor is a stale entry, it appears in messages - preserved(4) + anchor(1) + post(4) = 9 max All 224 SDK tests pass. * fix(sdk): MCP type:sdk tools properly convert SdkMcpToolDefinition to Tool - Import MCPTool base from tools/MCPTool/MCPTool.js - Spread MCPTool properties for proper Tool interface compliance - Add tools field to SdkMcpSdkConfig type declaration - Add regression tests for type:sdk tools wiring Fix ensures in-process SDK tools match Tool interface expected by QueryEngine and permission handlers. * test(sdk): strengthen preserved segment and MCP tools tests Preserved segment test improvements: - Fix content extraction (access message.content, not message) - Add exact count assert: messages.length === 6 - Add exact content asserts: preserved turn 1/2, post-boundary present - Assert no stale, no system entries in final messages MCP tools test additions: - Direct test of connectSdkMcpServers() function - Assert clients.length === 0 (in-process, no MCP connections) - Assert tools.length === 1 with proper name/description - Verify handler works via direct call (not via Tool.call which needs context) * fix(sdk): published types complete, init errors fatal, permission session IDs Three fixes for SDK production readiness: 1. HIGH: Published SDK types incomplete - Add coreTypes.generated.d.ts to package.json "files" array - sdk.d.ts re-exports from ./sdk/coreTypes.generated.js which was missing - TypeScript consumers would get module resolution errors 2. MEDIUM: query() swallows real init() failures - Add _engineWasInjected field to track pre-injected vs fresh engine - Check _engineWasInjected, not _engine !== null (always true after setEngine) - Auth/config/init errors now properly fatal for normal query() calls 3. MEDIUM: SDK permission events lose real session id - Pass sessionId to createExternalCanUseTool() in both query.ts and v2.ts - Permission_request/timeout messages now have correct session_id - Hosts can correlate permission callbacks to sessions Test result: 225 pass, 0 fail * fix(sdk): complete package types + dynamic permission session_id Two fixes for SDK production readiness: 1. Published SDK types now include actual definitions - Replace 215-byte wrapper with 63KB coreTypes.generated.ts - TypeScript consumers get full type definitions (SDKMessage, etc.) - npm pack now includes real generated types 2. Permission event session_id dynamic for all query() paths - createExternalCanUseTool accepts string | (() => string | undefined) - query.ts passes () => queryImpl.sessionId getter - Fresh/fork/continue queries emit correct session_id at event time - V2 passes static sessionId (stable at creation/resume) - Add 4 tests: static sessionId, getter resolution, undefined fallback, timeout Test result: 229 pass, 0 fail * fix(sdk): fix sdk.d.ts for real TypeScript consumer compilation Two issues prevented external consumers from compiling against packed SDK types: 1. SDKRateLimitError used constructor parameter properties (readonly resetsAt, readonly rateLimitType) which are invalid in .d.ts declarations — moved to class properties with separate constructor signature. 2. Re-exported SDKMessage/SDKUserMessage/SDKResultMessage were not imported into local scope — added import type alongside export type so TypeScript can resolve them for use in other declarations within the same file. Added package-consumer-types.test.ts that compiles a real temp project against the SDK types with skipLibCheck:false, catching both regressions. * fix(sdk): eliminate React/Ink imports from SDK bundle SDK bundle leaked React/Ink imports via tool UI modules, keybindings, react-compiler-runtime, and spawnMultiAgent's static React import. Changes: - Stub root ink.js barrel, tool UI.js, keybindings/, react-compiler-runtime, It2SetupPrompt, and React hook files in SDK build - Add local no-op stub for react/jsx-dev-runtime (jsxDEV returns null) - Convert spawnMultiAgent's static React/It2SetupPrompt imports to dynamic await import() — spawnTeammate logic stays fully intact - Add post-build leakage validation (fails on from "react"/"ink"/jsx-dev-runtime) - Remove react/jsx-dev-runtime from SDK externals (now handled by build plugin) * fix(sdk): wire disallowedTools through permission context QueryOptions.disallowedTools was declared but never used. buildPermissionContext() now passes it to alwaysDenyRules.cliArg so getTools() filters denied tools from the model-visible list. Also added to V2 SDKSessionOptions for API consistency. * fix(sdk): defer permission warning to execution time createDefaultCanUseTool() warned at construction time even when the caller provided canUseTool/onPermissionRequest. Move warning to first actual default denial so valid SDK consumers never see false warnings. Add tests for disallowedTools filtering, tool exclusion, and warning timing. * refactor(sdk): extract transcript helpers + fix permission typing - Extract shared transcript utilities to transcript.ts (parseJsonlEntries, findLastCompactBoundary, applyPreservedSegmentRelinks, buildConversationChain, stripExtraFields) deduplicating query.ts and v2.ts - Add PermissionTarget interface to hide internal pendingPermissionPrompts map from createExternalCanUseTool, with deletePendingPermission and denyPendingPermission methods on QueryImpl and SDKSessionImpl - Fix sessionId stability: preserve constructor UUID for fresh queries when continue:true finds no existing sessions, and when explicit sessionId does not resolve to a valid transcript file - Add getMcpClients/setMcpClients to QueryEngine for SDK cleanup access * fix(sdk): resolve remaining TypeScript errors in SDK modules - Fix PermissionDecision type compatibility: import from types/permissions and cast PermissionResolveDecision to PermissionDecision properly - Fix AsyncIterator/AsyncGenerator: async generators must return AsyncGenerator (which implements AsyncIterable), not AsyncIterator - Fix Map method callable errors: cast additionalWorkingDirectories to Map<string, unknown> before calling .set() and .keys() - Fix ApiKeySource type: map internal ApiKeySource to SDK's narrower type using conversion function, spread info before apiKeySource to avoid override - Fix MCP config scope type: cast 'session' scope to ScopedMcpServerConfig for connectToServer compatibility - Add PermissionMode import and cast for decisionReason.mode - Deny pending permissions in interrupt(): resolve all pending promises with deny before clearing the map (both query.ts and v2.ts) * fix(sdk): correct init skip logic and test mocks - query.ts: skip init() entirely for injected engines (mocks, SDK host overrides) instead of calling init() and swallowing errors. Pass { injected: false } from query() factory to distinguish real engine from test mocks. - mock-engine.ts: add getMcpClients() and setMcpClients() methods to match QueryEngine API added in this PR. - permissions.test.ts: use filterToolsByDenyRules instead of getTools for disallowedTools tests, with proper base tool fixtures. * fix: address code review feedback for exports and build script package.json exports (Breaking Change Mitigation): - Add "./package.json": "./package.json" for tool compatibility - Add "./dist/cli.mjs": "./dist/cli.mjs" for CLI bundle access - Keep ./sdk as sole library entrypoint - Root import intentionally blocked (CLI-first package, no main field) build.ts (Bug Fix): - Add | undefined to result/sdkResult type declarations - Add optional chaining: result?.success, sdkResult?.success - Prevents TypeError masking actual build errors when Bun.build throws tests/sdk/package-consumer-types.test.ts: - Update simulated exports to match real package.json - Add tests verifying exports map structure and file existence --------- Co-authored-by: Ali Alakbarli <ali.alakbarli@users.noreply.github.com> | 4 个月前 | |
Harden test isolation for smoke stability (#1192) * Fix flaky smoke build checks Replace feature-flag build preprocessing with a Bun onLoad transform so smoke/build no longer rewrites tracked src files while tests may be reading them. Keep telemetry stubs ahead of the feature transform in both CLI and SDK builds, preserve non-empty text token counts in hybrid context splitting, and make the corrupted Orama stress test assert against the actual project directory used by the test. Verified with bun run smoke, bun test src/utils/hybridContextStrategy.test.ts, bun test src/utils/knowledgeGraph.stress.test.ts --rerun-each 3, and bun test --max-concurrency=1. * Harden KnowledgeGraph smoke stress isolation Give each KnowledgeGraph stress test its own temporary config directory and remove it during teardown so Orama, SQLite, and corrupted-file state cannot bleed between stress cases or later PR test runs. Reviewed at least 20 open PRs and found the recurring smoke-and-tests failure cluster is the full unit suite, especially KnowledgeGraph corrupted Orama recovery. Verified with bun test src/utils/knowledgeGraph.stress.test.ts --rerun-each 5, bun test --max-concurrency=1, and bun run smoke. * Harden smoke test isolation Audit and harden broad smoke-adjacent test suites for process-global leaks, including env/config restoration, shared registry/module mock cleanup, fetch/axios/mock restoration, and global MACRO/platform/sandbox mutations. Replace fragile render sleeps in interactive tests with output-driven waits, and isolate provider/model/profile tests behind the shared mutation lock so unrelated PRs do not inherit stale process state. Make SQLite knowledge graph cleanup clear closed on-disk databases before best-effort file cleanup, with coverage for the stale database reset path. Verified: bun run smoke; bun test --max-concurrency=1; python -m pytest -q python/tests; bun run security:pr-scan -- --base origin/main; bun run test:provider; npm run test:provider-recommendation. * Harden test isolation across smoke suite Guard process-global test mutations with the shared mutation lock across env, module mock, config cache, and storage tests.\n\nDeep-copy global config snapshots, restore transient globals precisely, and make plugin/LSP mocks expose compatible export surfaces so concurrent test loading does not poison unrelated suites.\n\nReplace fixed SDK cleanup sleeps with call polling to remove timing sensitivity.\n\nVerification:\n- bun test --max-concurrency=1\n- bun run smoke\n- python -m pytest -q python/tests\n- bun run test:provider\n- npm run test:provider-recommendation\n- bun run security:pr-scan -- --base origin/main\n- git diff --check * Close remaining test global-state leaks Guard remaining cache, plugin, console, and VS Code module-mock tests with the shared mutation lock.\n\nThis follow-up audit covers non-env process-global state that can leak across test files: tool schema cache, cache stats tracker state, plugin loader caches, console.error replacement, and VS Code mock.module usage.\n\nVerification:\n- leak-surface scans for env/global/mock.module/cache outliers\n- duplicate top-level mock collision cluster\n- affected tests cluster\n- bun test --max-concurrency=1\n- bun run smoke\n- git diff --check * Guard remaining mock restore cleanup Lock tests that call bun:test mock.restore without installing module mocks themselves.\n\nmock.restore is process-global, so these cleanup hooks can still tear down another test file's active module mocks when files run concurrently.\n\nVerification:\n- expanded leak scans for env, globals, module mocks, mock.restore, timers, argv, and caches\n- bun test src/components/useCodexOAuthFlow.test.tsx src/services/github/deviceFlow.test.ts\n- bun test --max-concurrency=1\n- bun run smoke\n- git diff --check * Harden test isolation for smoke stability Serialize tests that mutate process-global state behind the shared mutation lock, including process.env, transient globals, global config/cache state, storage mocks, and Bun module mocks. Add isolated env mutex instances for SDK mutex tests so timeout coverage no longer manipulates the live process-global mutex. Move top-level mock.module setup behind lock acquisition and restore mocks before releasing locks to prevent cross-file leakage under parallel smoke runs. Verified with: bun test --max-concurrency=1; bun test; bun run smoke. | 4 个月前 | |
fix(sdk): make stub-leak detection TDZ-safe + defer to next microtask (#1287) (#1398) * fix(sdk): make stub-leak detection TDZ-safe + defer to next microtask (#1287) `bun run scripts/start-grpc.ts` crashed at startup with: ReferenceError: Cannot access 'QueryEngine' before initialization. at detectStubLeaks (src/entrypoints/sdk/index.ts:29:33) at src/entrypoints/sdk/index.ts:47:1 The detector ran at module-load time and read each critical import directly. When the start script's circular-import chain reached the SDK barrel before `QueryEngine.js` had finished initializing its own export bindings, the QueryEngine reference at line 29 hit the temporal dead zone and threw. Stub-leak detection is meant to catch `__stub: true` markers from the esbuild plugin — TDZ is a different bug class (an uninitialized binding can't carry `__stub`), so the detector should treat the access failure as 'nothing to check here' rather than crashing the entire SDK entry. Two changes: 1. Wrap each import read in safelyAccess(() => binding) so a TDZ ReferenceError on one returns undefined and the loop continues. Real stub markers still surface as the explicit SDK init error. 2. Defer detectStubLeaks() from module-load to queueMicrotask, so every same-tick init in the circular chain (start-grpc.ts → SDK index → QueryEngine → ... → SDK index) completes before we read bindings. Microtask runs before any actual SDK usage, so a real stub leak still surfaces well before the first query() call. Tests (3): SDK barrel imports without throwing, anti-regression on real __stub: true bindings, TDZ-shaped access returns undefined. * test(sdk): exercise the real stub-leak detector with stubbed fixtures (#1287) The regression test asserted only that a local object literal had __stub === true and re-implemented safelyAccess inline, so it never ran the real detector: removing queueMicrotask(detectStubLeaks), dropping the loop, or swallowing the __stub case would all still pass. Split the detection primitives (safelyAccess + the critical-import scan) into src/entrypoints/sdk/stubLeakDetection.ts and have the SDK entry point import them. The test now feeds stub-shaped fixtures through the real checkCriticalImportsForStubs / safelyAccess and asserts: a real __stub: true binding throws the explicit SDK init error; non-stub modules pass; a TDZ ReferenceError is tolerated (skipped) without crashing; a stub behind a skipped TDZ access is still caught; and the SDK barrel import never throws on its own load. Detector runtime behavior is unchanged. | 3 个月前 | |
Harden test isolation for smoke stability (#1192) * Fix flaky smoke build checks Replace feature-flag build preprocessing with a Bun onLoad transform so smoke/build no longer rewrites tracked src files while tests may be reading them. Keep telemetry stubs ahead of the feature transform in both CLI and SDK builds, preserve non-empty text token counts in hybrid context splitting, and make the corrupted Orama stress test assert against the actual project directory used by the test. Verified with bun run smoke, bun test src/utils/hybridContextStrategy.test.ts, bun test src/utils/knowledgeGraph.stress.test.ts --rerun-each 3, and bun test --max-concurrency=1. * Harden KnowledgeGraph smoke stress isolation Give each KnowledgeGraph stress test its own temporary config directory and remove it during teardown so Orama, SQLite, and corrupted-file state cannot bleed between stress cases or later PR test runs. Reviewed at least 20 open PRs and found the recurring smoke-and-tests failure cluster is the full unit suite, especially KnowledgeGraph corrupted Orama recovery. Verified with bun test src/utils/knowledgeGraph.stress.test.ts --rerun-each 5, bun test --max-concurrency=1, and bun run smoke. * Harden smoke test isolation Audit and harden broad smoke-adjacent test suites for process-global leaks, including env/config restoration, shared registry/module mock cleanup, fetch/axios/mock restoration, and global MACRO/platform/sandbox mutations. Replace fragile render sleeps in interactive tests with output-driven waits, and isolate provider/model/profile tests behind the shared mutation lock so unrelated PRs do not inherit stale process state. Make SQLite knowledge graph cleanup clear closed on-disk databases before best-effort file cleanup, with coverage for the stale database reset path. Verified: bun run smoke; bun test --max-concurrency=1; python -m pytest -q python/tests; bun run security:pr-scan -- --base origin/main; bun run test:provider; npm run test:provider-recommendation. * Harden test isolation across smoke suite Guard process-global test mutations with the shared mutation lock across env, module mock, config cache, and storage tests.\n\nDeep-copy global config snapshots, restore transient globals precisely, and make plugin/LSP mocks expose compatible export surfaces so concurrent test loading does not poison unrelated suites.\n\nReplace fixed SDK cleanup sleeps with call polling to remove timing sensitivity.\n\nVerification:\n- bun test --max-concurrency=1\n- bun run smoke\n- python -m pytest -q python/tests\n- bun run test:provider\n- npm run test:provider-recommendation\n- bun run security:pr-scan -- --base origin/main\n- git diff --check * Close remaining test global-state leaks Guard remaining cache, plugin, console, and VS Code module-mock tests with the shared mutation lock.\n\nThis follow-up audit covers non-env process-global state that can leak across test files: tool schema cache, cache stats tracker state, plugin loader caches, console.error replacement, and VS Code mock.module usage.\n\nVerification:\n- leak-surface scans for env/global/mock.module/cache outliers\n- duplicate top-level mock collision cluster\n- affected tests cluster\n- bun test --max-concurrency=1\n- bun run smoke\n- git diff --check * Guard remaining mock restore cleanup Lock tests that call bun:test mock.restore without installing module mocks themselves.\n\nmock.restore is process-global, so these cleanup hooks can still tear down another test file's active module mocks when files run concurrently.\n\nVerification:\n- expanded leak scans for env, globals, module mocks, mock.restore, timers, argv, and caches\n- bun test src/components/useCodexOAuthFlow.test.tsx src/services/github/deviceFlow.test.ts\n- bun test --max-concurrency=1\n- bun run smoke\n- git diff --check * Harden test isolation for smoke stability Serialize tests that mutate process-global state behind the shared mutation lock, including process.env, transient globals, global config/cache state, storage mocks, and Bun module mocks. Add isolated env mutex instances for SDK mutex tests so timeout coverage no longer manipulates the live process-global mutex. Move top-level mock.module setup behind lock acquisition and restore mocks before releasing locks to prevent cross-file leakage under parallel smoke runs. Verified with: bun test --max-concurrency=1; bun test; bun run smoke. | 4 个月前 |
| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
| 3 个月前 | ||
| 4 个月前 | ||
| 4 个月前 | ||
| 2 个月前 | ||
| 4 个月前 | ||
| 2 个月前 | ||
| 1 个月前 | ||
| 14 天前 | ||
| 2 个月前 | ||
| 1 个月前 | ||
| 4 个月前 | ||
| 14 天前 | ||
| 4 个月前 | ||
| 4 个月前 | ||
| 4 个月前 | ||
| 14 天前 | ||
| 4 个月前 | ||
| 4 个月前 | ||
| 3 个月前 | ||
| 4 个月前 |