@kodax-ai/kodax 0.7.44 → 0.7.46

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/CHANGELOG.md +87 -0
  2. package/README.md +6 -5
  3. package/README_CN.md +6 -5
  4. package/dist/builtin/skill-creator/scripts/utils.js +12 -27
  5. package/dist/chunks/chunk-2GEKCIIW.js +31 -0
  6. package/dist/chunks/chunk-ARUWXX25.js +2 -0
  7. package/dist/chunks/chunk-JRT447X5.js +565 -0
  8. package/dist/chunks/chunk-K4RBLNSY.js +2 -0
  9. package/dist/chunks/chunk-VBIVZOSD.js +425 -0
  10. package/dist/chunks/{chunk-4YPL2UVZ.js → chunk-XUEINS3X.js} +253 -252
  11. package/dist/chunks/{chunk-RUDYNAK7.js → chunk-Z4UT32NN.js} +1 -1
  12. package/dist/chunks/{compaction-config-NAPRF7XR.js → compaction-config-PFTBIGQT.js} +1 -1
  13. package/dist/chunks/{construction-bootstrap-PHTGBRNU.js → construction-bootstrap-2FKNOZZE.js} +1 -1
  14. package/dist/chunks/devtools-4CRULTR2.js +2 -0
  15. package/dist/chunks/devtools-YINBSZC7.js +2 -0
  16. package/dist/chunks/dist-JLMKDPEL.js +2 -0
  17. package/dist/chunks/dist-QTV5M2JW.js +2 -0
  18. package/dist/chunks/{utils-TV3UYCHQ.js → utils-LY3LB65Z.js} +1 -1
  19. package/dist/index.d.ts +11 -11
  20. package/dist/index.js +2 -2
  21. package/dist/kodax_cli.js +913 -863
  22. package/dist/provider-capabilities.json +46 -18
  23. package/dist/sdk-agent.d.ts +55 -10
  24. package/dist/sdk-agent.js +1 -1
  25. package/dist/sdk-coding.d.ts +31 -13
  26. package/dist/sdk-coding.js +1 -1
  27. package/dist/sdk-llm.d.ts +84 -7
  28. package/dist/sdk-llm.js +1 -1
  29. package/dist/sdk-mcp.js +1 -1
  30. package/dist/sdk-repl.d.ts +19 -9
  31. package/dist/sdk-repl.js +2 -2
  32. package/dist/sdk-session.d.ts +27 -6
  33. package/dist/sdk-session.js +1 -1
  34. package/dist/sdk-skills.js +1 -1
  35. package/dist/types-chunks/{base.d-FUJahC0i.d.ts → base.d-BdJKSPO2.d.ts} +18 -1
  36. package/dist/types-chunks/{bash-prefix-extractor.d-DdoSeghD.d.ts → bash-prefix-extractor.d-BICYx8pt.d.ts} +184 -141
  37. package/dist/types-chunks/{file-tracker.d-DOfaoCbJ.d.ts → file-tracker.d-BNTIvsdb.d.ts} +11 -4
  38. package/dist/types-chunks/{resolver.d-B7ZnVuuf.d.ts → resolver.d-DkgJlEzr.d.ts} +80 -4
  39. package/dist/types-chunks/storage.d-B1Jk6ryM.d.ts +202 -0
  40. package/dist/types-chunks/{types.d-HBbWT-iA.d.ts → types.d-B_MIIApc.d.ts} +1 -1
  41. package/dist/types-chunks/{types.d-B1uGoVTE.d.ts → types.d-Cf-GCzac.d.ts} +82 -1
  42. package/dist/types-chunks/{types.d-DM8zEJgF.d.ts → types.d-D2RNa5Y7.d.ts} +2 -2
  43. package/dist/types-chunks/{utils.d-C5fzCE9W.d.ts → utils.d-pzHPkrb8.d.ts} +31 -5
  44. package/package.json +1 -1
  45. package/dist/chunks/chunk-35BDEEC5.js +0 -2
  46. package/dist/chunks/chunk-DI2G3YWL.js +0 -31
  47. package/dist/chunks/chunk-HHQ7YTGM.js +0 -425
  48. package/dist/chunks/chunk-QHILHQBB.js +0 -519
  49. package/dist/chunks/devtools-EYGFOXEU.js +0 -2
  50. package/dist/chunks/dist-CCYBJJZY.js +0 -2
  51. package/dist/chunks/dist-RHIHZAYX.js +0 -2
  52. package/dist/types-chunks/storage.d-DFD9ln5c.d.ts +0 -115
package/CHANGELOG.md CHANGED
@@ -4,6 +4,93 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  > Full history for versions prior to v0.7.0: [CHANGELOG_ARCHIVE.md](docs/CHANGELOG_ARCHIVE.md)
6
6
 
