@khorsheed/dsh-local-agent-kimi 0.2.0-rc.3 → 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.
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Generated by scripts/sync-icon-artwork.mts from @deepseek-ai/dsh-client-ui-primitives
3
- * (v0.1.7-rc.1, harness 46a7f68b09) — do not edit by hand. The 0.1.5 and rc.1 host lines
3
+ * (v0.2.1-rc.1, harness 1600d8a1e1) — do not edit by hand. The 0.1.5 and rc.1 host lines
4
4
  * share no icon export name, so the rc.1 artwork is inlined here: pure SVG,
5
5
  * no host runtime identity, identical on every line. Re-run the generator
6
6
  * after adding an icon import or bumping the harness checkout.
@@ -11,7 +11,7 @@
11
11
  import { randomUUID } from 'node:crypto';
12
12
  import { SessionId } from '@deepseek-ai/dsh-session';
13
13
  import { NO_START_CAPABILITIES, SUBAGENT_DESCRIPTOR_VERSION, settleRunResult, subprocessRunHandle, } from '@deepseek-ai/dsh-subagent';
14
- import { assertResumeCwdUnchanged, assertResumeScopeUnchanged, resolveRoundModel, containerExecSpawn, containerScopedHome, delegationEnv, establishSubagentCatalogChild, probeCliVersion, resolveChildCwd, subagentDelegationLabel, } from '@khorsheed/dsh-local-agent';
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
17
  import { injectMemberBridge, memberBridgeServerKey, removeMemberBridge } from './member-bridge-config.js';
@@ -623,8 +623,11 @@ async function mirrorKimiAfterExit(spec, stderr) {
623
623
  // The round's turn selects which transcript lines the tool-call accounting
624
624
  // belongs to — the mirror window would under-count a settle pass a live
625
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.
626
628
  const delta = await mirrorKimiDelta(spec.ctx, spec.childSession, spec.homeDir, kimiSessionId, {
627
629
  turn: spec.resume?.turn ?? 1,
630
+ settle: true,
628
631
  });
629
632
  // Report the final mirrored-line count even when the live mirror already
630
633
  // advanced the offset (empty delta): the settle report is authoritative.
@@ -818,24 +821,6 @@ export async function startKimiCliRun(request, spec) {
818
821
  // or not the round is session-backed.
819
822
  if (settled.stopReason === 'completed')
820
823
  spec.onAuthSuccess?.();
821
- // The turn closes at the real settle moment, so the timing projection's
822
- // duration equals the actual CLI runtime. Every terminal path closes the
823
- // window — a failed or cancelled run settles 'error'/'aborted' instead of
824
- // leaving the turn open with a distorted tiny duration.
825
- if (spec.childSession !== undefined) {
826
- if (settled.stopReason === 'completed') {
827
- spec.childSession.append('turn/end', { turn, reason: { kind: 'completed' } });
828
- }
829
- else if (settled.stopReason === 'aborted') {
830
- spec.childSession.append('turn/end', { turn, reason: { kind: 'aborted', reason: { kind: 'parent' } } });
831
- }
832
- else {
833
- spec.childSession.append('turn/end', {
834
- turn,
835
- reason: { kind: 'error', error: { message: 'kimi -p exited before the run completed', code: 'UNKNOWN' } },
836
- });
837
- }
838
- }
839
824
  return settled;
840
825
  });
841
826
  // After the child EXITS — however it ended (completed, killed by dispose, or
@@ -843,9 +828,12 @@ export async function startKimiCliRun(request, spec) {
843
828
  // subagent session, so a cancelled round still preserves its partial work
844
829
  // and real token usage instead of leaving the child blank. The mirror reads
845
830
  // the wire file, so it only reflects what kimi flushed before the kill.
846
- // Waits for the settle chain first (so turn/end is already appended) AND for
847
- // the process to actually exit (so the wire file is complete) before
848
- // 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.
849
837
  void result.then(() => child.done).then(() => {
850
838
  // Auth detection runs post-exit: streams are drained by then, so a fast
851
839
  // failure's stderr is complete (a settle-time read could race the flush).
@@ -863,16 +851,41 @@ export async function startKimiCliRun(request, spec) {
863
851
  // usage into the round total, then reports the round's settled
864
852
  // observation — the wire's model plus the round's accumulated usage.
865
853
  enqueueMirror(async () => {
866
- const settled = await mirrorKimiAfterExit(spec, stderr);
867
- 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
+ }
868
881
  // The wire log names the model but never the CLI build, so the version
869
882
  // is the executable's own answer, probed once per binary.
870
883
  const cliVersion = await spec.cliVersion?.();
871
884
  spec.onRoundSettled?.({
872
- ...settled?.model === undefined ? {} : { observedModel: settled.model },
885
+ ...mirrored?.model === undefined ? {} : { observedModel: mirrored.model },
873
886
  ...cliVersion === undefined ? {} : { cliVersion },
874
887
  ...roundUsage === undefined ? {} : { usage: roundUsage },
875
- ...settled?.toolCalls === undefined ? {} : { toolCalls: settled.toolCalls },
888
+ ...mirrored?.toolCalls === undefined ? {} : { toolCalls: mirrored.toolCalls },
876
889
  });
877
890
  });
878
891
  }, () => { });
@@ -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