dsh-connect-trae 3.4.1 → 3.4.3

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.
package/lib/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import z from "@deepseek-ai/schemastery";
2
+ import { Context, ProviderStreams } from "@earendil-works/pi-ai";
2
3
  import { PiAiAdapter } from "@deepseek-ai/dsh-llm-pi-ai";
3
- import { Context } from "@deepseek-ai/cordis";
4
+ import { Context as Context$1 } from "@deepseek-ai/cordis";
4
5
  import { AttachmentStore } from "@deepseek-ai/dsh-attachment";
5
6
  //#region src/reasoning.d.ts
6
7
  declare const TRAE_REASONING_EFFORTS: readonly ["minimal", "low", "medium", "high", "xhigh"];
@@ -295,6 +296,113 @@ declare class TraeCatalog {
295
296
  set(models: readonly TraeModelInfo[]): void;
296
297
  }
297
298
  //#endregion
299
+ //#region src/pi-ai-runtime.d.ts
300
+ /**
301
+ * Which `@earendil-works/pi-ai` copy THIS plugin resolves at runtime, and which
302
+ * context shape that copy consumes (issue #27).
303
+ *
304
+ * WHY THIS EXISTS. The plugin declares pi-ai as a peer, so the module it
305
+ * imports is whatever the install provides — and the two generations consume
306
+ * DIFFERENT context shapes:
307
+ *
308
+ * - **modern** (0.86+): every public entry point calls `normalizeContext()`
309
+ * once, so what reaches a provider is a normalized *transcript*:
310
+ * `{ messages: [ { role: 'system', content, toolsAdded }, … ] }`. The system
311
+ * prompt and the tool declarations live ONLY on that leading system message;
312
+ * the top-level `systemPrompt` / `tools` fields are gone, and the api never
313
+ * reads them back.
314
+ * - **legacy** (≤0.85): expects `{ systemPrompt, tools, messages }` and has no
315
+ * branch for a `system` message inside `messages` at all. Its token estimator
316
+ * crashes on one — `estimateMessageTokens` iterates the string `content`
317
+ * character by character and reads `.name` off a character — and its wire
318
+ * converter silently drops it.
319
+ *
320
+ * The mismatch is reachable in BOTH directions, because DSH itself spans both
321
+ * generations (`dsh-llm-pi-ai` ≤0.2.0-rc.1 declares `^0.85.1`; 0.2.0-rc.2 and
322
+ * later declare `^0.87.1`, 0.2.1-alpha.2 declares `^1.0.2`), while this plugin
323
+ * accepts the whole range (`>=0.1.7-rc.1 <0.3.0-0`). Whichever side is newer,
324
+ * the other one is wrong:
325
+ *
326
+ * | host | resolved api | outcome |
327
+ * |---|---|---|
328
+ * | legacy | legacy | fine |
329
+ * | legacy | modern | no crash, but prompt + tools silently LOST |
330
+ * | modern | legacy | CRASH: `Cannot read properties of undefined (reading 'length')` |
331
+ * | modern | modern | fine |
332
+ *
333
+ * THE BOUNDARY IS 0.86, NOT 0.87. Checked against the published tarballs:
334
+ * `dist/utils/transcript.js` — and with it `normalizeContext`, the
335
+ * `role === 'system'` estimator branch, and the switch away from reading
336
+ * `systemPrompt`/`tools` — first appears in **0.86.0**. A `>= 0.87` gate
337
+ * misclassifies 0.86.x as legacy and folds a context the modern api consumes
338
+ * natively, which deletes the only carrier of the prompt and the tools.
339
+ *
340
+ * HOW the generation is decided, in order:
341
+ *
342
+ * 1. **The manifest version** of the copy this plugin resolved. Numeric semver
343
+ * comparison (`minor >= 86`), so `0.86.0` and the already-shipped `1.x` line
344
+ * both land on modern, and `0.9.0` correctly sorts below `0.86.0`.
345
+ * 2. **The package's own layout**, when no version could be read: the transcript
346
+ * module sits at `dist/utils/transcript.js` (the exports map's
347
+ * `"./utils/*" → "./dist/utils/*.js"` convention is identical in every
348
+ * published generation, 0.82 through 1.x).
349
+ *
350
+ * Both checks are deliberately resolver-independent. `import.meta.resolve()` is
351
+ * NOT used to answer this question: bundler-style resolvers (vite, and equally
352
+ * `vitest` under it) expand the `"./utils/*"` wildcard to a path and return it
353
+ * WITHOUT checking that the file exists, so a probe built on it answers "modern"
354
+ * for 0.85.1 — the exact false positive that would leave issue #27 unfixed while
355
+ * claiming to be capability-based. Reading the package directory cannot be
356
+ * fooled that way.
357
+ *
358
+ * The version is therefore the primary signal, and it also keeps the gate
359
+ * forward-compatible for free: a future 2.x needs no change here, whereas a
360
+ * file-name check alone would have to know about it.
361
+ *
362
+ * @module dsh-connect-trae/pi-ai-runtime
363
+ */
364
+ /** The context generation a resolved pi-ai api module speaks. */
365
+ type PiAiGeneration = 'legacy' | 'modern';
366
+ /** What {@link piAiRuntimeInfo} reports about the resolved pi-ai copy. */
367
+ interface PiAiRuntimeInfo {
368
+ /** `modern` (0.86+: consumes normalized transcripts) or `legacy` (≤0.85 shape). */
369
+ generation: PiAiGeneration;
370
+ /** Version of the resolved copy, when the manifest could be read. */
371
+ version: string | undefined;
372
+ /** Package directory of the resolved copy, when it could be located. */
373
+ resolvedFrom: string | undefined;
374
+ /**
375
+ * Whether the copy could be inspected at all. `false` means pi-ai does not
376
+ * resolve from this plugin — the normal state of a market install probed
377
+ * OUTSIDE the DSH host, where the host supplies the module at plugin runtime.
378
+ * Inside DSH the copy is always inspectable: a nested one shadows host
379
+ * injection, and without a nested one the host supplies its own.
380
+ */
381
+ inspected: boolean;
382
+ }
383
+ /** Resolve (once) which pi-ai copy this plugin runs against. */
384
+ declare function piAiRuntimeInfo(): PiAiRuntimeInfo;
385
+ /**
386
+ * The generation decision table, exported so every branch is pinned by tests
387
+ * rather than by a comment here — including the two this development tree can
388
+ * never reach (no local copy, and a copy with no readable version), which are
389
+ * exactly the states a market install founds its gate on.
390
+ *
391
+ * @param version - manifest version of the resolved copy, if readable.
392
+ * @param transcriptPresent - whether that copy actually ships
393
+ * `dist/utils/transcript.js`; `undefined` when nothing was inspected.
394
+ */
395
+ declare function decidePiAiGeneration(version: string | undefined, transcriptPresent: boolean | undefined): PiAiGeneration;
396
+ /**
397
+ * The generation a version string belongs to, or `undefined` when the string
398
+ * cannot be read as a version at all.
399
+ *
400
+ * Only the minor of the `0.x` line matters for the boundary, and `major >= 1`
401
+ * is modern by definition (1.0.x is already in use by `dsh-llm-pi-ai@0.2.1-alpha.2`
402
+ * and consumes transcripts natively).
403
+ */
404
+ declare function generationOf(version: string | undefined): PiAiGeneration | undefined;
405
+ //#endregion
298
406
  //#region src/upstream.d.ts