7
+ ## [0.7.46] - 2026-06-07
8
+
9
+ ### Added
10
+
11
+ - **FEATURE_219 — Per-project session storage (目录治理 + 归档分家 + 自动迁移).** Sessions move from a single flat `~/.kodax/sessions/<id>.jsonl` pool to a per-project layout `~/.kodax/sessions/<projectKey>/<id>.jsonl`, where `projectKey` is the canonical git repo root (worktrees of one repo merge into the same folder, surfaced with a `[wt: …]` tag), the raw cwd for non-git directories, or `_unknown/` when no path is recorded. The session picker / SDK listing now reads a single project directory (O(sessions-in-project)) instead of scanning + filtering the whole pool — eliminating the root cause of the prior listing-performance patches and the Windows drive-letter-case resume-loss class. Folder names are a readable slug plus a short hash suffix (collision-safe), with a `project.json` manifest per folder. Three previously-overlapping "archive" notions are split cleanly: the per-session island sidecar is renamed `.archive.jsonl` → `.islands.jsonl`; **whole-session archive** is now real (`archiveSession` / `unarchiveSession` move the session + sidecar into `<projectKey>/archived/`, hidden from the default list, resurfaced with `listSessions({ includeArchived: true })`); the legacy `sessions-archive/` directory is retired. Session ids gain a uniqueness suffix (`YYYYMMDD_HHMMSS_<suffix>`) so two same-second sessions in different projects never collide. **Upgrade is transparent**: the first session operation auto-migrates the flat pool under a cross-process directory lock (with stale-owner reclaim) + a resumable journal + a `.layout.json` marker, never deleting data (orphan sidecars are relocated to `_unknown/orphan-islands/`, not removed) and reading both layouts throughout via an id-only locator so an interrupted migration is never unreadable. SDK additions: `archiveSession`, `unarchiveSession`, `SessionSummary.projectKey` / `.archived`, `listSessions({ includeArchived })`. See [ADR-038](docs/ADR.md#adr-038-per-project-session-storage--canonical-keyed-directory-layout--archive-semantics-split--flat-pool-migration-feature_219-v0746) + [docs/features/v0.7.46.md#feature_219](docs/features/v0.7.46.md#feature_219-per-project-session-storage--canonical-keyed-目录治理--归档语义分家--平铺池迁移).
12
+
13
+ ### Changed
14
+
15
+ - **FEATURE_214 — codex-style true inline viewport.** The inline (non-fullscreen) rendering path was rebuilt into a real bounded inline viewport (Live region + source-backed scrollback ledger) so finalized history is committed to native scrollback exactly once instead of being repeatedly repainted — fixing the "switch-to-transcript duplicates a large block" and "streaming re-paints" artifacts. Fullscreen stays the default off-SSH (env/config can switch to inline); SSH defaults to inline with `Ctrl+O` opening an alt-screen transcript modal. CJK large-scroll lag is a terminal glyph-rendering cost and is explicitly out of scope. See [docs/features/v0.7.46.md#feature_214](docs/features/v0.7.46.md).
16
+
17
+ ### Performance
18
+
19
+ - **`estimateTokens` per-message WeakMap cache (`@kodax-ai/agent`).** Token estimation now memoizes per-message counts keyed by message object identity, so repeated context-size checks within a turn no longer re-tokenize unchanged history. Pure cache — identical inputs yield identical counts, no behavior change.
20
+
21
+ ### Fixed
22
+
23
+ - **Repo-intelligence atomic writes retry transient Windows `EPERM` on rename.** `writeJsonFileAtomic` (used for the analysis cache / index / manifest / overview) could intermittently fail when the target was momentarily locked by antivirus, the search indexer, or a concurrent reader — on Windows this surfaces as `EPERM`/`EBUSY`/`EACCES` from `fs.rename`. It now retries a few times with a short backoff before giving up (a genuinely permanent failure still throws), so a transient lock no longer fails the repo-intelligence pass that triggered the write.
24
+ - **MCP streamable HTTP session id is now persisted across requests (`@kodax-ai/agent`).** The streamable-HTTP MCP transport now captures the server-issued `Mcp-Session-Id` response header and replays it on subsequent POST/GET requests (and sends `DELETE` on close to end the session), defers the SSE notification stream until after the first successful POST, and clears the session id on a `404` (expired session) so it reconnects cleanly. Without this, MCP servers that mandate a session id rejected every request after the initialize call.
25
+ - **SDK session listing — fast/slow path parity + 5 footgun fixes for in-process embedders.** KodaX Space reported `listSessions` returning sessions tagged with the host process's directory + capped at 10 entries; an audit of the surrounding surface uncovered 3 sibling issues with the same root cause (the SDK was assuming `process.cwd()` = project). All five are now fixed in `interactive/storage.ts` + `session/public-api.ts` + `common/utils.ts`:
26
+ - **F1 — `storage.list()` fast path now falls back to top-level `gitRoot` when meta has no nested `runtimeInfo`** (legacy session shape). Mirrors the existing slow-path fallback at `public-api.ts:188-195`. Pre-fix, legacy sessions came back with `runtimeInfo: undefined` even though `gitRoot` was right there at the top level → SDK consumers couldn't group by project. The fallback wraps as `{ canonicalRepoRoot: gitRoot }` (the modern equivalent field; `extractRuntimeInfoSummary` remaps it back to `{ gitRoot }` on the consumer side for `SessionSummary`).
27
+ - **F2 — `storage.list()` accepts a `limit` parameter (default 10 preserves REPL picker; SDK passes its own)**. Pre-fix `.slice(0, 10)` was hardcoded inside `list()` so any caller asking for more (`limit: 200`) silently got 10. The public-api fast path now forwards the caller's `limit` directly.
28
+ - **F3 — `storage.list()` return type now includes `createdAt`** (previously absent from the return shape even though it was computed internally), and `toSessionSummary` carries it through. Pre-fix, every session in the fast-path return had `SessionSummary.createdAt = undefined` → SDK consumers sorting sessions by date got random order on the common-case call.
29
+ - **F4 — `FileSessionStorage` accepts an optional `cwd` constructor arg.** In-process embedders pass the project root they opened; `load()` then threads it into `getGitRoot(cwd)` + `inspectWorkspaceRuntime({cwd})` so the workspace-mismatch check compares against the embedder's project, NOT the embedder process's startup directory. Bonus: when `cwd` is set, the CLI-mode "[Warning] Session project mismatch" stderr write is suppressed (the embedder is authoritative; the warning is noise that bleeds into the host process's UI output channel).
30
+ - **F5 — `deleteAll()` no longer silently caps at 10.** Pre-fix it reused `list()`'s default cap, so "delete all sessions for this project" left up to N−10 sessions behind. Now passes `limit: Number.MAX_SAFE_INTEGER` so it enumerates the full set.
31
+ - **Defense-in-depth**: `common/utils.ts` `getGitRoot()` now accepts an optional `cwd` parameter (same shape as the agent-runtime `getGitRoot` fixed earlier in v0.7.45). Existing zero-arg callers (5 sites: storage, REPL, InkREPL) unchanged.
32
+ - 10 new regression tests (`interactive/storage-sdk-consumer.test.ts`) cover all 5 fixes + 2 existing tests in `storage.test.ts` updated to assert the new `createdAt` field. Pre-existing 237 interactive/session tests still pass.
33
+ - **SDK session listing — F6 follow-up: fast path no longer auto-filters by `process.cwd()` when no project intent is supplied.** KodaX Space reported that even after the F1–F5 fixes above + FEATURE_219 (per-project session storage), the sidebar still showed only Space-repo sessions when the user had opened a different project. Root cause: `FileSessionStorage.list(undefined)` resolved `currentGitRoot` via `getGitRoot(this.hostCwd)`, and when an embedder constructs `new FileSessionStorage()` WITHOUT `cwd`, that fell through to `git rev-parse` in the host process's `process.cwd()` — the embedder's own startup directory, NOT the project the user opened. The resulting `currentGitRoot` then drove the per-project loop at `storage.ts:1237` (`projectDirNames = currentGitRoot ? [currentProjectKey] : <all>`) to scan only the embedder's project subdir → user saw nothing. F4 above had inadvertently preserved the broken behavior for the common SDK-consumer shape (storage constructed with no `cwd`). The Space team shipped a workaround in their own code that forces the slow path by injecting `before: '2999-01-01T00:00:00.000Z'` — fragile and surprising.
34
+ - **Fix**: when both `gitRoot` arg AND `this.hostCwd` are unset, no auto-resolve from `process.cwd()` — `currentGitRoot` stays null, and the per-project loop scans all project dirs (matching the slow path's semantic). Applied to both `list()` (Space's reported case) and `deleteAll()` (no production callers; consistency). The CLI is unaffected: REPL paths already pass `gitRoot` explicitly via `storage.list(context.gitRoot ?? undefined)`.
35
+ - **Side update**: `packages/coding/src/agent-runtime/middleware/auto-resume.ts` now passes `options.context?.gitRoot` to `storage.list()` so CLI `kodax --resume` preserves its "pick the most recent session in this project" semantic (pre-fix it relied on the implicit `process.cwd()` resolution we just removed).
36
+ - 2 new regression tests in `storage-sdk-consumer.test.ts` — cross-project listing without project intent + filter-by-cwd when set.
37
+ - SDK consumers can now revert workarounds that force the slow path; the natural call shape works.
38
+ - **SDK session storage — F7 + F8 follow-up audit fixes (load()-side noise + ambiguity-resolution bug).** A focused audit after the F6 fix surfaced two more bugs that bit SDK consumers without an explicit `cwd`:
39
+ - **F7 — `load()` mismatch warning now opt-in instead of "fires whenever no hostCwd".** The pre-fix gate was `!this.hostCwd && (...)`, meant to signal "embedder mode = silent; CLI mode = warn", but `!this.hostCwd` also matched SDK consumers that don't pass `cwd` — so KodaX Space (and any third-party embedder following the same shape) got a yellow `[Warning] Session project mismatch:` block written to `stderr` on every cross-project session load, bleeding into the host process's UI output channel. The text even named the *embedder's startup directory* as "Current workspace" — wrong and noisy. v0.7.46 inverts the gate: a new `FileSessionStorage({ emitMismatchWarnings: true })` constructor option is the explicit opt-in (default `false`). CLI surfaces that want the legacy warning pass `true`; SDK consumers get silence by default. When the flag is off, the warning path also skips the `getGitRoot` + `inspectWorkspaceRuntime` resolution entirely — cheap on the common SDK path.
40
+ - **F8 — `resolveSessionLocation()` no longer fails silently when an ambiguous id has no current-project match.** When two sessions in different projects share an id (legacy same-second cross-project duplicates; FEATURE_219's id-uniqueness suffix prevents this going forward), the resolver picked a "preferred" match by `path.dirname(m) === this.projectDir(deriveProjectKeyFromRoot(this.hostCwd ?? process.cwd()).key)`. For SDK consumers without `cwd`, this resolved to the embedder's startup directory's projectKey — which matched neither candidate → `preferred = undefined` → `null` returned → `load()` returned null and the caller saw "session not found". Post-fix: cwd-based disambiguation runs only when `this.hostCwd` is set; otherwise the first match is returned best-effort. The diagnostic notice still fires so the caller can debug.
41
+ - 5 new tests in `storage-sdk-consumer.test.ts` (3 F7 + 2 F8). The pre-existing F4 stderr-suppression test also had a latent issue — it was patching `process.stderr.write` but `writeStorageNotice` short-circuits on `NODE_ENV === 'test'`, so the test passed vacuously. The new F7 tests temporarily flip `NODE_ENV` to `'development'` so the real emission path runs. Full interactive + session + auto-resume sweep: 266/266 pass.
42
+
43
+ ## [0.7.45] - 2026-06-01
44
+
45
+ ### ⚠️ BREAKING — coding-plan providers now use dedicated API-key env vars
46
+
47
+ The coding-plan providers used to read the **same** env var as their regular-API
48
+ sibling (e.g. both `zhipu` and `zhipu-coding` read `ZHIPU_API_KEY`), which meant
49
+ you couldn't enable the two providers independently — and the regular key could
50
+ get handed to a coding-plan provider you hadn't actually subscribed to. Each
51
+ coding-plan provider now reads its **own** key, so setting both keys lets you use
52
+ both providers' models at once.
53
+
54
+ | Provider | Old env var | **New env var** |
55
+ |---|---|---|
56
+ | `zhipu-coding` | `ZHIPU_API_KEY` | `ZHIPU_CODING_API_KEY` |
57
+ | `kimi-code` | `KIMI_API_KEY` | `KIMI_CODE_API_KEY` |
58
+ | `minimax-coding` | `MINIMAX_API_KEY` | `MINIMAX_CODING_API_KEY` |
59
+ | `mimo-coding` | `MIMO_API_KEY` | `MIMO_CODING_API_KEY` |
60
+ | `ark-coding` | `ARK_API_KEY` | `ARK_CODING_API_KEY` |
61
+
62
+ **Migration**: set the new env var(s) for whichever coding plans you use. Regular
63
+ providers (`zhipu`, `kimi`, …) are unchanged. There is no fallback to the old
64
+ shared names — the new name is required for the coding-plan provider to start.
65
+
66
+ ### Added
67
+
68
+ - **FEATURE_207 — `@` picker: recent files**: typing `@` now surfaces your current git working set (modified + untracked files) at the top of the completion list, before the plain directory listing — these are the files you're actively changing, which is overwhelmingly what you'll reference next. Cross-directory (nested files appear without navigating into them), filtered by basename prefix as you type, and suppressed once you navigate into a specific path (`@src/` keeps the normal listing). Non-git workspaces degrade gracefully to the directory listing. Directories were already completable; skill (`/skill:`) / MCP / plugin entries are untouched. (The recency source is the git working set rather than the originally-planned session-lineage tool events — chosen for cold-start usefulness + layer independence; see docs/features/v0.7.45.md#feature_207.)
69
+ - **FEATURE_102 — Adaptive Multi-Provider Orchestration (P1-auto + P2 + P3)**: the main agent can now run a child task on a *different* provider/model than its own. Three layers ship; P4's adaptive-scoring *mechanism* (bandit/bayesian auto-selection) is intentionally **not** built — its only actionable output, the gating eval below, shipped, and the mechanism itself is speculative with no current consumer (YAGNI per `CLAUDE.md`), so it is **deferred to 0.9.x as an independent feature** if a real auto-selection need arises. Static tier routing (P1-auto) already covers the concrete need, and is **off by default** — it activates only when you point a tier env var at a model.
70
+ - **P2 — explicit per-dispatch override.** `dispatch_child_task` gains optional `provider` / `model` params, so the agent can deliberately send a child to another model family — e.g. a second independent review of the same change by a different family, to catch blind spots a single family would share. Resolution priority in `child-executor`: `bundle.provider/model` > specialist's declared model (FEATURE_191) > parent default. Omitting both is byte-identical to the prior behavior. Tolerant parse (empty/whitespace → undefined) so a misuse never fails the dispatch.
71
+ - **P3 — cross-provider fallback + `doctor --ping`.** When a child's primary provider is *exhausted or down* (the LLM layer's same-provider `withRateLimit` retries gave up, a 5xx, or a network error), KodaX re-runs the child once on the next provider in an operator-configured chain instead of failing the whole child. Configured via `/fallback ark-coding,kimi-code` (persists to `~/.kodax/config.json` + mirrors to `KODAX_FALLBACK_PROVIDERS`; `/fallback off` to clear, `/fallback status` to inspect). Empty chain = OFF. Scope is deliberately minimal (per user direction): only hard availability errors trigger fallback — a returned `success:false` is a task outcome (not retried elsewhere) and aborts are never faked over; the speculative tool-call-fidelity / context-overflow / quality-anomaly triggers are unbuilt (YAGNI). `kodax doctor --ping` completes the health check: it sends one minimal request per configured provider (10s timeout, concurrent) to prove the key actually works and the subscription is active — opt-in, small token cost, never on the default `doctor`. 16 tests (11 fallback core + 5 `/fallback` command).
72
+ - **P1-auto — `model_hint` → tier routing.** The previously dormant `model_hint` field (`fast`/`balanced`/`deep`, FEATURE_120) now selects an operator-configured tier: `fast` → `KODAX_FAST_PROVIDER`/`KODAX_FAST_MODEL` (**read-only children only** — the gating eval validated cheap-model quality on read-only investigation but not write/codegen, so write children stay on the parent tier), `deep` → `KODAX_DEEP_PROVIDER`/`KODAX_DEEP_MODEL` (read or write). `balanced`/unset, or any unconfigured tier, falls back to the parent — **routing is OFF by default** and turns on only when you point a tier env var at a model (no separate toggle). Specialist and explicit-P2 overrides both win over the hint. Per KodaX minimalism the original 5-name capability-alias layer (`vision`/`long-context`/…) was descoped to this env-tier form — no consumer exists for the rest yet (YAGNI). Pure routing wiring; the Worker prompt is unchanged (an eval non-trigger per `CLAUDE.md`), so $0 / no panel. Gating eval (`tests/feature-102-model-tier-quality.eval.ts`, canonical 5-alias × 3 read-only investigation × 5 run) kept as permanent regression: cheap floor `ark/v4flash` = 15/15, ≥ strong-mean 92% → cheap PRESERVES read-only quality. New unit + integration tests (7 `model-hint-routing` + 5 child-executor P2/P1-auto priority); also de-flaked the pre-existing `merges findings` child-executor test (parallel children made `mergeChildResults` emit in completion order — now deterministic dispatch order). Design: [docs/features/v0.7.45.md](docs/features/v0.7.45.md#phase-1--model_hint--真实跨-provider-子派发mvp).
73
+ - **FEATURE_216 — `verifyProviderCredential(name)` SDK API + per-provider strategy.** New top-level helper for SDK consumers (KodaX Space, third-party embedders) to validate a provider's API key against the actual upstream — never-throws, mostly zero-token. Closes the "test connection" UI gap: existing `isConfigured()` only checks env presence; `stream()`/`sideQuery()` are too heavy. Three primitives, one per provider, baked into `provider-capabilities.json` `verifyStrategy` field: **`count-tokens`** (Anthropic `messages.countTokens()`, 0 token) for `anthropic` + 4 anthropic-coding providers; **`models-list`** (`models.list()`, 0 token) for `openai`/`deepseek`/`kimi`/`qwen`; **`minimal-message`** (~6-7 token `chat.completions.create({max_tokens:1})`) for `zhipu`/`mimo`/`mimo-coding` (where `models.list()` is publicly-served or `count_tokens` returns 404). CLI-bridge providers (`gemini-cli`, `codex-cli`) return `unsupported` — their credentials live in the CLI binary's token store, outside SDK reach. Top-level `verifyProviderCredential(name, opts?)` short-circuits unknown name / unsupported strategy / missing env var BEFORE instantiating the provider class — avoids the constructor throw on missing key, and ALSO wraps the actual `verifyCredential()` call in try/catch so the never-throws contract survives runtime-registered providers whose 3rd-party overrides might throw instead of returning an envelope. Error categorization (`unauthorized` / `network` / `timeout` / `unsupported` / `unconfigured` / `server_error` / `rate_limited` / `unknown`) is stable for UI consumers to map to user-facing states — `rate_limited` (429) is distinct from `unauthorized` so a transiently throttled key isn't misread as invalid. The error `message` field redacts `sk-...` key fragments before truncation so upstream error bodies that echo the submitted key don't leak the fragment into UI logs. The `kimi-code`-specific 400→`unauthorized` mapping is gated by `providerName` to avoid false-positives on other count-tokens providers that might 400 on a legitimate bad request (bad model id / schema mismatch). Network error detection extended to `ETIMEDOUT` + `ENETUNREACH` + `EHOSTUNREACH` + `ENETDOWN` beyond the original `ENOTFOUND`/`ECONNREFUSED`/`ECONNRESET`/`EAI_AGAIN`/`EPIPE` set. Companion `listProviderModels(name)` returns the static curated list (always `source: 'static'` in v0.7.45 — upstream `/v1/models` is noisy + inconsistent across providers per the 2026-05-28 12-provider probe matrix). Custom providers auto-inherit by `protocol`; explicit `verifyStrategy` overrides allowed. 88 tests total (28 orchestrator + 14 resolver + 38 schema + 10 real-key integration gated on `KODAX_INTEGRATION_TEST=1`). Reference industry validator: opencode's `setup-recording-env.ts` makes the same per-provider decision across 20+ providers — per-provider strategy IS industry standard, no universal 0-token primitive exists. Design + probe data: [docs/features/v0.7.45.md#feature_216](docs/features/v0.7.45.md#feature_216-provider-credential-verification-api). SDK usage guide: [docs/SDK_EMBEDDER_GUIDE.md §10](docs/SDK_EMBEDDER_GUIDE.md#10-provider-credential-verification--verifyprovidercredential-feature_216-v0745).
74
+
75
+ ### Performance
76
+
77
+ - **FEATURE_212 — fullscreen render no longer lags as history grows.** In the fullscreen (alt-screen) UI, every render previously rewrote the whole viewport (~6 KB of ANSI) on each frame; on Windows/ConPTY each synchronous write blocks the event loop, so typing, streaming output, scrolling, and the spinner all got progressively choppier the longer a session ran. The renderer now takes a **cell-diff fast path** in fullscreen — it writes only the cells that actually changed (default ON; escape hatch `KODAX_FULLSCREEN_CELLDIFF=0`). Typing stays smooth regardless of transcript length.
78
+ - **FEATURE_212 — DECSTBM hardware-scroll fast path.** When the transcript scrolls, the renderer now emits a single terminal scroll-region command and repaints only the rows that scrolled *in*, instead of repainting every shifted row. Reduces per-scroll-frame write volume by an order of magnitude on full screens (default ON; escape hatch `KODAX_SCROLL_DECSTBM=0`). Proven correct by a calibrated cursor+grid terminal-model differential gate.
79
+
80
+ ### Fixed
81
+
82
+ - **FEATURE_212 — fullscreen viewport drifted up one row.** The cell-diff fast path above shifted the entire managed viewport up by one row (the banner's top line was clipped and a blank line appeared under the status bar). Root cause was two scroll-inducing trailing newlines that fire when the frame fills the viewport: the per-row line-feed `renderFrameSlice` emits after the *last* row, and the `restoreCursor` line-feed that moves the cursor one row past the last content row. Both are now suppressed for a viewport-filling frame (the resting cursor is clamped to the last visible row), so nothing scrolls. The cell-diff perf win is fully preserved. Reproduced and verified by an offline terminal-model gate that faithfully scrolls on a bottom-row line-feed.
83
+ - **FEATURE_213 — a follow-up typed while waiting for a sub-agent was answered but never shown.** When you queued a message while the agent was idle-yielding (waiting for a `dispatch_child_task` child), the agent received and answered it, but it never appeared in the transcript. There are two mid-turn drain paths and only one notified the UI: the `beforeNextTurn` mid-turn drain routes through `onMidTurnUserMessages` (recorded), but the idle-yield **wake** drain (`composeIdleYieldUserMessage`) spliced the prompt straight into the agent transcript with no UI signal. Fixed by giving the wake path the same UI sink: `composeIdleYieldUserMessage` reports the drained prompt(s) via a new `onUserPrompts` callback, surfaced through `runWithIdleYield`'s `onResumedUserPrompts` option (both new, optional, non-breaking SDK additions on `@kodax-ai/agent`), which coding wires to `onMidTurnUserMessages`. A message is dequeued exactly once, so no duplication. Also hardened the recorded path: `clearManagedForegroundTurnHistory` now rescues any not-yet-committed mid-turn user message before wiping the foreground ledger (id-deduped against the round-end / fresh-submit / interrupt commits), so a premature clear can't drop it either.
84
+ - **FEATURE_173 — `kodax -r` only restored the first round of a resumed session.** After FEATURE_173 consolidated session ids, the runner's snapshot writer (`saveSessionSnapshot`, a flat full-rewrite) and the REPL's incremental writer raced on the same `<id>.jsonl`: a stale runner save whose messages were a *prefix* of what the REPL had already written rebuilt the lineage and reset `activeEntryId` back to round 1 — so resume walked the active path only as far as the first round (entries were never lost, just the active pointer regressed). Fixed in two layers: (1) a new `KodaXSessionOptions.persistedByHost?: boolean` SDK option marks host-owned sessions so the runner skips routine snapshot writes and stops racing the REPL — error/crash-recovery saves (which carry `errorMetadata`) still bypass the gate and persist, so recovery is unchanged; headless print/SDK/ACP callers leave the flag unset and remain the sole writer. (2) `resolveSnapshotLineage` now reuses the existing lineage verbatim when a lineage-less snapshot's messages are a prefix of (or empty vs) the persisted active path, so `activeEntryId` can never regress structurally. New tests cover 5 writer interleavings including the exact repro and the empty-message error save.
85
+ - **archived sessions no longer appear in the session picker.** `session/public-api.ts` documents an `archived-` filename-prefix archive mechanism, and the public-api *slow* path already filtered it out, but `FileSessionStorage.list()` — used by the interactive picker and the public-api fast path — did not, so archived sessions still showed up there. Added the `!startsWith('archived-')` filter so the archive mechanism is consistent end-to-end.
86
+ - **repo-intelligence atomic writes no longer leak `.tmp` files.** `writeJsonFileAtomic` wrote to a uniquely-named `<file>.<pid>.<ts>.tmp` and renamed it into place, but had no cleanup path: any failed `rename` (common on Windows as `EPERM` when the target is briefly locked by a concurrent reader) or a hard kill between write and rename left the temp behind, and the unique naming meant each failure accumulated a fresh orphan — over a thousand could pile up in `.agent/repo-intelligence/` (hundreds of MB) over weeks. The write now removes its own temp on failure (try/catch) and best-effort sweeps stale sibling orphans (older than 1h, same base file — never a concurrent writer's in-flight temp) on each successful write, so any existing backlog self-heals on normal use.
87
+ - **empty provider completions now retry instead of silently ending the task.** A `finish_reason`-complete turn with no text, no tool calls, and no thinking is a degraded response — common on budget OpenAI-compatible providers (e.g. zhipu-coding) under load or right after a 429. The runner's no-tool terminal branch misread it as a clean text-only completion and exited the task silently (the user saw the task stop right after a `[Rate Limit] Retrying…` line, with no error). The managed-task LLM adapter now re-streams such a turn up to `KODAX_MAX_EMPTY_COMPLETION_RETRIES` times (short linear backoff) on an independent counter before falling through to the existing terminal behavior, so it does not consume the resilience error budget. Canonical text-only termination (text present, no tool) is untouched.
88
+ - **`saveSessionSnapshot` now honors `options.context.gitRoot` instead of silently dropping it.** In-process embedders (KodaX Space ADR-003) that serve multiple projects from a single runtime were having every session tagged with the host process's startup directory, because the snapshot middleware only read `data.gitRoot` (never passed by 3 of 5 call sites) and fell through to a `process.cwd()`-bound `git rev-parse`. Users saw "session disappears on restart" — sessions were being persisted under the wrong project. The middleware now follows a 4-tier resolution: (1) explicit `data.gitRoot` (existing API), (2) **`options.context.gitRoot`** (NEW — closes the bug independently of the call-site fix), (3) `getGitRoot()` running in **`options.context.executionCwd`** (NEW — defense even when neither field is set), (4) `''` (legacy default). Also patched the 3 SA-mode call sites (`run-substrate.ts:1455`, `catch-terminals.ts:106`, `iteration-limit-terminal.ts:63`) to thread `gitRoot: input.options.context?.gitRoot ?? undefined` explicitly — matches the existing convention at runner-driven.ts:407+1786 and makes the project-tag intent visible at every call site. The KodaX CLI itself was unaffected (its `process.cwd()` is the project), but any SDK consumer with a multi-project popout UI hit this. 4 new tests cover the precedence layers; existing 9 tests unchanged.
89
+
90
+ ### Known issues
91
+
92
+ - **Issue 136 (open) — spinner stutter during streaming/scroll.** The spinner animation can still stutter while output is streaming or while scrolling a long transcript. This is **not** terminal-write volume (the two fixes above ruled that out) — the bottleneck is CPU-side per-frame work (React reconciliation + full screen-grid rebuild). Cosmetic only; tracked for a dedicated fix. See `docs/KNOWN_ISSUES.md` #136.
93
+
7
94
  ## [0.7.44] - 2026-05-28
8
95
 
9
96
  ### Theme
package/README.md CHANGED
@@ -909,13 +909,14 @@ import { InkREPL } from '@kodax-ai/kodax/repl';
909
909
  | anthropic | `ANTHROPIC_API_KEY` | Native | claude-sonnet-4-6 |
910
910
  | openai | `OPENAI_API_KEY` | Native | gpt-5.3-codex |
911
911
  | kimi | `KIMI_API_KEY` | Native | kimi-k2.6 |
912
- | kimi-code | `KIMI_API_KEY` | Native | kimi-for-coding |
912
+ | kimi-code | `KIMI_CODE_API_KEY` | Native | kimi-for-coding |
913
913
  | qwen | `QWEN_API_KEY` | Native | qwen3.5-plus |
914
914
  | zhipu | `ZHIPU_API_KEY` | Native | glm-5 |
915
- | zhipu-coding | `ZHIPU_API_KEY` | Native | glm-5 |
916
- | minimax-coding | `MINIMAX_API_KEY` | Native | MiniMax-M2.7 |
917
- | mimo-coding | `MIMO_API_KEY` | Native | mimo-v2.5-pro (Xiaomi Token Plan, Anthropic-compat) |
918
- | ark-coding | `ARK_API_KEY` | Native | glm-5.1 (Volcengine Ark Coding Plan, multi-model gateway, Anthropic-compat) |
915
+ | zhipu-coding | `ZHIPU_CODING_API_KEY` | Native | glm-5 |
916
+ | minimax-coding | `MINIMAX_CODING_API_KEY` | Native | MiniMax-M2.7 (M2 family default; `MiniMax-M3` Frontier Coding with native multimodal + 1M ctx available via `/model`, plus M2.7-highspeed / M2.5 / M2.5-highspeed / M2.1 / M2.1-highspeed / M2 on the same gateway) |
917
+ | mimo | `MIMO_API_KEY` | Native | mimo-v2.5-pro (Xiaomi MiMo pay-per-token, Anthropic-compat) |
918
+ | mimo-coding | `MIMO_CODING_API_KEY` | Native | mimo-v2.5-pro (Xiaomi Token Plan, Anthropic-compat) |
919
+ | ark-coding | `ARK_CODING_API_KEY` | Native | glm-5.1 (Volcengine Ark Coding Plan, multi-model gateway, Anthropic-compat) |
919
920
  | deepseek | `DEEPSEEK_API_KEY` | Native | deepseek-v4-flash |
920
921
  | gemini-cli | `GEMINI_API_KEY` | Prompt-only / CLI bridge | (via gemini CLI) |
921
922
  | codex-cli | `OPENAI_API_KEY` | Prompt-only / CLI bridge | (via codex CLI) |
package/README_CN.md CHANGED
@@ -327,13 +327,14 @@ dist/binary/linux-x64/
327
327
  | anthropic | `ANTHROPIC_API_KEY` | Native | claude-sonnet-4-6 |
328
328
  | openai | `OPENAI_API_KEY` | Native | gpt-5.3-codex |
329
329
  | kimi | `KIMI_API_KEY` | Native | kimi-k2.6 |
330
- | kimi-code | `KIMI_API_KEY` | Native | kimi-for-coding |
330
+ | kimi-code | `KIMI_CODE_API_KEY` | Native | kimi-for-coding |
331
331
  | qwen | `QWEN_API_KEY` | Native | qwen3.5-plus |
332
332
  | zhipu | `ZHIPU_API_KEY` | Native | glm-5 |
333
- | zhipu-coding | `ZHIPU_API_KEY` | Native | glm-5(GLM Coding Plan 端点) |
334
- | minimax-coding | `MINIMAX_API_KEY` | Native | MiniMax-M2.7 |
335
- | mimo-coding | `MIMO_API_KEY` | Native | mimo-v2.5-pro(小米 MiMo Token Plan,Anthropic 协议) |
336
- | ark-coding | `ARK_API_KEY` | Native | glm-5.1(火山方舟 Coding Plan,多模型网关,Anthropic 协议) |
333
+ | zhipu-coding | `ZHIPU_CODING_API_KEY` | Native | glm-5(GLM Coding Plan 端点) |
334
+ | minimax-coding | `MINIMAX_CODING_API_KEY` | Native | MiniMax-M2.7(M2 系列默认;`MiniMax-M3` 原生多模态 + 1M 上下文 Frontier Coding 模型可 `/model` 切换,同网关还有 M2.7-highspeed / M2.5 / M2.5-highspeed / M2.1 / M2.1-highspeed / M2)|
335
+ | mimo | `MIMO_API_KEY` | Native | mimo-v2.5-pro(小米 MiMo 按量计费,Anthropic 协议) |
336
+ | mimo-coding | `MIMO_CODING_API_KEY` | Native | mimo-v2.5-pro(小米 MiMo Token PlanAnthropic 协议) |
337
+ | ark-coding | `ARK_CODING_API_KEY` | Native | glm-5.1(火山方舟 Coding Plan,多模型网关,Anthropic 协议) |
337
338
  | deepseek | `DEEPSEEK_API_KEY` | Native | deepseek-v4-flash |
338
339
  | gemini-cli | `GEMINI_API_KEY` | Prompt-only / CLI bridge | (通过 gemini CLI) |
339
340
  | codex-cli | `OPENAI_API_KEY` | Prompt-only / CLI bridge | (通过 codex CLI) |
@@ -19,21 +19,14 @@ import YAML from 'yaml';
19
19
  // Resolution: relative path '../../../index.js' (3 levels up from scripts/ to dist/)
20
20
  // → Strategy 1 covers 99%+ of bundle-installed users.
21
21
  //
22
- // 2. Dev monorepo (npm run dev / direct invocation against built sub-packages):
23
- // this file → <repo>/packages/skills/dist/builtin/skill-creator/scripts/utils.js
24
- // SDK → not at relative path; resolved via npm workspace symlink
25
- // Resolution: bare-name `@kodax-ai/coding` (workspace alias)
26
- //
27
- // 3. Rare edge case bundle install with non-standard layout (manual copy,
28
- // symlinked dist dir, etc.) where Strategy 1 path-existence check fails:
29
- // fall back to bare-name `@kodax-ai/kodax`.
30
- //
31
- // Legacy fallback (`@kodax-ai/cli` v0.7.37/v0.7.38 RC, `@kodax-ai/kodax-cli`
32
- // v0.7.38 dual-publish) is intentionally NOT in this chain. Both legacy names
33
- // are deprecated/unpublished on npm and the user base was effectively zero
34
- // (placeholder publish + 2-hour dual-publish window). Anyone who installed
35
- // the legacy names still hits Strategy 1 (relative path resolution doesn't
36
- // care about package name) — bare-name fallback was over-defensive.
22
+ // 2. Dev monorepo / unusual bundle layouts:
23
+ // this file → <repo>/packages/skills/dist/builtin/skill-creator/scripts/utils.js
24
+ // SDK → resolved via npm workspace symlink or installed package
25
+ // Resolution: bare-name `@kodax-ai/kodax` (canonical SDK package)
26
+ //
27
+ // Workspace-internal package names and legacy package aliases are intentionally
28
+ // NOT in this chain. Published helper scripts must not rely on workspace-only
29
+ // package names.
37
30
  //
38
31
  // See docs/ADR.md ADR-022 / ADR-024 + docs/HLD.md §12 for the SDK distribution
39
32
  // contract.
@@ -55,18 +48,10 @@ export async function loadKodaXSDK() {
55
48
  }
56
49
  errors.push(`relative SDK path not found: ${relSdkPath}`);
57
50
 
58
- // Strategy 2: dev monorepo workspace symlink resolves bare name
59
- try {
60
- _cachedSdk = await import('@kodax-ai/coding');
61
- return _cachedSdk;
62
- } catch (err) {
63
- errors.push(`bare-name @kodax-ai/coding failed: ${err?.code ?? err?.message}`);
64
- }
65
-
66
- // Strategy 3: bare-name canonical (rare edge case fallback)
67
- try {
68
- _cachedSdk = await import('@kodax-ai/kodax');
69
- return _cachedSdk;
51
+ // Strategy 2: bare-name canonical (dev monorepo / rare edge-case fallback)
52
+ try {
53
+ _cachedSdk = await import('@kodax-ai/kodax');
54
+ return _cachedSdk;
70
55
  } catch (err) {
71
56
  errors.push(`bare-name @kodax-ai/kodax failed: ${err?.code ?? err?.message}`);
72
57
  }
@@ -0,0 +1,31 @@
1
+ // @kodax-ai/kodax — bundled distribution. See docs/ADR.md ADR-022 + ADR-024.
2
+ import{a as i}from"./chunk-V4WSBIXB.js";var G=class extends Error{static{i(this,"KodaXError")}code;constructor(e,n="KODAX_ERROR"){super(e),this.code=n,this.name="KodaXError"}},R=class extends G{static{i(this,"KodaXProviderError")}provider;constructor(e,n){super(e,"PROVIDER_ERROR"),this.provider=n,this.name="KodaXProviderError"}},ge=class extends G{static{i(this,"KodaXRateLimitError")}retryAfter;constructor(e,n){super(e,"RATE_LIMIT_ERROR"),this.retryAfter=n,this.name="KodaXRateLimitError"}},nt=class extends G{static{i(this,"KodaXNetworkError")}isTimeout;constructor(e,n=!1){super(e,"NETWORK_ERROR"),this.isTimeout=n,this.name="KodaXNetworkError"}},ot=class extends G{static{i(this,"KodaXToolCallIdError")}constructor(e){super(e,"TOOL_CALL_ID_ERROR"),this.name="KodaXToolCallIdError"}};var it=32768,Nn=32e3,Kn=64e3,Fn=.5;var $n=["off","auto","quick","balanced","deep"],Gt={low:6e3,medium:1e4,high:2e4},Ht=4096;function rt(t){return t.reasoningCapability?t.reasoningCapability:t.supportsThinking?"native-toggle":"prompt-only"}i(rt,"getReasoningCapability");function Q(t){return Te(t).enabled}i(Q,"isReasoningEnabled");function Te(t){if(typeof t=="boolean")return{enabled:t,mode:t?"auto":"off",depth:t?"medium":"off",taskType:"unknown",executionMode:"implementation"};let e=t?.mode??"off",n=t?.depth??Jt(e),o=t?.enabled??(e!=="off"&&n!=="off");return{enabled:o&&e!=="off"&&n!=="off",mode:e,depth:o?n:"off",taskType:t?.taskType??"unknown",executionMode:t?.executionMode??"implementation"}}i(Te,"normalizeReasoningRequest");function Jt(t){switch(t){case"quick":return"low";case"balanced":case"auto":return"medium";case"deep":return"high";default:return"off"}}i(Jt,"getDefaultThinkingDepthForMode");function Z(t,e,n="unknown"){if(e==="off")return 0;let o={...Gt,...t.defaultThinkingBudgets??{}},s=t.taskBudgetOverrides?.[n]?.[e]??o[e];return t.thinkingBudgetCap?Math.min(s,t.thinkingBudgetCap):s}i(Z,"resolveThinkingBudget");function ee(t,e,n=Ht){let o=Math.max(1024,e-n);return Math.max(1024,Math.min(t,o))}i(ee,"clampThinkingBudget");function st(t){switch(t){case"low":return"low";case"medium":return"medium";case"high":return"high";default:return}}i(st,"mapDepthToOpenAIReasoningEffort");import Me from"fs";import Un from"os";import lt from"path";var at=null;function jn(t){return t==="budget"||t==="effort"||t==="toggle"||t==="none"}i(jn,"isReasoningOverride");function Xn(t){if(!t||typeof t!="object"||Array.isArray(t))return!1;let e=t.providerReasoningOverrides;return e===void 0?!0:!e||typeof e!="object"||Array.isArray(e)?!1:Object.values(e).every(jn)}i(Xn,"isStoredConfig");function Wn(){return process.env.KODAX_HOME??lt.join(Un.homedir(),".kodax")}i(Wn,"getKodaxDir");function Yt(){return process.env.KODAX_CONFIG_FILE??lt.join(Wn(),"config.json")}i(Yt,"getConfigFilePath");function ct(t,e){return at={filePath:t,config:e},e}i(ct,"updateStoredConfigCache");function zn(t){let e=JSON.parse(Me.readFileSync(t,"utf-8"));return Xn(e)?e:{}}i(zn,"readStoredConfigFromDisk");function ut(t){switch(t){case"native-budget":return"budget";case"native-effort":return"effort";case"native-toggle":return"toggle";case"none":return"none";default:return}}i(ut,"reasoningCapabilityToOverride");function dt(t){switch(t){case"budget":return"native-budget";case"effort":return"native-effort";case"toggle":return"native-toggle";default:return"none"}}i(dt,"reasoningOverrideToCapability");function te(t,e,n){return[t,e.baseUrl??"",n??e.model].join("|")}i(te,"buildReasoningOverrideKey");function pt(){let t=Yt();if(at?.filePath===t)return at.config;try{if(Me.existsSync(t))return ct(t,zn(t))}catch{}return ct(t,{})}i(pt,"loadStoredConfig");function qt(t){let e=Yt();try{Me.mkdirSync(lt.dirname(e),{recursive:!0}),Me.writeFileSync(e,JSON.stringify(t,null,2)),ct(e,t)}catch(n){process.env.KODAX_DEBUG_OVERRIDES&&console.error("[ReasoningOverride] Failed to save config:",n)}}i(qt,"saveStoredConfig");function Ee(t,e,n){let o=pt(),r=te(t,e,n);return o.providerReasoningOverrides?.[r]}i(Ee,"loadReasoningOverride");function ft(t,e,n,o){let r=pt(),s=te(t,e,o);r.providerReasoningOverrides={...r.providerReasoningOverrides??{},[s]:n},qt(r)}i(ft,"saveReasoningOverride");function Vn(t,e,n){let o=pt(),r=te(t,e,n);if(!o.providerReasoningOverrides?.[r])return;let s={...o.providerReasoningOverrides};delete s[r],o.providerReasoningOverrides=Object.keys(s).length>0?s:void 0,qt(o)}i(Vn,"clearReasoningOverride");function Qt(t,e){if(!t)return;if(typeof t.get=="function")return t.get(e)??void 0;let n=t,o=e.toLowerCase(),r=n[e]??n[o]??n[o.replace(/\b\w/g,s=>s.toUpperCase())];if(r!==void 0)return Array.isArray(r)?r[0]:r}i(Qt,"readHeader");function Zt(t){if(!t)return;let e=t.trim();if(e.length===0)return;let n=Number(e);if(!(!Number.isFinite(n)||n<=0))return n}i(Zt,"parsePositiveNumber");function Gn(t,e){if(!t)return;let n=t.trim();if(n.length===0)return;let o=Date.parse(n);if(!Number.isFinite(o))return;let r=o-e;return r>0?r:void 0}i(Gn,"parseHttpDate");function Pe(t,e){let n=e.baseBackoffMs??1e3,o=e.maxBackoffMs??3e4,r=e.maxHeaderWaitMs??12e4,s=e.now?e.now():Date.now(),a=Zt(Qt(t,"retry-after-ms"));if(a!==void 0){let y=Math.min(a,r);return{type:"header",waitMs:Math.round(y),source:"retry-after-ms",cappedFromHeader:y!==a}}let c=Qt(t,"retry-after");if(c!==void 0&&c.trim().length>0){let y=Zt(c);if(y!==void 0){let h=y*1e3,f=Math.min(h,r);return{type:"header",waitMs:Math.round(f),source:"retry-after-seconds",cappedFromHeader:f!==h}}let k=Gn(c,s);if(k!==void 0){let h=Math.min(k,r);return{type:"header",waitMs:Math.round(h),source:"retry-after-date",cappedFromHeader:h!==k}}}let u=n*Math.pow(2,Math.max(0,e.attempt)),d=Math.min(u,o),p=e.withJitter!==!1?Math.random()*.25*d:0;return{type:"backoff",waitMs:Math.round(d+p),source:"exponential-backoff",attempt:e.attempt}}i(Pe,"parseRetryAfter");function Se(t){if(!t||typeof t!="object")return;let e=t;return e.headers??e.response?.headers??e.cause?.headers??e.cause?.response?.headers}i(Se,"extractHeadersFromError");var Y={transport:"native-api",conversationSemantics:"full-history",mcpSupport:"none",contextFidelity:"full",toolCallingFidelity:"full",sessionSupport:"full",longRunningSupport:"full",multimodalSupport:"none",evidenceSupport:"full"},en={...Y,multimodalSupport:"image-input"},he={transport:"cli-bridge",conversationSemantics:"last-user-message",mcpSupport:"none",contextFidelity:"lossy",toolCallingFidelity:"limited",sessionSupport:"stateless",longRunningSupport:"limited",multimodalSupport:"none",evidenceSupport:"limited"},tn={...he,multimodalSupport:"image-input"};function Re(t){return{transport:t.transport,conversationSemantics:t.conversationSemantics,mcpSupport:t.mcpSupport,contextFidelity:t.contextFidelity??"full",toolCallingFidelity:t.toolCallingFidelity??"full",sessionSupport:t.sessionSupport??"full",longRunningSupport:t.longRunningSupport??"full",multimodalSupport:t.multimodalSupport??"none",evidenceSupport:t.evidenceSupport??"full"}}i(Re,"normalizeCapabilityProfile");function U(t){return{...Re(t)}}i(U,"cloneCapabilityProfile");function Hn(t){if(!t)return;let e=parseInt(t,10);return Number.isFinite(e)&&e>0?e:void 0}i(Hn,"parseEnvInt");var z=class{static{i(this,"KodaXBaseProvider")}maxOutputTokensOverride;setMaxOutputTokensOverride(e){this.maxOutputTokensOverride=e}getEffectiveMaxOutputTokens(e){if(this.maxOutputTokensOverride!==void 0)return this.maxOutputTokensOverride;let n=Hn(process.env.KODAX_MAX_OUTPUT_TOKENS);if(n!==void 0)return n;let o=this.getModelDescriptor(e)?.maxOutputTokens;return o!==void 0?o:this.config.maxOutputTokens??32768}getStreamMaxDurationMs(e){let n=this.getModelDescriptor(e)?.streamMaxDurationMs;return n!==void 0?n:this.config.streamMaxDurationMs}getEffectiveReplayReasoningContent(e){let n=this.getModelDescriptor(e)?.replayReasoningContent;return n!==void 0?n:this.config.replayReasoningContent??!1}getEffectiveStrictThinkingSignature(e){let n=this.getModelDescriptor(e)?.strictThinkingSignature;return n!==void 0?n:this.config.strictThinkingSignature??!1}supportsNonStreamingFallback(){return!1}async complete(e,n,o,r,s,a){throw new R(`${this.name} does not support non-streaming fallback`)}isConfigured(){return!!process.env[this.config.apiKeyEnv]}async verifyCredential(e){return{ok:!1,error:"unsupported",strategy:this.config.verifyStrategy??"unsupported",durationMs:0,approxTokensSpent:0,message:`Provider class "${this.name}" does not implement verifyCredential()`}}getModel(){return this.config.model}getAvailableModels(){return this.config.models?.length?[...new Set([this.config.model,...this.config.models.map(e=>e.id)])]:[this.config.model]}getModelDescriptor(e){return!e||e===this.config.model?{id:this.config.model}:this.config.models?.find(n=>n.id===e)}getBaseUrl(){return this.config.baseUrl}getApiKeyEnv(){return this.config.apiKeyEnv}getCapabilityProfile(){return U(this.config.capabilityProfile??Y)}getConfiguredReasoningCapability(e){let n=this.getModelDescriptor(e);return n?.reasoningCapability?n.reasoningCapability:rt(this.config)}getReasoningCapability(e){let n=Ee(this.name,this.config,e);return n?dt(n):this.getConfiguredReasoningCapability(e)}getReasoningOverride(e){return Ee(this.name,this.config,e)}getReasoningOverrideKey(e){return te(this.name,this.config,e)}persistReasoningCapabilityOverride(e,n){let o=ut(e);o&&ft(this.name,this.config,o,n)}shouldFallbackForReasoningError(e,...n){let o=e instanceof Error?e.message.toLowerCase():String(e).toLowerCase(),s=n.map(c=>c.toLowerCase()).some(c=>o.includes(c)),a=o.includes("parameter")||s;return o.includes("unknown parameter")||o.includes("invalid parameter")||o.includes("unsupported")&&a}shouldFallbackForSpecificReasoningError(e,...n){let o=e instanceof Error?e.message.toLowerCase():String(e).toLowerCase();return n.map(a=>a.toLowerCase()).some(a=>o.includes(a))?o.includes("unknown parameter")||o.includes("invalid parameter")||o.includes("unsupported"):!1}getReasoningFallbackChain(e){switch(e){case"native-budget":return["native-budget","native-toggle","none"];case"native-effort":return["native-effort","none"];case"native-toggle":return["native-toggle","none"];default:return["none"]}}getContextWindow(){return this.getEffectiveContextWindow()}getEffectiveContextWindow(e){let n=this.getModelDescriptor(e)?.contextWindow;return n!==void 0?n:this.config.contextWindow??2e5}getApiKey(){let e=process.env[this.config.apiKeyEnv];if(!e)throw new Error(`${this.config.apiKeyEnv} not set`);return e}shouldLogStreamDiagnostics(){return!!process.env.KODAX_DEBUG_STREAM}logStreamDiagnostic(...e){this.shouldLogStreamDiagnostics()&&console.error(...e)}normalizeReasoning(e){return Te(e)}onStaleConnection(){}isRateLimitError(e){if(!(e instanceof Error))return!1;let n=e.message.toLowerCase();return["rate","limit","\u901F\u7387","\u9891\u7387","1302","429","too many","overload","overwhelmed","503","529","busy"].some(o=>n.includes(o))}classifyRateLimitReason(e){let n=e instanceof Error?e.message.toLowerCase():String(e).toLowerCase();return n.includes("overload")||n.includes("overwhelmed")||n.includes("503")||n.includes("529")||n.includes("busy")?"overloaded":"rate-limit"}extractRetryAfterMs(e){let n=Se(e);if(!n)return;let o=Pe(n,{attempt:0,withJitter:!1});return o.type==="header"?o.waitMs:void 0}parseContextOverflow(e){let n=String(e?.message??""),o=[/(\d[\d,]*)\s*tokens?.*?(\d[\d,]*)\s*(?:maximum|limit|context)/i,/maximum.*?(\d[\d,]*)\s*tokens?.*?requested.*?(\d[\d,]*)/i,/exceeds?\s+.*?(\d[\d,]*)\s*.*?(?:limit|max|上限).*?(\d[\d,]*)/i];for(let r of o){let s=n.match(r);if(s){let a=Number(s[1].replace(/,/g,"")),c=Number(s[2].replace(/,/g,"")),u=Math.min(a,c),d=Math.max(a,c);return Math.max(3e3,d-u-1e3)}}}isContextOverflowError(e){let n=String(e?.message??"").toLowerCase();return n.includes("prompt is too long")||n.includes("prompt too long")||n.includes("context length")||n.includes("context_length_exceeded")||n.includes("context window")||n.includes("\u4E0A\u4E0B\u6587\u957F\u5EA6")}async withRateLimit(e,n,o=3,r,s){for(let a=0;a<o;a++)try{let c=await e();return this.maxOutputTokensOverride=void 0,c}catch(c){if(this.isContextOverflowError(c)&&!this.maxOutputTokensOverride){let u=this.parseContextOverflow(c);if(u){this.maxOutputTokensOverride=u,r?.(a+1,o,0);continue}}if(this.isRateLimitError(c)){if(a===o-1)throw new ge(`API rate limit exceeded after ${o} retries. Please wait and try again later.`,6e4);let u=Se(c)??{},d=Pe(u,{attempt:a,baseBackoffMs:500,maxBackoffMs:32e3,withJitter:!0}),l=d.waitMs,p=this.classifyRateLimitReason(c);if(s?.({provider:this.name,waitMs:l,reason:p,source:d.source,attempt:a+1,maxAttempts:o}),r?r(a+1,o,l):s||console.log(`[Rate Limit] Retrying in ${l/1e3}s (${a+1}/${o})...`),n?.aborted)throw new DOMException("Request aborted","AbortError");if(await new Promise(y=>setTimeout(y,l)),n?.aborted)throw new DOMException("Request aborted","AbortError");continue}if(c instanceof Error){if((c.name==="AbortError"||c.name==="APIUserAbortError")&&n?.aborted)throw c.name==="AbortError"?c:new DOMException(c.message||"Request aborted","AbortError");let u=c?.cause?.code??c?.code??"";throw(u==="ECONNRESET"||u==="EPIPE")&&this.onStaleConnection(),new R(`${this.name} API error: ${c.message}`,this.name)}throw c}throw new G("Unexpected end of withRateLimit")}};import eo from"@anthropic-ai/sdk";import{parse as Jn}from"partial-json";function ne(t){if(!t)return{};try{let e=JSON.parse(t);return e&&typeof e=="object"&&!Array.isArray(e)?e:{}}catch{}try{let e=Jn(t);return process.env.KODAX_DEBUG_TOOL_STREAM&&console.warn("[Tool Block Salvaged] partial JSON recovered, rawLength=",t.length),e&&typeof e=="object"&&!Array.isArray(e)?e:{}}catch{return{}}}i(ne,"parseToolInputWithSalvage");async function oe(t){if(t.strategy==="unsupported")return{ok:!1,error:"unsupported",strategy:"unsupported",durationMs:0,approxTokensSpent:0,message:"Provider does not support credential verification"};let e=t.runners.find(d=>d.strategy===t.strategy);if(!e)return{ok:!1,error:"unsupported",strategy:t.strategy,durationMs:0,approxTokensSpent:0,message:`verifyStrategy="${t.strategy}" is not implemented for this provider's base class`};if(t.signal?.aborted)return{ok:!1,error:"unknown",strategy:t.strategy,durationMs:0,approxTokensSpent:0,message:"caller aborted before verifyCredential started"};let n=new AbortController,o=t.timeoutMs??8e3,r,s=i(d=>{r||(r=d),n.abort()},"recordAbort"),a=setTimeout(()=>s("timeout"),o),c=i(()=>s("parent"),"onParentAbort");t.signal&&t.signal.addEventListener("abort",c,{once:!0});let u=Date.now();try{return await e.run(n.signal),{ok:!0,strategy:t.strategy,durationMs:Date.now()-u,approxTokensSpent:e.approxTokensSpent}}catch(d){return mt(d,{strategy:t.strategy,durationMs:Date.now()-u,approxTokensSpent:0,abortCause:r,providerName:t.providerName})}finally{clearTimeout(a),t.signal&&t.signal.removeEventListener("abort",c)}}i(oe,"runVerifyCredential");function Yn(t){return t.replace(/\bsk-[A-Za-z0-9_-]{6,}/g,"sk-***")}i(Yn,"redactKeyMaterial");function mt(t,e){let n=t,o=n.status??n.statusCode??n.response?.status,r=Yn(String(n.message??t)).slice(0,240),s=n.cause?.code??n.code,a=n.constructor?.name??"",c;return e.abortCause==="timeout"?c="timeout":e.abortCause==="parent"?c="unknown":a==="AuthenticationError"||a==="PermissionDeniedError"||o===401||o===403?c="unauthorized":o===429?c="rate_limited":o===400&&e.strategy==="count-tokens"&&e.providerName==="kimi-code"?c="unauthorized":o!==void 0&&o>=500&&o<600?c="server_error":s!==void 0&&/ENOTFOUND|ECONNREFUSED|ECONNRESET|EAI_AGAIN|EPIPE|ETIMEDOUT|ENETUNREACH|ENETDOWN|EHOSTUNREACH/i.test(s)?c="network":/timeout/i.test(r)?c="timeout":c="unknown",{ok:!1,error:c,status:o,message:r,durationMs:e.durationMs,approxTokensSpent:e.approxTokensSpent,strategy:e.strategy}}i(mt,"classifyVerifyError");function gt(t,e){let n=t[t.length-1];if(n&&ie(n)&&n.hint===e)return t;let o=e?{type:"cache-boundary",hint:e}:{type:"cache-boundary"};return[...t,o]}i(gt,"insertCacheBoundary");function ie(t){if(typeof t!="object"||t===null||t.type!=="cache-boundary")return!1;let e=Object.keys(t);return e.length<=2&&e.every(n=>n==="type"||n==="hint")}i(ie,"isCacheBoundary");function ht(t,e){let n=[];for(let o of t){if(ie(o)){if(e==="attach"&&n.length>0){let r=n[n.length-1];n[n.length-1]={...r,cache_control:{type:"ephemeral"}}}continue}n.push(o)}return n}i(ht,"lowerCacheBoundaries");function ye(t){return t.filter(e=>!ie(e))}i(ye,"stripCacheBoundaries");import{readFile as qn}from"node:fs/promises";import Qn from"node:path";var Zn={".gif":"image/gif",".jpeg":"image/jpeg",".jpg":"image/jpeg",".png":"image/png",".webp":"image/webp"};function we(t,e){return e??Zn[Qn.extname(t).toLowerCase()]??"image/png"}i(we,"resolveImageMediaType");async function Ae(t){return(await qn(t)).toString("base64")}i(Ae,"readImageFileAsBase64");async function nn(t,e){let n=we(t,e),o=await Ae(t);return`data:${n};base64,${o}`}i(nn,"buildImageDataUrl");var to="KodaX";function no(t){return t.userAgentMode==="sdk"?void 0:{"User-Agent":to}}i(no,"getAnthropicCompatDefaultHeaders");function yt(t,e){if(!t)return e;let n=t.input_tokens!==void 0&&t.input_tokens!==null||t.cache_creation_input_tokens!==void 0&&t.cache_creation_input_tokens!==null||t.cache_read_input_tokens!==void 0&&t.cache_read_input_tokens!==null,o=typeof t.input_tokens=="number"?t.input_tokens:n?0:e?.inputTokens??0,r=typeof t.cache_creation_input_tokens=="number"?t.cache_creation_input_tokens:n?0:e?.cachedWriteTokens??0,s=typeof t.cache_read_input_tokens=="number"?t.cache_read_input_tokens:n?0:e?.cachedReadTokens??0,a=typeof t.output_tokens=="number"?t.output_tokens:e?.outputTokens??0,c=n?o+r+s:e?.inputTokens??0;if(![c,a].some(u=>!Number.isFinite(u)||u<0))return{inputTokens:c,outputTokens:a,totalTokens:c+a,cachedReadTokens:s||void 0,cachedWriteTokens:r||void 0}}i(yt,"normalizeAnthropicUsage");var I=class extends z{static{i(this,"KodaXAnthropicCompatProvider")}supportsThinking=!0;client;initClient(){let e=no(this.config);this.client=new eo({apiKey:this.getApiKey(),baseURL:this.config.baseUrl,...e?{defaultHeaders:e}:{}})}onStaleConnection(){this.initClient()}async verifyCredential(e){let n=this.config.model,o=this.client,r=[{strategy:"count-tokens",approxTokensSpent:0,run:i(async s=>{await o.messages.countTokens({model:n,messages:[{role:"user",content:"hi"}]},{signal:s})},"run")},{strategy:"models-list",approxTokensSpent:0,run:i(async s=>{await o.models.list({},{signal:s})},"run")},{strategy:"minimal-message",approxTokensSpent:7,run:i(async s=>{await o.messages.create({model:n,max_tokens:1,messages:[{role:"user",content:"hi"}]},{signal:s})},"run")}];return oe({strategy:this.config.verifyStrategy??"count-tokens",runners:r,timeoutMs:e?.timeoutMs,signal:e?.signal,providerName:this.name})}applyCacheControlToSystem(e){if(!e.trim()||process.env.KODAX_DISABLE_PROMPT_CACHE==="1")return e;let n=gt([{type:"text",text:e}],"system");return ht(n,"attach")}applyCacheControlToTools(e){if(e.length===0||process.env.KODAX_DISABLE_PROMPT_CACHE==="1")return e;let n=e.slice(),o=n[n.length-1];return n[n.length-1]={...o,cache_control:{type:"ephemeral"}},n}async stream(e,n,o,r,s,a){return this.withRateLimit(async()=>{let c=this.normalizeReasoning(r),u=s?.modelOverride??this.config.model,d=this.getEffectiveMaxOutputTokens(u),l=await this.convertMessages(e,u),p=c.enabled?this.getReasoningCapability(u):"none",y=c.enabled?this.getReasoningFallbackChain(p).filter(x=>x==="native-budget"||x==="native-toggle"||x==="none"):["none"],k=i(x=>{let P={model:u,max_tokens:d,system:this.applyCacheControlToSystem(this.buildSystemPrompt(o,e)),messages:l,tools:this.applyCacheControlToTools(n),stream:!0};if(x==="native-budget"){let D=Z(this.config,c.depth,c.taskType);P.thinking={type:"enabled",budget_tokens:ee(D,d)}}else x==="native-toggle"&&(P.thinking={type:"enabled"});return P},"buildRequest");if(a?.aborted)throw new DOMException("Request aborted","AbortError");let h=[],f=[],C=[],b,_=null,m="",g="",M="",S="",j="",B="",X="",E=!1,W,O=Date.now(),xe=Date.now(),pe,fe;for(let x of y)try{pe=await this.client.messages.create(k(x),a?{signal:a}:{}),x!==p&&this.persistReasoningCapabilityOverride(x,u);break}catch(P){fe=P;let D=x==="native-budget"?["budget_tokens","thinking"]:x==="native-toggle"?["thinking"]:[];if(!this.shouldFallbackForReasoningError(P,...D))throw P}if(!pe)throw fe??new R("All reasoning capability attempts failed without a captured error",this.name);let me=Date.now(),T=0,L=0,N=3e4;for await(let x of pe){if(a?.aborted)throw new DOMException("Request aborted","AbortError");let P=Date.now(),D=P-me;if(D>N&&(T++,L+=D,this.logStreamDiagnostic(`[Stream] stall detected: ${Math.round(D/1e3)}s gap before ${x.type}`,{stallCount:T,totalStallMs:L,eventType:x.type})),me=P,x.type==="content_block_start"||x.type==="content_block_stop"?s?.onHeartbeat?.(!0):s?.onHeartbeat?.(),x.type==="content_block_start"){O=Date.now();let v=x.content_block;_=v.type,process.env.KODAX_DEBUG_TOOL_STREAM&&v.type==="tool_use"&&console.error("[ToolStream] content_block_start:",{type:v.type,id:v.id,name:v.name}),v.type==="thinking"?(g="",M=v.signature??""):v.type==="redacted_thinking"?(_="redacted_thinking",S=v.data??""):v.type==="text"?m="":v.type==="tool_use"&&(j=v.id,B=v.name,X="")}else if(x.type==="content_block_delta"){O=Date.now();let v=x.delta;v.type==="thinking_delta"?(g+=v.thinking??"",s?.onThinkingDelta?.(v.thinking??"")):v.type==="text_delta"?(m+=v.text??"",s?.onTextDelta?.(v.text??"")):v.type==="input_json_delta"&&(X+=v.partial_json??"",s?.onToolInputDelta?.(B,v.partial_json??"",j?{toolId:j}:void 0))}else if(x.type==="content_block_stop")O=Date.now(),_==="thinking"?g&&(C.push({type:"thinking",thinking:g,signature:M}),s?.onThinkingEnd?.(g)):_==="redacted_thinking"?(S&&C.push({type:"redacted_thinking",data:S}),S=""):_==="text"?m&&h.push({type:"text",text:m}):_==="tool_use"&&(!j||!B?console.error("[Tool Block Invalid] Missing tool id or name:",{id:JSON.stringify(j),name:JSON.stringify(B),input:X.slice(0,100)}):f.push({type:"tool_use",id:j,name:B,input:ne(X)})),_=null;else if(x.type==="message_stop"){if(E=!0,O=Date.now(),process.env.KODAX_DEBUG_STREAM){let v=Date.now()-xe;this.logStreamDiagnostic(`[Stream] message_stop received after ${v}ms`)}}else if(x.type==="message_delta"){O=Date.now(),b=yt(x.usage,b);let v=x.delta;v?.stop_reason&&(W=v.stop_reason,process.env.KODAX_DEBUG_STREAM&&this.logStreamDiagnostic(`[Stream] message_delta with stop_reason: ${W}`))}else x.type==="message_start"&&(O=Date.now(),b=yt(x.message?.usage,b),process.env.KODAX_DEBUG_STREAM&&this.logStreamDiagnostic("[Stream] message_start received"))}if(!E){let x=Date.now()-xe,P=Date.now()-O;if(a?.aborted){let v=a.reason instanceof Error?a.reason.message:typeof a.reason=="string"?a.reason:"Request aborted";throw this.logStreamDiagnostic("[Stream] Stream ended after abort before message_stop:",{duration:x,lastEventAge:P,reason:v,textBlocks:h.length,toolBlocks:f.length,thinkingBlocks:C.length}),new DOMException(v,"AbortError")}let D=new Error(`Stream incomplete: message_stop event not received. Duration: ${x}ms, Last event: ${P}ms ago. This may indicate a network disconnection or API timeout.`);throw D.name="StreamIncompleteError",this.logStreamDiagnostic("[Stream] Incomplete stream detected:",{duration:x,lastEventAge:P,textBlocks:h.length,toolBlocks:f.length,thinkingBlocks:C.length}),D}return{textBlocks:h,toolBlocks:f,thinkingBlocks:C,usage:b,stopReason:W}},a,3,s?.onRateLimit,s?.onRetryAfter)}supportsNonStreamingFallback(){return!0}async complete(e,n,o,r,s,a){return this.withRateLimit(async()=>{let c=this.normalizeReasoning(r),u=s?.modelOverride??this.config.model,d=this.getEffectiveMaxOutputTokens(u),l=await this.convertMessages(e,u),p=c.enabled?this.getReasoningCapability(u):"none",y=c.enabled?this.getReasoningFallbackChain(p).filter(m=>m==="native-budget"||m==="native-toggle"||m==="none"):["none"],k=i(m=>{let g={model:u,max_tokens:d,system:this.applyCacheControlToSystem(this.buildSystemPrompt(o,e)),messages:l,tools:this.applyCacheControlToTools(n)};if(m==="native-budget"){let M=Z(this.config,c.depth,c.taskType);g.thinking={type:"enabled",budget_tokens:ee(M,d)}}else m==="native-toggle"&&(g.thinking={type:"enabled"});return g},"buildRequest"),h,f;for(let m of y)try{h=await this.client.messages.create(k(m),a?{signal:a}:{}),m!==p&&this.persistReasoningCapabilityOverride(m,u);break}catch(g){f=g;let M=m==="native-budget"?["budget_tokens","thinking"]:m==="native-toggle"?["thinking"]:[];if(!this.shouldFallbackForReasoningError(g,...M))throw g}if(!h)throw f??new R("All reasoning capability attempts failed without a captured error",this.name);let C=[],b=[],_=[];for(let m of h.content)m.type==="text"?(C.push({type:"text",text:m.text}),s?.onTextDelta?.(m.text)):m.type==="thinking"?(_.push({type:"thinking",thinking:m.thinking,signature:m.signature??""}),s?.onThinkingDelta?.(m.thinking),s?.onThinkingEnd?.(m.thinking)):m.type==="redacted_thinking"?_.push({type:"redacted_thinking",data:m.data}):m.type==="tool_use"&&b.push({type:"tool_use",id:m.id,name:m.name,input:typeof m.input=="object"&&m.input!==null?m.input:{}});return{textBlocks:C,toolBlocks:b,thinkingBlocks:_,usage:yt(h.usage),stopReason:h.stop_reason??void 0}},a,3,s?.onRateLimit,s?.onRetryAfter)}serializeSystemMessageContent(e){if(typeof e=="string")return e.trim();for(let n of e)if(ie(n))throw new R("cache-boundary marker reached system message serialization unlowered. Provider base class lowering must run before any wire-level serialization.",this.name);return e.filter(n=>n.type==="text").map(n=>n.text.trim()).filter(Boolean).join(`
3
+ `)}buildSystemPrompt(e,n){let o=n.filter(r=>r.role==="system").map(r=>this.serializeSystemMessageContent(r.content)).filter(Boolean);return[e,...o].filter(r=>typeof r=="string"&&r.trim().length>0).join(`
4
+
5
+ `)}async convertMessages(e,n){let o=[],r=this.getEffectiveStrictThinkingSignature(n);for(let s of e.filter(a=>a.role!=="system")){let a=s.role==="user"?"user":"assistant";if(typeof s.content=="string"){o.push({role:a,content:s.content});continue}let c=[],u=[];for(let l of s.content)l.type==="thinking"?!r||typeof l.signature=="string"&&l.signature.length>0?c.push({type:"thinking",thinking:l.thinking,signature:l.signature??""}):l.thinking&&u.push(l.thinking):l.type==="redacted_thinking"&&(r||c.push({type:"redacted_thinking",data:l.data}));u.length>0&&s.role==="assistant"&&c.push({type:"text",text:`<prior_reasoning>
6
+ ${u.join(`
7
+
8
+ `)}
9
+ </prior_reasoning>`});for(let l of s.content)if(l.type==="tool_result"&&s.role==="user"){let p;if(typeof l.content=="string")p=l.content;else{let y=[];for(let k of l.content)k.type==="text"?y.push({type:"text",text:k.text}):k.type==="image"&&y.push({type:"image",source:{type:"base64",media_type:we(k.path,k.mediaType),data:await Ae(k.path)}});p=y}c.push({type:"tool_result",tool_use_id:l.tool_use_id,content:p,...l.is_error===!0?{is_error:!0}:{}})}for(let l of s.content)l.type==="tool_use"&&s.role==="assistant"&&c.push({type:"tool_use",id:l.id,name:l.name,input:l.input});for(let l of s.content)l.type==="text"?c.push({type:"text",text:l.text}):l.type==="image"&&s.role==="user"&&c.push({type:"image",source:{type:"base64",media_type:we(l.path,l.mediaType),data:await Ae(l.path)}});if(a==="assistant"&&this.config.supportsThinking&&!r){let l=c.some(y=>y.type==="tool_use"),p=c.some(y=>y.type==="thinking"||y.type==="redacted_thinking");l&&!p&&c.unshift({type:"thinking",thinking:"...",signature:""})}let d=c.length===0||a==="assistant"&&c.every(l=>{let p=l;return p.type==="thinking"&&!p.thinking||p.type==="text"&&!p.text});o.push({role:s.role,content:d?[{type:"text",text:"..."}]:c})}return o}};import oo from"openai";var io="KodaX";function ro(t){return t.userAgentMode==="sdk"?void 0:{"User-Agent":io}}i(ro,"getOpenAICompatDefaultHeaders");function on(t){if(!t)return;let e=typeof t.prompt_tokens=="number"?t.prompt_tokens:0,n=typeof t.completion_tokens=="number"?t.completion_tokens:0,o=typeof t.total_tokens=="number"?t.total_tokens:e+n;if([e,n,o].some(s=>!Number.isFinite(s)||s<0)||o<e||o<n)return;let r=typeof t.prompt_tokens_details?.cached_tokens=="number"&&t.prompt_tokens_details.cached_tokens>=0?t.prompt_tokens_details.cached_tokens:typeof t.prompt_cache_hit_tokens=="number"&&t.prompt_cache_hit_tokens>=0?t.prompt_cache_hit_tokens:void 0;return{inputTokens:e,outputTokens:n,totalTokens:o,...r!==void 0?{cachedReadTokens:r}:{}}}i(on,"normalizeOpenAIUsage");function so(t){return t?t.type==="function"&&"function"in t:!1}i(so,"isOpenAIFunctionToolCall");function ao(t){return typeof t=="string"?t:Array.isArray(t)?t.map(e=>{if(!e||typeof e!="object")return"";let n=Reflect.get(e,"text");if(typeof n=="string")return n;if(n&&typeof n=="object"){let o=Reflect.get(n,"value");return typeof o=="string"?o:""}return""}).filter(Boolean).join(""):""}i(ao,"extractOpenAIMessageText");function co(t){if(!t||typeof t!="object")return"";let e=Reflect.get(t,"reasoning_content");return typeof e=="string"?e:Array.isArray(e)?e.map(n=>typeof n=="string"?n:n&&typeof n=="object"&&"text"in n&&typeof n.text=="string"?n.text:"").join(""):""}i(co,"extractOpenAIMessageReasoning");var K=class extends z{static{i(this,"KodaXOpenAICompatProvider")}supportsThinking=!0;client;initClient(){let e=ro(this.config);this.client=new oo({apiKey:this.getApiKey(),baseURL:this.config.baseUrl,...e?{defaultHeaders:e}:{}})}onStaleConnection(){this.initClient()}async verifyCredential(e){let n=this.config.model,o=this.client,r=[{strategy:"models-list",approxTokensSpent:0,run:i(async s=>{await o.models.list({signal:s})},"run")},{strategy:"minimal-message",approxTokensSpent:6,run:i(async s=>{await o.chat.completions.create({model:n,max_tokens:1,messages:[{role:"user",content:"hi"}]},{signal:s})},"run")}];return oe({strategy:this.config.verifyStrategy??"models-list",runners:r,timeoutMs:e?.timeoutMs,signal:e?.signal,providerName:this.name})}stripCacheBoundariesFromMessages(e){return e.map(n=>{if(typeof n.content=="string")return n;let o=ye(n.content);return o.length===n.content.length?n:{...n,content:o}})}normalizeSystemForWire(e,n){let o=[];e&&e.trim().length>0&&o.push(e);let r=[];for(let s of n){if(s.role!=="system"){r.push(s);continue}let a=typeof s.content=="string"?s.content:Array.isArray(s.content)?s.content.filter(c=>typeof c=="object"&&c!==null&&c.type==="text"&&typeof c.text=="string").map(c=>c.text).join(`
10
+ `):"";a.trim().length>0&&o.push(a)}return{system:o.join(`
11
+
12
+ `),rest:r}}appendExtraBody(e,n){let o=typeof e.extra_body=="object"&&e.extra_body!==null?e.extra_body:{};e.extra_body={...o,...n}}resetReasoningCapabilityParams(e){delete e.reasoning_effort,delete e.thinking;let n=typeof e.extra_body=="object"&&e.extra_body!==null?{...e.extra_body}:void 0;if(n){if(delete n.enable_thinking,delete n.thinking_budget,delete n.thinking,Object.keys(n).length===0){delete e.extra_body;return}e.extra_body=n}}applyReasoningCapability(e,n,o){let r=e,s=this.getEffectiveMaxOutputTokens(e.model),a=ee(Z(this.config,o.depth,o.taskType),s);switch(n){case"native-effort":{let c=st(o.depth);c&&(r.reasoning_effort=c);break}case"native-budget":{this.name==="qwen"?this.appendExtraBody(r,{enable_thinking:!0,thinking_budget:a}):this.name==="zhipu"&&(r.thinking={type:"enabled",budget_tokens:a});break}case"native-toggle":{this.name==="qwen"?this.appendExtraBody(r,{enable_thinking:!0}):this.name==="zhipu"&&(r.thinking={type:"enabled"});break}default:break}}getFallbackTerms(e){switch(e){case"native-budget":return["thinking_budget","budget_tokens","thinking"];case"native-effort":return["reasoning_effort"];case"native-toggle":return["enable_thinking","thinking"];default:return[]}}async stream(e,n,o,r,s,a){return this.withRateLimit(async()=>{let c=this.stripCacheBoundariesFromMessages(e),{system:u,rest:d}=this.normalizeSystemForWire(o,c),l=s?.modelOverride??this.config.model,p=[{role:"system",content:u},...await this.convertMessages(d,l)],y=n.map(T=>({type:"function",function:{name:T.name,description:T.description,parameters:T.input_schema}}));if(a?.aborted)throw new DOMException("Request aborted","AbortError");let k=new Map,h="",f="",C,b=!0,_=null,m=Date.now(),g=this.normalizeReasoning(r),M=Q(g)?this.getReasoningCapability(l):"none",S=Q(g)?this.getReasoningFallbackChain(M).filter(T=>T==="native-budget"||T==="native-effort"||T==="native-toggle"||T==="none"):["none"],j={model:l,messages:p,tools:y,max_completion_tokens:this.getEffectiveMaxOutputTokens(l),stream:!0},B,X;for(let T of S){for(;!B;){let L={...j};b&&(L.stream_options={include_usage:!0}),this.resetReasoningCapabilityParams(L),this.applyReasoningCapability(L,T,g);try{B=await this.client.chat.completions.create(L,a?{signal:a}:{}),T!==M&&this.persistReasoningCapabilityOverride(T,l)}catch(N){if(X=N,b&&this.shouldFallbackForSpecificReasoningError(N,"stream_options","include_usage")){b=!1;continue}if(!this.shouldFallbackForReasoningError(N,...this.getFallbackTerms(T)))throw N;break}}if(B)break}if(!B)throw X??new R("All reasoning capability attempts failed without a captured error",this.name);let E=Date.now(),W=0,O=0,xe=3e4;for await(let T of B){if(a?.aborted)throw new DOMException("Request aborted","AbortError");let L=Date.now(),N=L-E;N>xe&&(W++,O+=N,this.logStreamDiagnostic(`[Stream] stall detected: ${Math.round(N/1e3)}s gap`,{stallCount:W,totalStallMs:O})),E=L,s?.onHeartbeat?.(),C=on(T.usage)??C;let x=T.choices[0],P=x?.delta;if(x?.finish_reason&&(_=x.finish_reason,process.env.KODAX_DEBUG_STREAM)){let v=Date.now()-m;this.logStreamDiagnostic(`[Stream] finish_reason: ${_} after ${v}ms`)}P?.content&&(h+=P.content,s?.onTextDelta?.(P.content));let D=this.extractReasoningDelta(P);if(D&&(f+=D,s?.onThinkingDelta?.(D)),P?.tool_calls)for(let v of P.tool_calls){let J=k.get(v.index)??{id:"",name:"",arguments:""};v.id&&(J.id=v.id),v.function?.name&&(J.name=v.function.name),v.function?.arguments&&(J.arguments+=v.function.arguments,s?.onToolInputDelta?.(J.name,v.function.arguments,J.id?{toolId:J.id}:void 0)),k.set(v.index,J)}}if(!_){let T=Date.now()-m;if(a?.aborted){let N=a.reason instanceof Error?a.reason.message:typeof a.reason=="string"?a.reason:"Request aborted";throw this.logStreamDiagnostic("[Stream] Stream ended after abort before finish_reason:",{duration:T,reason:N,textContentLength:h.length,toolCallsCount:k.size}),new DOMException(N,"AbortError")}let L=new Error(`Stream incomplete: finish_reason not received. Duration: ${T}ms. This may indicate a network disconnection or API timeout.`);throw L.name="StreamIncompleteError",this.logStreamDiagnostic("[Stream] Incomplete stream detected:",{duration:T,textContentLength:h.length,toolCallsCount:k.size}),L}let pe=h?[{type:"text",text:h}]:[],fe=[],me=[];f&&(me.push({type:"thinking",thinking:f}),s?.onThinkingEnd?.(f));for(let[,T]of k)T.id&&T.name&&fe.push({type:"tool_use",id:T.id,name:T.name,input:ne(T.arguments)});return{textBlocks:pe,toolBlocks:fe,thinkingBlocks:me,usage:C,stopReason:_??void 0}},a,3,s?.onRateLimit,s?.onRetryAfter)}supportsNonStreamingFallback(){return!0}async complete(e,n,o,r,s,a){return this.withRateLimit(async()=>{let c=this.stripCacheBoundariesFromMessages(e),{system:u,rest:d}=this.normalizeSystemForWire(o,c),l=s?.modelOverride??this.config.model,p=[{role:"system",content:u},...await this.convertMessages(d,l)],y=n.map(E=>({type:"function",function:{name:E.name,description:E.description,parameters:E.input_schema}})),k=this.normalizeReasoning(r),h=Q(k)?this.getReasoningCapability(l):"none",f=Q(k)?this.getReasoningFallbackChain(h).filter(E=>E==="native-budget"||E==="native-effort"||E==="native-toggle"||E==="none"):["none"],C={model:l,messages:p,tools:y,max_completion_tokens:this.getEffectiveMaxOutputTokens(l)},b,_;for(let E of f){let W={...C};this.resetReasoningCapabilityParams(W),this.applyReasoningCapability(W,E,k);try{b=await this.client.chat.completions.create(W,a?{signal:a}:{}),E!==h&&this.persistReasoningCapabilityOverride(E,l);break}catch(O){if(_=O,!this.shouldFallbackForReasoningError(O,...this.getFallbackTerms(E)))throw O}}if(!b)throw _??new R("All reasoning capability attempts failed without a captured error",this.name);let m=b.choices[0],g=m?.message,M=ao(g?.content),S=co(g),j=(g?.tool_calls??[]).filter(so).map(E=>({type:"tool_use",id:E.id,name:E.function.name,input:ne(E.function.arguments)}));M&&s?.onTextDelta?.(M);let B=M?[{type:"text",text:M}]:[],X=[];return S&&(X.push({type:"thinking",thinking:S}),s?.onThinkingDelta?.(S),s?.onThinkingEnd?.(S)),{textBlocks:B,toolBlocks:j,thinkingBlocks:X,usage:on(b.usage),stopReason:m?.finish_reason??void 0}},a,3,s?.onRateLimit,s?.onRetryAfter)}extractReasoningDelta(e){let n=e?.reasoning_content;return typeof n=="string"?n:Array.isArray(n)?n.map(o=>typeof o=="string"?o:typeof o=="object"&&o!==null&&"text"in o&&typeof o.text=="string"?o.text:"").join(""):""}serializeAssistantMessage(e,n){let o=e.filter(d=>d.type==="text").map(d=>d.text).join(`
13
+ `),r=e.filter(d=>d.type==="tool_use").map(d=>({id:d.id,type:"function",function:{name:d.name,arguments:JSON.stringify(d.input??{})}})),s=e.filter(d=>d.type==="thinking"||d.type==="redacted_thinking").map(d=>d.type==="thinking"?d.thinking:"").filter(Boolean).join(`
14
+
15
+ `),a=e.some(d=>d.type==="thinking"||d.type==="redacted_thinking");if(!o&&r.length===0&&!a)return[];let c;o?c=o:r.length>0?c=null:c="...";let u={role:"assistant",content:c};return r.length>0&&(u.tool_calls=r),this.getEffectiveReplayReasoningContent(n)&&(u.reasoning_content=s||""),[u]}async serializeUserMessage(e){let n=[],o=e.filter(a=>a.type==="text").map(a=>a.text).join(`
16
+ `),r=e.filter(a=>a.type==="image");for(let a of e)if(a.type==="tool_result"){let c;typeof a.content=="string"?c=a.content:c=a.content.map(u=>u.type==="text"?u.text:`[Image at ${u.path}${u.mediaType?` (${u.mediaType})`:""}] (provider does not support image content in tool_result; if the image was previously visible to you in the conversation, refer to it directly via native vision)`).join(`
17
+ `),n.push({role:"tool",tool_call_id:a.tool_use_id,content:c})}if(r.length===0)return o&&n.push({role:"user",content:o}),n;let s=[];o&&s.push({type:"text",text:o});for(let a of r)s.push({type:"image_url",image_url:{url:await nn(a.path,a.mediaType)}});return n.push({role:"user",content:s}),n}serializeSystemMessage(e){if(typeof e=="string")return[{role:"system",content:e}];let n=e.filter(o=>o.type==="text").map(o=>o.text).join(`
18
+ `);return n?[{role:"system",content:n}]:[]}async convertMessages(e,n){let o=[];for(let r of e){if(r.role==="system"){o.push(...this.serializeSystemMessage(r.content));continue}if(typeof r.content=="string"){o.push({role:r.role,content:r.content});continue}if(r.role==="assistant"){o.push(...this.serializeAssistantMessage(r.content,n));continue}o.push(...await this.serializeUserMessage(r.content))}return o}};import{spawn as lo}from"node:child_process";import{Readable as uo,Writable as po}from"node:stream";import vt from"node:process";import{ClientSideConnection as fo,PROTOCOL_VERSION as mo,ndJsonStream as go}from"@agentclientprotocol/sdk";var Oe=class{static{i(this,"AcpClient")}client=null;agentProcess=null;options;constructor(e){this.options=e}async connect(){let e,n;if(this.options.inputStream&&this.options.outputStream)e=this.options.inputStream,n=this.options.outputStream;else if(this.options.command){let s=vt.platform==="win32"&&!this.options.command.endsWith(".cmd")?`${this.options.command}.cmd`:this.options.command;if(this.agentProcess=lo(s,this.options.args??[],{cwd:this.options.cwd??vt.cwd(),stdio:["pipe","pipe","inherit"]}),!this.agentProcess.stdin||!this.agentProcess.stdout)throw new Error("Failed to create ACP stdio pipes");n=po.toWeb(this.agentProcess.stdin),e=uo.toWeb(this.agentProcess.stdout)}else throw new Error("AcpClient requires either a command or I/O streams");let o=go(n,e);this.client=new fo(()=>({sessionUpdate:i(async r=>{this.options.onSessionUpdate?.(r)},"sessionUpdate"),requestPermission:i(async r=>{let s=r.options??[],a=s.find(c=>c.kind==="allow_once"||c.kind==="allow_always")??s[0];return a?{outcome:{outcome:"selected",optionId:a.optionId}}:{outcome:{outcome:"cancelled"}}},"requestPermission")}),o),await this.client.initialize({protocolVersion:mo,clientCapabilities:{},clientInfo:{name:"kodax-ai-acp-client",version:"1.0.0"}})}async createNewSession(){if(!this.client)throw new Error("Client not connected");return(await this.client.newSession({cwd:this.options.cwd??vt.cwd(),mcpServers:[]})).sessionId}async prompt(e,n,o,r){if(!this.client)throw new Error("Client not connected");let s={sessionId:n,prompt:[{type:"text",text:e}]};r?.model&&(s.model=r.model);let a=this.client.prompt(s);if(o){let c=i(()=>{this.client?.cancel({sessionId:n}).catch(()=>{})},"onAbort");o.addEventListener("abort",c),a=a.finally(()=>{o.removeEventListener("abort",c)})}return await a}disconnect(){this.agentProcess?.kill(),this.options.abort?.();try{this.client?.close?.()}catch{}this.client=null}};function ho(t){if(!t||typeof t!="object")return;let e=t,n=typeof e.inputTokens=="number"?e.inputTokens:0,o=typeof e.outputTokens=="number"?e.outputTokens:0,r=typeof e.totalTokens=="number"?e.totalTokens:n+o;if(![n,o,r].some(s=>!Number.isFinite(s)||s<0)&&!(r<n||r<o))return{inputTokens:n,outputTokens:o,totalTokens:r,cachedReadTokens:typeof e.cachedReadTokens=="number"?e.cachedReadTokens:void 0,cachedWriteTokens:typeof e.cachedWriteTokens=="number"?e.cachedWriteTokens:void 0,thoughtTokens:typeof e.thoughtTokens=="number"?e.thoughtTokens:void 0}}i(ho,"normalizeAcpUsage");var re=class extends z{static{i(this,"KodaXAcpProvider")}_client=null;_sessionMap=new Map;_activeStreams=new Map;isConfigured(){return!0}async verifyCredential(){return{ok:!1,error:"unsupported",strategy:"unsupported",durationMs:0,approxTokensSpent:0,message:`CLI-bridge provider "${this.name}" manages credentials in its CLI binary's token store; not verifiable from the KodaX SDK`}}getCapabilityProfile(){return U(he)}serializeImageBlockToPromptToken(e){return null}stripCacheBoundariesFromMessages(e){return e.map(n=>{if(typeof n.content=="string")return n;let o=ye(n.content);return o.length===n.content.length?n:{...n,content:o}})}async stream(e,n,o,r,s,a){if(e=this.stripCacheBoundariesFromMessages(e),this.acpClientOptions.executor&&typeof this.acpClientOptions.executor.isInstalled=="function"&&!await this.acpClientOptions.executor.isInstalled())throw new Error(`${this.name} requires a local CLI environment, but the CLI was not found or is not configured correctly.`);let c=[],u=[],d=e[e.length-1],l="";if(d&&typeof d.content=="string")l=d.content;else if(d&&Array.isArray(d.content)){let C=[];for(let b of d.content)if(b.type==="text")C.push(b.text);else if(b.type==="image"){let _=this.serializeImageBlockToPromptToken(b);_&&C.push(_)}l=C.join(`
19
+ `)}let p={...this.acpClientOptions,onSessionUpdate:i(C=>{let b=C.update,_=C.sessionId;if(!("sessionUpdate"in b))return;let m=_?this._activeStreams.get(_):void 0;if(m)switch(b.sessionUpdate){case"agent_message_chunk":if(b.content?.type==="text"){let g=b.content.text;m.output.text+=g,m.streamOptions?.onTextDelta?.(g)}break;case"tool_call":{let g="{}",M=b.arguments??b.parameters;M&&(g=typeof M=="string"?M:JSON.stringify(M)),m.streamOptions?.onToolInputDelta?.(b.title,g);let S=`
20
+ > [Tool Use] ${b.title}: ${g}
21
+ `;m.output.text+=S,m.streamOptions?.onTextDelta?.(S);break}case"tool_call_update":if(b.status){let g=`> [Tool Result] ${b.status}
22
+
23
+ `;m.output.text+=g,m.streamOptions?.onTextDelta?.(g)}break}},"onSessionUpdate")},y=s?.sessionId??"default";this._client||(this._client=new Oe(p),await this._client.connect());let k=this._sessionMap.get(y);k||(k=await this._client.createNewSession(),this._sessionMap.set(y,k));let h={text:""};this._activeStreams.set(k,{streamOptions:s,output:h});let f;try{f=await this._client.prompt(l,k,a,{model:s?.modelOverride})}catch(C){if(!(C instanceof Error&&C.name==="AbortError"))throw C}finally{this._activeStreams.delete(k)}return h.text&&c.push({type:"text",text:h.text}),{textBlocks:c,toolBlocks:u,thinkingBlocks:[],usage:ho(f?.usage)}}disconnect(){this._client&&(this._client.disconnect(),this._client=null),this._activeStreams.clear(),this._sessionMap.clear()}};import{spawn as yo}from"node:child_process";import kt from"node:process";var se=class{static{i(this,"CLIExecutor")}config;_installedCache=null;constructor(e){this.config=e}async isInstalled(){return this._installedCache!==null?this._installedCache:(this._installedCache=await this.checkInstalled(),this._installedCache)}async*execute(e){let n=this.buildArgs(e),o={...kt.env,...this.config.env},s=kt.platform==="win32"&&!this.config.command.endsWith(".cmd")?`${this.config.command}.cmd`:this.config.command,a=yo(s,n,{cwd:this.config.cwd??kt.cwd(),env:o,stdio:["ignore","pipe","pipe"]}),c="";a.stderr?.on("data",l=>{c+=l.toString()});let u=!1,d=i(()=>{u||a.kill("SIGTERM")},"abortHandler");e.signal?.addEventListener("abort",d),a.on("exit",()=>{u=!0});try{yield*this.parseOutputStream(a.stdout,e.signal),c.trim()&&console.error(`[CLIExecutor] stderr: ${c.trim()}`)}finally{e.signal?.removeEventListener("abort",d),u||a.kill()}}async*parseOutputStream(e,n){let o="";for await(let r of e){if(n?.aborted)break;o+=r.toString();let s=o.split(`
24
+ `);o=s.pop()??"";for(let a of s){if(!a.trim())continue;let c=this.parseLine(a.trim());c&&(yield c)}}if(o.trim()&&!n?.aborted){let r=this.parseLine(o.trim());r&&(yield r)}}};import{spawn as vo}from"node:child_process";import ko from"node:process";async function De(t){try{let e=ko.platform==="win32",n=vo(e?`${t}.cmd`:t,["--version"]);return await new Promise(o=>{n.on("close",r=>o(r===0)),n.on("error",()=>o(!1))})}catch{return!1}}i(De,"checkCliCommandInstalled");var Ie=class extends se{static{i(this,"GeminiCLIExecutor")}model;constructor(e){super({command:"gemini",baseArgs:["--output-format","stream-json","--approval-mode","yolo"],timeout:3e5,...e}),this.model=e?.model??"gemini-2.5-pro"}async checkInstalled(){return De("gemini")}buildArgs(e){let n=["-m",e.model??this.model];return e.sessionId?(n.push("-r",e.sessionId),n.push(e.prompt)):n.push("-p",e.prompt),[...n,...this.config.baseArgs]}parseLine(e){if(!e.startsWith("{"))return null;try{let n=JSON.parse(e);return this.convertEvent(n)}catch{return null}}convertEvent(e){let n=e.timestamp?Date.parse(e.timestamp):Date.now();switch(e.type){case"init":return{type:"session_start",timestamp:n,sessionId:e.session_id??"",model:e.model??this.model,raw:e};case"message":return{type:"message",timestamp:n,role:e.role,content:e.content??"",delta:e.delta,raw:e};case"tool_use":return{type:"tool_use",timestamp:n,toolId:e.tool_id??"",toolName:e.tool_name??"",parameters:e.parameters??{},raw:e};case"tool_result":return{type:"tool_result",timestamp:n,toolId:e.tool_id??"",status:e.status==="success"?"success":"error",output:e.output??"",raw:e};case"error":return{type:"error",timestamp:n,errorType:"error",message:e.message??"Unknown error",raw:e};case"result":return{type:"complete",timestamp:n,status:e.status==="success"?"success":"failed",usage:e.stats?{inputTokens:e.stats.input_tokens??0,outputTokens:e.stats.output_tokens??0,totalTokens:e.stats.total_tokens??0}:void 0,raw:e};default:return null}}};import{TransformStream as rn}from"node:stream/web";import{randomUUID as bt}from"node:crypto";function Be(t){let e=new rn,n=new rn,o=e.readable.getReader(),r=n.writable.getWriter(),s=new TextDecoder,a=new TextEncoder,c="",u=bt(),d=new AbortController,l=new Map;(async()=>{try{for(;;){let{done:f,value:C}=await o.read();if(f)break;c+=s.decode(C,{stream:!0});let b=c.split(`
25
+ `);c=b.pop()??"";for(let _ of b)if(_.trim())try{let m=JSON.parse(_);y(m).catch(g=>{console.error("[PseudoAcpServer] Failed to handle request:",g)})}catch{console.error("[PseudoAcpServer] Failed to parse message:",_)}}}catch(f){console.error("[PseudoAcpServer] Stream read error:",f)}})();let p=i(async f=>{let C=JSON.stringify(f)+`
26
+ `;await r.write(a.encode(C))},"sendMsg");async function y(f){if(f.method==="initialize")await p({jsonrpc:"2.0",id:f.id,result:{protocolVersion:f.params.protocolVersion,serverInfo:{name:"pseudo-acp-server",version:"1.0.0"},serverCapabilities:{}}});else if(f.method==="session/new"||f.method==="sessions/new")u=f.params?.sessionId??bt(),await p({jsonrpc:"2.0",id:f.id,result:{sessionId:u}});else if(f.method==="session/prompt"||f.method==="chat/prompt"){let C=new AbortController,b=f.params.sessionId;l.set(b,C);let _=i(()=>C.abort(),"onGlobalAbort");d.signal.addEventListener("abort",_);let g=await k(b,f.params.prompt,typeof f.params.model=="string"?f.params.model:void 0,C.signal).finally(()=>{l.delete(b),d.signal.removeEventListener("abort",_)});await p({jsonrpc:"2.0",id:f.id,result:g.usage?{stopReason:g.stopReason,usage:g.usage}:{stopReason:g.stopReason}})}else if(f.method==="session/cancel"||f.method==="chat/cancel"){let C=l.get(f.params.sessionId);C&&C.abort(),f.id!==void 0&&await p({jsonrpc:"2.0",id:f.id,result:{}})}else f.id!==void 0&&await p({jsonrpc:"2.0",id:f.id,result:{}})}i(y,"handleRequest");let k=i(async(f,C,b,_)=>{let m=C.find(g=>g.type==="text")?.text??"";try{let g=t.execute({prompt:m,model:b,sessionId:f==="default"?void 0:f,signal:_});for await(let M of g){let S=h(M);if(S&&await p({jsonrpc:"2.0",method:"session/update",params:{sessionId:f,update:S}}),M.type==="complete")return{stopReason:_.aborted?"cancelled":"end_turn",usage:M.usage}}return{stopReason:_.aborted?"cancelled":"end_turn"}}catch(g){return _.aborted||g instanceof Error&&g.name==="AbortError"?{stopReason:"cancelled"}:(console.error("[PseudoAcpServer] Error executing prompt:",g),await p({jsonrpc:"2.0",method:"session/update",params:{sessionId:f,update:{sessionUpdate:"agent_message_chunk",content:{type:"text",text:`
27
+ [Fatal Error: ${g}]
28
+ `}}}}),{stopReason:"end_turn"})}},"executePrompt"),h=i(f=>{switch(f.type){case"message":if(f.role==="assistant"&&f.content)return{sessionUpdate:"agent_message_chunk",content:{type:"text",text:f.content}};break;case"tool_use":return{sessionUpdate:"tool_call",title:f.toolName,arguments:f.parameters,status:"running",toolCallId:f.toolId||bt()};case"tool_result":return{sessionUpdate:"tool_call_update",toolCallId:f.toolId,status:f.status};case"error":return{sessionUpdate:"agent_message_chunk",content:{type:"text",text:`
29
+ [Error: ${f.message}]
30
+ `}};case"complete":break}return null},"mapToAcpNotification");return{inputStream:n.readable,outputStream:e.writable,abort:i(()=>{d.abort(),e.readable.cancel().catch(()=>{}),n.writable.abort().catch(()=>{})},"abort"),executor:t}}i(Be,"createPseudoAcpServer");import sn from"node:fs";import an from"node:os";import cn from"node:path";var bo="gpt-5.4",Co="auto-gemini-3",_o=["gpt-5.4","gpt-5.3-codex","gpt-5.3-codex-spark"],xo=["auto-gemini-3","gemini-3.1-pro-preview","gemini-3-flash-preview","gemini-2.5-pro","gemini-2.5-flash"];function ln(t){let e=new Set,n=[];for(let o of t){let r=o.trim();if(!r)continue;let s=r.toLowerCase();e.has(s)||(e.add(s),n.push(r))}return n}i(ln,"dedupePreserveOrder");function To(){let t=cn.join(an.homedir(),".codex","config.toml");try{return sn.readFileSync(t,"utf8").match(/^\s*model\s*=\s*"([^"]+)"/m)?.[1]?.trim()||null}catch{return null}}i(To,"readCodexConfiguredModel");function Mo(){let t=cn.join(an.homedir(),".gemini","settings.json");try{let e=JSON.parse(sn.readFileSync(t,"utf8")),n=typeof e.model=="string"?e.model.trim():"";return n||(typeof e.general?.model=="string"?e.general.model.trim():"")||null}catch{return null}}i(Mo,"readGeminiConfiguredModel");function ae(){return To()||bo}i(ae,"getCodexCliDefaultModel");function ce(){return Mo()||Co}i(ce,"getGeminiCliDefaultModel");function ve(){return ln([ae(),..._o])}i(ve,"getCodexCliKnownModels");function ke(){return ln([ce(),...xo])}i(ke,"getGeminiCliKnownModels");var Ct=ce(),Eo=ke(),Po={transport:"cli-bridge",conversationSemantics:"last-user-message",mcpSupport:"none",contextFidelity:"lossy",toolCallingFidelity:"limited",sessionSupport:"stateless",longRunningSupport:"limited",multimodalSupport:"image-input",evidenceSupport:"limited"},Le=class extends re{static{i(this,"KodaXGeminiCliProvider")}name="gemini-cli";supportsThinking=!1;config={apiKeyEnv:"GEMINI_CLI_API_KEY",model:Ct,models:Eo.filter(e=>e!==Ct).map(e=>({id:e,displayName:e})),supportsThinking:!1,reasoningCapability:"prompt-only",contextWindow:1048576};acpClientOptions;constructor(){super();let e=new Ie({model:Ct});this.acpClientOptions=Be(e)}getCapabilityProfile(){return U(Po)}serializeImageBlockToPromptToken(e){return/\s/.test(e.path)?(console.warn(`[gemini-cli] Image path contains whitespace and cannot be safely passed via @<path> syntax \u2014 dropping image block. Path: ${JSON.stringify(e.path)}`),null):`@${e.path}`}};var Ne=class extends se{static{i(this,"CodexCLIExecutor")}model;constructor(e){super({command:"codex",baseArgs:["exec","--json","--full-auto"],timeout:3e5,...e}),this.model=e?.model??"gpt-5.4"}async checkInstalled(){return De("codex")}buildArgs(e){this.model=e.model??this.model;let n=e.model?["-m",e.model]:[];return e.sessionId?["exec","resume",e.sessionId,...n,e.prompt,...this.config.baseArgs.filter(o=>o!=="exec")]:[...this.config.baseArgs,...n,e.prompt]}parseLine(e){if(!e.startsWith("{"))return null;try{let n=JSON.parse(e);return this.convertEvent(n)}catch{return null}}convertEvent(e){let n=Date.now();switch(e.type){case"thread.started":return{type:"session_start",timestamp:n,sessionId:e.thread_id??"",model:this.model,raw:e};case"item.completed":return e.item?.type==="agent_message"?{type:"message",timestamp:n,role:"assistant",content:e.item.text??"",raw:e}:e.item?.type==="command_execution"?{type:"tool_use",timestamp:n,toolId:e.item.id,toolName:"Bash",parameters:{command:e.item.command},raw:e}:null;case"turn.completed":return{type:"complete",timestamp:n,status:"success",usage:e.usage?{inputTokens:e.usage.input_tokens,outputTokens:e.usage.output_tokens,totalTokens:e.usage.input_tokens+e.usage.output_tokens}:void 0,raw:e};case"error":case"turn.failed":return{type:"error",timestamp:n,errorType:e.type,message:e.message??"Unknown error",raw:e};default:return null}}};var _t=ae(),So=ve(),Ke=class extends re{static{i(this,"KodaXCodexCliProvider")}name="codex-cli";supportsThinking=!1;config={apiKeyEnv:"CODEX_CLI_API_KEY",model:_t,models:So.filter(e=>e!==_t).map(e=>({id:e,displayName:e})),supportsThinking:!1,reasoningCapability:"prompt-only",contextWindow:128e3};acpClientOptions;constructor(){super();let e=new Ne({model:_t});this.acpClientOptions=Be(e)}};import{existsSync as Io,readFileSync as Bo}from"node:fs";import le from"node:path";import{fileURLToPath as Lo}from"node:url";var un=["none","prompt-only","native-effort","native-budget","native-toggle","unknown"],dn=["native","image-input-native","cli-bridge","image-input-cli-bridge"],pn=["count-tokens","models-list","minimal-message","unsupported"];function be(t){return typeof t=="object"&&t!==null&&!Array.isArray(t)}i(be,"isPlainObject");function $e(t,e){if(typeof t!="string"||t.length===0)throw new Error(`provider-capabilities.json: ${e} must be a non-empty string`);return t}i($e,"requireString");function fn(t,e){if(t!==void 0)return $e(t,e)}i(fn,"optionalString");function q(t,e){if(t!==void 0){if(typeof t!="number"||!Number.isFinite(t)||t<0)throw new Error(`provider-capabilities.json: ${e} must be a non-negative finite number`);return t}}i(q,"optionalNumber");function Fe(t,e){if(t!==void 0){if(typeof t!="boolean")throw new Error(`provider-capabilities.json: ${e} must be a boolean`);return t}}i(Fe,"optionalBoolean");function xt(t,e){if(typeof t!="string"||!un.includes(t))throw new Error(`provider-capabilities.json: ${e} must be one of ${un.join(", ")}, got ${JSON.stringify(t)}`);return t}i(xt,"requireReasoningCapability");function Ro(t,e){if(typeof t!="string"||!dn.includes(t))throw new Error(`provider-capabilities.json: ${e} must be one of ${dn.join(", ")}, got ${JSON.stringify(t)}`);return t}i(Ro,"requireProfileName");function wo(t,e){if(typeof t!="string"||!pn.includes(t))throw new Error(`provider-capabilities.json: ${e} must be one of ${pn.join(", ")}, got ${JSON.stringify(t)}`);return t}i(wo,"requireVerifyStrategy");function Ao(t,e){if(!be(t))throw new Error(`provider-capabilities.json: ${e} must be an object`);let n=$e(t.id,`${e}.id`),o=fn(t.displayName,`${e}.displayName`),r=q(t.contextWindow,`${e}.contextWindow`),s=q(t.maxOutputTokens,`${e}.maxOutputTokens`),a=q(t.thinkingBudgetCap,`${e}.thinkingBudgetCap`),c=Fe(t.replayReasoningContent,`${e}.replayReasoningContent`),u=Fe(t.strictThinkingSignature,`${e}.strictThinkingSignature`),d=q(t.streamMaxDurationMs,`${e}.streamMaxDurationMs`),l=t.reasoningCapability===void 0?void 0:xt(t.reasoningCapability,`${e}.reasoningCapability`),p={id:n};return o!==void 0&&(p.displayName=o),r!==void 0&&(p.contextWindow=r),s!==void 0&&(p.maxOutputTokens=s),a!==void 0&&(p.thinkingBudgetCap=a),l!==void 0&&(p.reasoningCapability=l),c!==void 0&&(p.replayReasoningContent=c),u!==void 0&&(p.strictThinkingSignature=u),d!==void 0&&(p.streamMaxDurationMs=d),p}i(Ao,"validateModelDescriptor");function Oo(t,e){if(t===void 0)return;if(!be(t))throw new Error(`provider-capabilities.json: ${e} must be an object`);let n={};for(let[o,r]of Object.entries(t))n[o]=xt(r,`${e}.${o}`);return n}i(Oo,"validateModelReasoningCapabilities");function Do(t,e){if(!be(t))throw new Error(`provider-capabilities.json: providers.${e} must be an object`);let n=Fe(t.cliBridge,`providers.${e}.cliBridge`),o=$e(t.apiKeyEnv,`providers.${e}.apiKeyEnv`),r=xt(t.reasoningCapability,`providers.${e}.reasoningCapability`),s=Ro(t.capabilityProfile,`providers.${e}.capabilityProfile`),a=wo(t.verifyStrategy,`providers.${e}.verifyStrategy`);if(n&&a!=="unsupported")throw new Error(`provider-capabilities.json: providers.${e} is a cliBridge entry but verifyStrategy="${a}" \u2014 must be "unsupported" (CLI binary owns credentials)`);let c=q(t.contextWindow,`providers.${e}.contextWindow`),u=q(t.maxOutputTokens,`providers.${e}.maxOutputTokens`),d=q(t.thinkingBudgetCap,`providers.${e}.thinkingBudgetCap`),l=Fe(t.supportsThinking,`providers.${e}.supportsThinking`),p=Oo(t.modelReasoningCapabilities,`providers.${e}.modelReasoningCapabilities`),y=fn(t.model,`providers.${e}.model`);if(n&&(y!==void 0||t.models!==void 0))throw new Error(`provider-capabilities.json: providers.${e} is a cliBridge entry but defines model/models \u2014 must be omitted (filled at load time)`);if(!n&&y===void 0)throw new Error(`provider-capabilities.json: providers.${e}.model is required (only cliBridge entries may omit)`);let k;if(t.models!==void 0){if(!Array.isArray(t.models))throw new Error(`provider-capabilities.json: providers.${e}.models must be an array`);k=t.models.map((f,C)=>Ao(f,`providers.${e}.models[${C}]`))}let h={apiKeyEnv:o,reasoningCapability:r,capabilityProfile:s,verifyStrategy:a};return y!==void 0&&(h.model=y),k!==void 0&&(h.models=k),c!==void 0&&(h.contextWindow=c),u!==void 0&&(h.maxOutputTokens=u),d!==void 0&&(h.thinkingBudgetCap=d),l!==void 0&&(h.supportsThinking=l),p!==void 0&&(h.modelReasoningCapabilities=p),n&&(h.cliBridge=!0),h}i(Do,"validateProviderEntry");function mn(t){if(!be(t))throw new Error("provider-capabilities.json: root must be an object");if(t.version!==1)throw new Error(`provider-capabilities.json: version must be 1, got ${JSON.stringify(t.version)}`);let e=$e(t.updatedAt,"updatedAt");if(!be(t.providers))throw new Error("provider-capabilities.json: providers must be an object");let n={};for(let[o,r]of Object.entries(t.providers))n[o]=Do(r,o);return{version:1,updatedAt:e,providers:n}}i(mn,"validateProviderCapabilitiesJson");var No=Object.freeze({native:Y,"image-input-native":en,"cli-bridge":he,"image-input-cli-bridge":tn}),Ue=null;function Ko(){if(process.env.KODAX_BUNDLED==="true")return le.join(le.dirname(process.execPath),"provider-capabilities.json");let t=le.dirname(Lo(import.meta.url)),e=[le.join(t,"provider-capabilities.json"),le.join(le.dirname(t),"provider-capabilities.json")];for(let n of e)if(Io(n))return n;return e[0]}i(Ko,"resolveJsonPath");function Fo(t){if(t.models){for(let e of t.models)Object.freeze(e);Object.freeze(t.models)}return t.modelReasoningCapabilities&&Object.freeze(t.modelReasoningCapabilities),Object.freeze(t)}i(Fo,"deepFreezeSnapshot");function $o(t){if(t==="gemini-cli"){let e=ce();return{model:e,models:ke().filter(n=>n!==e).map(n=>({id:n}))}}if(t==="codex-cli"){let e=ae();return{model:e,models:ve().filter(n=>n!==e).map(n=>({id:n}))}}throw new Error(`provider-capabilities loader: unknown cliBridge provider '${t}'`)}i($o,"resolveCliBridgeModels");function Uo(t,e){let n=No[e.capabilityProfile];if(!n)throw new Error(`provider-capabilities loader: unknown capabilityProfile '${e.capabilityProfile}' for provider '${t}'`);if(e.cliBridge){let r=$o(t),s={model:r.model,models:r.models,apiKeyEnv:e.apiKeyEnv,reasoningCapability:e.reasoningCapability,capabilityProfile:n,verifyStrategy:e.verifyStrategy};return e.supportsThinking!==void 0&&(s.supportsThinking=e.supportsThinking),s}let o={model:e.model,apiKeyEnv:e.apiKeyEnv,reasoningCapability:e.reasoningCapability,capabilityProfile:n,verifyStrategy:e.verifyStrategy};return e.models!==void 0&&(o.models=e.models),e.modelReasoningCapabilities!==void 0&&(o.modelReasoningCapabilities=e.modelReasoningCapabilities),e.contextWindow!==void 0&&(o.contextWindow=e.contextWindow),e.maxOutputTokens!==void 0&&(o.maxOutputTokens=e.maxOutputTokens),e.thinkingBudgetCap!==void 0&&(o.thinkingBudgetCap=e.thinkingBudgetCap),e.supportsThinking!==void 0&&(o.supportsThinking=e.supportsThinking),o}i(Uo,"buildSnapshot");function gn(){if(Ue)return Ue;let t=Ko(),e;try{let r=Bo(t,"utf8");e=JSON.parse(r)}catch(r){let s=r instanceof Error?r.message:String(r);throw new Error(`provider-capabilities loader: failed to read ${t}: ${s}`)}let n=mn(e),o={};for(let[r,s]of Object.entries(n.providers))o[r]=Fo(Uo(r,s));return Ue=Object.freeze(o),Ue}i(gn,"getProviderSnapshots");import jo from"@anthropic-ai/sdk";var w=gn();function $(t,e={}){let n=w[t];return{apiKeyEnv:n.apiKeyEnv,model:n.model,reasoningCapability:n.reasoningCapability,models:n.models,contextWindow:n.contextWindow,maxOutputTokens:n.maxOutputTokens,thinkingBudgetCap:n.thinkingBudgetCap,supportsThinking:n.supportsThinking??!1,verifyStrategy:n.verifyStrategy,...e}}i($,"buildProviderConfig");var Tt=class extends I{static{i(this,"AnthropicProvider")}name="anthropic";config=$("anthropic",{strictThinkingSignature:!0});constructor(){super(),this.client=new jo({apiKey:this.getApiKey()})}},Mt=class extends I{static{i(this,"ZhipuCodingProvider")}name="zhipu-coding";config=$("zhipu-coding",{baseUrl:"https://open.bigmodel.cn/api/anthropic",streamMaxDurationMs:3e5});constructor(){super(),this.initClient()}},Et=class extends I{static{i(this,"KimiCodeProvider")}name="kimi-code";config=$("kimi-code",{baseUrl:"https://api.kimi.com/coding/"});constructor(){super(),this.initClient()}},Pt=class extends I{static{i(this,"MiniMaxCodingProvider")}name="minimax-coding";config=$("minimax-coding",{baseUrl:"https://api.minimaxi.com/anthropic"});constructor(){super(),this.initClient()}},St=class extends I{static{i(this,"MimoCodingProvider")}name="mimo-coding";config=$("mimo-coding",{baseUrl:"https://token-plan-cn.xiaomimimo.com/anthropic"});constructor(){super(),this.initClient()}},Rt=class extends I{static{i(this,"MimoProvider")}name="mimo";config=$("mimo",{baseUrl:"https://api.xiaomimimo.com/anthropic"});constructor(){super(),this.initClient()}},wt=class extends I{static{i(this,"ArkCodingProvider")}name="ark-coding";config=$("ark-coding",{baseUrl:"https://ark.cn-beijing.volces.com/api/coding"});constructor(){super(),this.initClient()}},At=class extends K{static{i(this,"OpenAIProvider")}name="openai";config=$("openai");constructor(){super(),this.initClient()}},Ot=class extends K{static{i(this,"DeepSeekProvider")}name="deepseek";config=$("deepseek",{baseUrl:"https://api.deepseek.com",replayReasoningContent:!0});constructor(){super(),this.initClient()}},Dt=class extends K{static{i(this,"KimiProvider")}name="kimi";config=$("kimi",{baseUrl:"https://api.moonshot.cn/v1",replayReasoningContent:!0});constructor(){super(),this.initClient()}},It=class extends K{static{i(this,"QwenProvider")}name="qwen";config=$("qwen",{baseUrl:"https://dashscope.aliyuncs.com/compatible-mode/v1",replayReasoningContent:!0});constructor(){super(),this.initClient()}},Bt=class extends K{static{i(this,"ZhipuProvider")}name="zhipu";config=$("zhipu",{baseUrl:"https://open.bigmodel.cn/api/paas/v4",replayReasoningContent:!0});constructor(){super(),this.initClient()}},F={anthropic:i(()=>new Tt,"anthropic"),openai:i(()=>new At,"openai"),deepseek:i(()=>new Ot,"deepseek"),kimi:i(()=>new Dt,"kimi"),"kimi-code":i(()=>new Et,"kimi-code"),qwen:i(()=>new It,"qwen"),zhipu:i(()=>new Bt,"zhipu"),"zhipu-coding":i(()=>new Mt,"zhipu-coding"),"minimax-coding":i(()=>new Pt,"minimax-coding"),"mimo-coding":i(()=>new St,"mimo-coding"),mimo:i(()=>new Rt,"mimo"),"ark-coding":i(()=>new wt,"ark-coding"),"gemini-cli":i(()=>new Le,"gemini-cli"),"codex-cli":i(()=>new Ke,"codex-cli")},Lt=process.env.KODAX_PROVIDER??"zhipu-coding",hn=new Map;function Xo(t){if(A(t))return w[t].apiKeyEnv}i(Xo,"resolveApiKeyEnvForProvider");function yn(t){let e=t??Lt,n=F[e];if(!n)throw new R(`Unknown provider: ${e}. Available: ${Object.keys(F).join(", ")}`,e);let o=Xo(e),r=o?process.env[o]:void 0,s=hn.get(e);if(s&&s.apiKey===r)return s.instance;let a=n();return hn.set(e,{apiKey:r,instance:a}),a}i(yn,"getProvider");function vn(t){return A(t)?!!process.env[w[t].apiKeyEnv]:!1}i(vn,"isProviderConfigured");function kn(t){return A(t)?w[t].model:null}i(kn,"getProviderModel");function bn(t,e){if(!A(t))return"unknown";let n=w[t],o=e??n.model;return n.modelReasoningCapabilities?.[o]??n.reasoningCapability}i(bn,"getProviderConfiguredReasoningCapability");function Cn(t){return A(t)?U(w[t].capabilityProfile):null}i(Cn,"getProviderConfiguredCapabilityProfile");function _n(){let t=[];for(let e of Object.keys(F)){let n=w[e];t.push({name:e,model:n.model,models:n.models?[n.model,...n.models.map(o=>o.id)]:[n.model],configured:!!process.env[n.apiKeyEnv],reasoningCapability:n.reasoningCapability,capabilityProfile:U(n.capabilityProfile)})}return t}i(_n,"getProviderList");function xn(t){let e=w[t];return e?e.models?[e.model,...e.models.map(n=>n.id)]:[e.model]:[]}i(xn,"getProviderModels");function A(t){return t in F}i(A,"isProviderName");function Nt(t){return{id:t.model}}i(Nt,"makeDefaultDescriptor");function je(t,e,n){let o=n.id===e.model;return{provider:t,model:n.id,displayName:n.displayName??n.id,supportsThinking:e.supportsThinking??!1,reasoningCapability:n.reasoningCapability??e.reasoningCapability,contextWindow:n.contextWindow??e.contextWindow,maxOutputTokens:n.maxOutputTokens??e.maxOutputTokens,thinkingBudgetCap:n.thinkingBudgetCap??e.thinkingBudgetCap,isDefault:o}}i(je,"effectiveCapabilities");function Xe(t){let e=w[t];return e?[Nt(e),...e.models??[]]:[]}i(Xe,"getProviderModelDescriptors");function We(t,e){let n=w[t];if(!n)return;if(e===n.model)return je(t,n,Nt(n));let o=n.models?.find(r=>r.id===e);if(o)return je(t,n,o)}i(We,"getModelCapabilities");function ze(){let t=[];for(let e of Object.keys(F)){let n=w[e];t.push(je(e,n,Nt(n)));for(let o of n.models??[])t.push(je(e,n,o))}return t}i(ze,"listBuiltinModelCapabilities");var Wo=new Set(["compat","sdk"]),zo=new Set(["count-tokens","models-list","minimal-message","unsupported"]);function Vo(t){return t==="anthropic"?"count-tokens":"models-list"}i(Vo,"defaultVerifyStrategyForProtocol");function Ce(t){if(!t.name||!t.baseUrl||!t.apiKeyEnv||!t.model)throw new Error(`Custom provider requires name, baseUrl, apiKeyEnv, and model. Got: ${JSON.stringify({name:t.name,baseUrl:t.baseUrl,apiKeyEnv:t.apiKeyEnv,model:t.model})}`);if(t.protocol!=="anthropic"&&t.protocol!=="openai")throw new Error(`Unknown protocol "${t.protocol}" for custom provider "${t.name}". Must be "anthropic" or "openai".`);if(t.userAgentMode!==void 0&&!Wo.has(t.userAgentMode))throw new Error(`Unknown userAgentMode "${t.userAgentMode}" for custom provider "${t.name}". Must be "compat" or "sdk".`);if(t.verifyStrategy!==void 0){if(!zo.has(t.verifyStrategy))throw new Error(`Unknown verifyStrategy "${t.verifyStrategy}" for custom provider "${t.name}". Must be one of "count-tokens" | "models-list" | "minimal-message" | "unsupported".`);if(t.protocol==="openai"&&t.verifyStrategy==="count-tokens")throw new Error(`Custom provider "${t.name}": verifyStrategy="count-tokens" requires Anthropic protocol; got protocol="openai". Use "models-list" or "minimal-message" for OpenAI-compat.`)}}i(Ce,"validateCustomProviderConfig");function Go(t){let e=t.models?.length?t.models.map(n=>typeof n=="string"?{id:n}:n):void 0;return{apiKeyEnv:t.apiKeyEnv,model:t.model,baseUrl:t.baseUrl,models:e,userAgentMode:t.userAgentMode,supportsThinking:t.supportsThinking??!1,reasoningCapability:t.reasoningCapability??"none",capabilityProfile:t.capabilityProfile,contextWindow:t.contextWindow,maxOutputTokens:t.maxOutputTokens,thinkingBudgetCap:t.thinkingBudgetCap,replayReasoningContent:t.replayReasoningContent??!1,strictThinkingSignature:t.strictThinkingSignature??!1,streamMaxDurationMs:t.streamMaxDurationMs,verifyStrategy:t.verifyStrategy??Vo(t.protocol)}}i(Go,"buildProviderConfig");function Ve(t){Ce(t);let e=Go(t);return t.protocol==="anthropic"?new Kt(t.name,e):new Ft(t.name,e)}i(Ve,"createCustomProvider");var Kt=class extends I{static{i(this,"DynamicAnthropicProvider")}name;config;constructor(e,n){super(),this.name=e,this.config=n,this.initClient()}},Ft=class extends K{static{i(this,"DynamicOpenAIProvider")}name;config;constructor(e,n){super(),this.name=e,this.config=n,this.initClient()}};var V=new Map,$t=new Map;function Tn(t){let e=new Set,n=new Map,o=new Map;for(let r of t){if(Ce(r),e.has(r.name))throw new Error(`Duplicate custom provider name: "${r.name}". Each custom provider must have a unique name.`);r.name in F&&console.warn(`[kodax] Custom provider "${r.name}" shadows a built-in provider. The built-in provider will be used. Choose a different name to use your custom provider.`),e.add(r.name),n.set(r.name,r),o.set(r.name,()=>Ve(r))}V.clear(),$t.clear();for(let[r,s]of n)V.set(r,s);for(let[r,s]of o)$t.set(r,s)}i(Tn,"registerCustomProviders");function Ge(t){let e=$t.get(t);return e?e():void 0}i(Ge,"getCustomProvider");function de(t){return V.has(t)}i(de,"isCustomProviderName");function He(){return[...V.keys()]}i(He,"getCustomProviderNames");function Mn(){let t=[];for(let[e,n]of V){let o=!!process.env[n.apiKeyEnv],r=(n.models??[]).map(a=>typeof a=="string"?a:a.id),s=n.model&&r.length?[...new Set([n.model,...r])]:[n.model];t.push({name:e,model:n.model,models:s,configured:o,reasoningCapability:n.reasoningCapability??"none",capabilityProfile:U(n.capabilityProfile??Y),custom:!0})}return t}i(Mn,"getCustomProviderList");function En(t){let e=V.get(t);if(!e)return;let n=(e.models??[]).map(o=>typeof o=="string"?o:o.id);return e.model&&n.length?[...new Set([e.model,...n])]:[e.model]}i(En,"getCustomProviderModels");function Pn(t){return typeof t=="string"?{id:t}:t}i(Pn,"customDescriptorToFull");function Je(t){let e=V.get(t);if(!e)return;let n={id:e.model},o=(e.models??[]).map(Pn).filter(r=>r.id!==e.model);return[n,...o]}i(Je,"getCustomProviderModelDescriptors");function ue(t,e){let n=V.get(t);if(!n)return;let o=e===n.model,r=o?{id:n.model}:(n.models??[]).map(Pn).find(s=>s.id===e);if(r)return{provider:t,model:r.id,displayName:r.displayName??r.id,supportsThinking:n.supportsThinking??!1,reasoningCapability:r.reasoningCapability??n.reasoningCapability??"none",contextWindow:r.contextWindow??n.contextWindow,maxOutputTokens:r.maxOutputTokens??n.maxOutputTokens,thinkingBudgetCap:r.thinkingBudgetCap??n.thinkingBudgetCap,isDefault:o}}i(ue,"getCustomModelCapabilities");function Sn(t){let e=V.get(t);if(!e)return;let n=e.verifyStrategy??(e.protocol==="anthropic"?"count-tokens":"models-list");return{apiKeyEnv:e.apiKeyEnv,verifyStrategy:n}}i(Sn,"getCustomProviderVerifyMetadata");function Ye(){let t=[];for(let[e,n]of V){let o=ue(e,n.model);o&&t.push(o);for(let r of n.models??[]){let s=typeof r=="string"?r:r.id;if(s===n.model)continue;let a=ue(e,s);a&&t.push(a)}}return t}i(Ye,"listCustomProviderModelCapabilities");var H=new Map,Ho=0;function Ut(t){let e=H.get(t);if(!(!e||e.length===0))return e[e.length-1]}i(Ut,"getActiveRuntimeProviderRegistration");function Jo(t){for(let[e,n]of H){let o=n.filter(r=>r.id!==t);if(o.length!==n.length){o.length===0?H.delete(e):H.set(e,o);return}}}i(Jo,"removeRuntimeProviderRegistration");function Rn(t,e){let n=t.trim();if(!n)throw new Error("Model provider name cannot be empty.");if(A(n))throw new Error(`Runtime model provider "${n}" conflicts with a built-in provider.`);if(de(n))throw new Error(`Runtime model provider "${n}" conflicts with an existing config-defined custom provider.`);let o={id:`runtime-provider:${++Ho}`,name:n,factory:e},r=H.get(n)??[];return H.set(n,[...r,o]),()=>{Jo(o.id)}}i(Rn,"registerModelProvider");function qe(t){let e=Ut(t);return e?e.factory():void 0}i(qe,"getRuntimeModelProvider");function _e(t){return Ut(t)!==void 0}i(_e,"isRuntimeModelProviderName");function Qe(){return Array.from(H.keys()).filter(t=>Ut(t)!==void 0)}i(Qe,"getRuntimeModelProviderNames");function wn(){H.clear()}i(wn,"clearRuntimeModelProviders");function jt(t){if(A(t))return F[t]();let e=qe(t);if(e)return e;let n=Ge(t);if(n)return n;let o=et();throw new Error(`Unknown provider: ${t}. Available: ${o.join(", ")}`)}i(jt,"resolveProvider");function Ze(t){return A(t)||_e(t)||de(t)}i(Ze,"isKnownProvider");function et(){let t=Object.keys(F),e=Qe(),n=He();return[...new Set([...t,...e,...n])]}i(et,"getAvailableProviderNames");function Xt(t){return A(t)?Xe(t):Je(t)??[]}i(Xt,"resolveProviderModelDescriptors");function An(t,e){return A(t)?We(t,e):ue(t,e)}i(An,"resolveModelCapabilities");function On(){return[...ze(),...Ye()]}i(On,"listAllModelCapabilities");function Yo(t){if(A(t)){let e=w[t];return{apiKeyEnv:e.apiKeyEnv,verifyStrategy:e.verifyStrategy}}if(!_e(t))return Sn(t)}i(Yo,"getProviderVerifyMetadata");async function Dn(t,e){let n=Yo(t);if(!n&&!Ze(t))return{ok:!1,error:"unsupported",strategy:"unsupported",durationMs:0,approxTokensSpent:0,message:`Unknown provider: "${t}". Available: ${et().join(", ")}`};if(n?.verifyStrategy==="unsupported")return{ok:!1,error:"unsupported",strategy:"unsupported",durationMs:0,approxTokensSpent:0,message:`Provider "${t}" does not support credential verification (cli-bridge or strategy marked unsupported)`};if(n&&!process.env[n.apiKeyEnv])return{ok:!1,error:"unconfigured",strategy:n.verifyStrategy,durationMs:0,approxTokensSpent:0,message:`Environment variable "${n.apiKeyEnv}" is not set for provider "${t}"`};let o;try{o=jt(t)}catch(r){return{ok:!1,error:"unconfigured",strategy:n?.verifyStrategy??"unsupported",durationMs:0,approxTokensSpent:0,message:String(r?.message??r).slice(0,240)}}try{return await o.verifyCredential(e)}catch(r){return{ok:!1,error:"unknown",strategy:n?.verifyStrategy??"unsupported",durationMs:0,approxTokensSpent:0,message:String(r?.message??r).slice(0,240)}}}i(Dn,"verifyProviderCredential");async function In(t,e){if(!Ze(t))return{ok:!1,source:"failed",error:`Unknown provider: "${t}"`,durationMs:0};let n=Xt(t);return n.length===0?{ok:!1,source:"failed",error:`Provider "${t}" has no static model list`,durationMs:0}:{ok:!0,source:"static",models:n.map(r=>r.id),durationMs:0}}i(In,"listProviderModels");var Bn={anthropic:{"claude-opus-4-6":{inputPer1M:15,outputPer1M:75,cachePer1M:1.875},"claude-sonnet-4-6":{inputPer1M:3,outputPer1M:15,cachePer1M:.375},"claude-haiku-4-5":{inputPer1M:.8,outputPer1M:4,cachePer1M:.08}},openai:{"gpt-5.4":{inputPer1M:30,outputPer1M:120},"gpt-5.3-codex-spark":{inputPer1M:10,outputPer1M:40}},deepseek:{"deepseek-v4-flash":{inputPer1M:.14,outputPer1M:.28,cachePer1M:.028},"deepseek-v4-pro":{inputPer1M:1.68,outputPer1M:3.36,cachePer1M:.14}},kimi:{"k2.5":{inputPer1M:.005,outputPer1M:.015},"kimi-k2.6":{inputPer1M:.005,outputPer1M:.015}},"kimi-code":{"kimi-for-coding":{inputPer1M:.005,outputPer1M:.015}},qwen:{"qwen3.5-plus":{inputPer1M:.003,outputPer1M:.006}},zhipu:{"glm-5":{inputPer1M:.05,outputPer1M:.1},"glm-5.1":{inputPer1M:.05,outputPer1M:.1},"glm-5-turbo":{inputPer1M:.01,outputPer1M:.03}},"zhipu-coding":{"glm-5":{inputPer1M:.05,outputPer1M:.1},"glm-5.1":{inputPer1M:.05,outputPer1M:.1},"glm-5-turbo":{inputPer1M:.01,outputPer1M:.03}},"minimax-coding":{"MiniMax-M3":{inputPer1M:.01,outputPer1M:.03},"MiniMax-M2.7":{inputPer1M:.01,outputPer1M:.03},"MiniMax-M2.7-highspeed":{inputPer1M:.01,outputPer1M:.03},"MiniMax-M2.5":{inputPer1M:.01,outputPer1M:.03},"MiniMax-M2.5-highspeed":{inputPer1M:.01,outputPer1M:.03},"MiniMax-M2.1":{inputPer1M:.01,outputPer1M:.03},"MiniMax-M2.1-highspeed":{inputPer1M:.01,outputPer1M:.03},"MiniMax-M2":{inputPer1M:.01,outputPer1M:.03}},"mimo-coding":{"mimo-v2.5-pro":{inputPer1M:.01,outputPer1M:.03},"mimo-v2.5":{inputPer1M:.01,outputPer1M:.03}},mimo:{"mimo-v2.5-pro":{inputPer1M:.01,outputPer1M:.03},"mimo-v2.5":{inputPer1M:.01,outputPer1M:.03}},"ark-coding":{"glm-5.1":{inputPer1M:.005,outputPer1M:.015},"glm-4.7":{inputPer1M:.005,outputPer1M:.015},"kimi-k2.6":{inputPer1M:.005,outputPer1M:.015},"kimi-k2.5":{inputPer1M:.005,outputPer1M:.015},"minimax-latest":{inputPer1M:.005,outputPer1M:.015},"deepseek-v3.2":{inputPer1M:.005,outputPer1M:.015},"deepseek-v4-pro":{inputPer1M:.005,outputPer1M:.015},"deepseek-v4-flash":{inputPer1M:.005,outputPer1M:.015},"doubao-seed-2.0-code":{inputPer1M:.005,outputPer1M:.015},"doubao-seed-2.0-pro":{inputPer1M:.005,outputPer1M:.015},"doubao-seed-2.0-lite":{inputPer1M:.005,outputPer1M:.015}},"gemini-cli":{},"codex-cli":{}};function Wt(t,e,n){let o=n?.[t]?.[e];return o||Bn[t]?.[e]}i(Wt,"getCostRate");function zt(t,e,n,o=0){let r=e/1e6*t.inputPer1M,s=n/1e6*t.outputPer1M,a=t.cachePer1M?o/1e6*t.cachePer1M:0;return r+s+a}i(zt,"calculateCost");function qo(){return{records:[],retries:[]}}i(qo,"createCostTracker");function Qo(t,e){let n={timestamp:Date.now(),provider:e.provider,waitMs:e.waitMs,reason:e.reason,source:e.source};return{records:t.records,retries:[...t.retries,n]}}i(Qo,"recordRetry");function Vt(t,e,n){let o=Wt(e.provider,e.model,n),r=(e.cacheReadTokens??0)+(e.cacheWriteTokens??0),s=o?zt(o,e.inputTokens,e.outputTokens,r):0,a={timestamp:Date.now(),provider:e.provider,model:e.model,inputTokens:e.inputTokens,outputTokens:e.outputTokens,cacheReadTokens:e.cacheReadTokens??0,cacheWriteTokens:e.cacheWriteTokens??0,cost:s,role:e.role};return{records:[...t.records,a],retries:t.retries}}i(Vt,"recordUsage");function Zo(t){let e=0,n=0,o=0,r=0,s=0,a=0,c={},u={};for(let p of t.records){e+=p.cost,n+=p.inputTokens,o+=p.outputTokens,r+=p.cacheReadTokens+p.cacheWriteTokens,s+=p.cacheReadTokens,a+=p.cacheWriteTokens;let y=c[p.provider];c[p.provider]={cost:(y?.cost??0)+p.cost,calls:(y?.calls??0)+1,inputTokens:(y?.inputTokens??0)+p.inputTokens,outputTokens:(y?.outputTokens??0)+p.outputTokens};let k=p.role??"default",h=u[k];u[k]={cost:(h?.cost??0)+p.cost,calls:(h?.calls??0)+1,inputTokens:(h?.inputTokens??0)+p.inputTokens,outputTokens:(h?.outputTokens??0)+p.outputTokens}}let d=0;for(let p of t.retries)d+=p.waitMs;let l=r>0?s/r:0;return{totalCost:e,totalInputTokens:n,totalOutputTokens:o,totalCacheTokens:r,totalCacheReadTokens:s,totalCacheWriteTokens:a,cacheHitRate:l,callCount:t.records.length,retryCount:t.retries.length,retryWaitMs:d,byProvider:c,byRole:u}}i(Zo,"getSummary");function tt(t){return t<.01?`$${t.toFixed(4)}`:t<1?`$${t.toFixed(3)}`:`$${t.toFixed(2)}`}i(tt,"formatCost");function ei(t){let e=[];if(e.push(`Session Cost: ${tt(t.totalCost)} (${t.callCount} calls)`),e.push(`Tokens: ${t.totalInputTokens.toLocaleString()} in / ${t.totalOutputTokens.toLocaleString()} out`),t.totalCacheTokens>0){let r=(t.cacheHitRate*100).toFixed(0);e.push(`Cache: ${t.totalCacheTokens.toLocaleString()} tokens (${t.totalCacheReadTokens.toLocaleString()} read / ${t.totalCacheWriteTokens.toLocaleString()} write, ${r}% hit rate)`)}if(t.retryCount>0){let r=(t.retryWaitMs/1e3).toFixed(1);e.push(`Retries: ${t.retryCount} (${r}s total wait)`)}e.push("");let n=Object.entries(t.byProvider).sort((r,s)=>s[1].cost-r[1].cost);if(n.length>0){e.push("By Provider:");for(let[r,s]of n)e.push(` ${r}: ${tt(s.cost)} (${s.calls} calls, ${s.inputTokens.toLocaleString()} in / ${s.outputTokens.toLocaleString()} out)`);e.push("")}let o=Object.entries(t.byRole).sort((r,s)=>s[1].cost-r[1].cost);if(o.length>1){e.push("By Role:");for(let[r,s]of o)e.push(` ${r}: ${tt(s.cost)} (${s.calls} calls)`)}return e.join(`
31
+ `)}i(ei,"formatCostReport");var Ln={inputTokens:0,outputTokens:0,totalTokens:0},ti=3e4;async function ni(t){let e=new AbortController,n=t.timeoutMs??ti,o,r=i(c=>{o||(o=c),e.abort()},"recordAbort"),s=setTimeout(()=>r("timeout"),n),a=i(()=>r("parent"),"onParentAbort");t.abortSignal&&(t.abortSignal.aborted?r("parent"):t.abortSignal.addEventListener("abort",a,{once:!0}));try{let c=await t.provider.stream([...t.messages],[],t.system,t.reasoning??{mode:"off"},{modelOverride:t.model},e.signal),u=c.usage??Ln,d=c.textBlocks??[],l=c.toolBlocks??[],p=d.map(k=>k.text).join("");if(l.length>0)return{text:p,usage:u,costTracker:t.costTracker,stopReason:"error",error:new Error(`sideQuery: provider returned ${l.length} tool_use block(s); sideQuery expects text-only output`)};let y=t.costTracker;return y&&(y=Vt(y,{provider:t.provider.name,model:t.model,inputTokens:u.inputTokens,outputTokens:u.outputTokens,cacheReadTokens:u.cachedReadTokens,cacheWriteTokens:u.cachedWriteTokens,role:t.querySource})),{text:p,usage:u,costTracker:y,stopReason:oi(c.stopReason)}}catch(c){let u=c instanceof Error?c:new Error(String(c)),d="error";return e.signal.aborted&&(d=o==="timeout"?"timeout":"aborted"),{text:"",usage:Ln,costTracker:t.costTracker,stopReason:d,error:u}}finally{clearTimeout(s),t.abortSignal&&t.abortSignal.removeEventListener("abort",a)}}i(ni,"sideQuery");function oi(t){return t==="max_tokens"?"max_tokens":"end_turn"}i(oi,"mapStopReason");export{G as a,R as b,ge as c,nt as d,ot as e,it as f,Nn as g,Kn as h,Fn as i,$n as j,Gt as k,Ht as l,rt as m,Q as n,Te as o,Jt as p,Z as q,ee as r,st as s,ut as t,dt as u,te as v,Ee as w,ft as x,Vn as y,Pe as z,Se as A,Re as B,z as C,ne as D,oe as E,mt as F,gt as G,ie as H,ht as I,ye as J,I as K,K as L,ae as M,ce as N,ve as O,ke as P,w as Q,F as R,Lt as S,yn as T,vn as U,kn as V,bn as W,Cn as X,_n as Y,xn as Z,A as _,Xe as $,We as aa,ze as ba,Ce as ca,Ve as da,Tn as ea,Ge as fa,de as ga,He as ha,Mn as ia,En as ja,Je as ka,ue as la,Ye as ma,Rn as na,qe as oa,_e as pa,Qe as qa,wn as ra,jt as sa,Ze as ta,et as ua,Xt as va,An as wa,On as xa,Dn as ya,In as za,Bn as Aa,Wt as Ba,zt as Ca,qo as Da,Qo as Ea,Vt as Fa,Zo as Ga,tt as Ha,ei as Ia,ni as Ja};
@@ -0,0 +1,2 @@
1
+ // @kodax-ai/kodax — bundled distribution. See docs/ADR.md ADR-022 + ADR-024.
2
+ import{a as o}from"./chunk-V4WSBIXB.js";var t=o(()=>{},"noop"),c={initialize:t,connectToDevTools:t};var e=class{static{o(this,"WebSocketStub")}},l=e;export{l as a,c as b};