@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.
- package/CHANGELOG.md +16 -1
- package/README.en.md +24 -112
- package/README.i18n.yaml +2 -2
- package/README.md +24 -112
- package/lib/client.js +63 -63
- package/lib/index.js +618 -343
- package/lib/types/client/icons.d.ts +1 -1
- package/lib/types/kimi-cli-provider.js +39 -26
- package/lib/types/live-driver.d.ts +8 -5
- package/lib/types/live-driver.js +324 -119
- package/lib/types/model-broker.js +42 -1
- package/lib/types/session-mirror.d.ts +115 -52
- package/lib/types/session-mirror.js +286 -210
- package/lib/types/session-view.d.ts +23 -2
- package/lib/types/session-view.js +37 -7
- package/package.json +2 -2
|
@@ -35,6 +35,42 @@ function dedupe(candidates) {
|
|
|
35
35
|
}
|
|
36
36
|
return out;
|
|
37
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* Scope the served directory to the surface asking for it (the same helper
|
|
40
|
+
* lives in each provider's broker — fold into the family core when the
|
|
41
|
+
* directory faces next evolve):
|
|
42
|
+
*
|
|
43
|
+
* - memberless (the card's add-model dialog, the invite picker): the
|
|
44
|
+
* endpoint's served catalog only — the configuration/history extras the
|
|
45
|
+
* broker's info extension adds for STATUS display (a config-defined model
|
|
46
|
+
* the endpoint does not serve) must never become add candidates;
|
|
47
|
+
* - a member whose harness has a curated list: the list IS the vocabulary,
|
|
48
|
+
* in the list's own order (the same rule `choices` already follows) — a
|
|
49
|
+
* curated value the endpoint no longer serves stays as a bare row (the
|
|
50
|
+
* card shows it the same way), and the member's current effective model is
|
|
51
|
+
* kept visible as a bare entry when it sits outside the list, so a pre-list
|
|
52
|
+
* binding never renders blank;
|
|
53
|
+
* - a member without a curated list: the legacy extended vocabulary.
|
|
54
|
+
*/
|
|
55
|
+
function scopeServedDirectory(directory, opts) {
|
|
56
|
+
if (opts.member && opts.curated.length === 0)
|
|
57
|
+
return directory;
|
|
58
|
+
const served = directory.entries.filter(entry => entry.source === 'native');
|
|
59
|
+
if (!opts.member)
|
|
60
|
+
return { ...directory, entries: served };
|
|
61
|
+
const byValue = new Map(served.flatMap(entry => entry.resolvedModel === undefined
|
|
62
|
+
? [[entry.value, entry]]
|
|
63
|
+
: [[entry.value, entry], [entry.resolvedModel, entry]]));
|
|
64
|
+
// Curated ORDER leads (the card's first row is the default); a curated value
|
|
65
|
+
// the endpoint no longer serves stays listed as a bare row, exactly like the
|
|
66
|
+
// card shows it.
|
|
67
|
+
const entries = opts.curated.map(value => byValue.get(value) ?? { value, label: value, source: 'configuration' });
|
|
68
|
+
const current = opts.current?.trim();
|
|
69
|
+
if (current !== undefined && current !== '' && !entries.some(entry => entry.value === current || entry.resolvedModel === current)) {
|
|
70
|
+
entries.push({ value: current, label: current, source: 'configuration' });
|
|
71
|
+
}
|
|
72
|
+
return { ...directory, entries };
|
|
73
|
+
}
|
|
38
74
|
/**
|
|
39
75
|
* The kimi harness's {@link LocalAgentModelBroker}. Also the provider-facing
|
|
40
76
|
* member-model ledger: the provider records each delegation's start model
|
|
@@ -92,7 +128,12 @@ export class KimiModelBroker {
|
|
|
92
128
|
if (refresh)
|
|
93
129
|
await this.deps.catalog?.refresh(childSessionId);
|
|
94
130
|
const info = await this.modelInfo(childSessionId);
|
|
95
|
-
|
|
131
|
+
const directory = info.directory ?? { entries: [], complete: false, customInput: true, status: 'unsupported', refreshing: false, revision: 0 };
|
|
132
|
+
return scopeServedDirectory(directory, {
|
|
133
|
+
member: childSessionId !== undefined,
|
|
134
|
+
curated: this.deps.modelList?.(this.deps.homeDir(childSessionId)) ?? [],
|
|
135
|
+
current: info.effective,
|
|
136
|
+
});
|
|
96
137
|
}
|
|
97
138
|
async *followModelDirectory(childSessionId, signal) {
|
|
98
139
|
if (this.deps.catalog === undefined) {
|
|
@@ -11,24 +11,45 @@ import type { ContentBlock, TokenUsage } from '@deepseek-ai/dsh-llm';
|
|
|
11
11
|
import type { Session } from '@deepseek-ai/dsh-session';
|
|
12
12
|
import type { LocalAgentToolCalls } from '@khorsheed/dsh-local-agent/types';
|
|
13
13
|
import { type KimiTranscriptLine } from './session-view.ts';
|
|
14
|
+
/**
|
|
15
|
+
* The synthesized result for a call whose result the wire never recorded
|
|
16
|
+
* (a settle-pass fold, or a live reservation kimi never logged). The
|
|
17
|
+
* released-log contract resolves every advertised call before its step
|
|
18
|
+
* closes, so an interrupted call folds with this error result.
|
|
19
|
+
*/
|
|
20
|
+
export declare const KIMI_INTERRUPTED_TOOL_RESULT = "\uFF08\u5DE5\u5177\u8C03\u7528\u88AB\u4E2D\u65AD\uFF0C\u672A\u80FD\u8BB0\u5F55\u8FD4\u56DE\u7ED3\u679C\uFF09";
|
|
14
21
|
/** One assistant-role message event, attributed to the kimi route. */
|
|
15
22
|
export declare function assistantEvent(blocks: readonly ContentBlock[], model?: string): import("@deepseek-ai/dsh-llm").AssistantMessage;
|
|
16
23
|
/**
|
|
17
|
-
* The step the next fold pass would assign in one turn
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
24
|
+
* The step the next fold pass would assign in one turn: the smallest step
|
|
25
|
+
* nothing holds (never written, never reserved at call time — callers skip
|
|
26
|
+
* their own reservations on top). Filling the smallest free step keeps the
|
|
27
|
+
* stored log dense: the released-log reader admits only dense, strictly
|
|
28
|
+
* sequential steps, and a reservation released mid-round (its content rode an
|
|
29
|
+
* earlier fold) leaves a gap only the next first-free write fills.
|
|
22
30
|
* @param childSession - the run's child session.
|
|
23
31
|
* @param turn - the round's turn number.
|
|
24
|
-
* @returns the
|
|
32
|
+
* @returns the smallest free step in the turn.
|
|
25
33
|
*/
|
|
26
34
|
export declare function nextKimiSessionStep(childSession: Session, turn: number): number;
|
|
35
|
+
/**
|
|
36
|
+
* The step a durable write takes: one past the highest step this turn has
|
|
37
|
+
* opened in the log. The released-log reader admits steps only in order —
|
|
38
|
+
* one past the last, never below it — so the coordinate is decided when the
|
|
39
|
+
* step is written, never reserved ahead (a reservation filled later lands
|
|
40
|
+
* below a higher step already written: a gap, then a step out of order).
|
|
41
|
+
* Stream and tool reservations only key the live preview.
|
|
42
|
+
*/
|
|
43
|
+
export declare function nextWrittenKimiStep(childSession: Session, turn: number): number;
|
|
27
44
|
/** The run-progress delta text for one transcript line (any kind). */
|
|
28
45
|
export declare function kimiLineProgressText(line: KimiTranscriptLine): string;
|
|
29
46
|
/** The result of one mirror pass: the new offset plus what was newly mirrored. */
|
|
30
47
|
export interface KimiMirrorDelta {
|
|
31
|
-
/**
|
|
48
|
+
/**
|
|
49
|
+
* The new mirror offset (transcript lines folded so far). An unresulted
|
|
50
|
+
* tool line (or a wire step the wire has not closed yet) HOLDS a mid-round
|
|
51
|
+
* pass: the offset stops before it and the next pass retries from there.
|
|
52
|
+
*/
|
|
32
53
|
total: number;
|
|
33
54
|
/**
|
|
34
55
|
* The folded text of each transcript line THIS pass newly mirrored, in
|
|
@@ -38,17 +59,24 @@ export interface KimiMirrorDelta {
|
|
|
38
59
|
*/
|
|
39
60
|
texts: string[];
|
|
40
61
|
/**
|
|
41
|
-
* The usage this pass computed for its window. Attached
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
62
|
+
* The usage this pass computed for its window. Attached per wire step: the
|
|
63
|
+
* records whose line position falls inside a folded unit's span ride THAT
|
|
64
|
+
* unit's message (one usage.record per LLM request, matching the host
|
|
65
|
+
* session's own shape); anything no fold carried comes back as
|
|
66
|
+
* `usageUnattached` for the caller to carry.
|
|
45
67
|
*/
|
|
46
68
|
usage?: TokenUsage;
|
|
47
69
|
/**
|
|
48
|
-
* True when this pass's usage rode
|
|
70
|
+
* True when this pass's usage rode folded assistant messages — the caller
|
|
49
71
|
* must never attach that accounting a second time.
|
|
50
72
|
*/
|
|
51
73
|
usageAttached?: boolean;
|
|
74
|
+
/**
|
|
75
|
+
* The window's accounting that NO fold carried (a pass with only held or
|
|
76
|
+
* fully-sealed folds): the token-granularity live driver sums it into the
|
|
77
|
+
* round's uncarried remainder, which the settle's final snapshot carries.
|
|
78
|
+
*/
|
|
79
|
+
usageUnattached?: TokenUsage;
|
|
52
80
|
/**
|
|
53
81
|
* The model identifier the wire named (a `usage.record` or `llm.request`
|
|
54
82
|
* `model` field, last one seen) — the delegation's observed-model source.
|
|
@@ -66,35 +94,51 @@ export interface KimiMirrorDelta {
|
|
|
66
94
|
/**
|
|
67
95
|
* The live stream ledger a fold pass coordinates with.
|
|
68
96
|
* Absent on the exec path, where no streams reserve steps.
|
|
97
|
+
*
|
|
98
|
+
* The durable contract this enforces: the released-log reader admits only
|
|
99
|
+
* dense, strictly sequential steps (one open at a time) — so the fold writes
|
|
100
|
+
* every step as one atomic step/start→…→step/end triple, and a fold landing
|
|
101
|
+
* at a reserved step first finalizes every stream reservation below it.
|
|
69
102
|
*/
|
|
70
103
|
export interface KimiMirrorStreams {
|
|
71
104
|
/**
|
|
72
|
-
* Steps in-flight streams reserved for this turn (their
|
|
73
|
-
*
|
|
74
|
-
* mid-stream lands ABOVE the stream's step and the
|
|
75
|
-
* round's chronological order.
|
|
105
|
+
* Steps in-flight streams reserved for this turn (their live updates ride
|
|
106
|
+
* `local-agent/stream` events there). Sequential folds skip them, so a
|
|
107
|
+
* tool card folded mid-stream lands ABOVE the stream's step and the
|
|
108
|
+
* projection renders the round's chronological order.
|
|
76
109
|
*/
|
|
77
110
|
reservedSteps(turn: number): readonly number[];
|
|
78
|
-
/**
|
|
79
|
-
|
|
111
|
+
/**
|
|
112
|
+
* Finalize every in-flight stream reserved below `step`, in ascending
|
|
113
|
+
* order, writing each one's atomic step triple with its accumulated text.
|
|
114
|
+
* Called before any fold writes at `step`. Stream reservations only: an
|
|
115
|
+
* unresulted tool line HOLDS the pass instead, so a tool reservation
|
|
116
|
+
* never sits inside the gap.
|
|
117
|
+
*/
|
|
118
|
+
finalizeBefore?(turn: number, step: number): void;
|
|
119
|
+
/**
|
|
120
|
+
* Release the ACP tool notification's chronological reservation for this
|
|
121
|
+
* wire line WITHOUT writing: the line's fold carries the whole native
|
|
122
|
+
* lifecycle inside its step, so the reserved coordinate frees for a later
|
|
123
|
+
* fold. (A reservation kimi never logs to the wire stays reserved and
|
|
124
|
+
* finalizes as the driver's settle-time orphan fold.)
|
|
125
|
+
*/
|
|
126
|
+
releaseTool?(line: KimiTranscriptLine & {
|
|
80
127
|
kind: 'tool';
|
|
81
|
-
}):
|
|
82
|
-
readonly step: number;
|
|
83
|
-
readonly opened: boolean;
|
|
84
|
-
} | undefined;
|
|
128
|
+
}): void;
|
|
85
129
|
/**
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
130
|
+
* Consume the round's in-flight content streams of the given kinds, pairing
|
|
131
|
+
* them with one fold: `step` is the fold coordinate (the earliest consumed
|
|
132
|
+
* reservation still needing one — absent when every consumed kind was
|
|
133
|
+
* already sealed, so the fold goes sequential), and `sealed` lists the
|
|
134
|
+
* kinds already durable (a gap fill wrote their triples — the fold must
|
|
135
|
+
* EXCLUDE their content). Reservations whose content rides this fold are
|
|
136
|
+
* released, so a later sequential fold may take their steps. Undefined when
|
|
137
|
+
* no stream pairs — the fold goes sequential.
|
|
92
138
|
*/
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
readonly step: number;
|
|
97
|
-
readonly opened: boolean;
|
|
139
|
+
completeStreams(turn: number, kinds: readonly ('think' | 'text')[]): {
|
|
140
|
+
readonly step?: number;
|
|
141
|
+
readonly sealed: readonly ('think' | 'text')[];
|
|
98
142
|
} | undefined;
|
|
99
143
|
}
|
|
100
144
|
/** Mirror behavior switches shared by the exec and live paths. */
|
|
@@ -113,12 +157,22 @@ export interface KimiMirrorOptions {
|
|
|
113
157
|
* think/assistant lines with their streams and skip reserved steps.
|
|
114
158
|
*/
|
|
115
159
|
streams?: KimiMirrorStreams;
|
|
160
|
+
/**
|
|
161
|
+
* Settle pass: a tool line whose result the wire never recorded folds with
|
|
162
|
+
* a synthesized interrupted result instead of holding the pass (the round
|
|
163
|
+
* is over — the result will never arrive). Mid-round passes hold there.
|
|
164
|
+
*/
|
|
165
|
+
settle?: boolean;
|
|
116
166
|
}
|
|
117
167
|
/**
|
|
118
|
-
* Mirror one kimi session's transcript into
|
|
119
|
-
* user prompt becomes a `user/message`, each
|
|
120
|
-
*
|
|
121
|
-
*
|
|
168
|
+
* Mirror one kimi session's transcript into a dsh subagent session: each
|
|
169
|
+
* user prompt becomes a `user/message`, and each WIRE STEP (one LLM request:
|
|
170
|
+
* its thinking, reply text and tool calls share the wire's own step number)
|
|
171
|
+
* folds into one dsh step whose `assistant/message` carries the reasoning,
|
|
172
|
+
* text and tool-call blocks together — the host session's own shape, so the
|
|
173
|
+
* standard surfaces (conversation, Context Lens) read one request per LLM
|
|
174
|
+
* call. Then the events are appended to persistence so the child session is
|
|
175
|
+
* viewable.
|
|
122
176
|
*
|
|
123
177
|
* A resumed round (or a live poll) passes the already-mirrored
|
|
124
178
|
* transcript-line count so only the delta is appended — re-mirroring earlier
|
|
@@ -128,22 +182,29 @@ export interface KimiMirrorOptions {
|
|
|
128
182
|
* already in the child session, so a round mirrored across several live polls
|
|
129
183
|
* does not restart step 1 per poll.
|
|
130
184
|
*
|
|
131
|
-
* Every folded step is
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
* sequential fold never takes a
|
|
185
|
+
* Every folded step is one ATOMIC `step/start`–`step/end` triple, written
|
|
186
|
+
* only when its content lands: the released-log reader admits only dense,
|
|
187
|
+
* strictly sequential steps (one open at a time — a second `step/start`
|
|
188
|
+
* while another step is open fails the session's reload validation), and the
|
|
189
|
+
* live conversation assembler needs the boundary to materialize the step at
|
|
190
|
+
* all. A fold paired with in-flight streams lands at the earliest consumed
|
|
191
|
+
* reservation (see {@link KimiMirrorStreams}), after `finalizeBefore` has
|
|
192
|
+
* closed every stream reservation below it; a sequential fold never takes a
|
|
193
|
+
* step a stream reserved. Mid-round, a wire step the wire has not CLOSED yet
|
|
194
|
+
* (no `step.end`) holds the pass — its parts may still arrive — and so does
|
|
195
|
+
* an unresulted tool call (the reader resolves every advertised call before
|
|
196
|
+
* the step closes; the result merges into the call's line in place, so the
|
|
197
|
+
* next pass sees it). The settle pass folds the remainder with a synthesized
|
|
198
|
+
* interrupted result. Lines the wire never step-tagged (older kimi) fold one
|
|
199
|
+
* by one, immediately, as before.
|
|
139
200
|
*
|
|
140
201
|
* Usage accounting is exactly-once ACROSS passes: each `usage.record` is
|
|
141
|
-
* tagged with the transcript position it occurred at,
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
*
|
|
202
|
+
* tagged with the transcript position it occurred at, a pass owns the records
|
|
203
|
+
* between the old offset and its own consumed end, and inside the window each
|
|
204
|
+
* record rides the unit whose line span encloses it — one record per wire
|
|
205
|
+
* step, landing on its own step's message. (A record raced PAST its content
|
|
206
|
+
* encloses into the next unit instead: attribution off by one in a flush
|
|
207
|
+
* race, totals still exact.)
|
|
147
208
|
* @param ctx - host context carrying the session persistence service.
|
|
148
209
|
* @param childSession - the dsh subagent session created for this delegation.
|
|
149
210
|
* @param homeDir - the `kimi` harness's scoped home.
|
|
@@ -151,7 +212,9 @@ export interface KimiMirrorOptions {
|
|
|
151
212
|
* recent session (a one-shot `kimi -p` run always creates a fresh one).
|
|
152
213
|
* @param fromLines - transcript lines already mirrored into the child session
|
|
153
214
|
* (0 for the first round); only lines after this offset are appended.
|
|
154
|
-
* @returns the new
|
|
215
|
+
* @returns the new mirror offset and the newly mirrored lines' texts. A held
|
|
216
|
+
* unit (open wire step or unresulted tool) stops the offset before itself;
|
|
217
|
+
* the next pass retries from there.
|
|
155
218
|
*/
|
|
156
219
|
export declare function mirrorKimiSessionDelta(ctx: Context, childSession: Session, homeDir: string, kimiSessionId?: string, fromLines?: number, options?: KimiMirrorOptions): Promise<KimiMirrorDelta>;
|
|
157
220
|
/**
|