ソースを参照

Use explicit ts specifiers for declarations

Restore explicit .ts relative specifiers in source and enable rewriteRelativeImportExtensions so emitted JS uses .js while declarations keep explicit .ts specifiers. Add a NodeNext declaration-consumer gate to prevent extensionless declaration regressions.
Tianyi Cui 3 ヶ月 前
親
コミット
07f4047ff0
56 ファイル変更、323 行追加、147 行削除
  1. 4 3
      .github/workflows/ci.yml
  2. 4 2
      AGENTS.md
  3. 2 0
      docs/cookbook/adding-a-package.md
  4. 2 0
      docs/cookbook/adding-a-vendored-package.md
  5. 3 2
      docs/development.md
  6. 1 1
      docs/rfc/implemented/process/2026-06-11-quality-gates.md
  7. 7 2
      docs/rfc/implemented/process/2026-06-17-ts-build-config.md
  8. 2 1
      package.json
  9. 1 1
      packages/README.md
  10. 4 4
      packages/bash/bash-local/src/index.ts
  11. 3 3
      packages/bash/bash/src/index.ts
  12. 2 2
      packages/core/agent-loop/src/agent.ts
  13. 4 4
      packages/core/agent-loop/src/index.ts
  14. 1 1
      packages/core/agent-loop/src/loop.ts
  15. 2 2
      packages/core/agent/src/index.ts
  16. 6 6
      packages/core/session/src/index.ts
  17. 1 1
      packages/core/session/src/repair.ts
  18. 1 1
      packages/core/tools/src/index.ts
  19. 1 1
      packages/core/tools/src/schema.ts
  20. 5 5
      packages/llm/llm-deepseek/src/adapter.ts
  21. 8 8
      packages/llm/llm-deepseek/src/index.ts
  22. 1 1
      packages/llm/llm-deepseek/src/serialize.ts
  23. 2 2
      packages/llm/llm-deepseek/src/translate.ts
  24. 1 1
      packages/llm/llm-pi-ai/src/adapter.ts
  25. 5 5
      packages/llm/llm-pi-ai/src/index.ts
  26. 3 3
      packages/llm/llm/src/assembler.ts
  27. 7 7
      packages/llm/llm/src/index.ts
  28. 1 1
      packages/llm/llm/src/types.ts
  29. 1 1
      packages/session-persistence/session-persistence-jsonl/src/index.ts
  30. 2 2
      packages/session-persistence/session-persistence-sqlite/src/index.ts
  31. 1 1
      packages/session-persistence/session-persistence/src/coordinator.ts
  32. 2 2
      packages/session-persistence/session-persistence/src/index.ts
  33. 1 1
      packages/ui/acp/src/index.ts
  34. 160 0
      scripts/verify-node-next-types.ts
  35. 2 0
      tsconfig.base.json
  36. 1 1
      vendor/README.md
  37. 6 6
      vendor/cordis/src/context.ts
  38. 4 4
      vendor/cordis/src/events.ts
  39. 5 5
      vendor/cordis/src/fiber.ts
  40. 7 7
      vendor/cordis/src/index.ts
  41. 4 4
      vendor/cordis/src/logger.ts
  42. 4 4
      vendor/cordis/src/reflect.ts
  43. 4 4
      vendor/cordis/src/registry.ts
  44. 2 2
      vendor/cordis/src/service.ts
  45. 1 1
      vendor/cordis/src/utils.ts
  46. 1 1
      vendor/cosmokit/src/array.ts
  47. 5 5
      vendor/cosmokit/src/index.ts
  48. 1 1
      vendor/cosmokit/src/types.ts
  49. 1 1
      vendor/hmr/src/index.ts
  50. 4 4
      vendor/loader/src/config/entry.ts
  51. 2 2
      vendor/loader/src/config/group.ts
  52. 2 2
      vendor/loader/src/config/isolate.ts
  53. 2 2
      vendor/loader/src/config/tree.ts
  54. 10 10
      vendor/loader/src/index.ts
  55. 2 2
      vendor/logger-console/src/browser.ts
  56. 2 2
      vendor/logger-console/src/index.ts

+ 4 - 3
.github/workflows/ci.yml

@@ -69,12 +69,13 @@ jobs:
         run: pnpm run test:snapshot
 
       # Before hygiene: publint validates the packed artifacts (lib/index.js),
-      # which only the tsdown bundling step emits.
+      # which only the tsdown bundling step emits, and verify-node-next-types
+      # validates the built declarations.
       - name: Build (tsc -b + tsdown bundles)
         run: pnpm run build
 
-      - name: Hygiene (knip + publint)
-        run: pnpm run knip && pnpm run publint
+      - name: Hygiene (knip + publint + constraints + NodeNext types)
+        run: pnpm run hygiene
 
       - name: Demo smoke test
         run: |

+ 4 - 2
AGENTS.md

@@ -145,7 +145,7 @@ pnpm run lint:fix       # eslint . --fix
 pnpm run build          # tsc emits lib/types, then tsdown bundles runtime lib/index.*
 pnpm run knip           # dead-code / unused-dependency check
 pnpm run publint        # package.json publish-correctness check (every packages/*/* package)
-pnpm run hygiene        # knip + publint + workspace constraints
+pnpm run hygiene        # knip + publint + workspace constraints + NodeNext type-consumer check
 pnpm run doc-typecheck  # typecheck every ```ts block in README.md, docs/**/*.md,
                     # packages/*/*.md + packages/*/*/*.md (doc/code drift gate)
 pnpm run gen-cordis-catalog  # regenerate docs/cordis-catalog/events-and-services.md
@@ -160,6 +160,8 @@ pnpm run verify-package-paths  # assert every packages/<path> cited in Markdown
 pnpm run verify-rfc-classification  # assert every RFC lives in a valid
                     # {lifecycle}/{class}/ folder and docs/rfc/README.md lists it
                     # under the matching heading (closed class set + index completeness)
+pnpm run verify-node-next-types  # assert built declarations typecheck for a
+                    # standard external NodeNext ESM TypeScript consumer
 pnpm run doc-sync       # doc-typecheck + verify-cordis-catalog + verify-md-wrap + verify-md-links + verify-doc-refs + verify-package-paths + verify-rfc-classification + verify-type-equiv (CI runs this)
 pnpm run demo:echo      # run examples/echo-agent (no API key; type "echo hi" to
                     # see a tool call) — the mock skeleton
