4 Commits
Author SHA1 Message Date
BogdanandGitHub eea0a1a740 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.
2026-06-27 09:22:25 +08:00
9755550137 Typecheck/zero tsc errors (#1597)
* ci(typecheck): add error-count ratchet toward zero tsc errors

tsc --noEmit currently reports 697 pre-existing errors (issue #473), so
PRs cannot be gated on a clean typecheck yet. This adds
scripts/typecheck-ratchet.ts and a per-file baseline: CI fails when the
count rises above the baseline (listing exactly which files regressed),
passes at or below it, and --update lowers the baseline to lock in
gains. Wired into pr-checks as its own step; once the baseline reaches
zero the step becomes a plain `bun run typecheck`.

Co-Authored-By: OpenClaude <openclaude@gitlawb.com>

* fix(typecheck): mechanical sweep — 697 → 624 tsc errors

Type-only fixes with no runtime behavior change, except the deliberate
NODE_ENV restorations:

- Restore process.env.NODE_ENV comparisons that the source snapshot had
  baked into the literal "production", making the conditions constant
  (AutoUpdater dev/test skip, useTypeahead, ink devtools injection,
  interactiveHelpers onboarding skip, TestingPermissionTool.isEnabled —
  the last now correctly enables under bun test, +3 tests run green)
- Type stream read helpers in openaiShim/codexShim as
  Bun.ReadableStreamDefaultReadResult<Uint8Array<ArrayBuffer>> and
  annotate throwClassifiedTransportError as never-returning, clearing
  the reader/response undefined cascades (29 errors)
- Delete 14 stale @ts-expect-error directives
- Widen useState/useRef/array generics inferred from null/[] literals
- as-const notification priority/color literals to match Priority
- Accept readonly Tool[] in checkLocalModelContextLoad/getCombinedTools

Baseline lowered via typecheck:ratchet --update; full suite green
(3690 tests), smoke + bundle guard green.

Co-Authored-By: OpenClaude <openclaude@gitlawb.com>

* fix(typecheck): recreate missing modules — 624 → 415 tsc errors

The open snapshot never mirrored ~60 modules; the bundler noop-stubs
them at build time (() => null named exports), so every recreated
module here is runtime-inert by construction: no import-time side
effects, gated features stay off (isAssistantMode/isSkillSearchEnabled
→ false, tools isEnabled → false, dialogs render null), lookups return
empty, telemetry no-ops. Types are honest and derived from importer
usage — no any.

Highlights:
- sdk: runtimeTypes re-exports/aliases, sdkUtilityTypes
  (NonNullableUsage), settingsTypes.generated; coreTypes.generated
  usage fields regenerated as a self-contained structural type (the
  consumer package ships without sdkUtilityTypes/@anthropic-ai/sdk, so
  the generated file must stay dependency-free — generator override
  updated to match, package-consumer-types tests green)
- services: contextCollapse operations/persist/stats, compact
  cachedMicrocompact state/types + reactiveCompact, skillSearch (7
  modules), oauth/types, lsp/types, sessionTranscript
- cli/server/daemon: Transport interface, parseConnectUrl, server/*
  (7), daemon/*, bg/templateJobs/runners; assistant/* (KAIROS), ssh/*
- tools/components: WorkflowTool trio, ReviewArtifact pair,
  OverflowTest/TerminalCapture/VerifyPlanExecution/DiscoverSkills,
  WebBrowserPanel, task dialogs, message variants, ink events/cursor
- types: statusLine, fileSuggestion, notebook, messageQueueTypes;
  SerializedMessage rebuilt as distributed Omit-union so transcript
  guards narrow again; vitest-compat.d.ts mirrors Bun's runtime
  'vitest' → 'bun:test' aliasing
- TS2304 names: ant-model helpers imported from existing antModels.ts,
  inert Ultraplan/Gates/LogoV2 stubs, PromiseWithResolvers local type
- build.ts: ACCEPTABLE_RUNTIME_STUBS emptied — both grandfathered
  bundle-reaching stubs (MonitorMcpDetailDialog,
  VerifyPlanExecutionTool/constants) are now real typed modules, so
  the degrade-on-use debt the guard tracked is retired

Validation: full suite 3690 green, smoke + bundle guard green,
typecheck:type-tests green, sdk package-consumer tests green; baseline
lowered via typecheck:ratchet --update.

Co-Authored-By: OpenClaude <openclaude@gitlawb.com>

* fix(typecheck): reconstruct Message discriminated union — 415 → 342 tsc errors

src/types/message.ts was a stub where all ~40 message type aliases were
'export type X = any'. Bare-any aliases break the one thing the union is
for: narrowing. Type predicates like isHookAttachmentMessage collapsed to
'never' in guard chains, cascading TS2339/TS2345 through utils/messages.ts,
messageFilters.ts, groupToolUses.ts, collapseReadSearch.ts, REPL.tsx,
compact.ts, stopHooks.ts and the message components.

Envelope design (permissive-body discriminated union):
- Each variant declares its literal discriminant(s) — message.type for the
  envelope union (user/assistant/attachment/progress/system), subtype for
  the 17-variant System family — plus the properties constructor functions
  in utils/messages.ts actually populate, with '[key: string]: any' as an
  escape hatch so unreconstructed properties never error.
- UserMessage<C> / AssistantMessage<T> are generic over content shape so
  NormalizedUserMessage / NormalizedAssistantMessage<T> reuse the envelope
  without Omit (Omit over an index-signature type collapses keyof to
  string and silently drops the discriminant, breaking narrowing).
- AssistantMessage.message is a structural AssistantMessageContent<T>, not
  the SDK's BetaMessage: synthetic constructors don't populate every
  SDK-required field (stop_details), and SDK-facing consumers need
  assignability to Record<string, unknown>-style bodies.
- AttachmentMessage<T = Attachment> / ProgressMessage<T = Progress> stay
  generic over their payloads (utils/attachments.ts and Tool.ts types).
- UI wrappers (GroupedToolUseMessage, CollapsedReadSearchGroup,
  CollapsibleMessage, RenderableMessage) and stream/control envelopes
  (StreamEvent over BetaRawMessageStreamEvent, RequestStartEvent,
  TombstoneMessage, ToolUseSummaryMessage) reconstructed from call sites.
- logs.ts SerializedMessage switched from the Omit<Message, never> trick
  (only sound against an any stub) to an Extract-based distributed union,
  keeping TranscriptMessage assignable to Message.

All other touched files are type-level-only adjustments (annotations on
evolving arrays that inferred never[], predicate types, casts in SDK wire
adapters and test fixtures) — no runtime logic changed anywhere; the full
bun test suite passes 3690/0 before and after.

Result: 415 → 342 tsc errors, every never-cascade in the message pipeline
resolved, no file above its per-file baseline (ratchet updated).

Part of issue #473.

Co-Authored-By: OpenClaude <openclaude@gitlawb.com>

* fix(typecheck): narrow unknowns and fix signature drift — 342 → 94 tsc errors

Clears every remaining non-test error. Honest fixes dominate: evolving
array/let/useState/useRef annotations (the repo's noImplicitAny:false
disables evolving types), real type guards over unknown wire payloads,
hoisted react-compiler-style params annotated with their components'
real Props, and callee signature corrections (useRegisterOverlay
optional param, generic useVoiceState<T>, growthbook shim's accepted
refresh-interval param) that each cleared several call sites. Targeted
reason-commented casts only at SDK/stub/wire boundaries; no any, no
new suppressions.

Runtime deviations are confined to already-broken paths: benchmark.ts
imported a function name that never existed (module-load crash),
caches.ts called stub methods unguarded (TypeError for ant-gated
users), messageActions returned undefined from a string function;
CACHE_EDITING_BETA_HEADER is a best-effort reconstruction of a
squash-lost constant, reachable only behind feature-gated first-party
paths (flagged for review).

Also: ConnectorTextBlock gains its wire-proven optional signature
field; MCP server factory ambient types gain close(); ink
render-node-to-output's nodeType cast fixed (intersection was
collapsing the intended widening); upstreamproxy relay normalizes the
socket data union.

Validation: full suite 3690 green, smoke + bundle guard green;
remaining 94 errors are all in test files (PR 5). Baseline lowered via
typecheck:ratchet --update.

Co-Authored-By: OpenClaude <openclaude@gitlawb.com>

* fix(typecheck): clean test typing, gate CI on zero tsc errors — 94 → 0

Closes the typecheck burn-down (issue #473): bun run typecheck now
exits 0 across the whole repo and CI fails on any new error.

Test typing: new src/test/typedMocks.ts centralizes the two bun:test
gaps (asMockFetch — Mock<T> lacks fetch.preconnect; callArgs —
argless-signature mocks collapse mock.calls to []). Beyond the
helpers, fixes are honest: discriminated-union narrowing before
member access, fixture typing with boundary casts, assertion-type
corrections, and two tests realigned to production signatures they
had drifted from (requestLogging logApiCallEnd args,
incrementalTokenCounter tokenBudget rename) with identical assert
outcomes. No assertion semantics changed; all touched suites pass.

CI: the ratchet served its purpose and is retired — pr-checks now
runs a plain `bun run typecheck` step; ratchet script and baseline
deleted.

Burn-down summary across the series: 697 → 624 (mechanical sweep) →
415 (recreate ~60 missing modules) → 342 (Message discriminated
union) → 94 (narrowing + signature drift) → 0 (this PR).

Validation: tsc --noEmit exit 0, full suite 3690 green, smoke +
bundle guard green.

Co-Authored-By: OpenClaude <openclaude@gitlawb.com>

* fix(typecheck): reconcile with upstream parallel typecheck fixes

Upstream landed #1591/#1592/#1595 while this series was in flight,
fixing some of the same errors differently. Rebase resolutions prefer
upstream where it is authoritative: their CACHE_EDITING_BETA_HEADER
value ('cache-editing-2025-12-01', unconditional) replaces this
series' feature-gated reconstruction; their cachedMicrocompact stub
shapes (with their new test file) replace ours, with boundary casts in
claude.ts where the stub's unknown[] edits meet the local pinned
delete-edit shape; their reader/ReadResult stream typing in openaiShim
replaces ours. MessageWithoutProgress now matches its name
(Exclude<NormalizedMessage, ProgressMessage>), reconciling upstream's
RenderableMessage GroupingResult with this series' message union; the
@ts-expect-error upstream added for settingsTypes.generated is removed
since the module now exists.

tsc exit 0; full suite 3697 green (incl. upstream's new
cachedMicrocompact tests); smoke + bundle guard green.

Co-Authored-By: OpenClaude <openclaude@gitlawb.com>

* fix(sdk): keep result usage counters required, fix assistant stub exports

Addresses jatmn's and chioarub's review on the typecheck PR:

1. SDK usage contract restored: the generated result types' usage now
   keeps input_tokens, output_tokens, cache_creation_input_tokens, and
   cache_read_input_tokens as REQUIRED numbers — result messages are
   populated from QueryEngine.totalUsage (initialized from
   EMPTY_USAGE), so they are always present at runtime and strict
   consumers may sum them without undefined guards. The richer nested
   metadata (cache_creation, server_tool_use, service_tier) is modeled
   explicitly instead of hiding behind the index signature; the nested
   objects carry no index signature so the SDK's interface types stay
   assignable. Generator override updated and artifacts regenerated; a
   new package-consumer type test sums the counters and reads the
   nested fields so this contract cannot silently regress. The
   sessionHistory test fixture now carries all four counters, matching
   runtime shape.

2. Assistant install wizard stub mismatch fixed: dialogLaunchers
   imported NewInstallWizard/computeDefaultInstallDir through a module
   shape cast, but the assistant stub only exported default — a
   guaranteed runtime crash if the gated path lit up. The stub now
   provides real typed exports: a wizard that cancels immediately (so
   the launcher resolves null/user-cancelled instead of hanging on an
   empty dialog) and an inert computeDefaultInstallDir; the unsafe
   cast in dialogLaunchers is gone.

Validation: tsc exit 0; full suite 3698 green (incl. the new consumer
counters test); smoke + bundle guard green.

Co-Authored-By: OpenClaude <openclaude@gitlawb.com>

---------

Co-authored-by: OpenClaude <openclaude@gitlawb.com>
2026-06-11 07:59:18 +08:00
chioarubandGitHub 3a308c11d4 fix(typecheck): restore control protocol type exports (#1497)
* fix(typecheck): restore control protocol type exports

* fix(sdk): align control initialize contract

* fix(sdk): expose control initialize response types
2026-06-08 12:05:16 +08:00
60c76b6599 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>
2026-05-04 20:56:30 +08:00