@kodax-ai/kodax 0.7.45 → 0.7.47

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 (53) hide show
  1. package/CHANGELOG.md +57 -1
  2. package/README.md +1 -1
  3. package/README_CN.md +1 -1
  4. package/dist/builtin/skill-creator/scripts/utils.js +12 -27
  5. package/dist/chunks/{chunk-FKB7BWQT.js → chunk-2SMCCP44.js} +1 -1
  6. package/dist/chunks/chunk-66B6ZOU7.js +31 -0
  7. package/dist/chunks/chunk-ARUWXX25.js +2 -0
  8. package/dist/chunks/chunk-KRHAO2T3.js +567 -0
  9. package/dist/chunks/chunk-QZFZIGPF.js +2 -0
  10. package/dist/chunks/{chunk-X6EHEQWP.js → chunk-VGRG2F6H.js} +301 -263
  11. package/dist/chunks/chunk-VX6HN3JP.js +415 -0
  12. package/dist/chunks/{compaction-config-WCNGYWT3.js → compaction-config-YU7SI6L6.js} +1 -1
  13. package/dist/chunks/{construction-bootstrap-OB5SDNBD.js → construction-bootstrap-6UN3OY7X.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-DS2KIZQG.js +2 -0
  17. package/dist/chunks/dist-IDNOAB4M.js +2 -0
  18. package/dist/chunks/{utils-CHXCBR3Q.js → utils-3ISOUEFC.js} +1 -1
  19. package/dist/index.d.ts +15 -12
  20. package/dist/index.js +5 -5
  21. package/dist/kodax_cli.js +986 -926
  22. package/dist/provider-capabilities.json +32 -23
  23. package/dist/sdk-agent.d.ts +42 -11
  24. package/dist/sdk-agent.js +1 -1
  25. package/dist/sdk-coding.d.ts +74 -14
  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.d.ts +1 -1
  30. package/dist/sdk-mcp.js +1 -1
  31. package/dist/sdk-repl.d.ts +19 -9
  32. package/dist/sdk-repl.js +2 -2
  33. package/dist/sdk-session.d.ts +27 -6
  34. package/dist/sdk-session.js +1 -1
  35. package/dist/sdk-skills.js +1 -1
  36. package/dist/types-chunks/{base.d-FUJahC0i.d.ts → base.d-BdJKSPO2.d.ts} +18 -1
  37. package/dist/types-chunks/{bash-prefix-extractor.d-HrTUwtV7.d.ts → bash-prefix-extractor.d-D6hL0Cuv.d.ts} +284 -6
  38. package/dist/types-chunks/{file-tracker.d-DOfaoCbJ.d.ts → file-tracker.d-BNTIvsdb.d.ts} +11 -4
  39. package/dist/types-chunks/{manager.d-87belpiS.d.ts → manager.d-U3UEwY1P.d.ts} +15 -0
  40. package/dist/types-chunks/{resolver.d-OMwxURit.d.ts → resolver.d-DkgJlEzr.d.ts} +79 -3
  41. package/dist/types-chunks/{storage.d-BvTdjYQF.d.ts → storage.d-B1Jk6ryM.d.ts} +79 -4
  42. package/dist/types-chunks/{types.d-HBbWT-iA.d.ts → types.d-B_MIIApc.d.ts} +1 -1
  43. package/dist/types-chunks/{types.d-B1uGoVTE.d.ts → types.d-Cf-GCzac.d.ts} +82 -1
  44. package/dist/types-chunks/{types.d-DM8zEJgF.d.ts → types.d-D2RNa5Y7.d.ts} +2 -2
  45. package/dist/types-chunks/{utils.d-DSEX6Rq1.d.ts → utils.d-umRKgMAu.d.ts} +19 -5
  46. package/package.json +2 -1
  47. package/dist/chunks/chunk-CZHIUJQS.js +0 -535
  48. package/dist/chunks/chunk-FT2XFFNP.js +0 -2
  49. package/dist/chunks/chunk-IJUB7QXG.js +0 -425
  50. package/dist/chunks/chunk-PGF5EZ7C.js +0 -31
  51. package/dist/chunks/devtools-EYGFOXEU.js +0 -2
  52. package/dist/chunks/dist-C2VOGY5Z.js +0 -2
  53. package/dist/chunks/dist-Q2PQM7U7.js +0 -2
package/CHANGELOG.md CHANGED
@@ -4,6 +4,60 @@ 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.47] - 2026-06-10
8
+
9
+ ### Added
10
+
11
+ - **FEATURE_132 — Native LSP integration: edit-time diagnostics reflux (TS/JS, Python, Go, Rust, Java).** After the `write` / `edit` / `multi_edit` tools change a file, KodaX now opens it through a language server and refluxes any type ERRORs straight back into the tool result, so the agent fixes them the same turn instead of waiting for the next build (saving ≥1 LLM round-trip). Servers are discovered on `PATH` / project `node_modules` (typescript-language-server, pyright, gopls, rust-analyzer, jdtls); when none is installed the feature is **silent** (no nagging), and a one-shot blacklist + spawn-dedup keep it cheap. It is **default-on but self-limiting**: only ERROR severity, ≤20 per file, a 5s per-file wait that runs OUTSIDE the file-mutation lock, and `KODAX_LSP=0` disables it entirely. Auto-installing a missing server is **opt-in** (`KODAX_LSP_DOWNLOAD=1`; `KODAX_LSP_NO_DOWNLOAD=1` hard-off) — KodaX never runs `go install` / `npm i` unprompted. The LSP TypeScript server runs as its own subprocess, isolated from the in-process repo-intelligence TS engine. It also adds four read-only navigation tools — `lsp_definition` / `lsp_hover` / `lsp_references` / `lsp_document_symbols` — for precise, position-anchored, real-time questions about the current code (complementing the repo-scope repo-intelligence symbol tools; the tool descriptions teach the boundary). Implemented under `packages/coding/src/lsp/`; new exports from `@kodax-ai/coding`: `LspService`, `getDefaultLspService`, `shutdownDefaultLspService`, `languageIdForPath`. See [docs/features/v0.7.47.md FEATURE_132](docs/features/v0.7.47.md).
12
+ - **FEATURE_221 — Injectable self-manual for SDK consumers (`selfManual`).** Products built on the KodaX SDK (e.g. KodaX-Space) can now inject their **own** product manual so the built-in `kodax_manual` tool answers *their* users' "how do I use / configure …?" questions on-brand, instead of returning KodaX's internal manual. `runKodaX({ selfManual: { productName, topics } })` re-brands the ≤250-token routing rule + scope anchors to `productName` and **extend-merges** your `KodaXManualTopicInput[]` over KodaX's base topics (same `id` overrides, new `id` appends), so users can still ask about the underlying provider / config / SDK topics. **Opt-in and backward-compatible** — omit `selfManual` and the system prompt is byte-identical to before. Topics stay **tool-on-demand and 4 KB-capped**: nothing large is injected into the prompt, only the short routing rule. New exports from `@kodax-ai/coding`: `KodaXManualTopicInput`, `KodaXSelfManualConfig`, `ResolveKodaXManualOptions`, `buildSelfKnowledgeRoutingRule`. See [SDK Embedder Guide §11](docs/SDK_EMBEDDER_GUIDE.md) + [docs/features/v0.7.47.md FEATURE_221](docs/features/v0.7.47.md).
13
+ - **FEATURE_218 — KodaX self-knowledge manual + `kodax_manual` help tool.** KodaX now ships a version-bound, structured product manual (17 topics: overview / install / providers / custom-providers / config / permissions / commands / tools / agents / skills / mcp / repo-intelligence / sessions / doctor / sdk / troubleshooting) behind a read-only `kodax_manual` tool, plus a ≤250-token routing rule that tells the model to look up KodaX usage/config questions instead of guessing or mixing in Claude Code / Codex CLI knowledge. The provider list is sourced from the single-source-of-truth capability snapshots (drift-proof, not hand-copied) and each answer is anchored to KodaX scope. REPL `/help <topic>` reuses the same registry. Lookup is deterministic (exact id → alias, incl. Chinese → query token-overlap → index) and never fabricates; single-topic output ≤4 KB, index ≤2 KB. No RAG, no vector DB, no background index, no prompt bloat. See [docs/features/v0.7.47.md FEATURE_218](docs/features/v0.7.47.md).
14
+ - **MCP client — protocol `2025-11-25` compatibility (P0+P1).** KodaX's self-built MCP client now speaks the `2025-11-25` revision. **P0 (protocol compliance):** answers a server `ping` with an empty result instead of `-32601`; emits `notifications/cancelled` when a request times out (the `initialize` request is exempt); and resumes a dropped Streamable-HTTP notification stream via SSE `id`/`retry` + `Last-Event-ID`, with a reconnect cap so an empty-EOF stream can't reconnect-loop forever. **P1 (version + data surface):** advertises protocol `2025-11-25` with negotiation validation + the `MCP-Protocol-Version` header; carries tool/resource/prompt `icons` into descriptors (filtering unsafe URI schemes); and surfaces `execution.taskSupport`, failing fast on task-required tools. Unwired `elicitation` dead code removed. (Reverse / server-side capabilities are planned as FEATURE_222 for v0.7.48.)
15
+
16
+ ### Changed
17
+
18
+ - **FEATURE_220 — Continuous thinking + tool-call block rendering.** Consecutive `thinking` blocks now render collapsed to a single line by default (`Ctrl+O` / show-all still expands the full content; `KODAX_THINKING_COLLAPSE=0` opts out), and a run of consecutive thinking / tool-call items reads as one continuous "working block" with the inter-item blank lines suppressed (`KODAX_TRANSCRIPT_TIGHT=0` opts out), instead of a stack of separately-titled cards. Pure transcript-rendering change — no LLM-facing prompt is touched. (The originally-planned read-only tool grouping was implemented then reverted after review, because the synthetic group key broke scroll / selection / search and churned the inline scrollback ledger.) See [docs/features/v0.7.47.md FEATURE_220](docs/features/v0.7.47.md).
19
+ - **Coding-plan provider model lineups refreshed (2026-06 gateway catch-up).** `ark-coding` drops the no-longer-routed `kimi-k2.5` and the retired `minimax-latest` floating alias, adding the two explicit MiniMax revisions the gateway now exposes — `MiniMax-M3` (Frontier Coding, native multimodal, 1M context) and `MiniMax-M2.7` (204K) — keeping its routed-model count at 11. `minimax-coding` trims the retired `M2.5` / `M2.1` / `M2` family (7 → 2 alternates), keeping `MiniMax-M2.7` (default) + `MiniMax-M3` + `MiniMax-M2.7-highspeed`, so `/model` completion and the default-model APIs only surface ids the gateway still routes (the removed ids were upstream-404 traps). Subscription placeholder cost rates updated to match.
20
+
21
+ ### Fixed
22
+
23
+ - **Spawned subprocesses are now torn down as a tree, not just the direct child.** Every subprocess KodaX spawns — language servers (FEATURE_132), the `bash` tool, MCP stdio transports, the ACP server, and CLI-event children — is tracked as a managed child-process tree and killed whole on shutdown / timeout / abort (POSIX process groups, with exited groups reaped), so a terminated parent no longer orphans its grandchildren (a shell's sub-processes, a language server's workers, etc.). Centralized in `@kodax-ai/agent` (`managed-child-processes` + `process-tree`) and wired into every spawn site.
24
+
25
+ ## [0.7.46] - 2026-06-07
26
+
27
+ ### Added
28
+
29
+ - **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-目录治理--归档语义分家--平铺池迁移).
30
+
31
+ ### Changed
32
+
33
+ - **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).
34
+
35
+ ### Performance
36
+
37
+ - **`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.
38
+
39
+ ### Fixed
40
+
41
+ - **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.
42
+ - **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.
43
+ - **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`:
44
+ - **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`).
45
+ - **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.
46
+ - **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.
47
+ - **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).
48
+ - **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.
49
+ - **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.
50
+ - 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.
51
+ - **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.
52
+ - **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)`.
53
+ - **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).
54
+ - 2 new regression tests in `storage-sdk-consumer.test.ts` — cross-project listing without project intent + filter-by-cwd when set.
55
+ - SDK consumers can now revert workarounds that force the slow path; the natural call shape works.
56
+ - **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`:
57
+ - **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.
58
+ - **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.
59
+ - 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.
60
+
7
61
  ## [0.7.45] - 2026-06-01
8
62
 
9
63
  ### ⚠️ BREAKING — coding-plan providers now use dedicated API-key env vars
@@ -34,6 +88,7 @@ shared names — the new name is required for the coding-plan provider to start.
34
88
  - **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.
35
89
  - **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).
36
90
  - **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).
91
+ - **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).
37
92
 
38
93
  ### Performance
39
94
 
@@ -48,6 +103,7 @@ shared names — the new name is required for the coding-plan provider to start.
48
103
  - **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.
49
104
  - **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.
50
105
  - **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.
106
+ - **`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.
51
107
 
