@khorsheed/dsh-local-agent-kimi 0.2.0-rc.1 → 0.2.0

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 (45) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.en.md +24 -112
  3. package/README.i18n.yaml +2 -2
  4. package/README.md +24 -112
  5. package/lib/client.js +816 -324
  6. package/lib/index.js +2121 -1311
  7. package/lib/types/client/SettingsCard.d.ts +75 -24
  8. package/lib/types/client/SettingsCard.js +110 -137
  9. package/lib/types/client/icons.d.ts +1 -1
  10. package/lib/types/client/index.d.ts +6 -25
  11. package/lib/types/client/index.js +134 -51
  12. package/lib/types/client/locales.d.ts +34 -24
  13. package/lib/types/client/locales.js +38 -28
  14. package/lib/types/endpoint-auth.d.ts +202 -0
  15. package/lib/types/endpoint-auth.js +287 -0
  16. package/lib/types/index.d.ts +19 -2
  17. package/lib/types/index.js +146 -19
  18. package/lib/types/kimi-cli-provider.d.ts +31 -4
  19. package/lib/types/kimi-cli-provider.js +110 -60
  20. package/lib/types/live-driver.d.ts +17 -5
  21. package/lib/types/live-driver.js +345 -129
  22. package/lib/types/live-switch.d.ts +16 -1
  23. package/lib/types/live-switch.js +11 -1
  24. package/lib/types/model-broker.d.ts +12 -2
  25. package/lib/types/model-broker.js +51 -6
  26. package/lib/types/model-catalog.d.ts +44 -1
  27. package/lib/types/model-catalog.js +110 -17
  28. package/lib/types/model-configuration.d.ts +26 -0
  29. package/lib/types/model-configuration.js +43 -0
  30. package/lib/types/provision.d.ts +102 -31
  31. package/lib/types/provision.js +257 -45
  32. package/lib/types/records.d.ts +0 -20
  33. package/lib/types/records.js +1 -40
  34. package/lib/types/session-mirror.d.ts +115 -52
  35. package/lib/types/session-mirror.js +286 -210
  36. package/lib/types/session-view.d.ts +23 -2
  37. package/lib/types/session-view.js +37 -7
  38. package/locale/en.json +2 -2
  39. package/locale/zh.json +2 -2
  40. package/package.json +21 -22
  41. package/lib/invariant.js +0 -31
  42. package/lib/types/credential-guard.d.ts +0 -29
  43. package/lib/types/credential-guard.js +0 -107
  44. package/lib/types/invariant.d.ts +0 -16
  45. package/lib/types/invariant.js +0 -33
@@ -10,13 +10,13 @@
10
10
  */
11
11
  import { randomUUID } from 'node:crypto';
12
12
  import { SessionId } from '@deepseek-ai/dsh-session';
13
- import { NO_START_CAPABILITIES, settleRunResult, subprocessRunHandle, } from '@deepseek-ai/dsh-subagent';
14
- import { assertResumeCwdUnchanged, assertResumeScopeUnchanged, resolveRoundModel, containerExecSpawn, containerScopedHome, delegationEnv, establishSubagentCatalogChild, probeCliVersion, resolveChildCwd, subagentDelegationLabel, } from '@khorsheed/dsh-local-agent';
13
+ import { NO_START_CAPABILITIES, SUBAGENT_DESCRIPTOR_VERSION, settleRunResult, subprocessRunHandle, } from '@deepseek-ai/dsh-subagent';
14
+ import { assertResumeCwdUnchanged, assertResumeScopeUnchanged, resolveRoundModel, containerExecSpawn, containerScopedHome, delegationEnv, establishSubagentCatalogChild, persistChildSession, probeCliVersion, resolveChildCwd, subagentDelegationLabel, } from '@khorsheed/dsh-local-agent';
15
15
  import { MEMBER_BRIDGE_SOCKET_ENV, MEMBER_BRIDGE_TOKEN_ENV } from '@khorsheed/dsh-local-agent/types';
16
16
  import { LiveChannelUnavailableError } from './live-driver.js';
17
- import { guardKimiCredential } from './credential-guard.js';
18
17
  import { injectMemberBridge, memberBridgeServerKey, removeMemberBridge } from './member-bridge-config.js';