@@ -188,7 +190,7 @@ Dev/test/demo run **unbuilt** via tsx + the source `paths` map in the root `tsco
 ## Conventions
 
 - **Package naming**: every npm package in this repo is `@deepseek-ai/dsh-<name>` (vendored packages keep their upstream names and are `private: true`).
-- **ESM everywhere** (`"type": "module"`); imports between workspace packages use package names, never relative paths across package boundaries. In-package relative imports are extensionless so generated `.d.ts` files stay extensionless; `lib/types/**/*.js` is a bundler-only intermediate, not a Node ESM entrypoint.
+- **ESM everywhere** (`"type": "module"`); imports between workspace packages use package names, never relative paths across package boundaries. In-package relative imports use explicit `.ts` extensions; `rewriteRelativeImportExtensions` turns those into `.js` in emitted JS, while declarations keep explicit `.ts` specifiers that NodeNext/Node16 TypeScript consumers can resolve to sibling `.d.ts` files. `lib/types/**/*.js` is a bundler-only intermediate, not a Node ESM entrypoint.
 - **`cordis` is a peerDependency** (+ devDependency) of every harness package, mirroring upstream convention.
 - **Registrations are effects**: anything a plugin contributes (adapter, tool, section, agent, event listener) goes through `ctx.effect()` / `ctx.on()` so disposal and HMR work. If you write a registry, `register()` must return the disposer.
 - **Typed events via declaration merging**: services declare their events in `declare module 'cordis' { interface Events { … } }`, and their ctx key in `interface Context`. Extensible unions use the merge-extensible-map pattern (see `ContentBlockMap`, `MessageSourceMap`).

+ 2 - 0
docs/cookbook/adding-a-package.md

@@ -17,6 +17,8 @@ packages/<name>/
 
 package.json invariants (enforced by `pnpm run constraints` / `scripts/check-workspace-constraints.ts`): `private: true`, `version: 0.0.1`, `type: module`, `main: "lib/index.js"`, `types: "lib/types/index.d.ts"`, `exports["."].types: "./lib/types/index.d.ts"`, `exports["."].default: "./lib/index.js"`, `cordis` in BOTH peerDependencies and devDependencies (same range). Mirror every dsh peer dependency in devDependencies. `schemastery` goes in `dependencies` (it is a runtime validator), matching agent-loop. The `files` list is precise: `lib/index.js`, `lib/types/**/*.d.ts`, `lib/types/**/*.d.ts.map`, and `src`; do not publish `lib/types` JS or JS-map intermediates or stale root declaration files. CLI app packages with a package `bin` include `lib/bin.js` immediately after `lib/index.js` in `files`.
 
+In-package relative imports use explicit `.ts` specifiers in source (for example, `export * from './types.ts'`). The compiler rewrites those to `.js` in emitted JS and leaves explicit `.ts` specifiers in declarations, which standard NodeNext/Node16 TypeScript consumers resolve to the sibling `.d.ts` files.
+
 ## 2. Register it in the root configs
 
 | File | Change |

+ 2 - 0
docs/cookbook/adding-a-vendored-package.md

@@ -29,6 +29,8 @@ vendor/<dir>/
 
 `package.json` invariants: `"private": true` (vendored packages are never published), keep upstream's `name`/`version`/`exports`/`type`, point declaration metadata at `lib/types`, publish `.d.ts` and `.d.ts.map` declaration outputs, and list its cordis deps in `peerDependencies` (matching the upstream manifest). Transitive upstream deps must themselves be vendored or already present — vendoring one package often means vendoring its dependency tree (e.g. `@cordisjs/plugin-http` pulls `@cordisjs/fetch-file`).
 
+Local relative imports/exports in vendored TypeScript source use explicit `.ts` specifiers after copying. This is a repo-local build-shape divergence from upstream: `rewriteRelativeImportExtensions` emits `.js` runtime imports while declarations keep explicit `.ts` specifiers that NodeNext/Node16 TypeScript consumers can resolve.
+
 ## 2. Register it in the root configs
 
 | File | Change |

+ 3 - 2
docs/development.md

@@ -39,7 +39,7 @@ If you are preparing to push from a fresh clone or worktree, also build once:
 pnpm run build
 ```
 
-`pnpm run hygiene` includes `publint`, which validates package entrypoints against the built `lib/*.js` files. A fresh worktree has no bundled JS until `pnpm run build` runs.
+`pnpm run hygiene` includes `publint`, which validates package entrypoints against the built `lib/*.js` files, and `verify-node-next-types`, which validates built declarations against a temporary NodeNext consumer. A fresh worktree has no bundled JS or declarations until `pnpm run build` runs.
 
 ## Environment variables
 
@@ -101,7 +101,8 @@ pnpm run doc-sync       # doc-typecheck, cordis-catalog freshness, markdown wrap
 pnpm run gen-module-graph     # regenerate docs/module-graph.md from package peerDeps
 pnpm run verify-module-graph  # fail if docs/module-graph.md is stale
 pnpm run build          # emit lib/types intermediates, then bundle lib/index.* runtime files
-pnpm run hygiene        # knip, publint, and workspace constraints
+pnpm run verify-node-next-types  # fail if built declarations are not NodeNext-consumable
+pnpm run hygiene        # knip, publint, workspace constraints, and NodeNext declaration check
 ```
 
 When changing package public behavior, update the relevant README or JSDoc in the same change. `pnpm run doc-sync` catches checked TypeScript snippets, cordis events/services catalog drift, and hard-wrapped markdown prose, but broader prose/API sync still needs review.

+ 1 - 1
docs/rfc/implemented/process/2026-06-11-quality-gates.md

@@ -15,7 +15,7 @@ Every AGENTS.md promise gets a command that exits non-zero, wired into git hooks
 - Max-strict TypeScript (`noUncheckedIndexedAccess`, `exactOptionalPropertyTypes`, …); examples, tests, and scripts typecheck in CI via the root no-emit `tsconfig.json` while package/vendor code stays behind its own project-reference boundary.
 - ESLint strict-type-checked + @stylistic (the house style, enforced); vendored code excluded.
 - Per-file 100% coverage on `packages/*/src` (v8); unreachable defensive guards carry `/* v8 ignore */ ` with stated reasons instead of deletion.
-- knip (dead code/deps), publint (package correctness), workspace constraints (workspace rules: private, cordis peer+dev, uniform version, ESM).
+- knip (dead code/deps), publint (package correctness), workspace constraints (workspace rules: private, cordis peer+dev, uniform version, ESM), and a NodeNext consumer typecheck for built package declarations.
 - lefthook pre-commit (lint staged, typecheck, vendor-manifest guard) and pre-push (tests, hygiene); CI runs the full matrix on node 24/26 plus a demo smoke test driving the echo-agent end to end.
 
 ## Consequences

+ 7 - 2
docs/rfc/implemented/process/2026-06-17-ts-build-config.md

@@ -17,7 +17,7 @@ Validation found several concrete technical issues and possible routes:
 
 - `tsdown` uses `oxc` to transform TypeScript, which is not the same behavior as `tsc`.
     - Bundled `.d.ts` emitted by `tsdown` conflicts with Cordis' internal relative module augmentation shape.
-    - The tsc output is affected by `allowImportingTsExtensions`, so we need to ensure that generated `.js` files do not import `.ts` files and generated `.d.ts` files do not import `.js` files. Therefore, we need to adjust the import specifiers to extensionless in the TypeScript source.
+    - The tsc output is affected by `allowImportingTsExtensions`, so we need to ensure that generated `.js` files do not import `.ts` files and generated `.d.ts` files do not contain extensionless relative imports. Therefore, in-package relative imports use explicit `.ts` specifiers in TypeScript source and `rewriteRelativeImportExtensions` rewrites those specifiers to `.js` in emitted JS.
     - Bundled `.js` emitted by `tsdown` is not the same behavior as per-file `.js` emitted by `tsc -b`, such as decorator transform behavior.
 - `vendor/*/src`, examples, tests, and scripts cannot all be plain-included in one root strict program.
     - Directly typechecking `vendor/*/src` under the root strict config triggers many type errors outside this project's ownership.
@@ -26,7 +26,7 @@ Validation found several concrete technical issues and possible routes:
 
 ## Decision
 
-In-package relative imports are extensionless.
+In-package relative imports use explicit `.ts` specifiers.
 
 `pnpm run build` is a two-stage build:
 
@@ -47,6 +47,9 @@ pnpm run build:
 tsc -b tsconfig.build.json
 tsdown
 
+pnpm run verify-node-next-types:
+tsx scripts/verify-node-next-types.ts
+
 pnpm run typecheck:
 tsc -b tsconfig.json
 ```