52
108
  ### Known issues
53
109
 
@@ -707,7 +763,7 @@ Two features close out v0.7.32 and the Plan B roadmap. **FEATURE_090** is the ro
707
763
  ### Documentation
708
764
 
709
765
  - **EVAL_GUIDELINES rewrite** — `benchmark/EVAL_GUIDELINES.md` now documents the **single-turn probe methodology** as the official KodaX eval pattern and removes end-to-end loop comparisons from the recommended set. Loops conflate prompt quality with tool-availability artefacts (model tries to verify with `read`/`grep`/`bash`, harness can't provide tools, benchmark scores the format-fail). Single-turn probes test the prompt-only contract.
710
- - **FEATURE_108 design** (`docs/features/v0.7.47.md`) — Session-Driven Reflective Prompt Patcher spec landed for v0.7.47 design preview.
766
+ - **FEATURE_108 design** (`docs/features/v0.7.47.md`) — Session-Driven Reflective Prompt Patcher spec landed for v0.7.47 design preview.(注:2026-06-05 版本重排后 FEATURE_108 迁至 [`docs/features/v0.7.54.md`](docs/features/v0.7.54.md);`v0.7.47.md` 现为 FEATURE_218 + FEATURE_132)
711
767
  - **FEATURE_109 design** (`docs/features/v0.7.48.md`) — Harness Observability Substrate (long-term memory + prediction contract + cross-family prose guard) spec landed for v0.7.48 design preview.
712
768
  - **`docs/features/v0.7.29.md` 1496-line expansion** — folds back the historical capability-inventory artifact (`v0.7.29-capability-inventory.md` deleted) and adds deeper FEATURE_103/104/107-related context to the v0.7.29 retrospective.
713
769
  - **`docs/CODING_AGENT_PROMPTS.md`** — cross-project prompt-system reference (4 open-source coding agents) for KodaX prompt design comparison. Research artefact, not a project doc.
package/README.md CHANGED
@@ -913,7 +913,7 @@ import { InkREPL } from '@kodax-ai/kodax/repl';
913
913
  | qwen | `QWEN_API_KEY` | Native | qwen3.5-plus |
914
914
  | zhipu | `ZHIPU_API_KEY` | Native | glm-5 |
915
915
  | zhipu-coding | `ZHIPU_CODING_API_KEY` | Native | glm-5 |
916
- | minimax-coding | `MINIMAX_CODING_API_KEY` | Native | MiniMax-M2.7 |
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
917
  | mimo | `MIMO_API_KEY` | Native | mimo-v2.5-pro (Xiaomi MiMo pay-per-token, Anthropic-compat) |
918
918
  | mimo-coding | `MIMO_CODING_API_KEY` | Native | mimo-v2.5-pro (Xiaomi Token Plan, Anthropic-compat) |
919
919
  | ark-coding | `ARK_CODING_API_KEY` | Native | glm-5.1 (Volcengine Ark Coding Plan, multi-model gateway, Anthropic-compat) |
package/README_CN.md CHANGED
@@ -331,7 +331,7 @@ dist/binary/linux-x64/
331
331
  | qwen | `QWEN_API_KEY` | Native | qwen3.5-plus |
332
332
  | zhipu | `ZHIPU_API_KEY` | Native | glm-5 |
333
333
  | zhipu-coding | `ZHIPU_CODING_API_KEY` | Native | glm-5(GLM Coding Plan 端点) |
334
- | minimax-coding | `MINIMAX_CODING_API_KEY` | Native | MiniMax-M2.7 |
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
335
  | mimo | `MIMO_API_KEY` | Native | mimo-v2.5-pro(小米 MiMo 按量计费,Anthropic 协议) |
336
336
  | mimo-coding | `MIMO_CODING_API_KEY` | Native | mimo-v2.5-pro(小米 MiMo Token Plan,Anthropic 协议) |
337
337
  | ark-coding | `ARK_CODING_API_KEY` | Native | glm-5.1(火山方舟 Coding Plan,多模型网关,Anthropic 协议) |
@@ -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
  }