299
407
  type TraeUpstreamErrorKind = 'authentication' | 'hard_credit' | 'soft_rate' | 'not_found' | 'server' | 'client' | 'unconfigured';
300
408
  type TraeChatResult = {
@@ -351,6 +459,67 @@ declare const TRAE_PROVIDER_DISPLAY_NAMES: Readonly<Record<TraeRegion, string>>;
351
459
  /** Region a provider route id belongs to. */
352
460
  declare function regionOfTraeProvider(provider: string): TraeRegion | undefined;
353
461
  declare const TRAE_STREAM_IDLE_TIMEOUT_MS = 300000;
462
+ /**
463
+ * Fold a NORMALIZED transcript back into the `{ systemPrompt, tools, messages }`
464
+ * shape a LEGACY (≤0.85) api consumes — issue #27's crash and its two silent
465
+ * siblings.
466
+ *
467
+ * WHY (issue #27). 0.86+ normalizes at every public entry point, and DSH's host
468
+ * calls `Models.streamSimple()`, so on a modern host the context arriving here
469
+ * ALWAYS carries its prompt and its tool declarations on a leading system
470
+ * message — the top-level fields are gone. A legacy api has no branch for that
471
+ * message: `estimateMessageTokens` iterates the string `content` character by
472
+ * character and reads `.name` off a character, throwing
473
+ * `Cannot read properties of undefined (reading 'length')` before the request
474
+ * is even built. That is what surfaced as `PI_AI_ERROR` with all-zero usage.
475
+ *
476
+ * The fold must carry MORE than the prompt. `toolsAdded` is the ONLY remaining
477
+ * carrier of the tool declarations once `normalizeContext` has run, and a
478
+ * legacy api reads tools exclusively from `context.tools` — so lifting the text
479
+ * alone would trade a loud crash for a silent one: a model that can never emit
480
+ * a `toolCall` because the request declared no tools.
481
+ *
482
+ * Shape-guarded, not version-guarded: it asks "is there a system message inside
483
+ * `messages`?" rather than "which pi-ai is loaded?". A version test would be
484
+ * wrong the moment either side moves, and this predicate is an identity for the
485
+ * legacy shape, so the ordinary path stays byte-for-byte untouched. WHICH
486
+ * branch runs at all IS generation-gated — see {@link withContextAdaptation}.
487
+ *
488
+ * Returns the SAME object when there is nothing to fold, so a legacy host
489
+ * (which never normalizes) pays one `Array.some` and nothing else.
490
+ */
491
+ declare function adaptLegacyPiAiContext(context: Context): Context;
492
+ /**
493
+ * Lift the legacy `{ systemPrompt, tools, messages }` shape into the normalized
494
+ * transcript a MODERN (0.86+) api consumes — the reverse mismatch, and just as
495
+ * silent.
496
+ *
497
+ * WHY. DSH spans both generations (`dsh-llm-pi-ai` ≤0.2.0-rc.1 pins `^0.85.1`)
498
+ * while this plugin may resolve a newer copy than its host. A LEGACY host never
499
+ * normalizes, so it hands the api the legacy shape; a modern api reads the
500
+ * prompt from a leading system message and the tools from its `toolsAdded`, and
501
+ * never looks at the top-level fields — so both are dropped without a sound.
502
+ * Verified against the published tarballs: the request that reaches the wire
503
+ * carries no system turn and `tools: []`.
504
+ *
505
+ * Identity for anything already in transcript shape, and for a context with
506
+ * neither a prompt nor tools (pi-ai's own `createInitialSystemMessage` returns
507
+ * `undefined` there, keeping an empty transcript empty).
508
+ */
509
+ declare function adaptModernPiAiContext(context: Context): Context;
510
+ /**
511
+ * Wrap one pi-ai API module so BOTH stream entry points receive a context its
512
+ * own generation can consume.
513
+ *
514
+ * The gate is the copy THIS plugin resolved (see {@link piAiRuntimeInfo}) —
515
+ * what the host runs says nothing about it, because a nested copy on disk
516
+ * shadows host injection and vice versa. The wrapping is cheap and lazy: only
517
+ * `stream`/`streamSimple` are intercepted, the deferred-fetch entry points take
518
+ * no context and are passed through untouched, and each adapter function is an
519
+ * identity unless the context is actually the other generation's shape. So a
520
+ * healthy install does one `Array.some` per request.
521
+ */
522
+ declare function withContextAdaptation(api: ProviderStreams, generation: PiAiGeneration): ProviderStreams;
354
523
  interface TraeAdapterOptions {
355
524
  shim: TraeShim;
356
525
  catalog: TraeCatalog;
@@ -359,6 +528,12 @@ interface TraeAdapterOptions {
359
528
  /** pi-ai provider name and profile display name; defaults to the CN name. */
360
529
  displayName?: string;
361
530
  resolveAttachments?: () => AttachmentStore | undefined;
531
+ /**
532
+ * Override the resolved pi-ai generation (see {@link withContextAdaptation});
533
+ * tests use it to exercise the combination this machine does not have
534
+ * installed.
535
+ */
536
+ piAiGeneration?: PiAiGeneration;
362
537
  }
363
538
  interface TraeAdapter {
364
539
  adapter: PiAiAdapter;
@@ -2116,7 +2291,7 @@ interface TraeUsageRouteOptions {
2116
2291
  */
2117
2292
  declare function traeWebUsage(deps: TraeUsageRouteOptions, region: TraeRegion): Promise<TraeWebUsage>;
2118
2293
  /** Mount the GET usage route on an optional webServer context. */
2119
- declare function registerTraeUsageRoute(ctx: Context, deps: TraeUsageRouteOptions): void;
2294
+ declare function registerTraeUsageRoute(ctx: Context$1, deps: TraeUsageRouteOptions): void;
2120
2295
  //#endregion
2121
2296
  //#region src/model-probe.d.ts
2122
2297
  /** How one model answered. */
@@ -2333,6 +2508,6 @@ declare function regionEnabled(config: Config, region: TraeRegion): boolean;
2333
2508
  */
2334
2509
  declare function asVolatile<S>(schema: S): S;
2335
2510
  declare const Config: z<Config>;
2336
- declare function apply(ctx: Context, config: Config): void;
2511
+ declare function apply(ctx: Context$1, config: Config): void;
2337
2512
  //#endregion
2338
- export { Config, FALLBACK_TRAE_MODELS, FALLBACK_TRAE_MODELS_AI, REGION_GATEWAYS, type RawChatDelta, type RawChatMessage, type RawChatTool, SseDecoder, type SseEvent, TRAE_ACCOUNT_STATUS_PATH, TRAE_AI_PROVIDER, TRAE_CHECKIN_PATH, TRAE_CN_AGENT_TASK_PATH, TRAE_CN_TITLE_PATH, TRAE_MODELS_TEST_PATH, TRAE_MODEL_DETAIL_FUNCTIONS, TRAE_MODEL_DETAIL_PATH, TRAE_PAY_BASE, TRAE_PROBE_TIMEOUT_MS, TRAE_PROVIDER, TRAE_PROVIDERS, TRAE_PROVIDER_DISPLAY_NAMES, TRAE_RAW_CHAT_V1_PATH, TRAE_RAW_CHAT_V2_PATH, TRAE_REASONING_EFFORTS, TRAE_REGIONS, TRAE_REGION_PARAM, TRAE_SETTINGS_NS, TRAE_SOLO_CHAT_PATH, TRAE_SOLO_FUNCTION, TRAE_SOLO_MODELS_PATH, TRAE_SOLO_REMOTE_BASE, TRAE_STATE_DB_FILENAME, TRAE_STREAM_IDLE_TIMEOUT_MS, TRAE_USAGE_PATH, TRAE_VERSION_CODE_FALLBACK, type TraeActivityRule, type TraeAdapter, type TraeCachedModelConfig, TraeCatalog, type TraeChatResult, type TraeCheckinClaim, type TraeCheckinStatus, type TraeContextBudget, type TraeCredential, TraeCredentialStore, TraeDelegatingUpstreamClient, type TraeDiscoveredModel, type TraeDiscoveredReasoning, type TraeEdition, TraeFallbackUpstreamClient, type TraeFallbackUpstreamOptions, type TraeFusionRawChatEnvelope, TraeGatedUpstreamClient, type TraeGatedUpstreamOptions, type TraeIdentity, type TraeInputModality, type TraeModelDetailRequest, type TraeModelInfo, type TraeModelProbeVerdict, type TraeModelsTestResponse, type TraeObservedModelConfig, type TraeProbeResult, type TraeProbeVerdict, TraeRawCapabilityController, type TraeRawCapabilityControllerOptions, type TraeRawCapabilitySnapshot, TraeRawCapabilityState, type TraeRawChatBehaviorConfig, type TraeRawChatCapability, type TraeRawChatClientOptions, type TraeRawChatConfig, type TraeRawChatFailureReason, type TraeRawChatRuntimeConfig, TraeRawChatUpstreamClient, type TraeRawDiagnostic, type TraeRawDiagnosticState, type TraeRawGateway, type TraeRawGatewayOptions, type TraeRawResolverResult, type TraeReasoningCapability, type TraeReasoningEffort, type TraeRefreshDevice, type TraeRegion, type TraeRegionGateways, TraeRegionState, type TraeShim, TraeSoloBridge, type TraeSoloClientOptions, TraeSoloRemoteCatalogClient, type TraeSoloRemoteCatalogOptions, TraeSoloUpstreamClient, type TraeStorageCandidate, type TraeStreamEvent, type TraeUpstreamClient, TraeUsageClient, type TraeUsageOptions, type TraeUsagePack, type TraeUsageRouteOptions, type TraeUsageSnapshot, type TraeUsageSummary, type TraeUsageView, type TraeWebAccountState, type TraeWebActivity, type TraeWebAppLogin, type TraeWebCheckin, type TraeWebCheckinClaim, type TraeWebCredits, type TraeWebUsage, type TraeWireModel, UnconfiguredTraeUpstreamClient, apply, applyContextBudgets, applyImageSelection, applyReasoningEffort, asVolatile, bridgeTraeSoloStream, buildTraeAgentTaskBody, buildTraeCnHeaders, buildTraeFusionRawChatEnvelope, buildTraeModelDetailRequest, buildTraeRawChatDraft, buildTraeRawChatRuntimeConfig, classifyProbeError, classifyProbeResult, classifyTraeRawChatFailure, configuredAccountsOf, createTraeAdapter, createTraeRawGateway, createTraeShim, decodeRawChatChunk, decodeTraeEvent, decryptTraeStorageValue, deriveCatalog, describeIdShape, describeNameShape, discoveredCatalog, fallbackModelsFor, followAppLoginEnabled, hashTraeRawChatArg, identityHeaders, inject, legacyTraeOwnAuthPath, maskUserPath, mergeTraeModelSources, name, nextRegionEnabled, nextRegionSlots, normalizeTraeCredential, normalizeTraeVersionCode, parseObservedModelConfig, parseReasoningCapability, parseTraeAuthValue, parseTraeCachedModel, parseTraeModelExtraConfigLogLine, parseTraeRawChatBehaviorConfig, parseTraeRemoteModel, parseTraeStorageDocument, pickTraeStorageIdentity, prepareSoloBody, probeBody, probeModelsSequentially, probeOneModel, probeTraeRawChatCapability, rawCapabilityDiagnostic, rawCapabilityFingerprint, readTraeCachedModel, readTraeCliIdentity, readTraeIdentity, refreshTraeCredential, regionEnabled, regionEnabledOf, regionOfCredential, regionOfEdition, regionOfHost, regionOfTraeProvider, regionOfTraeStatusUrl, regionOfUserRegion, regionStateOf, registerTraeUsageRoute, resolveTraeIdentity, resolveTraeRawRuntime, sanitizeCatalog, settingsNamespaceOf, traeEndpoint, traeInputModalities, traeModelDisplayName, traeOwnAuthPath, traeRawChatExtraInfo, traeStateDatabaseCandidates, traeStorageCandidates, traeWebUsage, traeWindowsAppNames, unwrapVolatile, unwrapVolatileDeep, withTraeRegion };
2513
+ export { Config, FALLBACK_TRAE_MODELS, FALLBACK_TRAE_MODELS_AI, type PiAiGeneration, type PiAiRuntimeInfo, REGION_GATEWAYS, type RawChatDelta, type RawChatMessage, type RawChatTool, SseDecoder, type SseEvent, TRAE_ACCOUNT_STATUS_PATH, TRAE_AI_PROVIDER, TRAE_CHECKIN_PATH, TRAE_CN_AGENT_TASK_PATH, TRAE_CN_TITLE_PATH, TRAE_MODELS_TEST_PATH, TRAE_MODEL_DETAIL_FUNCTIONS, TRAE_MODEL_DETAIL_PATH, TRAE_PAY_BASE, TRAE_PROBE_TIMEOUT_MS, TRAE_PROVIDER, TRAE_PROVIDERS, TRAE_PROVIDER_DISPLAY_NAMES, TRAE_RAW_CHAT_V1_PATH, TRAE_RAW_CHAT_V2_PATH, TRAE_REASONING_EFFORTS, TRAE_REGIONS, TRAE_REGION_PARAM, TRAE_SETTINGS_NS, TRAE_SOLO_CHAT_PATH, TRAE_SOLO_FUNCTION, TRAE_SOLO_MODELS_PATH, TRAE_SOLO_REMOTE_BASE, TRAE_STATE_DB_FILENAME, TRAE_STREAM_IDLE_TIMEOUT_MS, TRAE_USAGE_PATH, TRAE_VERSION_CODE_FALLBACK, type TraeActivityRule, type TraeAdapter, type TraeCachedModelConfig, TraeCatalog, type TraeChatResult, type TraeCheckinClaim, type TraeCheckinStatus, type TraeContextBudget, type TraeCredential, TraeCredentialStore, TraeDelegatingUpstreamClient, type TraeDiscoveredModel, type TraeDiscoveredReasoning, type TraeEdition, TraeFallbackUpstreamClient, type TraeFallbackUpstreamOptions, type TraeFusionRawChatEnvelope, TraeGatedUpstreamClient, type TraeGatedUpstreamOptions, type TraeIdentity, type TraeInputModality, type TraeModelDetailRequest, type TraeModelInfo, type TraeModelProbeVerdict, type TraeModelsTestResponse, type TraeObservedModelConfig, type TraeProbeResult, type TraeProbeVerdict, TraeRawCapabilityController, type TraeRawCapabilityControllerOptions, type TraeRawCapabilitySnapshot, TraeRawCapabilityState, type TraeRawChatBehaviorConfig, type TraeRawChatCapability, type TraeRawChatClientOptions, type TraeRawChatConfig, type TraeRawChatFailureReason, type TraeRawChatRuntimeConfig, TraeRawChatUpstreamClient, type TraeRawDiagnostic, type TraeRawDiagnosticState, type TraeRawGateway, type TraeRawGatewayOptions, type TraeRawResolverResult, type TraeReasoningCapability, type TraeReasoningEffort, type TraeRefreshDevice, type TraeRegion, type TraeRegionGateways, TraeRegionState, type TraeShim, TraeSoloBridge, type TraeSoloClientOptions, TraeSoloRemoteCatalogClient, type TraeSoloRemoteCatalogOptions, TraeSoloUpstreamClient, type TraeStorageCandidate, type TraeStreamEvent, type TraeUpstreamClient, TraeUsageClient, type TraeUsageOptions, type TraeUsagePack, type TraeUsageRouteOptions, type TraeUsageSnapshot, type TraeUsageSummary, type TraeUsageView, type TraeWebAccountState, type TraeWebActivity, type TraeWebAppLogin, type TraeWebCheckin, type TraeWebCheckinClaim, type TraeWebCredits, type TraeWebUsage, type TraeWireModel, UnconfiguredTraeUpstreamClient, adaptLegacyPiAiContext, adaptModernPiAiContext, apply, applyContextBudgets, applyImageSelection, applyReasoningEffort, asVolatile, bridgeTraeSoloStream, buildTraeAgentTaskBody, buildTraeCnHeaders, buildTraeFusionRawChatEnvelope, buildTraeModelDetailRequest, buildTraeRawChatDraft, buildTraeRawChatRuntimeConfig, classifyProbeError, classifyProbeResult, classifyTraeRawChatFailure, configuredAccountsOf, createTraeAdapter, createTraeRawGateway, createTraeShim, decidePiAiGeneration, decodeRawChatChunk, decodeTraeEvent, decryptTraeStorageValue, deriveCatalog, describeIdShape, describeNameShape, discoveredCatalog, fallbackModelsFor, followAppLoginEnabled, generationOf, hashTraeRawChatArg, identityHeaders, inject, legacyTraeOwnAuthPath, maskUserPath, mergeTraeModelSources, name, nextRegionEnabled, nextRegionSlots, normalizeTraeCredential, normalizeTraeVersionCode, parseObservedModelConfig, parseReasoningCapability, parseTraeAuthValue, parseTraeCachedModel, parseTraeModelExtraConfigLogLine, parseTraeRawChatBehaviorConfig, parseTraeRemoteModel, parseTraeStorageDocument, piAiRuntimeInfo, pickTraeStorageIdentity, prepareSoloBody, probeBody, probeModelsSequentially, probeOneModel, probeTraeRawChatCapability, rawCapabilityDiagnostic, rawCapabilityFingerprint, readTraeCachedModel, readTraeCliIdentity, readTraeIdentity, refreshTraeCredential, regionEnabled, regionEnabledOf, regionOfCredential, regionOfEdition, regionOfHost, regionOfTraeProvider, regionOfTraeStatusUrl, regionOfUserRegion, regionStateOf, registerTraeUsageRoute, resolveTraeIdentity, resolveTraeRawRuntime, sanitizeCatalog, settingsNamespaceOf, traeEndpoint, traeInputModalities, traeModelDisplayName, traeOwnAuthPath, traeRawChatExtraInfo, traeStateDatabaseCandidates, traeStorageCandidates, traeWebUsage, traeWindowsAppNames, unwrapVolatile, unwrapVolatileDeep, withContextAdaptation, withTraeRegion };
package/lib/index.js CHANGED
@@ -3,9 +3,11 @@ import { createProvider } from "@earendil-works/pi-ai";
3
3
  import { openAICompletionsApi } from "@earendil-works/pi-ai/api/openai-completions.lazy";
4
4
  import { resolveRetryPolicy } from "@deepseek-ai/dsh-llm";
5
5
  import { PiAiAdapter } from "@deepseek-ai/dsh-llm-pi-ai";
6
+ import { existsSync, readFileSync } from "node:fs";
7
+ import { dirname, join } from "node:path";
8
+ import { fileURLToPath } from "node:url";
6
9
  import { readFile, rm, stat } from "node:fs/promises";
7
10
  import { createDecipheriv, createHash, randomBytes, randomUUID, sign, timingSafeEqual } from "node:crypto";
8
- import { dirname, join } from "node:path";
9
11
  import { withFileLock, writeFileAtomic } from "@deepseek-ai/dsh-atomic-write";
10
12
  import { resolveDshHome } from "@deepseek-ai/dsh-home-paths";
11
13
  import { cpus, homedir, hostname, release, userInfo } from "node:os";
@@ -13,7 +15,6 @@ import { createServer } from "node:http";
13
15
  import { Readable } from "node:stream";
14
16
  import { execFile } from "node:child_process";
15
17
  import { promisify } from "node:util";
16
- import { existsSync } from "node:fs";
17
18
  //#region src/catalog.ts
18
19
  /**
19
20
  * Bootstrap catalog: identity only where current Trae metadata has not been
@@ -322,6 +323,179 @@ var TraeCatalog = class {
322
323
  }
323
324
  };
324
325
  //#endregion
326
+ //#region src/pi-ai-runtime.ts
327
+ /**
328
+ * Which `@earendil-works/pi-ai` copy THIS plugin resolves at runtime, and which
329
+ * context shape that copy consumes (issue #27).
330
+ *
331
+ * WHY THIS EXISTS. The plugin declares pi-ai as a peer, so the module it
332
+ * imports is whatever the install provides — and the two generations consume
333
+ * DIFFERENT context shapes:
334
+ *
335
+ * - **modern** (0.86+): every public entry point calls `normalizeContext()`
336
+ * once, so what reaches a provider is a normalized *transcript*:
337
+ * `{ messages: [ { role: 'system', content, toolsAdded }, … ] }`. The system
338
+ * prompt and the tool declarations live ONLY on that leading system message;
339
+ * the top-level `systemPrompt` / `tools` fields are gone, and the api never
340
+ * reads them back.
341
+ * - **legacy** (≤0.85): expects `{ systemPrompt, tools, messages }` and has no
342
+ * branch for a `system` message inside `messages` at all. Its token estimator
343
+ * crashes on one — `estimateMessageTokens` iterates the string `content`
344
+ * character by character and reads `.name` off a character — and its wire
345
+ * converter silently drops it.
346
+ *
347
+ * The mismatch is reachable in BOTH directions, because DSH itself spans both
348
+ * generations (`dsh-llm-pi-ai` ≤0.2.0-rc.1 declares `^0.85.1`; 0.2.0-rc.2 and
349
+ * later declare `^0.87.1`, 0.2.1-alpha.2 declares `^1.0.2`), while this plugin
350
+ * accepts the whole range (`>=0.1.7-rc.1 <0.3.0-0`). Whichever side is newer,
351
+ * the other one is wrong:
352
+ *
353
+ * | host | resolved api | outcome |
354
+ * |---|---|---|
355
+ * | legacy | legacy | fine |
356
+ * | legacy | modern | no crash, but prompt + tools silently LOST |
357
+ * | modern | legacy | CRASH: `Cannot read properties of undefined (reading 'length')` |
358
+ * | modern | modern | fine |
359
+ *
360
+ * THE BOUNDARY IS 0.86, NOT 0.87. Checked against the published tarballs:
361
+ * `dist/utils/transcript.js` — and with it `normalizeContext`, the
362
+ * `role === 'system'` estimator branch, and the switch away from reading
363
+ * `systemPrompt`/`tools` — first appears in **0.86.0**. A `>= 0.87` gate
364
+ * misclassifies 0.86.x as legacy and folds a context the modern api consumes
365
+ * natively, which deletes the only carrier of the prompt and the tools.
366
+ *
367
+ * HOW the generation is decided, in order:
368
+ *
369
+ * 1. **The manifest version** of the copy this plugin resolved. Numeric semver
370
+ * comparison (`minor >= 86`), so `0.86.0` and the already-shipped `1.x` line
371
+ * both land on modern, and `0.9.0` correctly sorts below `0.86.0`.
372
+ * 2. **The package's own layout**, when no version could be read: the transcript
373
+ * module sits at `dist/utils/transcript.js` (the exports map's
374
+ * `"./utils/*" → "./dist/utils/*.js"` convention is identical in every
375
+ * published generation, 0.82 through 1.x).
376
+ *
377
+ * Both checks are deliberately resolver-independent. `import.meta.resolve()` is
378
+ * NOT used to answer this question: bundler-style resolvers (vite, and equally
379
+ * `vitest` under it) expand the `"./utils/*"` wildcard to a path and return it
380
+ * WITHOUT checking that the file exists, so a probe built on it answers "modern"
381
+ * for 0.85.1 — the exact false positive that would leave issue #27 unfixed while
382
+ * claiming to be capability-based. Reading the package directory cannot be
383
+ * fooled that way.
384
+ *
385
+ * The version is therefore the primary signal, and it also keeps the gate
386
+ * forward-compatible for free: a future 2.x needs no change here, whereas a
387
+ * file-name check alone would have to know about it.
388
+ *
389
+ * @module dsh-connect-trae/pi-ai-runtime
390
+ */
391
+ let cached;
392
+ /** Resolve (once) which pi-ai copy this plugin runs against. */
393
+ function piAiRuntimeInfo() {
394
+ cached ??= inspectResolvedPiAi();
395
+ return cached;
396
+ }
397
+ function inspectResolvedPiAi() {
398
+ const entry = resolveModuleFile();
399
+ const root = entry === void 0 ? void 0 : findPackageRoot(entry);
400
+ const version = root === void 0 ? void 0 : readVersion(root);
401
+ return {
402
+ generation: decidePiAiGeneration(version, root === void 0 ? void 0 : hasTranscriptModule(root)),
403
+ version,
404
+ resolvedFrom: root,
405
+ inspected: root !== void 0
406
+ };
407
+ }
408
+ /**
409
+ * The generation decision table, exported so every branch is pinned by tests
410
+ * rather than by a comment here — including the two this development tree can
411
+ * never reach (no local copy, and a copy with no readable version), which are
412
+ * exactly the states a market install founds its gate on.
413
+ *
414
+ * @param version - manifest version of the resolved copy, if readable.
415
+ * @param transcriptPresent - whether that copy actually ships
416
+ * `dist/utils/transcript.js`; `undefined` when nothing was inspected.
417
+ */
418
+ function decidePiAiGeneration(version, transcriptPresent) {
419
+ const byVersion = generationOf(version);
420
+ if (byVersion !== void 0) return byVersion;
421
+ if (transcriptPresent === true) return "modern";
422
+ if (transcriptPresent === false) return "legacy";
423
+ return "modern";
424
+ }
425
+ /**
426
+ * The generation a version string belongs to, or `undefined` when the string
427
+ * cannot be read as a version at all.
428
+ *
429
+ * Only the minor of the `0.x` line matters for the boundary, and `major >= 1`
430
+ * is modern by definition (1.0.x is already in use by `dsh-llm-pi-ai@0.2.1-alpha.2`
431
+ * and consumes transcripts natively).
432
+ */
433
+ function generationOf(version) {
434
+ if (version === void 0) return void 0;
435
+ const match = /^(\d+)\.(\d+)\./u.exec(version);
436
+ if (match === null) return void 0;
437
+ const major = Number(match[1]);
438
+ const minor = Number(match[2]);
439
+ if (major > 0) return "modern";
440
+ return minor >= 86 ? "modern" : "legacy";
441
+ }
442
+ /**
443
+ * Whether the resolved package actually contains the transcript module.
444
+ *
445
+ * `dist/utils/transcript.js` is the file every modern generation ships and no
446
+ * legacy one has (checked: absent in 0.82.1 and 0.85.1; present in 0.86.0,
447
+ * 0.86.1, 0.87.1 and 1.0.2). Landing on the file directly, rather than asking a
448
+ * resolver to find it, is what keeps this honest under bundler-style resolution
449
+ * — see the module docstring.
450
+ */
451
+ function hasTranscriptModule(packageRoot) {
452
+ return existsSync(join(packageRoot, "dist", "utils", "transcript.js"));
453
+ }
454
+ /**
455
+ * The file the main entry resolved to; undefined when resolution itself fails.
456
+ *
457
+ * `import.meta.resolve` (not `require.resolve`): pi-ai's exports map exposes
458
+ * "." under the `import` condition only, so the CJS resolver refuses the bare
459
+ * specifier outright — the ESM resolver is the one this plugin actually imports
460
+ * with, so it is also the one whose answer is meaningful here. Only the package
461
+ * LOCATION is taken from it; the generation question is answered from disk.
462
+ */
463
+ function resolveModuleFile() {
464
+ try {
465
+ return fileURLToPath(import.meta.resolve("@earendil-works/pi-ai"));
466
+ } catch {
467
+ return;
468
+ }
469
+ }
470
+ /**
471
+ * The package directory owning `moduleFile`: `dist/index.js` sits directly
472
+ * inside the package, so walking up from the entry finds its manifest. The
473
+ * manifest is claimed only when its `name` matches, so a same-named ancestor (a
474
+ * pnpm virtual-store parent, for instance) cannot be misread.
475
+ */
476
+ function findPackageRoot(moduleFile) {
477
+ let directory = dirname(moduleFile);
478
+ for (let depth = 0; depth < 4; depth++) {
479
+ if (readManifest(directory)?.name === "@earendil-works/pi-ai") return directory;
480
+ const parent = dirname(directory);
481
+ if (parent === directory) return void 0;
482
+ directory = parent;
483
+ }
484
+ }
485
+ /** Version from the manifest already located by {@link findPackageRoot}. */
486
+ function readVersion(packageRoot) {
487
+ const version = readManifest(packageRoot)?.version;
488
+ return typeof version === "string" ? version : void 0;
489
+ }
490
+ function readManifest(directory) {
491
+ try {
492
+ const parsed = JSON.parse(readFileSync(join(directory, "package.json"), "utf8"));
493
+ return typeof parsed === "object" && parsed !== null ? parsed : void 0;
494
+ } catch {
495
+ return;
496
+ }
497
+ }
498
+ //#endregion
325
499
  //#region src/adapter.ts
326
500
  /** Provider route this bundle owns for the domestic (CN) gateway. */
327
501
  const TRAE_PROVIDER = "trae";
@@ -405,9 +579,172 @@ function toPiModel(info, baseUrl, providerId) {
405
579
  compat: { supportsReasoningEffort: info.reasoningEfforts !== void 0 }
406
580
  };
407
581
  }
582
+ function isSystemMessage(message) {
583
+ return typeof message === "object" && message !== null && message.role === "system";
584
+ }
585
+ /**
586
+ * The text of one system message: its `content` (a string, as
587
+ * `normalizeContext` writes, or text blocks, which a provider may send back)
588
+ * followed by its named `sections` — exactly the join pi-ai's own
589
+ * `getSystemMessageText` performs.
590
+ *
591
+ * `sections` is not decoration: it is the modern transcript's second carrier of
592
+ * prompt text ("named, ordered prompt sections rendered verbatim after
593
+ * content"). Folding `content` alone would silently drop a prompt that arrives
594
+ * section-shaped, which is the same asymmetric-loss failure this adapter exists
595
+ * to prevent.
596
+ */
597
+ function systemTextOf(message) {
598
+ const parts = [contentTextOf(message.content)];
599
+ const sections = message.sections;
600
+ if (typeof sections === "object" && sections !== null && !Array.isArray(sections)) {
601
+ for (const text of Object.values(sections)) if (typeof text === "string") parts.push(text);
602
+ }
603
+ return parts.filter((part) => part.length > 0).join("\n\n");
604
+ }
605
+ function contentTextOf(content) {
606
+ if (typeof content === "string") return content;
607
+ if (!Array.isArray(content)) return "";
608
+ const texts = [];
609
+ for (const block of content) {
610
+ if (typeof block !== "object" || block === null) continue;
611
+ const candidate = block;
612
+ if (candidate.type === "text" && typeof candidate.text === "string") texts.push(candidate.text);
613
+ }
614
+ return texts.join("\n\n");
615
+ }
616
+ /**
617
+ * Replay one transcript system message's tool deltas into `state`, the same
618
+ * merge pi-ai's own `getCurrentTools` performs: removals first, then additions,
619
+ * keyed by name so a mid-conversation redeclaration replaces the original.
620
+ */
621
+ function replayToolState(message, state) {
622
+ const removed = message.toolsRemoved;
623
+ if (Array.isArray(removed)) for (const tool of removed) {
624
+ const name = tool?.name;
625
+ if (typeof name === "string") state.delete(name);
626
+ }
627
+ const added = message.toolsAdded;
628
+ if (Array.isArray(added)) for (const tool of added) {
629
+ const name = tool?.name;
630
+ if (typeof name === "string") state.set(name, tool);
631
+ }
632
+ }
633
+ /**
634
+ * Fold a NORMALIZED transcript back into the `{ systemPrompt, tools, messages }`
635
+ * shape a LEGACY (≤0.85) api consumes — issue #27's crash and its two silent
636
+ * siblings.
637
+ *
638
+ * WHY (issue #27). 0.86+ normalizes at every public entry point, and DSH's host
639
+ * calls `Models.streamSimple()`, so on a modern host the context arriving here
640
+ * ALWAYS carries its prompt and its tool declarations on a leading system
641
+ * message — the top-level fields are gone. A legacy api has no branch for that
642
+ * message: `estimateMessageTokens` iterates the string `content` character by
643
+ * character and reads `.name` off a character, throwing
644
+ * `Cannot read properties of undefined (reading 'length')` before the request
645
+ * is even built. That is what surfaced as `PI_AI_ERROR` with all-zero usage.
646
+ *
647
+ * The fold must carry MORE than the prompt. `toolsAdded` is the ONLY remaining
648
+ * carrier of the tool declarations once `normalizeContext` has run, and a
649
+ * legacy api reads tools exclusively from `context.tools` — so lifting the text
650
+ * alone would trade a loud crash for a silent one: a model that can never emit
651
+ * a `toolCall` because the request declared no tools.
652
+ *
653
+ * Shape-guarded, not version-guarded: it asks "is there a system message inside
654
+ * `messages`?" rather than "which pi-ai is loaded?". A version test would be
655
+ * wrong the moment either side moves, and this predicate is an identity for the
656
+ * legacy shape, so the ordinary path stays byte-for-byte untouched. WHICH
657
+ * branch runs at all IS generation-gated — see {@link withContextAdaptation}.
658
+ *
659
+ * Returns the SAME object when there is nothing to fold, so a legacy host
660
+ * (which never normalizes) pays one `Array.some` and nothing else.
661
+ */
662
+ function adaptLegacyPiAiContext(context) {
663
+ if (context === null || typeof context !== "object") return context;
664
+ const messages = Array.isArray(context.messages) ? context.messages : void 0;
665
+ if (messages === void 0) return context;
666
+ if (!messages.some(isSystemMessage)) return context;
667
+ const systemTexts = [];
668
+ const rest = [];
669
+ const toolState = /* @__PURE__ */ new Map();
670
+ for (const message of messages) {
671
+ if (!isSystemMessage(message)) {
672
+ rest.push(message);
673
+ continue;
674
+ }
675
+ const text = systemTextOf(message);
676
+ if (text !== "") systemTexts.push(text);
677
+ replayToolState(message, toolState);
678
+ }
679
+ const texts = [...context.systemPrompt === void 0 || context.systemPrompt === "" ? [] : [context.systemPrompt], ...systemTexts];
680
+ const systemPrompt = texts.length > 0 ? texts.join("\n\n") : void 0;
681
+ const promoted = Array.isArray(context.tools) ? void 0 : [...toolState.values()];
682
+ return {
683
+ ...context,
684
+ ...systemPrompt === void 0 ? {} : { systemPrompt },
685
+ ...promoted === void 0 || promoted.length === 0 ? {} : { tools: promoted },
686
+ messages: rest
687
+ };
688
+ }
689
+ /**
690
+ * Lift the legacy `{ systemPrompt, tools, messages }` shape into the normalized
691
+ * transcript a MODERN (0.86+) api consumes — the reverse mismatch, and just as
692
+ * silent.
693
+ *
694
+ * WHY. DSH spans both generations (`dsh-llm-pi-ai` ≤0.2.0-rc.1 pins `^0.85.1`)
695
+ * while this plugin may resolve a newer copy than its host. A LEGACY host never
696
+ * normalizes, so it hands the api the legacy shape; a modern api reads the
697
+ * prompt from a leading system message and the tools from its `toolsAdded`, and
698
+ * never looks at the top-level fields — so both are dropped without a sound.
699
+ * Verified against the published tarballs: the request that reaches the wire
700
+ * carries no system turn and `tools: []`.
701
+ *
702
+ * Identity for anything already in transcript shape, and for a context with
703
+ * neither a prompt nor tools (pi-ai's own `createInitialSystemMessage` returns
704
+ * `undefined` there, keeping an empty transcript empty).
705
+ */
706
+ function adaptModernPiAiContext(context) {
707
+ if (context === null || typeof context !== "object") return context;
708
+ const messages = Array.isArray(context.messages) ? context.messages : void 0;
709
+ if (messages === void 0) return context;
710
+ const prompt = context.systemPrompt;
711
+ const tools = Array.isArray(context.tools) && context.tools.length > 0 ? context.tools : void 0;
712
+ if (!(typeof prompt === "string" && prompt.length > 0) && tools === void 0) return context;
713
+ const head = {
714
+ role: "system",
715
+ content: prompt ?? "",
716
+ ...tools === void 0 ? {} : { toolsAdded: tools }
717
+ };
718
+ const { systemPrompt: _lifted, tools: _liftedTools, ...rest } = context;
719
+ return {
720
+ ...rest,
721
+ messages: [head, ...messages]
722
+ };
723
+ }
724
+ /**
725
+ * Wrap one pi-ai API module so BOTH stream entry points receive a context its
726
+ * own generation can consume.
727
+ *
728
+ * The gate is the copy THIS plugin resolved (see {@link piAiRuntimeInfo}) —
729
+ * what the host runs says nothing about it, because a nested copy on disk
730
+ * shadows host injection and vice versa. The wrapping is cheap and lazy: only
731
+ * `stream`/`streamSimple` are intercepted, the deferred-fetch entry points take
732
+ * no context and are passed through untouched, and each adapter function is an
733
+ * identity unless the context is actually the other generation's shape. So a
734
+ * healthy install does one `Array.some` per request.
735
+ */
736
+ function withContextAdaptation(api, generation) {
737
+ const adapt = generation === "modern" ? adaptModernPiAiContext : adaptLegacyPiAiContext;
738
+ return {
739
+ ...api,
740
+ stream: (model, context, options) => api.stream(model, adapt(context), options),
741
+ streamSimple: (model, context, options) => api.streamSimple(model, adapt(context), options)
742
+ };
743
+ }
408
744
  function createTraeAdapter(options) {
409
745
  const providerId = options.provider ?? "trae";
410
746
  const providerName = options.displayName ?? "Trae";
747
+ const generation = options.piAiGeneration ?? piAiRuntimeInfo().generation;
411
748
  const buildModels = () => options.catalog.current().map((info) => toPiModel(info, `${options.shim.baseUrl()}/v1`, providerId));
412
749
  const provider = {
413
750
  ...createProvider({
@@ -424,7 +761,7 @@ function createTraeAdapter(options) {
424
761
  }
425
762
  } },
426
763
  models: buildModels(),
427
- api: openAICompletionsApi()
764
+ api: withContextAdaptation(openAICompletionsApi(), generation)
428
765
  }),
429
766
  getModels: () => buildModels()
430
767
  };
@@ -5804,6 +6141,8 @@ function apply(ctx, config) {
5804
6141
  Promise.all(REGION_KEYS.map((region) => stacks[region].shim.ready)).then(async () => {
5805
6142
  if (stopped) return;
5806
6143
  try {
6144
+ const piAi = piAiRuntimeInfo();
6145
+ ctx.logger.info(`dsh-connect-trae: pi-ai ${piAi.version ?? "unknown"} (${piAi.generation} generation)${piAi.inspected ? "" : " — not resolved locally, the host supplies it"}`);
5807
6146
  for (const region of REGION_KEYS) {
5808
6147
  const stack = stacks[region];
5809
6148
  const trae = createTraeAdapter({
@@ -5877,4 +6216,4 @@ function apply(ctx, config) {
5877
6216
  });
5878
6217
  }
5879
6218
  //#endregion
5880
- export { Config, FALLBACK_TRAE_MODELS, FALLBACK_TRAE_MODELS_AI, REGION_GATEWAYS, SseDecoder, TRAE_ACCOUNT_STATUS_PATH, TRAE_AI_PROVIDER, TRAE_CHECKIN_PATH, TRAE_CN_AGENT_TASK_PATH, TRAE_CN_TITLE_PATH, TRAE_MODELS_TEST_PATH, TRAE_MODEL_DETAIL_FUNCTIONS, TRAE_MODEL_DETAIL_PATH, TRAE_PAY_BASE, TRAE_PROBE_TIMEOUT_MS, TRAE_PROVIDER, TRAE_PROVIDERS, TRAE_PROVIDER_DISPLAY_NAMES, TRAE_RAW_CHAT_V1_PATH, TRAE_RAW_CHAT_V2_PATH, TRAE_REASONING_EFFORTS, TRAE_REGIONS, TRAE_REGION_PARAM, TRAE_SETTINGS_NS, TRAE_SOLO_CHAT_PATH, TRAE_SOLO_FUNCTION, TRAE_SOLO_MODELS_PATH, TRAE_SOLO_REMOTE_BASE, TRAE_STATE_DB_FILENAME, TRAE_STREAM_IDLE_TIMEOUT_MS, TRAE_USAGE_PATH, TRAE_VERSION_CODE_FALLBACK, TraeCatalog, TraeCredentialStore, TraeDelegatingUpstreamClient, TraeFallbackUpstreamClient, TraeGatedUpstreamClient, TraeRawCapabilityController, TraeRawCapabilityState, TraeRawChatUpstreamClient, TraeSoloBridge, TraeSoloRemoteCatalogClient, TraeSoloUpstreamClient, TraeUsageClient, UnconfiguredTraeUpstreamClient, apply, applyContextBudgets, applyImageSelection, applyReasoningEffort, asVolatile, bridgeTraeSoloStream, buildTraeAgentTaskBody, buildTraeCnHeaders, buildTraeFusionRawChatEnvelope, buildTraeModelDetailRequest, buildTraeRawChatDraft, buildTraeRawChatRuntimeConfig, classifyProbeError, classifyProbeResult, classifyTraeRawChatFailure, configuredAccountsOf, createTraeAdapter, createTraeRawGateway, createTraeShim, decodeRawChatChunk, decodeTraeEvent, decryptTraeStorageValue, deriveCatalog, describeIdShape, describeNameShape, discoveredCatalog, fallbackModelsFor, followAppLoginEnabled, hashTraeRawChatArg, identityHeaders, inject, legacyTraeOwnAuthPath, maskUserPath, mergeTraeModelSources, name, nextRegionEnabled, nextRegionSlots, normalizeTraeCredential, normalizeTraeVersionCode, parseObservedModelConfig, parseReasoningCapability, parseTraeAuthValue, parseTraeCachedModel, parseTraeModelExtraConfigLogLine, parseTraeRawChatBehaviorConfig, parseTraeRemoteModel, parseTraeStorageDocument, pickTraeStorageIdentity, prepareSoloBody, probeBody, probeModelsSequentially, probeOneModel, probeTraeRawChatCapability, rawCapabilityDiagnostic, rawCapabilityFingerprint, readTraeCachedModel, readTraeCliIdentity, readTraeIdentity, refreshTraeCredential, regionEnabled, regionEnabledOf, regionOfCredential, regionOfEdition, regionOfHost, regionOfTraeProvider, regionOfTraeStatusUrl, regionOfUserRegion, regionStateOf, registerTraeUsageRoute, resolveTraeIdentity, resolveTraeRawRuntime, sanitizeCatalog, settingsNamespaceOf, traeEndpoint, traeInputModalities, traeModelDisplayName, traeOwnAuthPath, traeRawChatExtraInfo, traeStateDatabaseCandidates, traeStorageCandidates, traeWebUsage, traeWindowsAppNames, unwrapVolatile, unwrapVolatileDeep, withTraeRegion };
6219
+ export { Config, FALLBACK_TRAE_MODELS, FALLBACK_TRAE_MODELS_AI, REGION_GATEWAYS, SseDecoder, TRAE_ACCOUNT_STATUS_PATH, TRAE_AI_PROVIDER, TRAE_CHECKIN_PATH, TRAE_CN_AGENT_TASK_PATH, TRAE_CN_TITLE_PATH, TRAE_MODELS_TEST_PATH, TRAE_MODEL_DETAIL_FUNCTIONS, TRAE_MODEL_DETAIL_PATH, TRAE_PAY_BASE, TRAE_PROBE_TIMEOUT_MS, TRAE_PROVIDER, TRAE_PROVIDERS, TRAE_PROVIDER_DISPLAY_NAMES, TRAE_RAW_CHAT_V1_PATH, TRAE_RAW_CHAT_V2_PATH, TRAE_REASONING_EFFORTS, TRAE_REGIONS, TRAE_REGION_PARAM, TRAE_SETTINGS_NS, TRAE_SOLO_CHAT_PATH, TRAE_SOLO_FUNCTION, TRAE_SOLO_MODELS_PATH, TRAE_SOLO_REMOTE_BASE, TRAE_STATE_DB_FILENAME, TRAE_STREAM_IDLE_TIMEOUT_MS, TRAE_USAGE_PATH, TRAE_VERSION_CODE_FALLBACK, TraeCatalog, TraeCredentialStore, TraeDelegatingUpstreamClient, TraeFallbackUpstreamClient, TraeGatedUpstreamClient, TraeRawCapabilityController, TraeRawCapabilityState, TraeRawChatUpstreamClient, TraeSoloBridge, TraeSoloRemoteCatalogClient, TraeSoloUpstreamClient, TraeUsageClient, UnconfiguredTraeUpstreamClient, adaptLegacyPiAiContext, adaptModernPiAiContext, apply, applyContextBudgets, applyImageSelection, applyReasoningEffort, asVolatile, bridgeTraeSoloStream, buildTraeAgentTaskBody, buildTraeCnHeaders, buildTraeFusionRawChatEnvelope, buildTraeModelDetailRequest, buildTraeRawChatDraft, buildTraeRawChatRuntimeConfig, classifyProbeError, classifyProbeResult, classifyTraeRawChatFailure, configuredAccountsOf, createTraeAdapter, createTraeRawGateway, createTraeShim, decidePiAiGeneration, decodeRawChatChunk, decodeTraeEvent, decryptTraeStorageValue, deriveCatalog, describeIdShape, describeNameShape, discoveredCatalog, fallbackModelsFor, followAppLoginEnabled, generationOf, hashTraeRawChatArg, identityHeaders, inject, legacyTraeOwnAuthPath, maskUserPath, mergeTraeModelSources, name, nextRegionEnabled, nextRegionSlots, normalizeTraeCredential, normalizeTraeVersionCode, parseObservedModelConfig, parseReasoningCapability, parseTraeAuthValue, parseTraeCachedModel, parseTraeModelExtraConfigLogLine, parseTraeRawChatBehaviorConfig, parseTraeRemoteModel, parseTraeStorageDocument, piAiRuntimeInfo, pickTraeStorageIdentity, prepareSoloBody, probeBody, probeModelsSequentially, probeOneModel, probeTraeRawChatCapability, rawCapabilityDiagnostic, rawCapabilityFingerprint, readTraeCachedModel, readTraeCliIdentity, readTraeIdentity, refreshTraeCredential, regionEnabled, regionEnabledOf, regionOfCredential, regionOfEdition, regionOfHost, regionOfTraeProvider, regionOfTraeStatusUrl, regionOfUserRegion, regionStateOf, registerTraeUsageRoute, resolveTraeIdentity, resolveTraeRawRuntime, sanitizeCatalog, settingsNamespaceOf, traeEndpoint, traeInputModalities, traeModelDisplayName, traeOwnAuthPath, traeRawChatExtraInfo, traeStateDatabaseCandidates, traeStorageCandidates, traeWebUsage, traeWindowsAppNames, unwrapVolatile, unwrapVolatileDeep, withContextAdaptation, withTraeRegion };
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "dsh-connect-trae",
3
3
  "displayName": "DSH Connect Trae",
4
4
  "description": "把本机登录的 Trae 模型接入 DeepSeek Harness:国内版与国际版双供应商并行,提供用量/积分概览与每日签到领取。",
5
- "version": "3.4.1",
5
+ "version": "3.4.3",
6
6
  "author": "dingminhua",
7
7
  "copyright": "Copyright (c) 2026 LaoDing",
8
8
  "license": "MIT",