19
- import { readKimiBaseUrl } from './provision.js';
18
+ import { ensureKimiEndpointProvider, KIMI_MANAGED_BASE_URL, readKimiBaseUrl } from './provision.js';
19
+ import { endpointAuthEnv, hasEndpointCredential } from './endpoint-auth.js';
20
20
  import { kimiExecConfiguration } from './model-configuration.js';
21
21
  import { addTokenUsage } from './session-view.js';
22
22
  import { mirrorKimiSessionDelta } from './session-mirror.js';
@@ -81,6 +81,7 @@ export class KimiCliProvider {
81
81
  live;
82
82
  model;
83
83
  memberModels;
84
+ endpoint;
84
85
  name = 'kimi-cli';
85
86
  capabilities = NO_START_CAPABILITIES;
86
87
  inheritsParentContext = false;
@@ -91,19 +92,43 @@ export class KimiCliProvider {
91
92
  * while a retiring generation still hosts it).
92
93
  * @param model - resolver for the configured model, read PER ROUND so a
93
94
  * settings-card write takes effect on the next delegation without a
94
- * reload. Undefined (the resolver absent, or returning undefined) leaves
95
- * the argv exactly as it was before the key rode every round — the scoped
96
- * config.toml's `default_model` then decides, as it always did.
95
+ * reload; it receives the round's scope (a named scope's curated list
96
+ * answers first). Undefined (the resolver absent, or returning
97
+ * undefined) leaves the argv exactly as it was before the key rode every
98
+ * round — the scoped config.toml's `default_model` then decides, as it
99
+ * always did.
97
100
  * @param memberModels - the broker's member-model ledger: the provider
98
101
  * records each delegation's start model here (the live driver's spawn
99
102
  * resolver binds it) and reads the session-level override as the exec
100
103
  * path's top layer. Absent (older wiring), both drop out.
101
104
  */
102
- constructor(ctx, live, model, memberModels) {
105
+ constructor(ctx, live, model, memberModels,
106
+ /**
107
+ * Resolver for the scope's endpoint credential, read PER ROUND so a
108
+ * settings-card write takes effect on the next delegation without a
109
+ * reload. A resolution carrying a key authenticates the round through the
110
+ * managed provider's `api_key_env` indirection (the key rides the child
111
+ * process's environment); the same resolution's baseUrl is pinned onto
112
+ * the managed provider by the pre-spawn config surgery.
113
+ */
114
+ endpoint) {
103
115
  this.ctx = ctx;
104
116
  this.live = live;
105
117
  this.model = model;
106
118
  this.memberModels = memberModels;
119
+ this.endpoint = endpoint;
120
+ }
121
+ /**
122
+ * Resolve the scope's endpoint for this round and land its baseUrl on the
123
+ * managed provider (the surgery writes only on drift), so a hand-edited
124
+ * endpoint-auth.json applies without a restart.
125
+ */
126
+ async prepareEndpoint(homeDir) {
127
+ if (this.endpoint === undefined)
128
+ return undefined;
129
+ const endpoint = await this.endpoint(homeDir).catch(() => undefined);
130
+ await ensureKimiEndpointProvider(homeDir, hasEndpointCredential(endpoint) ? endpoint.baseUrl ?? KIMI_MANAGED_BASE_URL : undefined).catch(() => false);
131
+ return endpoint;
107
132
  }
108
133
  /** Resolve the live driver for one round's member, if live is on for it. */
109
134
  liveDriver(childSessionId) {
@@ -200,7 +225,7 @@ export class KimiCliProvider {
200
225
  ...record?.configurationLock === undefined ? {} : { configurationLock: record.configurationLock },
201
226
  }, configuration => this.startKimiResume(request, intent, cwd, homeDir, exec, scope, record?.model, configuration), request.signal, intent?.onAdmitted);
202
227
  }
203
- const childSessionId = SessionId(intent?.preparedMemberId ?? randomUUID());
228
+ const childSessionId = SessionId(intent?.preparedMemberId ?? intent?.childSessionId ?? randomUUID());
204
229
  if (typeof this.ctx.localAgent.withMemberConfigurationRound !== 'function') {
205
230
  if (intent?.effort !== undefined)
206
231
  throw new Error('Kimi effort requires the configuration admission core');
@@ -234,20 +259,25 @@ export class KimiCliProvider {
234
259
  delegationDepth: (request.parent.session.header.delegationDepth ?? 0) + 1,
235
260
  },
236
261
  });