@@ -60,8 +63,10 @@ Build responsibilities are clearer:
 - Each module under `packages/<group>/<pkg>` and `vendor/*` has one local tsconfig for build, typecheck, and tools that run source directly, such as `tsx` and `vitest`.
 - The `build` command uses `tsconfig.build.json`. `tsc -b` owns the publishable per-module `.js` and `.d.ts` output, and the bundler owns only `lib/index.*`.
     - `lib/types/*.d.ts` and `.d.ts.map` are the publish declaration output.
+    - `lib/types/*.d.ts` uses explicit `.ts` relative specifiers, which TypeScript's NodeNext/Node16 resolver maps to sibling `.d.ts` files.
     - `lib/types/*.js` is only a bundler input and must not be used as a runtime entry or public import target.
     - `lib/index.*` is the publish runtime output and is generated by the bundler, currently `tsdown`.
+- `pnpm run verify-node-next-types` scans built declarations for extensionless relative specifiers, then typechecks a temporary external ESM consumer with `moduleResolution: "NodeNext"` against the built `types`/`exports` surface, so declaration specifier regressions fail before publish.
 - The `typecheck` command uses `tsconfig.json`. Examples, tests, and scripts are checked by the root no-emit project, while packages and vendor modules keep the same emit behavior as `build`. Package and vendor source stays behind project-reference boundaries.
 
 The Cordis vendor copy now has one more type-structure divergence from upstream. During upstream sync, that divergence must be reapplied or explicitly retired.

+ 2 - 1
package.json

@@ -31,13 +31,14 @@
     "verify-package-paths": "tsx scripts/verify-package-paths.ts",
     "verify-rfc-classification": "tsx scripts/verify-rfc-classification.ts",
     "verify-type-equiv": "tsx scripts/verify-type-equiv.ts",
+    "verify-node-next-types": "tsx scripts/verify-node-next-types.ts",
     "gen-cordis-catalog": "tsx scripts/gen-cordis-catalog.ts",
     "verify-cordis-catalog": "tsx scripts/gen-cordis-catalog.ts --check",
     "gen-module-graph": "tsx scripts/gen-module-graph.ts",
     "verify-module-graph": "tsx scripts/gen-module-graph.ts --check",
     "constraints": "tsx scripts/check-workspace-constraints.ts",
     "doc-sync": "pnpm run doc-typecheck && pnpm run verify-cordis-catalog && pnpm run verify-md-wrap && pnpm run verify-md-links && pnpm run verify-doc-refs && pnpm run verify-package-paths && pnpm run verify-rfc-classification && pnpm run verify-type-equiv",
-    "hygiene": "pnpm run knip && pnpm run publint && pnpm run constraints",
+    "hygiene": "pnpm run knip && pnpm run publint && pnpm run constraints && pnpm run verify-node-next-types",
     "demo:echo": "node --expose-internals --import tsx packages/ui/stdio-agent/src/bin.ts examples/echo-agent/cordis.yml",
     "demo:coding": "node --expose-internals --import tsx packages/ui/stdio-agent/src/bin.ts examples/coding-agent/cordis.yml",
     "demo:acp": "node --import tsx packages/ui/acp-agent/src/bin.ts examples/acp-agent/cordis.yml",

+ 1 - 1
packages/README.md

@@ -79,5 +79,5 @@ Each package has its own `README.md` with purpose, service API, events, extensio
 - **Declaration merging for events and ctx**: services declare their events in `declare module 'cordis' { interface Events { ... } }` and their ctx key in `interface Context`.
 - **Waterfall semantics**: `ctx.waterfall` listeners receive `(...args, next)` and MUST call `next()` to delegate; returning without it short-circuits (the veto mechanism).
 - **Extensible unions**: `ContentBlockMap`, `MessageSourceMap`, `FinishReasonMap`, `TurnTriggerMap`, `TurnEndReasonMap`, and `SessionEventMap` use the merge-extensible-map pattern so plugins can add variants via declaration merging.
-- **ESM everywhere**; imports use package names across package boundaries and extensionless relative specifiers within a package.
+- **ESM everywhere**; imports use package names across package boundaries and explicit `.ts` relative specifiers within a package.
 - **Tests**: vitest, colocated under `packages/<group>/<pkg>/tests/*.spec.ts`. Every registry needs an HMR-safety test. Err on the side of more tests.

+ 4 - 4
packages/bash/bash-local/src/index.ts

@@ -17,11 +17,11 @@ import { Context } from 'cordis'
 import z from 'schemastery'
 import { BashExecutor, BashTaskId } from '@deepseek-ai/dsh-bash'
 import type { BashExecRequest, BashExecSpec, BashRunResult, BashTask, BashTaskRead, OwnerToken } from '@deepseek-ai/dsh-bash'
-import { runBash } from './run'
-import type { RunInternals, RunningBash } from './run'
+import { runBash } from './run.ts'
+import type { RunInternals, RunningBash } from './run.ts'
 