@@ -1,2 +1,2 @@
1
1
  // @kodax-ai/kodax — bundled distribution. See docs/ADR.md ADR-022 + ADR-024.
2
- import{wa as e}from"./chunk-IJUB7QXG.js";import{a as n}from"./chunk-V4WSBIXB.js";import{readFile as i}from"fs/promises";var c={enabled:!0,triggerPercent:75};async function g(o){let t=e("config.json");try{let r=await a(t);if(r?.compaction)return{...c,...r.compaction}}catch{}return c}n(g,"loadCompactionConfig");async function a(o){try{let t=await i(o,"utf-8");return JSON.parse(t)}catch{return null}}n(a,"readConfigFile");export{g as a};
2
+ import{wa as e}from"./chunk-VX6HN3JP.js";import{a as n}from"./chunk-V4WSBIXB.js";import{readFile as i}from"fs/promises";var c={enabled:!0,triggerPercent:75};async function g(o){let t=e("config.json");try{let r=await a(t);if(r?.compaction)return{...c,...r.compaction}}catch{}return c}n(g,"loadCompactionConfig");async function a(o){try{let t=await i(o,"utf-8");return JSON.parse(t)}catch{return null}}n(a,"readConfigFile");export{g as a};
@@ -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"}},S=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"}},st=class extends G{static{i(this,"KodaXNetworkError")}isTimeout;constructor(e,n=!1){super(e,"NETWORK_ERROR"),this.isTimeout=n,this.name="KodaXNetworkError"}},at=class extends G{static{i(this,"KodaXToolCallIdError")}constructor(e){super(e,"TOOL_CALL_ID_ERROR"),this.name="KodaXToolCallIdError"}};var ct=32768,zn=32e3,Vn=64e3,Gn=.5;var Hn=["off","auto","quick","balanced","deep"],qt={low:6e3,medium:1e4,high:2e4},Qt=4096;function lt(t){return t.reasoningCapability?t.reasoningCapability:t.supportsThinking?"native-toggle":"prompt-only"}i(lt,"getReasoningCapability");function Q(t){return Me(t).enabled}i(Q,"isReasoningEnabled");function Me(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??Zt(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(Me,"normalizeReasoningRequest");function Zt(t){switch(t){case"quick":return"low";case"balanced":case"auto":return"medium";case"deep":return"high";default:return"off"}}i(Zt,"getDefaultThinkingDepthForMode");function Z(t,e,n="unknown"){if(e==="off")return 0;let o={...qt,...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=Qt){let o=Math.max(1024,e-n);return Math.max(1024,Math.min(t,o))}i(ee,"clampThinkingBudget");function ut(t){switch(t){case"low":return"low";case"medium":return"medium";case"high":return"high";default:return}}i(ut,"mapDepthToOpenAIReasoningEffort");import Pe from"fs";import Jn from"os";import ft from"path";var dt=null;function Yn(t){return t==="budget"||t==="effort"||t==="toggle"||t==="none"}i(Yn,"isReasoningOverride");function qn(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(Yn)}i(qn,"isStoredConfig");function Qn(){return process.env.KODAX_HOME??ft.join(Jn.homedir(),".kodax")}i(Qn,"getKodaxDir");function en(){return process.env.KODAX_CONFIG_FILE??ft.join(Qn(),"config.json")}i(en,"getConfigFilePath");function pt(t,e){return dt={filePath:t,config:e},e}i(pt,"updateStoredConfigCache");function Zn(t){let e=JSON.parse(Pe.readFileSync(t,"utf-8"));return qn(e)?e:{}}i(Zn,"readStoredConfigFromDisk");function mt(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(mt,"reasoningCapabilityToOverride");function gt(t){switch(t){case"budget":return"native-budget";case"effort":return"native-effort";case"toggle":return"native-toggle";default:return"none"}}i(gt,"reasoningOverrideToCapability");function te(t,e,n){return[t,e.baseUrl??"",n??e.model].join("|")}i(te,"buildReasoningOverrideKey");function ht(){let t=en();if(dt?.filePath===t)return dt.config;try{if(Pe.existsSync(t))return pt(t,Zn(t))}catch{}return pt(t,{})}i(ht,"loadStoredConfig");function tn(t){let e=en();try{Pe.mkdirSync(ft.dirname(e),{recursive:!0}),Pe.writeFileSync(e,JSON.stringify(t,null,2)),pt(e,t)}catch(n){process.env.KODAX_DEBUG_OVERRIDES&&console.error("[ReasoningOverride] Failed to save config:",n)}}i(tn,"saveStoredConfig");function we(t,e,n){let o=ht(),r=te(t,e,n);return o.providerReasoningOverrides?.[r]}i(we,"loadReasoningOverride");function yt(t,e,n,o){let r=ht(),s=te(t,e,o);r.providerReasoningOverrides={...r.providerReasoningOverrides??{},[s]:n},tn(r)}i(yt,"saveReasoningOverride");function eo(t,e,n){let o=ht(),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,tn(o)}i(eo,"clearReasoningOverride");function nn(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(nn,"readHeader");function on(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(on,"parsePositiveNumber");function to(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(to,"parseHttpDate");function Se(t,e){let n=e.baseBackoffMs??1e3,o=e.maxBackoffMs??3e4,r=e.maxHeaderWaitMs??12e4,s=e.now?e.now():Date.now(),a=on(nn(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=nn(t,"retry-after");if(c!==void 0&&c.trim().length>0){let y=on(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=to(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(Se,"parseRetryAfter");function Re(t){if(!t||typeof t!="object")return;let e=t;return e.headers??e.response?.headers??e.cause?.headers??e.cause?.response?.headers}i(Re,"extractHeadersFromError");var Y={transport:"native-api",conversationSemantics:"full-history",mcpSupport:"none",contextFidelity:"full",toolCallingFidelity:"full",sessionSupport:"full",longRunningSupport:"full",multimodalSupport:"none",evidenceSupport:"full"},rn={...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"},sn={...he,multimodalSupport:"image-input"};function Ae(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(Ae,"normalizeCapabilityProfile");function U(t){return{...Ae(t)}}i(U,"cloneCapabilityProfile");function no(t){if(!t)return;let e=parseInt(t,10);return Number.isFinite(e)&&e>0?e:void 0}i(no,"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=no(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 S(`${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:lt(this.config)}getReasoningCapability(e){let n=we(this.name,this.config,e);return n?gt(n):this.getConfiguredReasoningCapability(e)}getReasoningOverride(e){return we(this.name,this.config,e)}getReasoningOverrideKey(e){return te(this.name,this.config,e)}persistReasoningCapabilityOverride(e,n){let o=mt(e);o&&yt(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 Me(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=Re(e);if(!n)return;let o=Se(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=Re(c)??{},d=Se(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 S(`${this.name} API error: ${c.message}`,this.name)}throw c}throw new G("Unexpected end of withRateLimit")}};import co from"@anthropic-ai/sdk";import{parse as oo}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=oo(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 vt(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 io(t){return t.replace(/\bsk-[A-Za-z0-9_-]{6,}/g,"sk-***")}i(io,"redactKeyMaterial");function vt(t,e){let n=t,o=n.status??n.statusCode??n.response?.status,r=io(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(vt,"classifyVerifyError");function kt(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(kt,"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 bt(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(bt,"lowerCacheBoundaries");function ye(t){return t.filter(e=>!ie(e))}i(ye,"stripCacheBoundaries");import{readFile as ro}from"node:fs/promises";import so from"node:path";var ao={".gif":"image/gif",".jpeg":"image/jpeg",".jpg":"image/jpeg",".png":"image/png",".webp":"image/webp"};function Oe(t,e){return e??ao[so.extname(t).toLowerCase()]??"image/png"}i(Oe,"resolveImageMediaType");async function De(t){return(await ro(t)).toString("base64")}i(De,"readImageFileAsBase64");async function an(t,e){let n=Oe(t,e),o=await De(t);return`data:${n};base64,${o}`}i(an,"buildImageDataUrl");var lo="KodaX";function uo(t){return t.userAgentMode==="sdk"?void 0:{"User-Agent":lo}}i(uo,"getAnthropicCompatDefaultHeaders");function Ct(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(Ct,"normalizeAnthropicUsage");var I=class extends z{static{i(this,"KodaXAnthropicCompatProvider")}supportsThinking=!0;client;initClient(){let e=uo(this.config);this.client=new co({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=kt([{type:"text",text:e}],"system");return bt(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="",E="",w="",j="",L="",X="",M=!1,W,O=Date.now(),Ee=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 S("All reasoning capability attempts failed without a captured error",this.name);let me=Date.now(),T=0,B=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++,B+=D,this.logStreamDiagnostic(`[Stream] stall detected: ${Math.round(D/1e3)}s gap before ${x.type}`,{stallCount:T,totalStallMs:B,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="",E=v.signature??""):v.type==="redacted_thinking"?(_="redacted_thinking",w=v.data??""):v.type==="text"?m="":v.type==="tool_use"&&(j=v.id,L=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?.(L,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:E}),s?.onThinkingEnd?.(g)):_==="redacted_thinking"?(w&&C.push({type:"redacted_thinking",data:w}),w=""):_==="text"?m&&h.push({type:"text",text:m}):_==="tool_use"&&(!j||!L?console.error("[Tool Block Invalid] Missing tool id or name:",{id:JSON.stringify(j),name:JSON.stringify(L),input:X.slice(0,100)}):f.push({type:"tool_use",id:j,name:L,input:ne(X)})),_=null;else if(x.type==="message_stop"){if(M=!0,O=Date.now(),process.env.KODAX_DEBUG_STREAM){let v=Date.now()-Ee;this.logStreamDiagnostic(`[Stream] message_stop received after ${v}ms`)}}else if(x.type==="message_delta"){O=Date.now(),b=Ct(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=Ct(x.message?.usage,b),process.env.KODAX_DEBUG_STREAM&&this.logStreamDiagnostic("[Stream] message_start received"))}if(!M){let x=Date.now()-Ee,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 E=Z(this.config,c.depth,c.taskType);g.thinking={type:"enabled",budget_tokens:ee(E,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 E=m==="native-budget"?["budget_tokens","thinking"]:m==="native-toggle"?["thinking"]:[];if(!this.shouldFallbackForReasoningError(g,...E))throw g}if(!h)throw f??new S("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:Ct(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 S("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:Oe(k.path,k.mediaType),data:await De(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:Oe(l.path,l.mediaType),data:await De(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 po from"openai";var fo="KodaX";function mo(t){return t.userAgentMode==="sdk"?void 0:{"User-Agent":fo}}i(mo,"getOpenAICompatDefaultHeaders");function cn(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(cn,"normalizeOpenAIUsage");function go(t){return t?t.type==="function"&&"function"in t:!1}i(go,"isOpenAIFunctionToolCall");function ho(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(ho,"extractOpenAIMessageText");function yo(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(yo,"extractOpenAIMessageReasoning");var K=class extends z{static{i(this,"KodaXOpenAICompatProvider")}supportsThinking=!0;client;initClient(){let e=mo(this.config);this.client=new po({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=ut(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),E=Q(g)?this.getReasoningCapability(l):"none",w=Q(g)?this.getReasoningFallbackChain(E).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},L,X;for(let T of w){for(;!L;){let B={...j};b&&(B.stream_options={include_usage:!0}),this.resetReasoningCapabilityParams(B),this.applyReasoningCapability(B,T,g);try{L=await this.client.chat.completions.create(B,a?{signal:a}:{}),T!==E&&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(L)break}if(!L)throw X??new S("All reasoning capability attempts failed without a captured error",this.name);let M=Date.now(),W=0,O=0,Ee=3e4;for await(let T of L){if(a?.aborted)throw new DOMException("Request aborted","AbortError");let B=Date.now(),N=B-M;N>Ee&&(W++,O+=N,this.logStreamDiagnostic(`[Stream] stall detected: ${Math.round(N/1e3)}s gap`,{stallCount:W,totalStallMs:O})),M=B,s?.onHeartbeat?.(),C=cn(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 B=new Error(`Stream incomplete: finish_reason not received. Duration: ${T}ms. This may indicate a network disconnection or API timeout.`);throw B.name="StreamIncompleteError",this.logStreamDiagnostic("[Stream] Incomplete stream detected:",{duration:T,textContentLength:h.length,toolCallsCount:k.size}),B}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(M=>({type:"function",function:{name:M.name,description:M.description,parameters:M.input_schema}})),k=this.normalizeReasoning(r),h=Q(k)?this.getReasoningCapability(l):"none",f=Q(k)?this.getReasoningFallbackChain(h).filter(M=>M==="native-budget"||M==="native-effort"||M==="native-toggle"||M==="none"):["none"],C={model:l,messages:p,tools:y,max_completion_tokens:this.getEffectiveMaxOutputTokens(l)},b,_;for(let M of f){let W={...C};this.resetReasoningCapabilityParams(W),this.applyReasoningCapability(W,M,k);try{b=await this.client.chat.completions.create(W,a?{signal:a}:{}),M!==h&&this.persistReasoningCapabilityOverride(M,l);break}catch(O){if(_=O,!this.shouldFallbackForReasoningError(O,...this.getFallbackTerms(M)))throw O}}if(!b)throw _??new S("All reasoning capability attempts failed without a captured error",this.name);let m=b.choices[0],g=m?.message,E=ho(g?.content),w=yo(g),j=(g?.tool_calls??[]).filter(go).map(M=>({type:"tool_use",id:M.id,name:M.function.name,input:ne(M.function.arguments)}));E&&s?.onTextDelta?.(E);let L=E?[{type:"text",text:E}]:[],X=[];return w&&(X.push({type:"thinking",thinking:w}),s?.onThinkingDelta?.(w),s?.onThinkingEnd?.(w)),{textBlocks:L,toolBlocks:j,thinkingBlocks:X,usage:cn(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 an(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 Co}from"node:child_process";import{Readable as _o,Writable as xo}from"node:stream";import Ie from"node:process";import{ClientSideConnection as To,PROTOCOL_VERSION as Eo,ndJsonStream as Mo}from"@agentclientprotocol/sdk";import{spawn as vo}from"node:child_process";var ko=2e3,ve=2e3;function xt(t){return t.exitCode!==null||t.signalCode!==null}i(xt,"isExited");function _t(t,e){return xt(t)?Promise.resolve(!0):new Promise(n=>{let o=!1,r=i(c=>{o||(o=!0,clearTimeout(a),t.off("exit",s),t.off("error",s),n(c))},"finish"),s=i(()=>r(!0),"onExit"),a=setTimeout(()=>r(!1),e);a.unref?.(),t.once("exit",s),t.once("error",s)})}i(_t,"waitForExit");function bo(t){return new Promise(e=>{let n=vo("taskkill",["/pid",String(t),"/t","/f"],{stdio:"ignore",windowsHide:!0}),o=!1,r=i(()=>{o||(o=!0,clearTimeout(s),e())},"finish"),s=setTimeout(()=>{try{n.kill()}catch{}r()},ko);s.unref?.(),n.once("exit",r),n.once("error",r)})}i(bo,"runTaskkill");function ln(t,e){let n=!1;try{process.kill(-t,e),n=!0}catch{}try{process.kill(t,e),n=!0}catch{}return n}i(ln,"signalPosixPidTree");function un(t){try{return process.kill(t,0),!0}catch(e){return e.code!=="ESRCH"}}i(un,"signalTargetExists");function dn(t){return un(-t)||un(t)}i(dn,"isPosixPidTreeAlive");async function pn(t,e){let n=Date.now()+e;for(;Date.now()<n;){if(!dn(t))return!0;await new Promise(o=>setTimeout(o,50))}return!dn(t)}i(pn,"waitForPosixPidTreeExit");async function ke(t){if(process.platform==="win32"&&t.pid!==void 0){if(xt(t))return;await bo(t.pid),await _t(t,ve);return}if(t.pid!==void 0&&process.platform!=="win32"){if(!ln(t.pid,"SIGTERM")||await pn(t.pid,ve))return;ln(t.pid,"SIGKILL"),await pn(t.pid,ve);return}if(!xt(t)){try{t.kill("SIGTERM")}catch{return}if(!await _t(t,ve)){try{t.kill("SIGKILL")}catch{}await _t(t,ve)}}}i(ke,"killChildProcessTree");var Le=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=Ie.platform==="win32"&&!this.options.command.endsWith(".cmd")?`${this.options.command}.cmd`:this.options.command;if(this.agentProcess=Co(s,this.options.args??[],{cwd:this.options.cwd??Ie.cwd(),stdio:["pipe","pipe","inherit"],detached:Ie.platform!=="win32"}),!this.agentProcess.stdin||!this.agentProcess.stdout)throw new Error("Failed to create ACP stdio pipes");n=xo.toWeb(this.agentProcess.stdin),e=_o.toWeb(this.agentProcess.stdout)}else throw new Error("AcpClient requires either a command or I/O streams");let o=Mo(n,e);this.client=new To(()=>({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:Eo,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??Ie.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&&ke(this.agentProcess),this.options.abort?.();try{this.client?.close?.()}catch{}this.client=null,this.agentProcess=null}};function Po(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(Po,"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="{}",E=b.arguments??b.parameters;E&&(g=typeof E=="string"?E:JSON.stringify(E)),m.streamOptions?.onToolInputDelta?.(b.title,g);let w=`
20
+ > [Tool Use] ${b.title}: ${g}
21
+ `;m.output.text+=w,m.streamOptions?.onTextDelta?.(w);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 Le(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:Po(f?.usage)}}disconnect(){this._client&&(this._client.disconnect(),this._client=null),this._activeStreams.clear(),this._sessionMap.clear()}};import{spawn as wo}from"node:child_process";import Be 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={...Be.env,...this.config.env},s=Be.platform==="win32"&&!this.config.command.endsWith(".cmd")?`${this.config.command}.cmd`:this.config.command,a=wo(s,n,{cwd:this.config.cwd??Be.cwd(),env:o,stdio:["ignore","pipe","pipe"],detached:Be.platform!=="win32"}),c="";a.stderr?.on("data",l=>{c+=l.toString()});let u=!1,d=i(()=>{u||ke(a)},"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||await ke(a)}}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 So}from"node:child_process";import Ro from"node:process";async function Ne(t){try{let e=Ro.platform==="win32",n=So(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(Ne,"checkCliCommandInstalled");var Ke=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 Ne("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 fn}from"node:stream/web";import{randomUUID as Tt}from"node:crypto";function Fe(t){let e=new fn,n=new fn,o=e.readable.getReader(),r=n.writable.getWriter(),s=new TextDecoder,a=new TextEncoder,c="",u=Tt(),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??Tt(),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 E of g){let w=h(E);if(w&&await p({jsonrpc:"2.0",method:"session/update",params:{sessionId:f,update:w}}),E.type==="complete")return{stopReason:_.aborted?"cancelled":"end_turn",usage:E.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||Tt()};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(Fe,"createPseudoAcpServer");import mn from"node:fs";import gn from"node:os";import hn from"node:path";var Ao="gpt-5.4",Oo="auto-gemini-3",Do=["gpt-5.4","gpt-5.3-codex","gpt-5.3-codex-spark"],Io=["auto-gemini-3","gemini-3.1-pro-preview","gemini-3-flash-preview","gemini-2.5-pro","gemini-2.5-flash"];function yn(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(yn,"dedupePreserveOrder");function Lo(){let t=hn.join(gn.homedir(),".codex","config.toml");try{return mn.readFileSync(t,"utf8").match(/^\s*model\s*=\s*"([^"]+)"/m)?.[1]?.trim()||null}catch{return null}}i(Lo,"readCodexConfiguredModel");function Bo(){let t=hn.join(gn.homedir(),".gemini","settings.json");try{let e=JSON.parse(mn.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(Bo,"readGeminiConfiguredModel");function ae(){return Lo()||Ao}i(ae,"getCodexCliDefaultModel");function ce(){return Bo()||Oo}i(ce,"getGeminiCliDefaultModel");function be(){return yn([ae(),...Do])}i(be,"getCodexCliKnownModels");function Ce(){return yn([ce(),...Io])}i(Ce,"getGeminiCliKnownModels");var Et=ce(),No=Ce(),Ko={transport:"cli-bridge",conversationSemantics:"last-user-message",mcpSupport:"none",contextFidelity:"lossy",toolCallingFidelity:"limited",sessionSupport:"stateless",longRunningSupport:"limited",multimodalSupport:"image-input",evidenceSupport:"limited"},$e=class extends re{static{i(this,"KodaXGeminiCliProvider")}name="gemini-cli";supportsThinking=!1;config={apiKeyEnv:"GEMINI_CLI_API_KEY",model:Et,models:No.filter(e=>e!==Et).map(e=>({id:e,displayName:e})),supportsThinking:!1,reasoningCapability:"prompt-only",contextWindow:1048576};acpClientOptions;constructor(){super();let e=new Ke({model:Et});this.acpClientOptions=Fe(e)}getCapabilityProfile(){return U(Ko)}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 Ue=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 Ne("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 Mt=ae(),Fo=be(),je=class extends re{static{i(this,"KodaXCodexCliProvider")}name="codex-cli";supportsThinking=!1;config={apiKeyEnv:"CODEX_CLI_API_KEY",model:Mt,models:Fo.filter(e=>e!==Mt).map(e=>({id:e,displayName:e})),supportsThinking:!1,reasoningCapability:"prompt-only",contextWindow:128e3};acpClientOptions;constructor(){super();let e=new Ue({model:Mt});this.acpClientOptions=Fe(e)}};import{existsSync as zo,readFileSync as Vo}from"node:fs";import le from"node:path";import{fileURLToPath as Go}from"node:url";var vn=["none","prompt-only","native-effort","native-budget","native-toggle","unknown"],kn=["native","image-input-native","cli-bridge","image-input-cli-bridge"],bn=["count-tokens","models-list","minimal-message","unsupported"];function _e(t){return typeof t=="object"&&t!==null&&!Array.isArray(t)}i(_e,"isPlainObject");function We(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(We,"requireString");function Cn(t,e){if(t!==void 0)return We(t,e)}i(Cn,"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 Xe(t,e){if(t!==void 0){if(typeof t!="boolean")throw new Error(`provider-capabilities.json: ${e} must be a boolean`);return t}}i(Xe,"optionalBoolean");function Pt(t,e){if(typeof t!="string"||!vn.includes(t))throw new Error(`provider-capabilities.json: ${e} must be one of ${vn.join(", ")}, got ${JSON.stringify(t)}`);return t}i(Pt,"requireReasoningCapability");function $o(t,e){if(typeof t!="string"||!kn.includes(t))throw new Error(`provider-capabilities.json: ${e} must be one of ${kn.join(", ")}, got ${JSON.stringify(t)}`);return t}i($o,"requireProfileName");function Uo(t,e){if(typeof t!="string"||!bn.includes(t))throw new Error(`provider-capabilities.json: ${e} must be one of ${bn.join(", ")}, got ${JSON.stringify(t)}`);return t}i(Uo,"requireVerifyStrategy");function jo(t,e){if(!_e(t))throw new Error(`provider-capabilities.json: ${e} must be an object`);let n=We(t.id,`${e}.id`),o=Cn(t.displayName,`${e}.displayName`),r=q(t.contextWindow,`${e}.contextWindow`),s=q(t.maxOutputTokens,`${e}.maxOutputTokens`),a=q(t.thinkingBudgetCap,`${e}.thinkingBudgetCap`),c=Xe(t.replayReasoningContent,`${e}.replayReasoningContent`),u=Xe(t.strictThinkingSignature,`${e}.strictThinkingSignature`),d=q(t.streamMaxDurationMs,`${e}.streamMaxDurationMs`),l=t.reasoningCapability===void 0?void 0:Pt(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(jo,"validateModelDescriptor");function Xo(t,e){if(t===void 0)return;if(!_e(t))throw new Error(`provider-capabilities.json: ${e} must be an object`);let n={};for(let[o,r]of Object.entries(t))n[o]=Pt(r,`${e}.${o}`);return n}i(Xo,"validateModelReasoningCapabilities");function Wo(t,e){if(!_e(t))throw new Error(`provider-capabilities.json: providers.${e} must be an object`);let n=Xe(t.cliBridge,`providers.${e}.cliBridge`),o=We(t.apiKeyEnv,`providers.${e}.apiKeyEnv`),r=Pt(t.reasoningCapability,`providers.${e}.reasoningCapability`),s=$o(t.capabilityProfile,`providers.${e}.capabilityProfile`),a=Uo(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=Xe(t.supportsThinking,`providers.${e}.supportsThinking`),p=Xo(t.modelReasoningCapabilities,`providers.${e}.modelReasoningCapabilities`),y=Cn(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)=>jo(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(Wo,"validateProviderEntry");function _n(t){if(!_e(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=We(t.updatedAt,"updatedAt");if(!_e(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]=Wo(r,o);return{version:1,updatedAt:e,providers:n}}i(_n,"validateProviderCapabilitiesJson");var Ho=Object.freeze({native:Y,"image-input-native":rn,"cli-bridge":he,"image-input-cli-bridge":sn}),ze=null;function Jo(){if(process.env.KODAX_BUNDLED==="true")return le.join(le.dirname(process.execPath),"provider-capabilities.json");let t=le.dirname(Go(import.meta.url)),e=[le.join(t,"provider-capabilities.json"),le.join(le.dirname(t),"provider-capabilities.json")];for(let n of e)if(zo(n))return n;return e[0]}i(Jo,"resolveJsonPath");function Yo(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(Yo,"deepFreezeSnapshot");function qo(t){if(t==="gemini-cli"){let e=ce();return{model:e,models:Ce().filter(n=>n!==e).map(n=>({id:n}))}}if(t==="codex-cli"){let e=ae();return{model:e,models:be().filter(n=>n!==e).map(n=>({id:n}))}}throw new Error(`provider-capabilities loader: unknown cliBridge provider '${t}'`)}i(qo,"resolveCliBridgeModels");function Qo(t,e){let n=Ho[e.capabilityProfile];if(!n)throw new Error(`provider-capabilities loader: unknown capabilityProfile '${e.capabilityProfile}' for provider '${t}'`);if(e.cliBridge){let r=qo(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(Qo,"buildSnapshot");function xn(){if(ze)return ze;let t=Jo(),e;try{let r=Vo(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=_n(e),o={};for(let[r,s]of Object.entries(n.providers))o[r]=Yo(Qo(r,s));return ze=Object.freeze(o),ze}i(xn,"getProviderSnapshots");import Zo from"@anthropic-ai/sdk";var R=xn();function $(t,e={}){let n=R[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 wt=class extends I{static{i(this,"AnthropicProvider")}name="anthropic";config=$("anthropic",{strictThinkingSignature:!0});constructor(){super(),this.client=new Zo({apiKey:this.getApiKey()})}},St=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()}},Rt=class extends I{static{i(this,"KimiCodeProvider")}name="kimi-code";config=$("kimi-code",{baseUrl:"https://api.kimi.com/coding/"});constructor(){super(),this.initClient()}},At=class extends I{static{i(this,"MiniMaxCodingProvider")}name="minimax-coding";config=$("minimax-coding",{baseUrl:"https://api.minimaxi.com/anthropic"});constructor(){super(),this.initClient()}},Ot=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()}},Dt=class extends I{static{i(this,"MimoProvider")}name="mimo";config=$("mimo",{baseUrl:"https://api.xiaomimimo.com/anthropic"});constructor(){super(),this.initClient()}},It=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()}},Lt=class extends K{static{i(this,"OpenAIProvider")}name="openai";config=$("openai");constructor(){super(),this.initClient()}},Bt=class extends K{static{i(this,"DeepSeekProvider")}name="deepseek";config=$("deepseek",{baseUrl:"https://api.deepseek.com",replayReasoningContent:!0});constructor(){super(),this.initClient()}},Nt=class extends K{static{i(this,"KimiProvider")}name="kimi";config=$("kimi",{baseUrl:"https://api.moonshot.cn/v1",replayReasoningContent:!0});constructor(){super(),this.initClient()}},Kt=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()}},Ft=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 wt,"anthropic"),openai:i(()=>new Lt,"openai"),deepseek:i(()=>new Bt,"deepseek"),kimi:i(()=>new Nt,"kimi"),"kimi-code":i(()=>new Rt,"kimi-code"),qwen:i(()=>new Kt,"qwen"),zhipu:i(()=>new Ft,"zhipu"),"zhipu-coding":i(()=>new St,"zhipu-coding"),"minimax-coding":i(()=>new At,"minimax-coding"),"mimo-coding":i(()=>new Ot,"mimo-coding"),mimo:i(()=>new Dt,"mimo"),"ark-coding":i(()=>new It,"ark-coding"),"gemini-cli":i(()=>new $e,"gemini-cli"),"codex-cli":i(()=>new je,"codex-cli")},$t=process.env.KODAX_PROVIDER??"zhipu-coding",Tn=new Map;function ei(t){if(A(t))return R[t].apiKeyEnv}i(ei,"resolveApiKeyEnvForProvider");function En(t){let e=t??$t,n=F[e];if(!n)throw new S(`Unknown provider: ${e}. Available: ${Object.keys(F).join(", ")}`,e);let o=ei(e),r=o?process.env[o]:void 0,s=Tn.get(e);if(s&&s.apiKey===r)return s.instance;let a=n();return Tn.set(e,{apiKey:r,instance:a}),a}i(En,"getProvider");function Mn(t){return A(t)?!!process.env[R[t].apiKeyEnv]:!1}i(Mn,"isProviderConfigured");function Pn(t){return A(t)?R[t].model:null}i(Pn,"getProviderModel");function wn(t,e){if(!A(t))return"unknown";let n=R[t],o=e??n.model;return n.modelReasoningCapabilities?.[o]??n.reasoningCapability}i(wn,"getProviderConfiguredReasoningCapability");function Sn(t){return A(t)?U(R[t].capabilityProfile):null}i(Sn,"getProviderConfiguredCapabilityProfile");function Rn(){let t=[];for(let e of Object.keys(F)){let n=R[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(Rn,"getProviderList");function An(t){let e=R[t];return e?e.models?[e.model,...e.models.map(n=>n.id)]:[e.model]:[]}i(An,"getProviderModels");function A(t){return t in F}i(A,"isProviderName");function Ut(t){return{id:t.model}}i(Ut,"makeDefaultDescriptor");function Ve(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(Ve,"effectiveCapabilities");function Ge(t){let e=R[t];return e?[Ut(e),...e.models??[]]:[]}i(Ge,"getProviderModelDescriptors");function He(t,e){let n=R[t];if(!n)return;if(e===n.model)return Ve(t,n,Ut(n));let o=n.models?.find(r=>r.id===e);if(o)return Ve(t,n,o)}i(He,"getModelCapabilities");function Je(){let t=[];for(let e of Object.keys(F)){let n=R[e];t.push(Ve(e,n,Ut(n)));for(let o of n.models??[])t.push(Ve(e,n,o))}return t}i(Je,"listBuiltinModelCapabilities");var ti=new Set(["compat","sdk"]),ni=new Set(["count-tokens","models-list","minimal-message","unsupported"]);function oi(t){return t==="anthropic"?"count-tokens":"models-list"}i(oi,"defaultVerifyStrategyForProtocol");function xe(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&&!ti.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(!ni.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(xe,"validateCustomProviderConfig");function ii(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??oi(t.protocol)}}i(ii,"buildProviderConfig");function Ye(t){xe(t);let e=ii(t);return t.protocol==="anthropic"?new jt(t.name,e):new Xt(t.name,e)}i(Ye,"createCustomProvider");var jt=class extends I{static{i(this,"DynamicAnthropicProvider")}name;config;constructor(e,n){super(),this.name=e,this.config=n,this.initClient()}},Xt=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,Wt=new Map;function On(t){let e=new Set,n=new Map,o=new Map;for(let r of t){if(xe(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,()=>Ye(r))}V.clear(),Wt.clear();for(let[r,s]of n)V.set(r,s);for(let[r,s]of o)Wt.set(r,s)}i(On,"registerCustomProviders");function qe(t){let e=Wt.get(t);return e?e():void 0}i(qe,"getCustomProvider");function de(t){return V.has(t)}i(de,"isCustomProviderName");function Qe(){return[...V.keys()]}i(Qe,"getCustomProviderNames");function Dn(){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(Dn,"getCustomProviderList");function In(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(In,"getCustomProviderModels");function Ln(t){return typeof t=="string"?{id:t}:t}i(Ln,"customDescriptorToFull");function Ze(t){let e=V.get(t);if(!e)return;let n={id:e.model},o=(e.models??[]).map(Ln).filter(r=>r.id!==e.model);return[n,...o]}i(Ze,"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(Ln).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 Bn(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(Bn,"getCustomProviderVerifyMetadata");function et(){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(et,"listCustomProviderModelCapabilities");var H=new Map,ri=0;function zt(t){let e=H.get(t);if(!(!e||e.length===0))return e[e.length-1]}i(zt,"getActiveRuntimeProviderRegistration");function si(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(si,"removeRuntimeProviderRegistration");function Nn(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:${++ri}`,name:n,factory:e},r=H.get(n)??[];return H.set(n,[...r,o]),()=>{si(o.id)}}i(Nn,"registerModelProvider");function tt(t){let e=zt(t);return e?e.factory():void 0}i(tt,"getRuntimeModelProvider");function Te(t){return zt(t)!==void 0}i(Te,"isRuntimeModelProviderName");function nt(){return Array.from(H.keys()).filter(t=>zt(t)!==void 0)}i(nt,"getRuntimeModelProviderNames");function Kn(){H.clear()}i(Kn,"clearRuntimeModelProviders");function Vt(t){if(A(t))return F[t]();let e=tt(t);if(e)return e;let n=qe(t);if(n)return n;let o=it();throw new Error(`Unknown provider: ${t}. Available: ${o.join(", ")}`)}i(Vt,"resolveProvider");function ot(t){return A(t)||Te(t)||de(t)}i(ot,"isKnownProvider");function it(){let t=Object.keys(F),e=nt(),n=Qe();return[...new Set([...t,...e,...n])]}i(it,"getAvailableProviderNames");function Gt(t){return A(t)?Ge(t):Ze(t)??[]}i(Gt,"resolveProviderModelDescriptors");function Fn(t,e){return A(t)?He(t,e):ue(t,e)}i(Fn,"resolveModelCapabilities");function $n(){return[...Je(),...et()]}i($n,"listAllModelCapabilities");function ai(t){if(A(t)){let e=R[t];return{apiKeyEnv:e.apiKeyEnv,verifyStrategy:e.verifyStrategy}}if(!Te(t))return Bn(t)}i(ai,"getProviderVerifyMetadata");async function Un(t,e){let n=ai(t);if(!n&&!ot(t))return{ok:!1,error:"unsupported",strategy:"unsupported",durationMs:0,approxTokensSpent:0,message:`Unknown provider: "${t}". Available: ${it().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=Vt(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(Un,"verifyProviderCredential");async function jn(t,e){if(!ot(t))return{ok:!1,source:"failed",error:`Unknown provider: "${t}"`,durationMs:0};let n=Gt(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(jn,"listProviderModels");var Xn={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}},"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},"MiniMax-M3":{inputPer1M:.005,outputPer1M:.015},"MiniMax-M2.7":{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 Ht(t,e,n){let o=n?.[t]?.[e];return o||Xn[t]?.[e]}i(Ht,"getCostRate");function Jt(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(Jt,"calculateCost");function ci(){return{records:[],retries:[]}}i(ci,"createCostTracker");function li(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(li,"recordRetry");function Yt(t,e,n){let o=Ht(e.provider,e.model,n),r=(e.cacheReadTokens??0)+(e.cacheWriteTokens??0),s=o?Jt(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(Yt,"recordUsage");function ui(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(ui,"getSummary");function rt(t){return t<.01?`$${t.toFixed(4)}`:t<1?`$${t.toFixed(3)}`:`$${t.toFixed(2)}`}i(rt,"formatCost");function di(t){let e=[];if(e.push(`Session Cost: ${rt(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}: ${rt(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}: ${rt(s.cost)} (${s.calls} calls)`)}return e.join(`
31
+ `)}i(di,"formatCostReport");var Wn={inputTokens:0,outputTokens:0,totalTokens:0},pi=3e4;async function fi(t){let e=new AbortController,n=t.timeoutMs??pi,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??Wn,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=Yt(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:mi(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:Wn,costTracker:t.costTracker,stopReason:d,error:u}}finally{clearTimeout(s),t.abortSignal&&t.abortSignal.removeEventListener("abort",a)}}i(fi,"sideQuery");function mi(t){return t==="max_tokens"?"max_tokens":"end_turn"}i(mi,"mapStopReason");export{G as a,S as b,ge as c,st as d,at as e,ct as f,zn as g,Vn as h,Gn as i,Hn as j,qt as k,Qt as l,lt as m,Q as n,Me as o,Zt as p,Z as q,ee as r,ut as s,mt as t,gt as u,te as v,we as w,yt as x,eo as y,Se as z,Re as A,Ae as B,z as C,ne as D,oe as E,vt as F,kt as G,ie as H,bt as I,ye as J,I as K,K as L,ae as M,ce as N,be as O,Ce as P,R as Q,F as R,$t as S,En as T,Mn as U,Pn as V,wn as W,Sn as X,Rn as Y,An as Z,A as _,Ge as $,He as aa,Je as ba,xe as ca,Ye as da,On as ea,qe as fa,de as ga,Qe as ha,Dn as ia,In as ja,Ze as ka,ue as la,et as ma,Nn as na,tt as oa,Te as pa,nt as qa,Kn as ra,Vt as sa,ot as ta,it as ua,Gt as va,Fn as wa,$n as xa,Un as ya,jn as za,Xn as Aa,Ht as Ba,Jt as Ca,ci as Da,li as Ea,Yt as Fa,ui as Ga,rt as Ha,di as Ia,fi 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};