237
- // The durable one-shot descriptor the runtime resolved marks this child
238
- // as session-backed; without it the 子代理 projection cannot classify
239
- // the session and the delegation stays invisible. The label carries the
240
- // harness display name as the source marker so the dropdown shows which
241
- // local agent produced the conversation.
262
+ // The durable one-shot descriptor marks this child as session-backed;
263
+ // without it the 子代理 projection cannot classify the session and the
264
+ // delegation stays invisible. Host 0.2.1 no longer resolves a descriptor
265
+ // onto the request (the runtime's one-shot snapshot is gone), so the
266
+ // provider stamps the same payload itself. The label carries the harness
267
+ // display name as the source marker so the dropdown shows which local
268
+ // agent produced the conversation; the activation spec's required label
269
+ // arrives as the empty string when the caller named none.
242
270
  const harness = this.ctx.localAgent.get('kimi');
243
- const label = subagentDelegationLabel(harness?.displayName ?? 'kimi', request.descriptor.label);
271
+ const label = subagentDelegationLabel(harness?.displayName ?? 'kimi', request.label || undefined);
244
272
  childSession.append('subagent/descriptor', {
245
- ...request.descriptor,
273
+ version: SUBAGENT_DESCRIPTOR_VERSION,
274
+ mode: 'one-shot',
275
+ provider: this.name,
246
276
  label,
247
277
  });
248
- // The official runtime appends the parent-side subagent/catalog
249
- // discovery row only for in-process children (run.localAgent); this run
250
- // is remote, so the provider appends it here — once per child, where
278
+ // The 0.2.1 manager appends the parent-side subagent/catalog discovery
279
+ // row only for parent-delivered children; the family runs caller
280
+ // delivery, so the provider appends it here — once per child, where
251
281
  // the descriptor lands. A failure degrades to the warn below; a missing
252
282
  // row never blocks the delegation.
253
283
  establishSubagentCatalogChild(request.parent.session, childSession.header, label);