-export { DEFAULT_GRACE_MS, ENV_OVERRIDES, killGroup, OutputCollector, runBash } from './run'
-export type { RunInternals, RunningBash, SpawnOutcome, SpawnSpec } from './run'
+export { DEFAULT_GRACE_MS, ENV_OVERRIDES, killGroup, OutputCollector, runBash } from './run.ts'
+export type { RunInternals, RunningBash, SpawnOutcome, SpawnSpec } from './run.ts'
 
 /** Plugin config (all optional — `static Config` supplies the defaults). */
 export interface Config {

+ 3 - 3
packages/bash/bash/src/index.ts

@@ -15,9 +15,9 @@
  */
 
 import { Context, Service } from 'cordis'
-import type { BashExecRequest, BashExecSpec, BashRunResult, BashTask, BashTaskId, BashTaskListener, BashTaskRead, OwnerToken } from './types'
+import type { BashExecRequest, BashExecSpec, BashRunResult, BashTask, BashTaskId, BashTaskListener, BashTaskRead, OwnerToken } from './types.ts'
 
-export { BashTaskId, OwnerToken } from './types'
+export { BashTaskId, OwnerToken } from './types.ts'
 export type {
   BashExecRequest,
   BashExecSpec,
@@ -27,7 +27,7 @@ export type {
   BashTaskRead,
   BashTaskStatus,
   CollectedOutput,
-} from './types'
+} from './types.ts'
 
 declare module 'cordis' {
   interface Context {

+ 2 - 2
packages/core/agent-loop/src/agent.ts

@@ -11,8 +11,8 @@ import type { AgentId, AgentOptions, AgentStatus, SendOptions } from '@deepseek-
 import type { Agent } from '@deepseek-ai/dsh-agent'
 import type { ContentBlock, MessageSource } from '@deepseek-ai/dsh-llm'
 import type { Session } from '@deepseek-ai/dsh-session'
-import { Inbox } from './inbox'
-import { isTurnOpen, lastTurnNumber, runLoop } from './loop'
+import { Inbox } from './inbox.ts'
+import { isTurnOpen, lastTurnNumber, runLoop } from './loop.ts'
 
 /**
  * The concrete {@link Agent} implementation owned by the agent-loop plugin.

+ 4 - 4
packages/core/agent-loop/src/index.ts

@@ -17,11 +17,11 @@ import type { Session } from '@deepseek-ai/dsh-session'
 import type {} from '@deepseek-ai/dsh-system-prompt'
 import type {} from '@deepseek-ai/dsh-tools'
 import type { SessionPersistence } from '@deepseek-ai/dsh-session-persistence'
-import { ReactLoopAgent } from './agent'
+import { ReactLoopAgent } from './agent.ts'
 
-export { ReactLoopAgent } from './agent'
-export { Inbox, type InboxMessage } from './inbox'
-export { runLoop } from './loop'
+export { ReactLoopAgent } from './agent.ts'
+export { Inbox, type InboxMessage } from './inbox.ts'
+export { runLoop } from './loop.ts'
 
 declare module 'cordis' {
   interface Context {

+ 1 - 1
packages/core/agent-loop/src/loop.ts

@@ -13,7 +13,7 @@ import { BlockAssembler, HarnessError } from '@deepseek-ai/dsh-llm'
 import type { Session, TurnEndReason, TurnTrigger } from '@deepseek-ai/dsh-session'
 import { renderPrompt } from '@deepseek-ai/dsh-system-prompt'
 import type {} from '@deepseek-ai/dsh-tools'
-import type { ReactLoopAgent } from './agent'
+import type { ReactLoopAgent } from './agent.ts'
 
 /** An Error with an optional machine-readable code (e.g., from LlmError or a throwing plugin). */
 type CodedError = Error & { code?: string }

+ 2 - 2
packages/core/agent/src/index.ts

@@ -7,9 +7,9 @@
 
 import { Context, Service } from 'cordis'
 import type { SessionId } from '@deepseek-ai/dsh-session'
-import type { Agent, AgentId, AgentOptions } from './types'
+import type { Agent, AgentId, AgentOptions } from './types.ts'
 
-export * from './types'
+export * from './types.ts'
 
 declare module 'cordis' {
   interface Context {

+ 6 - 6
packages/core/session/src/index.ts

@@ -9,13 +9,13 @@
 import { Context, Service } from 'cordis'
 import { isAbsolute } from 'node:path'
 import type { ContentBlock, Message, MessageSource } from '@deepseek-ai/dsh-llm'
-import { SESSION_FORMAT_VERSION, SessionId } from './types'
-import type { CreateSessionOptions, SessionEvent, SessionEventMap, SessionEventType, SessionHeader } from './types'
-import { isJsonValue } from './json'
+import { SESSION_FORMAT_VERSION, SessionId } from './types.ts'
+import type { CreateSessionOptions, SessionEvent, SessionEventMap, SessionEventType, SessionHeader } from './types.ts'
+import { isJsonValue } from './json.ts'
 
-export * from './types'
-export { isJsonValue } from './json'
-export { interruptedTurnClosers } from './repair'
+export * from './types.ts'
+export { isJsonValue } from './json.ts'
+export { interruptedTurnClosers } from './repair.ts'
 
 declare module 'cordis' {
   interface Context {

+ 1 - 1
packages/core/session/src/repair.ts

@@ -36,7 +36,7 @@
  */
 
 import type { CallId } from '@deepseek-ai/dsh-llm'
-import type { SessionEvent } from './types'
+import type { SessionEvent } from './types.ts'
 
 /**
  * Scan `events` for an open turn/step at the tail and return the synthetic

+ 1 - 1
packages/core/tools/src/index.ts

@@ -24,7 +24,7 @@ export {
   type InferArgs,
   type DefineToolOptions,
   type JsonSchemaObject,
-} from './schema'
+} from './schema.ts'
 
 declare module 'cordis' {
   interface Context {

+ 1 - 1
packages/core/tools/src/schema.ts

@@ -21,7 +21,7 @@
 
 import type { ContentBlock } from '@deepseek-ai/dsh-llm'
 import { assertNever, HarnessError } from '@deepseek-ai/dsh-llm'
-import type { ToolCallPresentation, ToolDefinition, ToolExecution, ToolResult, ToolResultPresentation } from './index'
+import type { ToolCallPresentation, ToolDefinition, ToolExecution, ToolResult, ToolResultPresentation } from './index.ts'
 
 // ---------------------------------------------------------------------------
 // SchemaSpec — the author-facing per-property type

+ 5 - 5
packages/llm/llm-deepseek/src/adapter.ts

@@ -7,11 +7,11 @@
 
 import { LlmAdapter, LlmError } from '@deepseek-ai/dsh-llm'
 import type { GenerateOptions, StreamChunk } from '@deepseek-ai/dsh-llm'
-import { serializeRequest } from './serialize'
-import type { RequestDefaults } from './serialize'
-import { parseSse } from './sse'
-import { translate } from './translate'
-import type { WireError } from './types'
+import { serializeRequest } from './serialize.ts'
+import type { RequestDefaults } from './serialize.ts'
+import { parseSse } from './sse.ts'
+import { translate } from './translate.ts'
+import type { WireError } from './types.ts'
 
 export interface DeepSeekAdapterOptions {
   apiKey: string

+ 8 - 8
packages/llm/llm-deepseek/src/index.ts

@@ -21,15 +21,15 @@
 import type { Context } from 'cordis'
 import z from 'schemastery'
 import type {} from '@deepseek-ai/dsh-llm'
-import { DeepSeekAdapter } from './adapter'
+import { DeepSeekAdapter } from './adapter.ts'
 
-export { DeepSeekAdapter, httpErrorCode } from './adapter'
-export type { DeepSeekAdapterOptions } from './adapter'
-export { serializeMessages, serializeRequest } from './serialize'
-export type { RequestDefaults } from './serialize'
-export { DONE, parseSse } from './sse'
-export { mapFinishReason, mapUsage, translate } from './translate'
-export type * from './types'
+export { DeepSeekAdapter, httpErrorCode } from './adapter.ts'
+export type { DeepSeekAdapterOptions } from './adapter.ts'
+export { serializeMessages, serializeRequest } from './serialize.ts'
+export type { RequestDefaults } from './serialize.ts'
+export { DONE, parseSse } from './sse.ts'
+export { mapFinishReason, mapUsage, translate } from './translate.ts'
+export type * from './types.ts'
 
 export const name = 'llm-deepseek'
 export const inject = ['llm']

+ 1 - 1
packages/llm/llm-deepseek/src/serialize.ts

@@ -18,7 +18,7 @@
 
 import { LlmError } from '@deepseek-ai/dsh-llm'
 import type { ContentBlock, GenerateOptions, Message } from '@deepseek-ai/dsh-llm'
-import type { WireMessage, WireRequest, WireTool } from './types'
+import type { WireMessage, WireRequest, WireTool } from './types.ts'
 
 /** Adapter-level request defaults (from plugin config). */
 export interface RequestDefaults {

+ 2 - 2
packages/llm/llm-deepseek/src/translate.ts

@@ -16,8 +16,8 @@
 
 import { CallId, LlmError } from '@deepseek-ai/dsh-llm'
 import type { ContentBlock, FinishReason, StreamChunk, TokenUsage } from '@deepseek-ai/dsh-llm'
-import { DONE } from './sse'
-import type { WireChunk, WireUsage } from './types'
+import { DONE } from './sse.ts'
+import type { WireChunk, WireUsage } from './types.ts'
 
 /** One open block under assembly. */
 interface OpenBlock {

+ 1 - 1
packages/llm/llm-pi-ai/src/adapter.ts

@@ -16,7 +16,7 @@ import type { Model } from '@earendil-works/pi-ai'
 import { LlmAdapter, LlmError } from '@deepseek-ai/dsh-llm'
 import { CallId } from '@deepseek-ai/dsh-llm'
 import type { GenerateOptions, StreamChunk, ToolSchema } from '@deepseek-ai/dsh-llm'
-import { toPiContext, toStreamChunks } from './convert'
+import { toPiContext, toStreamChunks } from './convert.ts'
 
 /** Reasoning levels surfaced by this adapter (DeepSeek wire: high|max). */
 export type PiAiReasoning = 'off' | 'high' | 'xhigh'

+ 5 - 5
packages/llm/llm-pi-ai/src/index.ts

@@ -19,12 +19,12 @@
 import type { Context } from 'cordis'
 import z from 'schemastery'
 import type {} from '@deepseek-ai/dsh-llm'
-import { PiAiAdapter } from './adapter'
-import type { PiAiReasoning } from './adapter'
+import { PiAiAdapter } from './adapter.ts'
+import type { PiAiReasoning } from './adapter.ts'
 
-export { buildModel, PiAiAdapter } from './adapter'
-export type { PiAiAdapterOptions, PiAiReasoning } from './adapter'
-export { mapStopReason, mapUsage, toPiContext, toStreamChunks } from './convert'
+export { buildModel, PiAiAdapter } from './adapter.ts'
+export type { PiAiAdapterOptions, PiAiReasoning } from './adapter.ts'
+export { mapStopReason, mapUsage, toPiContext, toStreamChunks } from './convert.ts'
 
 export const name = 'llm-pi-ai'
 export const inject = ['llm']

+ 3 - 3
packages/llm/llm/src/assembler.ts

@@ -6,9 +6,9 @@
  * @module @deepseek-ai/dsh-llm/assembler
  */
 
-import { CallId } from './brand'
-import { assertNever } from './never'
-import type { ContentBlock, FinishReason, Message, StreamChunk, TokenUsage } from './types'
+import { CallId } from './brand.ts'
+import { assertNever } from './never.ts'
+import type { ContentBlock, FinishReason, Message, StreamChunk, TokenUsage } from './types.ts'
 
 interface PartialBlock {
   blockType: string

+ 7 - 7
packages/llm/llm/src/index.ts

@@ -7,14 +7,14 @@
  */
 
 import { Context, Service } from 'cordis'
-import type { GenerateOptions, StreamChunk } from './types'
-import { HarnessError } from './error'
+import type { GenerateOptions, StreamChunk } from './types.ts'
+import { HarnessError } from './error.ts'
 
-export * from './brand'
-export * from './never'
-export * from './error'
-export * from './types'
-export { BlockAssembler } from './assembler'
+export * from './brand.ts'
+export * from './never.ts'
+export * from './error.ts'
+export * from './types.ts'
+export { BlockAssembler } from './assembler.ts'
 
 declare module 'cordis' {
   interface Context {

+ 1 - 1
packages/llm/llm/src/types.ts

@@ -19,7 +19,7 @@
  * ```
  */
 
-import type { CallId } from './brand'
+import type { CallId } from './brand.ts'
 
 /** Cache hint attached to a content block (provider-interpreted). */
 export type CacheHint = 'ephemeral'

+ 1 - 1
packages/session-persistence/session-persistence-jsonl/src/index.ts

@@ -29,7 +29,7 @@ import {
 import type { Session, SessionEvent, SessionId, SessionHeader } from '@deepseek-ai/dsh-session'
 import {
   encodeSegment, eventLine, logPath, parseHeaderMeta, scanLog, sessionDir, toHeaderLine,
-} from './format'
+} from './format.ts'
 
 export interface Config {
   /**

+ 2 - 2
packages/session-persistence/session-persistence-sqlite/src/index.ts

@@ -29,9 +29,9 @@ import {
 import type { Session, SessionEvent, SessionId, SessionHeader } from '@deepseek-ai/dsh-session'
 import {
   openDatabase, rowToMeta, scanRows, type EventRow, type SessionRow,
-} from './schema'
+} from './schema.ts'
 
-export { SCHEMA_VERSION } from './schema'
+export { SCHEMA_VERSION } from './schema.ts'
 
 /** Plugin configuration. */
 export interface Config {

+ 1 - 1
packages/session-persistence/session-persistence/src/coordinator.ts

@@ -27,7 +27,7 @@
 import { Context } from 'cordis'
 import { interruptedTurnClosers, SESSION_FORMAT_VERSION } from '@deepseek-ai/dsh-session'
 import type { Session, SessionEvent, SessionId, SessionHeader } from '@deepseek-ai/dsh-session'
-import { assertSerializable, seedCoversPrefix } from './index'
+import { assertSerializable, seedCoversPrefix } from './index.ts'
 
 /**
  * A stored session's durable prefix as read back from a backend: its

+ 2 - 2
packages/session-persistence/session-persistence/src/index.ts

@@ -29,8 +29,8 @@ import type { SessionEvent, SessionId, SessionHeader } from '@deepseek-ai/dsh-se
 export type { SessionHeader } from '@deepseek-ai/dsh-session'
 
 // The backend-agnostic write-path orchestration first-party backends compose.
-export { PersistenceCoordinator } from './coordinator'
-export type { PersistenceBackend, StoredPrefix } from './coordinator'
+export { PersistenceCoordinator } from './coordinator.ts'
+export type { PersistenceBackend, StoredPrefix } from './coordinator.ts'
 
 declare module 'cordis' {
   interface Context {

+ 1 - 1
packages/ui/acp/src/index.ts

@@ -74,7 +74,7 @@ import {
   harnessBlockToAcpContent,
   promptHasUnsupportedContent,
   turnEndToStopReason,
-} from './codec'
+} from './codec.ts'
 
 export const name = 'acp'
 // The bridge programs against the interface packages only (architecture rule:

+ 160 - 0
scripts/verify-node-next-types.ts

@@ -0,0 +1,160 @@
+/**
+ * Verify that built package declarations are consumable by a standard external
+ * TypeScript ESM project using NodeNext resolution.
+ *
+ * Run after `pnpm run build` has emitted declaration files under package
+ * `lib/types` directories.
+ */
+
+import { execFileSync } from 'node:child_process'
+import { existsSync, globSync, mkdirSync, mkdtempSync, readFileSync, rmSync, symlinkSync, writeFileSync } from 'node:fs'
+import { dirname, resolve } from 'node:path'
+
+const root = resolve(import.meta.dirname, '..')
+
+interface ExportTarget {
+  types?: string
+}
+
+interface PackageManifest {
+  name?: string
+  types?: string
+  exports?: Record<string, ExportTarget | string | null>
+}
+
+interface WorkspacePackage {
+  dir: string
+  name: string
+  manifest: PackageManifest
+}
+
+function readPackage(path: string): WorkspacePackage | null {
+  const manifest = JSON.parse(readFileSync(path, 'utf8')) as PackageManifest
+  if (!manifest.name) return null
+  return { dir: dirname(path), name: manifest.name, manifest }
+}
+
+function workspacePackages(): WorkspacePackage[] {
+  return [
+    ...globSync('vendor/*/package.json', { cwd: root }),
+    ...globSync('packages/*/*/package.json', { cwd: root }),
+  ]
+    .map(path => readPackage(resolve(root, path)))
+    .filter(pkg => pkg !== null)
+    .sort((a, b) => a.name.localeCompare(b.name))
+}
+
+const declarationSpecifierPattern = /(?:from\s*|import\s*\(\s*|import\s+|declare\s+module\s*)["'](\.{0,2}(?:\/[^"']*)?)["']/g
+const hasExtension = /\.[^/.]+$/
+
+function extensionlessRelativeSpecifiers(): string[] {
+  const errors: string[] = []
+  const files = [
+    ...globSync('vendor/*/lib/types/**/*.d.ts', { cwd: root }),
+    ...globSync('packages/*/*/lib/types/**/*.d.ts', { cwd: root }),
+  ].sort()
+
+  for (const file of files) {
+    const text = readFileSync(resolve(root, file), 'utf8')
+    for (const match of text.matchAll(declarationSpecifierPattern)) {
+      const specifier = match[1]
+      if (!specifier) continue
+      const isRelative = specifier === '.' || specifier.startsWith('./') || specifier.startsWith('../')
+      if (isRelative && !hasExtension.test(specifier)) errors.push(`${file}: ${specifier}`)
+    }
+  }
+
+  return errors
+}
+
+function publicSpecifiers(pkg: WorkspacePackage): string[] {
+  const specifiers = new Set<string>()
+  if (pkg.manifest.types) specifiers.add(pkg.name)
+
+  for (const [key, target] of Object.entries(pkg.manifest.exports ?? {})) {
+    if (key.includes('*') || key === './package.json') continue
+    if (typeof target !== 'object' || target === null || !target.types) continue
+    specifiers.add(key === '.' ? pkg.name : `${pkg.name}/${key.slice(2)}`)
+  }
+
+  return [...specifiers].sort()
+}
+
+function linkPackage(pkg: WorkspacePackage, nodeModules: string): void {
+  const parts = pkg.name.split('/')
+  const link = resolve(nodeModules, ...parts)
+  mkdirSync(dirname(link), { recursive: true })
+  symlinkSync(pkg.dir, link, 'dir')
+}
+
+const packages = workspacePackages()
+const badSpecifiers = extensionlessRelativeSpecifiers()
+if (badSpecifiers.length > 0) {
+  console.error('verify-node-next-types: declaration files still contain extensionless relative specifiers.')
+  console.error(badSpecifiers.join('\n'))
+  process.exit(1)
+}
+
+const missingOutputs = packages
+  .filter(pkg => pkg.manifest.types && !existsSync(resolve(pkg.dir, pkg.manifest.types)))
+  .map(pkg => `${pkg.name}: missing ${pkg.manifest.types}`)
+
+if (missingOutputs.length > 0) {
+  console.error('verify-node-next-types: build outputs are missing; run `pnpm run build` first.')
+  console.error(missingOutputs.join('\n'))
+  process.exit(1)
+}
+
+const tmp = mkdtempSync(resolve(root, '.node-next-types-'))
+let failed = false
+
+try {
+  const nodeModules = resolve(tmp, 'node_modules')
+  mkdirSync(nodeModules, { recursive: true })
+  for (const pkg of packages) linkPackage(pkg, nodeModules)
+
+  const rootTypes = resolve(root, 'node_modules/@types/node')
+  if (existsSync(rootTypes)) {
+    const typesDir = resolve(nodeModules, '@types')
+    mkdirSync(typesDir, { recursive: true })
+    symlinkSync(rootTypes, resolve(typesDir, 'node'), 'dir')
+  }
+
+  writeFileSync(resolve(tmp, 'package.json'), `${JSON.stringify({ type: 'module', private: true }, null, 2)}\n`)
+  writeFileSync(resolve(tmp, 'tsconfig.json'), `${JSON.stringify({
+    compilerOptions: {
+      target: 'es2024',
+      module: 'NodeNext',
+      moduleResolution: 'NodeNext',
+      strict: true,
+      // Third-party SDK declarations can have their own lib-check noise under a
+      // symlinked temp install. The explicit scan above owns our regression:
+      // extensionless relative specifiers in built declarations.
+      skipLibCheck: true,
+      preserveSymlinks: true,
+      noEmit: true,
+      types: ['node'],
+    },
+    include: ['index.ts'],
+  }, null, 2)}\n`)
+
+  const imports = packages.flatMap(publicSpecifiers)
+    .map((specifier, index) => `import * as mod${index} from ${JSON.stringify(specifier)};\nvoid mod${index};`)
+    .join('\n')
+  writeFileSync(resolve(tmp, 'index.ts'), `${imports}\n`)
+
+  execFileSync(resolve(root, 'node_modules/.bin/tsc'), ['-p', resolve(tmp, 'tsconfig.json'), '--pretty', 'false'], {
+    cwd: root,
+    stdio: 'pipe',
+  })
+  console.log(`verify-node-next-types: ${packages.length} workspace package declaration surface(s) compile under NodeNext.`)
+} catch (error: unknown) {
+  failed = true
+  const output = error as { stdout?: Buffer; stderr?: Buffer }
+  console.error('verify-node-next-types: NodeNext consumer typecheck failed.\n')
+  console.error(`${output.stdout?.toString() ?? ''}${output.stderr?.toString() ?? ''}`)
+} finally {
+  rmSync(tmp, { recursive: true, force: true })
+}
+
+if (failed) process.exit(1)

+ 2 - 0
tsconfig.base.json

@@ -10,6 +10,8 @@
     "incremental": true,
     "skipLibCheck": true,
     "esModuleInterop": true,
+    "allowImportingTsExtensions": true,
+    "rewriteRelativeImportExtensions": true,
     "verbatimModuleSyntax": false,
     "strict": true,
     "noUncheckedIndexedAccess": true,

+ 1 - 1
vendor/README.md

@@ -33,7 +33,7 @@ Keep this log exhaustive — every divergence from upstream must be listed.
 1. **`hmr/src/index.ts`**: removed the `./locales/en-US.yml` / `./locales/zh-CN.yml` imports, the `.i18n({...})` call on the `Config` schema, and the `src/locales/` directory. Rationale: those imports require a runtime YAML loader hook (`@cordisjs/unyaml`) that we do not vendor; the i18n texts only localize config descriptions.
 2. **All `package.json` files**: regenerated — added `private: true`, added precise `files` entries for bundled runtime files and `lib/types/**/*.d.ts` / `.d.ts.map`, preserved `src` in `files` only for packages whose previous file list already shipped it, added a `./src/*` export where missing, pointed declaration metadata at `lib/types`, and removed upstream `devDependencies`/`scripts`/`repository` fields. Dependency and peer-dependency ranges preserved, except `hmr` declares `esbuild` as a direct dev dependency because its source imports the `BuildFailure` type and pnpm's strict workspace resolution requires the owner package to name that dependency.
 3. **All `tsconfig.json` files**: regenerated to extend the repo-root `tsconfig.base.json`, emit TypeScript intermediates to `lib/types`, and declare project references.
-4. **Vendored TypeScript source internal specifiers**: changed local relative imports/exports from explicit `.ts` / `.js` specifiers to extensionless specifiers so generated `.js` and `.d.ts` intermediates are extensionless and no declaration postprocess is needed. This includes `loader/src/config/isolate.ts` changing `declare module './entry.ts'` to `declare module './entry'`.
+4. **Vendored TypeScript source internal specifiers**: changed local relative imports/exports from upstream's specifier shape to explicit `.ts` specifiers so TypeScript rewrites emitted JS to `.js` while declarations keep explicit, NodeNext-safe `.ts` specifiers. This includes `loader/src/config/isolate.ts` using `declare module './entry.ts'`.
 5. **`schemastery/tsdown.config.ts` and `logger-console/tsdown.config.ts`**: ours, not upstream files — per-package build-shape overrides (dual ESM+CJS output; separate node/browser entries) for the repo-root tsdown build. They read the JS emitted under `lib/types` and then write the publish runtime entries under `lib/`. Like the regenerated tsconfigs, they are not part of the upstream sync surface.
 
 ## Sync procedure

+ 6 - 6
vendor/cordis/src/context.ts

@@ -1,10 +1,10 @@
 import { Dict } from 'cosmokit'
-import { EventsService } from './events'
-import { LoggerService } from './logger'
-import { ReflectService } from './reflect'
-import { InjectKey, RegistryService } from './registry'
-import { getTraceable, symbols } from './utils'
-import { Fiber } from './fiber'
+import { EventsService } from './events.ts'
+import { LoggerService } from './logger.ts'
+import { ReflectService } from './reflect.ts'
+import { InjectKey, RegistryService } from './registry.ts'
+import { getTraceable, symbols } from './utils.ts'
+import { Fiber } from './fiber.ts'
 
 /**
  * Public shape of a Cordis context.

+ 4 - 4
vendor/cordis/src/events.ts

@@ -1,7 +1,7 @@
 import { defineProperty, Promisify } from 'cosmokit'
-import { Context } from './context'
-import { Fiber, FiberState } from './fiber'
-import { DisposableList, symbols } from './utils'
+import { Context } from './context.ts'
+import { Fiber, FiberState } from './fiber.ts'
+import { DisposableList, symbols } from './utils.ts'
 
 /** Return whether an event result should stop a bail-style dispatch. */
 export function isBailed(value: any) {
@@ -25,7 +25,7 @@ export type ThisType<F> = F extends (this: infer T, ...args: any) => any ? T : n
  */
 export type DispatchMode = 'emit' | 'parallel' | 'serial' | 'bail' | 'waterfall'
 
-declare module './context' {
+declare module './context.ts' {
   export interface Context {
     /* eslint-disable max-len */
     parallel<K extends keyof Events>(name: K, ...args: Parameters<Events[K]>): Promise<void>

+ 5 - 5
vendor/cordis/src/fiber.ts

@@ -1,11 +1,11 @@
 import { Awaitable, defineProperty, Dict, isNullable } from 'cosmokit'
-import { Context } from './context'
-import { Plugin } from './registry'
-import { buildOuterStack, composeError, DisposableList, getTraceable, isConstructor, isObject, symbols } from './utils'
-import { Impl } from './reflect'
+import { Context } from './context.ts'
+import { Plugin } from './registry.ts'
+import { buildOuterStack, composeError, DisposableList, getTraceable, isConstructor, isObject, symbols } from './utils.ts'
+import { Impl } from './reflect.ts'
 import { StandardSchemaV1 } from '@standard-schema/spec'
 
-declare module './context' {
+declare module './context.ts' {
   export interface Context extends Pick<Fiber, 'effect'> {
     fiber: Fiber
   }

+ 7 - 7
vendor/cordis/src/index.ts

@@ -1,14 +1,14 @@
 /** Core context type and root context implementation. */
-export * from './context'
+export * from './context.ts'
 /** Event bus, dispatch modes, and event augmentation types. */
-export * from './events'
+export * from './events.ts'
 /** Plugin fiber lifecycle, effects, and config validation helpers. */
-export * from './fiber'
+export * from './fiber.ts'
 /** Logger facade, logger service, message, exporter, and formatting types. */
-export * from './logger'
+export * from './logger.ts'
 /** Plugin registry, dependency injection, and plugin entrypoint types. */
-export * from './registry'
+export * from './registry.ts'
 /** Base service class and service lifecycle symbols. */
-export * from './service'
+export * from './service.ts'
 /** Shared internal helpers used by context, services, and plugin fibers. */
-export * from './utils'
+export * from './utils.ts'

+ 4 - 4
vendor/cordis/src/logger.ts

@@ -1,9 +1,9 @@
 import { defineProperty, hyphenate } from 'cosmokit'
-import { Context } from './context'
-import { Fiber } from './fiber'
-import { createCallable, joinPrototype, symbols, Tracker } from './utils'
+import { Context } from './context.ts'
+import { Fiber } from './fiber.ts'
+import { createCallable, joinPrototype, symbols, Tracker } from './utils.ts'
 
-declare module './context' {
+declare module './context.ts' {
   interface Intercept {
     logger: LoggerService.Intercept
   }

+ 4 - 4
vendor/cordis/src/reflect.ts

@@ -1,9 +1,9 @@
 import { defineProperty, Dict, isNullable } from 'cosmokit'
-import { Context } from './context'
-import { getTraceable, symbols, withProps } from './utils'
-import { Fiber, FiberState } from './fiber'
+import { Context } from './context.ts'
+import { getTraceable, symbols, withProps } from './utils.ts'
+import { Fiber, FiberState } from './fiber.ts'
 
-declare module './context' {
+declare module './context.ts' {
   interface Context {
     get<K extends string & keyof this>(name: K, strict?: boolean): undefined | this[K]
     get(name: string, strict?: boolean): any

+ 4 - 4
vendor/cordis/src/registry.ts

@@ -1,8 +1,8 @@
 import { defineProperty, Dict } from 'cosmokit'
 import { StandardSchemaV1 } from '@standard-schema/spec'
-import { Context } from './context'
-import { Fiber } from './fiber'
-import { buildOuterStack, DisposableList, symbols, withProps } from './utils'
+import { Context } from './context.ts'
+import { Fiber } from './fiber.ts'
+import { buildOuterStack, DisposableList, symbols, withProps } from './utils.ts'
 
 function isApplicable(object: Plugin) {
   return object && typeof object === 'object' && typeof object.apply === 'function'
@@ -140,7 +140,7 @@ type GetPluginConfig<P> =
   ? S
   : GetPluginParameters<P>[0]
 
-declare module './context' {
+declare module './context.ts' {
   export interface Context {
     inject(deps: Inject, callback: Plugin.Function<void>): Fiber & PromiseLike<Fiber>
     plugin<P extends Plugin>(plugin: P, ...args: Spread<GetPluginConfig<P>>): Fiber & PromiseLike<Fiber>

+ 2 - 2
vendor/cordis/src/service.ts

@@ -1,6 +1,6 @@
 import { defineProperty } from 'cosmokit'
-import { Context } from './context'
-import { createCallable, joinPrototype, symbols, Tracker } from './utils'
+import { Context } from './context.ts'
+import { createCallable, joinPrototype, symbols, Tracker } from './utils.ts'
 
 /**
  * Base class for services that expose a named API on `ctx`.

+ 1 - 1
vendor/cordis/src/utils.ts

@@ -1,5 +1,5 @@
 import { defineProperty } from 'cosmokit'
-import type { Context, Service } from '.'
+import type { Context, Service } from './index.ts'
 
 /** Ordered collection of disposable values with O(1) deletion by value. */
 export class DisposableList<T extends WeakKey> {

+ 1 - 1
vendor/cosmokit/src/array.ts

@@ -1,4 +1,4 @@
-import { isNullable } from './misc'
+import { isNullable } from './misc.ts'
 
 /** Return true when every item in `array2` is present in `array1`. */
 export function contain(array1: readonly any[], array2: readonly any[]) {

+ 5 - 5
vendor/cosmokit/src/index.ts

@@ -1,10 +1,10 @@
 /** Array set and normalization helpers. */
-export * from './array'
+export * from './array.ts'
 /** Runtime type, binary, clone, and equality helpers. */
-export * from './types'
+export * from './types.ts'
 /** Shared utility types and object helpers. */
-export * from './misc'
+export * from './misc.ts'
 /** String case, path, and property formatting helpers. */
-export * from './string'
+export * from './string.ts'
 /** Time constants, parsing, and formatting helpers. */
-export * from './time'
+export * from './time.ts'

+ 1 - 1
vendor/cosmokit/src/types.ts

@@ -1,4 +1,4 @@
-import { isNullable } from './misc'
+import { isNullable } from './misc.ts'
 
 type GlobalConstructorNames = keyof {
   [K in keyof typeof globalThis as typeof globalThis[K] extends abstract new (...args: any) => any ? K : never]: K

+ 1 - 1
vendor/hmr/src/index.ts

@@ -4,7 +4,7 @@ import { ModuleJob, ModuleLoader, ResolveResult } from '@cordisjs/plugin-loader'
 import type { Include } from '@cordisjs/plugin-include'
 import { ChokidarOptions, FSWatcher, watch } from 'chokidar'
 import { relative, resolve } from 'node:path'
-import { handleError } from './error'
+import { handleError } from './error.ts'
 import type {} from '@cordisjs/plugin-timer'
 import { fileURLToPath, pathToFileURL } from 'node:url'
 import { createRequire } from 'node:module'

+ 4 - 4
vendor/loader/src/config/entry.ts

@@ -1,9 +1,9 @@
 import { Context, Fiber, Inject } from 'cordis'
 import { deepEqual, isNullable } from 'cosmokit'
-import { Loader } from '../index'
-import { EntryGroup } from './group'
-import { EntryTree } from './tree'
-import { evaluate, interpolate } from './utils'
+import { Loader } from '../index.ts'
+import { EntryGroup } from './group.ts'
+import { EntryTree } from './tree.ts'
+import { evaluate, interpolate } from './utils.ts'
 
 /** Serialized plugin entry options stored in loader config files. */
 export interface EntryOptions {

+ 2 - 2
vendor/loader/src/config/group.ts

@@ -1,6 +1,6 @@
 import { Context, Service } from 'cordis'
-import { Entry, EntryOptions } from './entry'
-import { EntryTree } from './tree'
+import { Entry, EntryOptions } from './entry.ts'
+import { EntryTree } from './tree.ts'
 
 /** Runtime owner for a list of child loader entries. */
 export class EntryGroup {

+ 2 - 2
vendor/loader/src/config/isolate.ts

@@ -1,8 +1,8 @@
 import { Context } from 'cordis'
 import { Dict } from 'cosmokit'
-import { Entry } from './entry'
+import { Entry } from './entry.ts'
 
-declare module './entry' {
+declare module './entry.ts' {
   interface EntryOptions {
     intercept?: Dict | null
     isolate?: Dict<true | string> | null

+ 2 - 2
vendor/loader/src/config/tree.ts

@@ -1,7 +1,7 @@
 import { composeError, Context } from 'cordis'
 import { Dict, isNonNullable } from 'cosmokit'
-import { Entry, EntryOptions } from './entry'
-import { EntryGroup } from './group'
+import { Entry, EntryOptions } from './entry.ts'
+import { EntryGroup } from './group.ts'
 
 /** Mutable tree of loader entries. Persistence is supplied by subclasses. */
 export abstract class EntryTree {

+ 10 - 10
vendor/loader/src/index.ts

@@ -1,22 +1,22 @@
 import { Context, Inject, Service } from 'cordis'
 import { defineProperty, Dict, isNullable } from 'cosmokit'
-import { ModuleLoader } from './internal'
-import { Entry, EntryOptions } from './config/entry'
-import isolate from './config/isolate'
-import { EntryTree } from './config/tree'
+import { ModuleLoader } from './internal.ts'
+import { Entry, EntryOptions } from './config/entry.ts'
+import isolate from './config/isolate.ts'
+import { EntryTree } from './config/tree.ts'
 
 /** Re-export entry node APIs. */
-export * from './config/entry'
+export * from './config/entry.ts'
 /** Re-export nested entry group APIs. */
-export * from './config/group'
+export * from './config/group.ts'
 /** Re-export service isolation helpers. */
-export * from './config/isolate'
+export * from './config/isolate.ts'
 /** Re-export entry tree persistence APIs. */
-export * from './config/tree'
+export * from './config/tree.ts'
 /** Re-export loader config expression helpers. */
-export * from './config/utils'
+export * from './config/utils.ts'
 /** Re-export Node internal module loader compatibility types. */
-export * from './internal'
+export * from './internal.ts'
 
 declare module 'cordis' {
   interface Events {

+ 2 - 2
vendor/logger-console/src/browser.ts

@@ -1,8 +1,8 @@
 import { Message } from 'cordis'
-import { ConsoleExporter as Base } from './shared'
+import { ConsoleExporter as Base } from './shared.ts'
 
 /** Re-export shared console exporter config and base implementation. */
-export * from './shared'
+export * from './shared.ts'
 
 /** Browser console exporter that dispatches to native console methods. */
 export class ConsoleExporter extends Base {

+ 2 - 2
vendor/logger-console/src/index.ts

@@ -1,10 +1,10 @@
 import { Formatter } from 'cordis'
 import { inspect } from 'node:util'
 import supportsColor from 'supports-color'
-import { ConsoleExporter as Base } from './shared'
+import { ConsoleExporter as Base } from './shared.ts'
 
 /** Re-export shared console exporter config and base implementation. */
-export * from './shared'
+export * from './shared.ts'
 
 const inspectFormatter: Formatter = (value, target) => {
   return inspect(value, { colors: !!target.colors, depth: Infinity, compact: true, breakLength: Infinity })