@@ -264,7 +294,13 @@ export class KimiCliProvider {
264
294
  // diagnostics: the CLI reads it directly, and a user-edited value is
265
295
  // authoritative. Log it so a failing delegation reports which endpoint
266
296
  // it actually used.
267
- const baseUrl = await readKimiBaseUrl(homeDir).catch(() => undefined);
297
+ // The round's endpoint credential: resolved per round (a card write
298
+ // applies to the next delegation) and pinned onto the managed provider
299
+ // by the surgery above; the key rides the spawn env under api_key_env.
300
+ const endpoint = await this.prepareEndpoint(homeDir);
301
+ const baseUrl = hasEndpointCredential(endpoint)
302
+ ? endpoint.baseUrl
303
+ : await readKimiBaseUrl(homeDir).catch(() => undefined);
268
304
  this.ctx.logger.info(`subagent-kimi: delegating via ${baseUrl ?? 'kimi default endpoint'}`);
269
305
  // Live driver: the round goes to the member's resident `kimi acp` process.
270
306
  // A channel that fails at spawn/handshake falls through to the exec
@@ -330,12 +366,12 @@ export class KimiCliProvider {
330
366
  try {
331
367
  const run = await startKimiCliRun(request, {
332
368
  cwd,
333
- env: delegationEnv({ KIMI_CODE_HOME: homeDir }),
369
+ env: delegationEnv({ KIMI_CODE_HOME: homeDir, ...endpointAuthEnv(endpoint) }),
334
370
  ...exec === undefined ? {} : { exec },
335
371
  endpointLabel: baseUrl,
336
372
  // The session-level override (the composer picker) outranks even the
337
373
  // delegation's own model — the family order puts it first.
338
- ...(configuration?.resolved ?? resolveRoundModel(this.memberModels?.overrideFor(runId) ?? requestedModel, this.model)),
374
+ ...(configuration?.resolved ?? resolveRoundModel(this.memberModels?.overrideFor(runId) ?? requestedModel, () => this.model?.(scope))),
339
375
  ...configuration === undefined ? {} : { controlled: true },
340
376
  disposeGraceMs: DEFAULT_DISPOSE_GRACE_MS,
341
377
  spawn: spec => this.ctx.subprocess.spawn(spec),
@@ -343,9 +379,6 @@ export class KimiCliProvider {
343
379
  this.ctx.logger.warn(`subagent-kimi: child run failed (${stopReason}) via ${baseUrl ?? 'kimi default endpoint'}: ${error instanceof Error ? error.message : String(error)}`);
344
380
  },
345
381
  onAuthFailure: (detail) => {
346
- // The sentinel: an empty-shell wipe restored here makes the
347
- // caller's retry (or the next round) succeed.
348
- void guardKimiCredential(homeDir, message => { this.ctx.logger.warn(message); });
349
382
  this.ctx.localAgent.reportAuthFailure('kimi', detail, scope);
350
383
  },
351
384
  onAuthSuccess: () => { markCredentialVerified(this.ctx, scope); },
@@ -442,18 +475,23 @@ export class KimiCliProvider {
442
475
  const member = exec === undefined && scope === undefined
443
476
  ? this.memberRun(intent.childSessionId, request.parent.session.id, homeDir)
444
477
  : undefined;
445
- const baseUrl = await readKimiBaseUrl(homeDir).catch(() => undefined);
478
+ // Same endpoint handling as the fresh path: resolve per round, pin
479
+ // the managed provider, inject the key under api_key_env.
480
+ const endpoint = await this.prepareEndpoint(homeDir);
481
+ const baseUrl = hasEndpointCredential(endpoint)
482
+ ? endpoint.baseUrl
483
+ : await readKimiBaseUrl(homeDir).catch(() => undefined);
446
484
  this.ctx.logger.info(`subagent-kimi: resuming via ${baseUrl ?? 'kimi default endpoint'}`);
447
485
  let run;
448
486
  try {
449
487
  run = await startKimiCliRun(request, {
450
488
  cwd,
451
- env: delegationEnv({ KIMI_CODE_HOME: homeDir }),
489
+ env: delegationEnv({ KIMI_CODE_HOME: homeDir, ...endpointAuthEnv(endpoint) }),
452
490
  ...exec === undefined ? {} : { exec },
453
491
  endpointLabel: baseUrl,
454
492
  // See the fresh path: the session-level override outranks the
455
493
  // delegation's recorded model on the exec path too.
456
- ...(configuration?.resolved ?? resolveRoundModel(this.memberModels?.overrideFor(intent.childSessionId) ?? requestedModel, this.model)),
494
+ ...(configuration?.resolved ?? resolveRoundModel(this.memberModels?.overrideFor(intent.childSessionId) ?? requestedModel, () => this.model?.(scope))),
457
495
  ...configuration === undefined ? {} : { controlled: true },
458
496
  disposeGraceMs: DEFAULT_DISPOSE_GRACE_MS,
459
497
  spawn: spec => this.ctx.subprocess.spawn(spec),
@@ -461,9 +499,6 @@ export class KimiCliProvider {
461
499
  this.ctx.logger.warn(`subagent-kimi: child run failed (${stopReason}) via ${baseUrl ?? 'kimi default endpoint'}: ${error instanceof Error ? error.message : String(error)}`);
462
500
  },
463
501
  onAuthFailure: (detail) => {
464
- // The sentinel: an empty-shell wipe restored here makes the
465
- // caller's retry (or the next round) succeed.
466
- void guardKimiCredential(homeDir, message => { this.ctx.logger.warn(message); });
467
502
  this.ctx.localAgent.reportAuthFailure('kimi', detail, scope);
468
503
  },
469
504
  onAuthSuccess: () => { markCredentialVerified(this.ctx, scope); },
@@ -588,8 +623,11 @@ async function mirrorKimiAfterExit(spec, stderr) {
588
623
  // The round's turn selects which transcript lines the tool-call accounting
589
624
  // belongs to — the mirror window would under-count a settle pass a live
590
625
  // poll already drained, and over-count a resume round's earlier turns.
626
+ // Settle mode: calls whose result never reached the wire fold with a
627
+ // synthesized interrupted result instead of holding the offset forever.
591
628
  const delta = await mirrorKimiDelta(spec.ctx, spec.childSession, spec.homeDir, kimiSessionId, {
592
629
  turn: spec.resume?.turn ?? 1,
630
+ settle: true,
593
631
  });
594
632
  // Report the final mirrored-line count even when the live mirror already
595
633
  // advanced the offset (empty delta): the settle report is authoritative.
@@ -783,24 +821,6 @@ export async function startKimiCliRun(request, spec) {
783
821
  // or not the round is session-backed.
784
822
  if (settled.stopReason === 'completed')
785
823
  spec.onAuthSuccess?.();
786
- // The turn closes at the real settle moment, so the timing projection's
787
- // duration equals the actual CLI runtime. Every terminal path closes the
788
- // window — a failed or cancelled run settles 'error'/'aborted' instead of
789
- // leaving the turn open with a distorted tiny duration.
790
- if (spec.childSession !== undefined) {
791
- if (settled.stopReason === 'completed') {
792
- spec.childSession.append('turn/end', { turn, reason: { kind: 'completed' } });
793
- }
794
- else if (settled.stopReason === 'aborted') {
795
- spec.childSession.append('turn/end', { turn, reason: { kind: 'aborted', reason: { kind: 'parent' } } });
796
- }
797
- else {
798
- spec.childSession.append('turn/end', {
799
- turn,
800
- reason: { kind: 'error', error: { message: 'kimi -p exited before the run completed', code: 'UNKNOWN' } },
801
- });
802
- }
803
- }
804
824
  return settled;
805
825
  });
806
826
  // After the child EXITS — however it ended (completed, killed by dispose, or
@@ -808,9 +828,12 @@ export async function startKimiCliRun(request, spec) {
808
828
  // subagent session, so a cancelled round still preserves its partial work
809
829
  // and real token usage instead of leaving the child blank. The mirror reads
810
830
  // the wire file, so it only reflects what kimi flushed before the kill.
811
- // Waits for the settle chain first (so turn/end is already appended) AND for
812
- // the process to actually exit (so the wire file is complete) before
813
- // reading it. Enqueued behind any in-flight live poll on the mirror queue.
831
+ // Waits for the settle chain first AND for the process to actually exit (so
832
+ // the wire file is complete) before reading it. Enqueued behind any
833
+ // in-flight live poll on the mirror queue. The turn closes AFTER the settle
834
+ // mirror, still inside the mirror queue: every mirrored step must land
835
+ // before turn/end — the released-log reader rejects a step outside an open
836
+ // turn on every cold read.
814
837
  void result.then(() => child.done).then(() => {
815
838
  // Auth detection runs post-exit: streams are drained by then, so a fast
816
839
  // failure's stderr is complete (a settle-time read could race the flush).
@@ -828,24 +851,51 @@ export async function startKimiCliRun(request, spec) {
828
851
  // usage into the round total, then reports the round's settled
829
852
  // observation — the wire's model plus the round's accumulated usage.
830
853
  enqueueMirror(async () => {
831
- const settled = await mirrorKimiAfterExit(spec, stderr);
832
- addRoundUsage(settled?.usage);
854
+ const settled = await result;
855
+ const mirrored = await mirrorKimiAfterExit(spec, stderr);
856
+ addRoundUsage(mirrored?.usage);
857
+ // The turn closes at the real settle moment, so the timing
858
+ // projection's duration equals the actual CLI runtime (plus the
859
+ // settle-mirror quiescence). Every terminal path closes the window —
860
+ // a failed or cancelled run settles 'error'/'aborted' instead of
861
+ // leaving the turn open with a distorted tiny duration.
862
+ if (spec.childSession !== undefined) {
863
+ if (settled.stopReason === 'completed') {
864
+ spec.childSession.append('turn/end', { turn, reason: { kind: 'completed' } });
865
+ }
866
+ else if (settled.stopReason === 'aborted') {
867
+ spec.childSession.append('turn/end', { turn, reason: { kind: 'aborted', reason: { kind: 'parent' } } });
868
+ }
869
+ else {
870
+ spec.childSession.append('turn/end', {
871
+ turn,
872
+ reason: { kind: 'error', error: { message: 'kimi -p exited before the run completed', code: 'UNKNOWN' } },
873
+ });
874
+ }
875
+ if (spec.ctx !== undefined) {
876
+ await persistChildSession(spec.ctx, spec.childSession).catch((error) => {
877
+ spec.ctx?.logger.warn(`subagent-kimi: turn/end persistence failed: ${thrown(error).message}`);
878
+ });
879
+ }
880
+ }
833
881
  // The wire log names the model but never the CLI build, so the version
834
882
  // is the executable's own answer, probed once per binary.
835
883
  const cliVersion = await spec.cliVersion?.();
836
884
  spec.onRoundSettled?.({
837
- ...settled?.model === undefined ? {} : { observedModel: settled.model },
885
+ ...mirrored?.model === undefined ? {} : { observedModel: mirrored.model },
838
886
  ...cliVersion === undefined ? {} : { cliVersion },
839
887
  ...roundUsage === undefined ? {} : { usage: roundUsage },
840
- ...settled?.toolCalls === undefined ? {} : { toolCalls: settled.toolCalls },
888
+ ...mirrored?.toolCalls === undefined ? {} : { toolCalls: mirrored.toolCalls },
841
889
  });
842
890
  });
843
891
  }, () => { });
844
892
  return Promise.resolve(subprocessRunHandle({
845
- // A session-backed run's id is the child session id (the seam's local-run
846
- // contract), so the tool's resume self-description names the same handle
847
- // the delegation registry records.
848
- id: spec.childSession?.id ?? SessionId(randomUUID()),
893
+ // Host 0.2.1: the published run id is a fresh PER-EXECUTION identity —
894
+ // the activation manager rejects a run id naming a live Session (the
895
+ // mirrored child session is exactly that), and resume rounds reuse the
896
+ // child session id. The durable child identity the family tracks rides
897
+ // the staged intent instead (spec.childSession.id == its childSessionId).
898
+ id: SessionId(randomUUID()),
849
899
  result,
850
900
  signal: request.signal,
851
901
  onAbort,
@@ -16,11 +16,14 @@
16
16
  * throttled `mirrorKimiDelta` passes and the settle pass stays authoritative
17
17
  * (one fold, one offset, zero divergence between the two drivers). Every
18
18
  * transcript item folds 1:1 into the child session log.
19
- * Live output also mirrors the in-flight item: chunk
20
- * deltas accumulate per streaming item and append throttled snapshot
21
- * assistant/messages at the item's reserved (turn, step) — the host folds
22
- * repeated settles at one coordinate into one live-updating chat node — and
23
- * the wire line completing the item folds at the same step, finalizing it.
19
+ * Live output rides the `local-agent/stream` channel: chunk deltas
20
+ * accumulate per streaming item and publish throttled updates at the item's
21
+ * reserved (turn, step) — the member live node renders them as-is — while
22
+ * the item's durable content lands as ONE atomic step/start→…→step/end
23
+ * triple at that step, written by the completing wire line's fold or at
24
+ * settle. Durable steps stay dense and strictly sequential (the released-log
25
+ * reader validates exactly that on every cold open); snapshots never hold a
26
+ * step open across appends.
24
27
  *
25
28
  * Lifecycle mirrors the dsh/codex live drivers' discipline: lazy spawn with an
26
29
  * initialize handshake (loadSession capability required — crash recovery is
@@ -38,6 +41,7 @@ import { type KimiNativeConfiguration } from './model-catalog.ts';
38
41
  import type { Context } from '@deepseek-ai/cordis';
39
42
  import type { Session } from '@deepseek-ai/dsh-session';
40
43
  import { type SubagentRun, type SubagentStartRequest, type SubagentStopReason } from '@deepseek-ai/dsh-subagent';
44
+ import { type KimiEndpointAuth } from './endpoint-auth.ts';
41
45
  /** Default idle lifetime of an unused resident runtime before reclaim. */
42
46
  export declare const DEFAULT_LIVE_IDLE_MS: number;
43
47
  /** Default timeout for a wire request acknowledgement (never for session/prompt). */
@@ -159,6 +163,14 @@ export declare class KimiAcpLiveDriver {
159
163
  * retired and respawned onto the new one.
160
164
  */
161
165
  model?: (childSessionId: string) => string | undefined;
166
+ /**
167
+ * Resolver for the scope's endpoint credential, read at each RUNTIME
168
+ * SPAWN so a settings-card write reaches the next respawn without a
169
+ * reload. A resolution carrying a key authenticates the runtime through
170
+ * the managed provider's `api_key_env` indirection (the key rides the
171
+ * process env; the baseUrl is pinned by the pre-spawn surgery).
172
+ */
173
+ endpoint?: (scopeHome: string) => Promise<KimiEndpointAuth>;
162
174
  }, timeouts?: KimiLiveDriverTimeouts);
163
175
  /** Whether the channel is in its post-failure cooldown (rounds fall back to exec). */
164
176
  get disabled(): boolean;