@khorsheed/dsh-local-agent 0.1.0-rc.6
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/README.en.md +181 -0
- package/README.i18n.yaml +2 -0
- package/README.md +181 -0
- package/cordis.patch.yml +21 -0
- package/lib/client.js +8832 -0
- package/lib/index.js +5281 -0
- package/lib/invariant.js +85 -0
- package/lib/member-bridge.js +298 -0
- package/lib/typert.host.d.ts +3 -0
- package/lib/typert.host.js +3137 -0
- package/lib/typert.remote-client.d.ts +67 -0
- package/lib/typert.remote-client.js +1971 -0
- package/lib/types/cli-version.d.ts +82 -0
- package/lib/types/cli-version.js +165 -0
- package/lib/types/client/AuthStatusDot.d.ts +17 -0
- package/lib/types/client/AuthStatusDot.js +9 -0
- package/lib/types/client/HarnessModelPicker.d.ts +17 -0
- package/lib/types/client/HarnessModelPicker.js +48 -0
- package/lib/types/client/LocalAgentRecordsAction.d.ts +41 -0
- package/lib/types/client/LocalAgentRecordsAction.js +74 -0
- package/lib/types/client/MemberComposer.d.ts +79 -0
- package/lib/types/client/MemberComposer.js +308 -0
- package/lib/types/client/MemberConfiguration.d.ts +9 -0
- package/lib/types/client/MemberConfiguration.js +46 -0
- package/lib/types/client/MemberInboxView.d.ts +12 -0
- package/lib/types/client/MemberInboxView.js +38 -0
- package/lib/types/client/MemberLiveNode.d.ts +14 -0
- package/lib/types/client/MemberLiveNode.js +74 -0
- package/lib/types/client/MemberPaintDiagnostics.d.ts +7 -0
- package/lib/types/client/MemberPaintDiagnostics.js +20 -0
- package/lib/types/client/ModelConfigurationFields.d.ts +15 -0
- package/lib/types/client/ModelConfigurationFields.js +23 -0
- package/lib/types/client/ProviderAuthBlock.d.ts +59 -0
- package/lib/types/client/ProviderAuthBlock.js +164 -0
- package/lib/types/client/auth-status.d.ts +28 -0
- package/lib/types/client/auth-status.js +56 -0
- package/lib/types/client/icons.d.ts +22 -0
- package/lib/types/client/icons.js +6 -0
- package/lib/types/client/index.d.ts +62 -0
- package/lib/types/client/index.js +135 -0
- package/lib/types/client/live-node.d.ts +14 -0
- package/lib/types/client/live-node.js +34 -0
- package/lib/types/client/live-output.d.ts +34 -0
- package/lib/types/client/live-output.js +94 -0
- package/lib/types/client/live-paint.d.ts +40 -0
- package/lib/types/client/live-paint.js +74 -0
- package/lib/types/client/locales.d.ts +120 -0
- package/lib/types/client/locales.js +227 -0
- package/lib/types/client/member-configuration.d.ts +41 -0
- package/lib/types/client/member-configuration.js +114 -0
- package/lib/types/client/member-dock.d.ts +94 -0
- package/lib/types/client/member-dock.js +102 -0
- package/lib/types/client/member-feed.d.ts +24 -0
- package/lib/types/client/member-feed.js +134 -0
- package/lib/types/client/request-id.d.ts +3 -0
- package/lib/types/client/request-id.js +6 -0
- package/lib/types/client/settings-scope.d.ts +74 -0
- package/lib/types/client/settings-scope.js +98 -0
- package/lib/types/client/use-configuration-menu.d.ts +10 -0
- package/lib/types/client/use-configuration-menu.js +61 -0
- package/lib/types/container.d.ts +69 -0
- package/lib/types/container.js +129 -0
- package/lib/types/env.d.ts +17 -0
- package/lib/types/env.js +89 -0
- package/lib/types/gateway.d.ts +162 -0
- package/lib/types/gateway.js +470 -0
- package/lib/types/index.d.ts +1463 -0
- package/lib/types/index.js +2704 -0
- package/lib/types/invariant.d.ts +16 -0
- package/lib/types/invariant.js +105 -0
- package/lib/types/live-flush.d.ts +21 -0
- package/lib/types/live-flush.js +59 -0
- package/lib/types/live-stream.d.ts +41 -0
- package/lib/types/live-stream.js +178 -0
- package/lib/types/member-bridge.d.ts +50 -0
- package/lib/types/member-bridge.js +216 -0
- package/lib/types/member-channel.d.ts +86 -0
- package/lib/types/member-channel.js +232 -0
- package/lib/types/member-control-storage.d.ts +10 -0
- package/lib/types/member-control-storage.js +100 -0
- package/lib/types/member-control.d.ts +62 -0
- package/lib/types/member-control.js +359 -0
- package/lib/types/member-controls.d.ts +23 -0
- package/lib/types/member-controls.js +143 -0
- package/lib/types/member-feed.d.ts +4 -0
- package/lib/types/member-feed.js +45 -0
- package/lib/types/member-inbox.d.ts +30 -0
- package/lib/types/member-inbox.js +225 -0
- package/lib/types/model-directory.d.ts +43 -0
- package/lib/types/model-directory.js +192 -0
- package/lib/types/prepared-members.d.ts +15 -0
- package/lib/types/prepared-members.js +61 -0
- package/lib/types/settings-face.d.ts +61 -0
- package/lib/types/settings-face.js +61 -0
- package/lib/types/types.d.ts +959 -0
- package/lib/types/types.js +24 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +112 -0
|
@@ -0,0 +1,959 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local-agent family type vocabulary, exported through the `./types` subpath
|
|
3
|
+
* so Typert Remote boundary types resolve from a public non-root subpath
|
|
4
|
+
* (the wire-schema generator's requirement for {@link LocalAgentGateway}
|
|
5
|
+
* method signatures).
|
|
6
|
+
* @module @khorsheed/dsh-local-agent/types
|
|
7
|
+
*/
|
|
8
|
+
import type { TokenUsage } from '@deepseek-ai/dsh-llm';
|
|
9
|
+
/** In-flight text item, identified by the durable turn/step coordinate. */
|
|
10
|
+
export interface LocalAgentStreamItem {
|
|
11
|
+
id: string;
|
|
12
|
+
turn: number;
|
|
13
|
+
step: number;
|
|
14
|
+
kind: 'think' | 'text';
|
|
15
|
+
text: string;
|
|
16
|
+
revision: number;
|
|
17
|
+
receivedAt: number;
|
|
18
|
+
}
|
|
19
|
+
/** Reconnection starts with a baseline; subsequent updates carry suffixes or explicit replacements. */
|
|
20
|
+
export interface LocalAgentStreamFrame {
|
|
21
|
+
baseline: boolean;
|
|
22
|
+
updates: readonly (LocalAgentStreamItem & {
|
|
23
|
+
append: boolean;
|
|
24
|
+
})[];
|
|
25
|
+
removed: readonly string[];
|
|
26
|
+
}
|
|
27
|
+
/** One browser connection multiplexes every currently rendered member surface. */
|
|
28
|
+
export interface LocalAgentMemberFeedRequest {
|
|
29
|
+
memberId: string;
|
|
30
|
+
channel: 'configuration' | 'directory' | 'output';
|
|
31
|
+
}
|
|
32
|
+
export type LocalAgentMemberFeedEvent = {
|
|
33
|
+
memberId: string;
|
|
34
|
+
channel: 'configuration';
|
|
35
|
+
value: LocalAgentMemberControlState;
|
|
36
|
+
} | {
|
|
37
|
+
memberId: string;
|
|
38
|
+
channel: 'directory';
|
|
39
|
+
value: LocalAgentModelDirectory;
|
|
40
|
+
} | {
|
|
41
|
+
memberId: string;
|
|
42
|
+
channel: 'output';
|
|
43
|
+
value: LocalAgentStreamFrame;
|
|
44
|
+
} | {
|
|
45
|
+
memberId: string;
|
|
46
|
+
channel: 'error';
|
|
47
|
+
source: 'configuration' | 'directory' | 'output';
|
|
48
|
+
message: string;
|
|
49
|
+
};
|
|
50
|
+
/** Log-only recovery checkpoint; never injected into a model's conversation. */
|
|
51
|
+
export interface LocalAgentStreamCheckpoint extends Omit<LocalAgentStreamItem, 'revision'> {
|
|
52
|
+
sessionId: string;
|
|
53
|
+
opening?: boolean;
|
|
54
|
+
/** Final native message has replaced this subitem presentation. */
|
|
55
|
+
closed?: boolean;
|
|
56
|
+
append: boolean;
|
|
57
|
+
}
|
|
58
|
+
declare module '@deepseek-ai/dsh-session/types' {
|
|
59
|
+
interface SessionEventMap {
|
|
60
|
+
/** Plugin-owned live presentation anchor and incremental recovery checkpoints. */
|
|
61
|
+
'local-agent/stream': LocalAgentStreamCheckpoint;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/** One session record a harness's records adapter lists. */
|
|
65
|
+
export interface LocalAgentSessionRecord {
|
|
66
|
+
/** Harness session id. */
|
|
67
|
+
id: string;
|
|
68
|
+
/** The workspace directory the session ran in. */
|
|
69
|
+
workDir: string;
|
|
70
|
+
/** Optional session title when the harness records one. */
|
|
71
|
+
title?: string;
|
|
72
|
+
/** Optional epoch-millis start time when the harness records one. */
|
|
73
|
+
startedAt?: number;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* How much is known about a harness's credential, from the two channels a
|
|
77
|
+
* status read has: the scoped home's own shape, and what delegation rounds
|
|
78
|
+
* observed.
|
|
79
|
+
*
|
|
80
|
+
* - `absent` — no credential in the scoped home (or the harness declares no
|
|
81
|
+
* probe at all). Nothing to try.
|
|
82
|
+
* - `present-unverified` — a credential record exists and nothing has
|
|
83
|
+
* exercised it in this host process. The record could be expired and
|
|
84
|
+
* unrefreshable and still look exactly like this; saying so is the point.
|
|
85
|
+
* - `verified` — a delegation round reached the CLI's endpoint and completed
|
|
86
|
+
* since the last login/logout, so the credential is known live.
|
|
87
|
+
* - `rejected` — a round's endpoint rejected the credential (a 401/403 a
|
|
88
|
+
* presence probe cannot see) and no fresh login has rewritten the
|
|
89
|
+
* credential marker since.
|
|
90
|
+
*
|
|
91
|
+
* The grade is per host process: after a restart a present credential reports
|
|
92
|
+
* `present-unverified` until a round exercises it, because that is what is
|
|
93
|
+
* actually known. An eager liveness probe at status time is deliberately NOT
|
|
94
|
+
* this field's job.
|
|
95
|
+
*/
|
|
96
|
+
export type LocalAgentCredentialState = 'absent' | 'present-unverified' | 'verified' | 'rejected';
|
|
97
|
+
/** One harness's auth-status snapshot, shared by the command and Remote channels. */
|
|
98
|
+
export interface LocalAgentStatus {
|
|
99
|
+
/** Harness command prefix. */
|
|
100
|
+
name: string;
|
|
101
|
+
/** Human display name. */
|
|
102
|
+
displayName: string;
|
|
103
|
+
/**
|
|
104
|
+
* Whether the scoped home holds usable credentials — the SHAPE answer,
|
|
105
|
+
* kept for every surface written before {@link LocalAgentCredentialState}
|
|
106
|
+
* existed. Exactly `credentialState === 'verified' || 'present-unverified'`:
|
|
107
|
+
* a record that is present but never exercised still reads true, which is
|
|
108
|
+
* why the finer field exists.
|
|
109
|
+
*/
|
|
110
|
+
authenticated: boolean;
|
|
111
|
+
/**
|
|
112
|
+
* How much is actually known about the credential — the liveness grade
|
|
113
|
+
* behind {@link LocalAgentStatus.authenticated}. Always present.
|
|
114
|
+
*/
|
|
115
|
+
credentialState: LocalAgentCredentialState;
|
|
116
|
+
/**
|
|
117
|
+
* Absolute scoped home — of the SCOPE this status was asked about (see
|
|
118
|
+
* {@link LocalAgentStatus.scope}), which for the default scope is the
|
|
119
|
+
* directory it always was.
|
|
120
|
+
*/
|
|
121
|
+
homeDir: string;
|
|
122
|
+
/**
|
|
123
|
+
* The named scope this status describes, when it describes one. Absent
|
|
124
|
+
* means the default scope — the only one that existed before named scopes,
|
|
125
|
+
* so every surface written before the field keeps reading exactly what it
|
|
126
|
+
* read. A named scope holds its own account: its credential grade, its
|
|
127
|
+
* session records and its delegation mappings are its own.
|
|
128
|
+
*/
|
|
129
|
+
scope?: string;
|
|
130
|
+
/** Subagent provider name, when the harness delegates. */
|
|
131
|
+
delegationProvider?: string;
|
|
132
|
+
/**
|
|
133
|
+
* Whether the harness declares a device-code login flow (`/<name> login`).
|
|
134
|
+
* A harness without one authenticates through the host instance (e.g. by
|
|
135
|
+
* resolving a credential), so surfaces must not offer a login action.
|
|
136
|
+
*/
|
|
137
|
+
loginable?: boolean;
|
|
138
|
+
/**
|
|
139
|
+
* A pty login is waiting for the user to paste the OAuth code
|
|
140
|
+
* (`/<name> code <value>`). Surfaces render the paste box while set.
|
|
141
|
+
*/
|
|
142
|
+
loginAwaitingCode?: boolean;
|
|
143
|
+
/**
|
|
144
|
+
* Whether the harness declares a sign-out path (`/<name> logout`). A
|
|
145
|
+
* harness without one has no account to switch, so surfaces must not offer
|
|
146
|
+
* a logout action.
|
|
147
|
+
*/
|
|
148
|
+
logoutable?: boolean;
|
|
149
|
+
/**
|
|
150
|
+
* The harness's fairness-relevant effective settings, when the harness
|
|
151
|
+
* declares a snapshot ({@link LocalAgentHarness.effectiveSettings}).
|
|
152
|
+
* Additive: surfaces and clients written before the field simply ignore it.
|
|
153
|
+
*/
|
|
154
|
+
effectiveSettings?: LocalAgentEffectiveSettings;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* One harness's fairness-relevant effective settings — the read side of the
|
|
158
|
+
* web-eval condition hash. Everything here is a knob the evaluation must hold
|
|
159
|
+
* equal across harnesses (drive, approval boundary, reasoning effort,
|
|
160
|
+
* endpoint route, CLI version) expressed as PURE JSON. Credentials never
|
|
161
|
+
* enter: no keys, no tokens, no full URLs (an endpoint reports its hostname
|
|
162
|
+
* only — a URL's path could carry tenant or project ids).
|
|
163
|
+
*
|
|
164
|
+
* A field the harness has no knob for — or one whose knob this deployment
|
|
165
|
+
* leaves unset — stays ABSENT (`sandbox`, `permissionMode`, `autoApprove`,
|
|
166
|
+
* `reasoningEffort`, `model`, `cliVersion`): absence is itself the honest
|
|
167
|
+
* condition-hash input, never a substituted default. `drive` and
|
|
168
|
+
* `baseUrlSet` are always present.
|
|
169
|
+
*/
|
|
170
|
+
export interface LocalAgentEffectiveSettings {
|
|
171
|
+
/**
|
|
172
|
+
* How delegation rounds run: a one-shot CLI process per round (`exec`) or a
|
|
173
|
+
* resident runtime driven over its wire (`live`). The reported value is the
|
|
174
|
+
* configured driver preference in force; a live round may still fall back
|
|
175
|
+
* to exec when its channel cannot come up.
|
|
176
|
+
*/
|
|
177
|
+
drive: 'exec' | 'live';
|
|
178
|
+
/**
|
|
179
|
+
* The file-effect boundary in force, in the harness's own vocabulary.
|
|
180
|
+
* Codex: the sandbox policy every round passes to `codex exec --sandbox`.
|
|
181
|
+
* dsh: the permission preset provisioning pins into the sub-profile
|
|
182
|
+
* (`read-only` / `workspace-write` / `danger-full-access`), which also
|
|
183
|
+
* decides the sub-dsh's approval policy through dsh-base's own preset
|
|
184
|
+
* table. Absent when the deployment pins none.
|
|
185
|
+
*/
|
|
186
|
+
sandbox?: string;
|
|
187
|
+
/** Claude Code: the `claude -p` permission handling (`skip` or `normal`). */
|
|
188
|
+
permissionMode?: string;
|
|
189
|
+
/**
|
|
190
|
+
* Kimi: whether the scoped config's permission rules auto-approve tool use
|
|
191
|
+
* (the `Bash(*)` allow rule the provisioning gate keys on).
|
|
192
|
+
*/
|
|
193
|
+
autoApprove?: boolean;
|
|
194
|
+
/**
|
|
195
|
+
* The reasoning effort in force, when the harness has one. Kimi reads its
|
|
196
|
+
* scoped config (`[thinking] effort`, falling back to the model's
|
|
197
|
+
* `default_effort`); codex reads its scoped config's
|
|
198
|
+
* `model_reasoning_effort`. Absent when the harness exposes no effort knob.
|
|
199
|
+
*/
|
|
200
|
+
reasoningEffort?: string;
|
|
201
|
+
/** Whether a non-default endpoint is in force (a provider-pinned or env-provided base URL). */
|
|
202
|
+
baseUrlSet: boolean;
|
|
203
|
+
/**
|
|
204
|
+
* The endpoint's HOSTNAME only, present exactly when `baseUrlSet`. Never
|
|
205
|
+
* the full URL — its path can carry credential-adjacent segments.
|
|
206
|
+
*/
|
|
207
|
+
baseUrlHost?: string;
|
|
208
|
+
/**
|
|
209
|
+
* The CLI's own version, as the CLI itself reports it — the version a
|
|
210
|
+
* delegation round would run with right now. Resolved by the family's
|
|
211
|
+
* `<cli> --version` probe, which spawns at most once per executable
|
|
212
|
+
* identity (resolved path + mtime + size) and is therefore cheap enough for
|
|
213
|
+
* a status read while still re-probing across an upgrade. Absent when the
|
|
214
|
+
* CLI cannot be asked (not installed, non-zero exit, timeout, no
|
|
215
|
+
* version-shaped token in its banner) — absence is the honest condition
|
|
216
|
+
* input, never a substituted guess.
|
|
217
|
+
*/
|
|
218
|
+
cliVersion?: string;
|
|
219
|
+
/**
|
|
220
|
+
* The configured model identifier a delegation round would run with — read
|
|
221
|
+
* in one fixed order, never guessed: the harness's OWN `model` plugin-config
|
|
222
|
+
* key first (codex, claude-code, and kimi each accept one, and a set key
|
|
223
|
+
* rides every round's CLI launch), then the harness's scoped configuration
|
|
224
|
+
* surface (kimi's `default_model`, codex's `model`, claude-code's
|
|
225
|
+
* `settings.json` `model`, dsh's host `agentDefaultModel` selection
|
|
226
|
+
* formatted `provider/model` — the CLI's own default stays unnamed), then
|
|
227
|
+
* absent. Absence is the honest answer, never a substituted default.
|
|
228
|
+
*
|
|
229
|
+
* This deliberately does NOT report a delegation-level model
|
|
230
|
+
* ({@link DelegationCallOptions.model}): that one belongs to one
|
|
231
|
+
* delegation, while this field answers "what would a round with no model of
|
|
232
|
+
* its own run" — the harness-wide setting, which is what the evaluation's
|
|
233
|
+
* condition snapshot is asking about.
|
|
234
|
+
*
|
|
235
|
+
* This is a LIVE read of what the NEXT such round would run with. A run's
|
|
236
|
+
* condition hash freezes it at run setup, so changing the key mid-run does
|
|
237
|
+
* not rewrite the recorded condition — the next round's model read-back
|
|
238
|
+
* fails the run as misattributed instead (web-eval frozen decision 5).
|
|
239
|
+
*/
|
|
240
|
+
model?: string;
|
|
241
|
+
/**
|
|
242
|
+
* Extra `NODE_OPTIONS` the harness injects when a round runs against a
|
|
243
|
+
* container exec target ({@link DelegationExecTarget}). Only the dsh
|
|
244
|
+
* harness declares one: its HTTP client is node's `fetch` (undici), which
|
|
245
|
+
* does not read `HTTP(S)_PROXY` by default, so a round inside a unit whose
|
|
246
|
+
* only egress is a whitelist proxy needs `--use-env-proxy` to open
|
|
247
|
+
* undici's `EnvHttpProxyAgent` — without it the CLI dials the API directly
|
|
248
|
+
* and the proxy never even sees a `CONNECT`. Reported unconditionally
|
|
249
|
+
* because the injection is unconditional in code; absent means the harness
|
|
250
|
+
* injects none. Never a credential — a node flag.
|
|
251
|
+
*/
|
|
252
|
+
containerNodeOptions?: string;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* The hostname of an endpoint URL, or undefined when the value does not parse
|
|
256
|
+
* as a URL (scheme-less `host:port` forms included — still reported via
|
|
257
|
+
* `baseUrlSet`, just without a hostname).
|
|
258
|
+
*/
|
|
259
|
+
export declare function endpointHost(url: string): string | undefined;
|
|
260
|
+
/** Roster row: enough of a harness for a client list. */
|
|
261
|
+
export interface LocalAgentRosterRow {
|
|
262
|
+
/** Harness command prefix. */
|
|
263
|
+
name: string;
|
|
264
|
+
/** Human display name. */
|
|
265
|
+
displayName: string;
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* One family delegation's CLI-session mapping, recorded by the harness
|
|
269
|
+
* provider after its first round settles (the CLI session id is only known
|
|
270
|
+
* from the round's output). The resume tool resolves a caller-supplied dsh
|
|
271
|
+
* child session id against this registry, and ownership is proven by the
|
|
272
|
+
* recorded parent session id: a forged handle naming another session is
|
|
273
|
+
* rejected instead of resuming someone else's conversation context.
|
|
274
|
+
*/
|
|
275
|
+
export interface LocalAgentDelegationRecord {
|
|
276
|
+
/** Creation-time reasoning request and optional frozen evaluation lock. */
|
|
277
|
+
effort?: string;
|
|
278
|
+
configurationLock?: string;
|
|
279
|
+
/** The dsh subagent child session id (the run id of the first round). */
|
|
280
|
+
childSessionId: string;
|
|
281
|
+
/** The `ctx.subagents` provider name that owns the CLI session. */
|
|
282
|
+
provider: string;
|
|
283
|
+
/** The delegating parent session id that created the child. */
|
|
284
|
+
parentSessionId: string;
|
|
285
|
+
/**
|
|
286
|
+
* The CLI's own session/thread id, used to build the resume command.
|
|
287
|
+
*/
|
|
288
|
+
cliSessionId: string;
|
|
289
|
+
/**
|
|
290
|
+
* The resolved working directory the FIRST round ran in — the anchor the
|
|
291
|
+
* resume-consistency check compares a later round's effective cwd against.
|
|
292
|
+
* Recorded by the provider at the delegation's first-round record point;
|
|
293
|
+
* absent on records written before the field existed (they opt out of the
|
|
294
|
+
* check rather than guess).
|
|
295
|
+
*/
|
|
296
|
+
cwd?: string;
|
|
297
|
+
/**
|
|
298
|
+
* The named scope whose scoped home this delegation ran against — the
|
|
299
|
+
* anchor a resume round must repeat. Absent means the DEFAULT scope, which
|
|
300
|
+
* is what every record written before the field existed is; a resume that
|
|
301
|
+
* names a scope against such a record (or names none against a scoped one)
|
|
302
|
+
* is refused rather than continuing the CLI session under another account's
|
|
303
|
+
* credentials.
|
|
304
|
+
*/
|
|
305
|
+
scope?: string;
|
|
306
|
+
/**
|
|
307
|
+
* The model identifier the provider observed in its own output stream for
|
|
308
|
+
* the latest settled round (claude's stream-json init, codex's rollout
|
|
309
|
+
* turn_context, kimi's wire usage/request records, the sub-dsh session's
|
|
310
|
+
* assistant source). Read back verbatim, never guessed: absent when the
|
|
311
|
+
* stream yielded none. This is the observed half of the evaluation's
|
|
312
|
+
* "declared model == actually-run model" check (web-eval frozen decision
|
|
313
|
+
* 5); it never enters any prompt or model-visible face.
|
|
314
|
+
*/
|
|
315
|
+
observedModel?: string;
|
|
316
|
+
/**
|
|
317
|
+
* The model this delegation REQUESTED — the `model` call option its first
|
|
318
|
+
* round carried, recorded so every resume round re-requests the same value.
|
|
319
|
+
* The requested half of the pair whose observed half is
|
|
320
|
+
* {@link LocalAgentDelegationRecord.observedModel}: a delegation that asked
|
|
321
|
+
* for one model and read back another is exactly what the evaluation's
|
|
322
|
+
* declared-vs-run check exists to catch.
|
|
323
|
+
*
|
|
324
|
+
* Absent means the delegation named none — its rounds fall back to the
|
|
325
|
+
* plugin config, the scoped file, then the CLI's default, and a resume of
|
|
326
|
+
* it does the same. Records written before this field existed are that
|
|
327
|
+
* case, unchanged.
|
|
328
|
+
*/
|
|
329
|
+
model?: string;
|
|
330
|
+
/**
|
|
331
|
+
* The CLI version the latest settled round actually ran, read back the same
|
|
332
|
+
* way {@link LocalAgentDelegationRecord.observedModel} is: from the CLI's
|
|
333
|
+
* own stream or records when it names one (codex's rollout `session_meta`
|
|
334
|
+
* carries `cli_version`, claude's stream-json init carries
|
|
335
|
+
* `claude_code_version`), otherwise from the family's `--version` probe of
|
|
336
|
+
* the executable the round spawned. Absent when neither channel answered.
|
|
337
|
+
*/
|
|
338
|
+
cliVersion?: string;
|
|
339
|
+
/**
|
|
340
|
+
* Kimi transcript lines already mirrored into the child session. The kimi
|
|
341
|
+
* provider advances this after every round so a resumed round mirrors only
|
|
342
|
+
* its delta instead of duplicating earlier messages.
|
|
343
|
+
*/
|
|
344
|
+
kimiMirroredLines?: number;
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* One delegation's record WITHOUT the CLI-session resume handle — the
|
|
348
|
+
* read-only projection {@link LocalAgentRegistry.delegationOf} returns for
|
|
349
|
+
* surfaces that need a delegation's facts (provider, parent, observed model,
|
|
350
|
+
* first-round cwd) but must never see the resume handle, which stays a
|
|
351
|
+
* first-class parameter plus the intent channel.
|
|
352
|
+
*/
|
|
353
|
+
export type LocalAgentDelegationInfo = Omit<LocalAgentDelegationRecord, 'cliSessionId'>;
|
|
354
|
+
/**
|
|
355
|
+
* The member-channel view of one delegation, projected by the gateway's
|
|
356
|
+
* `memberOf` Remote for the composer: everything the browser needs to decide
|
|
357
|
+
* "this child session is a family member" and to label it, without the
|
|
358
|
+
* CLI-session resume handle (which never leaves the host).
|
|
359
|
+
*/
|
|
360
|
+
export interface LocalAgentDelegationView {
|
|
361
|
+
/** The dsh subagent child session id. */
|
|
362
|
+
childSessionId: string;
|
|
363
|
+
/** The `ctx.subagents` provider name that owns the CLI session. */
|
|
364
|
+
provider: string;
|
|
365
|
+
/** The delegating parent session id that created the child. */
|
|
366
|
+
parentSessionId: string;
|
|
367
|
+
/** The owning harness's human display name, when a harness claims the provider. */
|
|
368
|
+
harnessDisplayName?: string;
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Result of one user-initiated member prompt (`promptMember` Remote). Facade
|
|
372
|
+
* failures (unknown delegation, parent not live, resume locked, …) arrive as
|
|
373
|
+
* structured errors for the composer to render inline, never as raw exceptions
|
|
374
|
+
* over the wire.
|
|
375
|
+
*/
|
|
376
|
+
export interface LocalAgentMemberInput {
|
|
377
|
+
id: string;
|
|
378
|
+
text: string;
|
|
379
|
+
status: 'queued' | 'running' | 'done' | 'failed' | 'cancelled' | 'uncertain';
|
|
380
|
+
createdAt: number;
|
|
381
|
+
updatedAt: number;
|
|
382
|
+
error?: string;
|
|
383
|
+
}
|
|
384
|
+
export interface LocalAgentMemberInbox {
|
|
385
|
+
memberId: string;
|
|
386
|
+
paused: boolean;
|
|
387
|
+
error?: string;
|
|
388
|
+
messages: LocalAgentMemberInput[];
|
|
389
|
+
}
|
|
390
|
+
export type LocalAgentPromptResult = {
|
|
391
|
+
ok: true;
|
|
392
|
+
requestId?: string;
|
|
393
|
+
} | {
|
|
394
|
+
ok: false;
|
|
395
|
+
error: string;
|
|
396
|
+
};
|
|
397
|
+
/**
|
|
398
|
+
* Where a member's effective model comes from, in the family's fixed
|
|
399
|
+
* resolution order (first match wins): the session-level override set through
|
|
400
|
+
* the composer's model picker, then the delegation's own recorded model, then
|
|
401
|
+
* the harness's plugin-config `model` (settings card / YAML), then the scoped
|
|
402
|
+
* config file's own default, and last the CLI's built-in default — which by
|
|
403
|
+
* itself names nothing, exactly as it did before any of these layers existed.
|
|
404
|
+
* A harness MAY still name that compiled default through a side channel:
|
|
405
|
+
* codex's app-server `model/list` marks the account's built-in default with
|
|
406
|
+
* `isDefault`, so a broker that probed the catalog reports `cli-builtin` WITH
|
|
407
|
+
* `effective` set (the one case this source names a model). And even when
|
|
408
|
+
* nothing names it, the delegation records show what last actually ran —
|
|
409
|
+
* {@link LocalAgentModelInfo.lastObserved}.
|
|
410
|
+
*/
|
|
411
|
+
export type LocalAgentModelSource = 'override' | 'delegation' | 'settings' | 'cli-config' | 'cli-builtin';
|
|
412
|
+
/** Provider-native values, never normalized into a supposedly equivalent budget. */
|
|
413
|
+
export interface LocalAgentReasoningOption {
|
|
414
|
+
value: string;
|
|
415
|
+
label: string;
|
|
416
|
+
description?: string;
|
|
417
|
+
}
|
|
418
|
+
/** A catalog candidate is not proof that an account can successfully run it. */
|
|
419
|
+
export interface LocalAgentModelEntry {
|
|
420
|
+
value: string;
|
|
421
|
+
label: string;
|
|
422
|
+
resolvedModel?: string;
|
|
423
|
+
description?: string;
|
|
424
|
+
source: 'native' | 'configuration' | 'history';
|
|
425
|
+
hidden?: boolean;
|
|
426
|
+
reasoning?: {
|
|
427
|
+
options: readonly LocalAgentReasoningOption[];
|
|
428
|
+
default?: string;
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
export interface LocalAgentModelDirectoryData {
|
|
432
|
+
entries: readonly LocalAgentModelEntry[];
|
|
433
|
+
defaultModel?: string;
|
|
434
|
+
/** Complete enumeration of this source, not a guarantee of account access. */
|
|
435
|
+
complete: boolean;
|
|
436
|
+
customInput: boolean;
|
|
437
|
+
unsupported?: boolean;
|
|
438
|
+
reason?: string;
|
|
439
|
+
}
|
|
440
|
+
export interface LocalAgentModelDirectory extends LocalAgentModelDirectoryData {
|
|
441
|
+
status: 'loading' | 'ready' | 'stale' | 'error' | 'unsupported';
|
|
442
|
+
refreshing: boolean;
|
|
443
|
+
revision: number;
|
|
444
|
+
refreshedAt?: number;
|
|
445
|
+
}
|
|
446
|
+
/** Inherit the member's creation settings, ask for the harness default, or pin a native value. */
|
|
447
|
+
export type LocalAgentConfigurationChoice = {
|
|
448
|
+
mode: 'inherit' | 'default';
|
|
449
|
+
} | {
|
|
450
|
+
mode: 'value';
|
|
451
|
+
value: string;
|
|
452
|
+
};
|
|
453
|
+
export interface LocalAgentMemberConfiguration {
|
|
454
|
+
model: LocalAgentConfigurationChoice;
|
|
455
|
+
effort: LocalAgentConfigurationChoice;
|
|
456
|
+
}
|
|
457
|
+
/** Resolved control facts; actual generation observations are recorded separately. */
|
|
458
|
+
export interface LocalAgentResolvedConfiguration {
|
|
459
|
+
model?: string;
|
|
460
|
+
effort?: string;
|
|
461
|
+
}
|
|
462
|
+
export interface LocalAgentAppliedConfiguration {
|
|
463
|
+
revision: number;
|
|
464
|
+
selection: LocalAgentMemberConfiguration;
|
|
465
|
+
resolved: LocalAgentResolvedConfiguration;
|
|
466
|
+
}
|
|
467
|
+
export interface LocalAgentPendingConfiguration {
|
|
468
|
+
revision: number;
|
|
469
|
+
requestId: string;
|
|
470
|
+
kind: 'selection' | 'cancel';
|
|
471
|
+
selection: LocalAgentMemberConfiguration;
|
|
472
|
+
}
|
|
473
|
+
export interface LocalAgentMemberControlState {
|
|
474
|
+
memberId: string;
|
|
475
|
+
/** Monotonic intent revision, including cancellation. */
|
|
476
|
+
revision: number;
|
|
477
|
+
current: LocalAgentAppliedConfiguration;
|
|
478
|
+
pending?: LocalAgentPendingConfiguration;
|
|
479
|
+
operation?: LocalAgentPendingConfiguration & {
|
|
480
|
+
previous: LocalAgentAppliedConfiguration;
|
|
481
|
+
};
|
|
482
|
+
status: 'idle' | 'pending' | 'applying' | 'reconciling' | 'failed';
|
|
483
|
+
/** Immutable configuration captured at admission, including tool continuation. */
|
|
484
|
+
round?: {
|
|
485
|
+
id: string;
|
|
486
|
+
configuration: LocalAgentAppliedConfiguration;
|
|
487
|
+
};
|
|
488
|
+
lockedReason?: string;
|
|
489
|
+
/** First admitted configuration of a frozen evaluation; defaults cannot drift on resume. */
|
|
490
|
+
frozen?: LocalAgentAppliedConfiguration;
|
|
491
|
+
error?: string;
|
|
492
|
+
}
|
|
493
|
+
export interface LocalAgentControlReceipt {
|
|
494
|
+
requestId: string;
|
|
495
|
+
revision: number;
|
|
496
|
+
status: 'pending' | 'applying' | 'applied' | 'cancelled' | 'failed' | 'conflict' | 'locked' | 'unsupported';
|
|
497
|
+
error?: string;
|
|
498
|
+
}
|
|
499
|
+
/**
|
|
500
|
+
* The model surface for one member — or, without a member, for a harness's
|
|
501
|
+
* next round (the settings card's "what would run" line). Every layer reports
|
|
502
|
+
* its own value so a UI can explain WHY the effective model is what it is;
|
|
503
|
+
* `choices` is the pickable vocabulary (settings + scoped-config default +
|
|
504
|
+
* config-discovered + recently used, deduped), never a hardcoded catalog.
|
|
505
|
+
*/
|
|
506
|
+
export interface LocalAgentModelInfo {
|
|
507
|
+
/** Durable current/pending selection, shared by every member entry point. */
|
|
508
|
+
configuration?: LocalAgentMemberControlState;
|
|
509
|
+
/** The model the next round would actually run with, when any layer names one. */
|
|
510
|
+
effective?: string;
|
|
511
|
+
/** Which layer {@link LocalAgentModelInfo.effective} came from. */
|
|
512
|
+
source: LocalAgentModelSource;
|
|
513
|
+
/** The session-level override, when one is active for the member. */
|
|
514
|
+
override?: string;
|
|
515
|
+
/** The delegation's recorded model, when the delegation named one. */
|
|
516
|
+
delegation?: string;
|
|
517
|
+
/** The harness's plugin-config `model`, when the settings layer sets one. */
|
|
518
|
+
settings?: string;
|
|
519
|
+
/** The scoped config file's own default model, when the file names one. */
|
|
520
|
+
cliDefault?: string;
|
|
521
|
+
/**
|
|
522
|
+
* The most recent model the provider was OBSERVED running (the delegation
|
|
523
|
+
* records' {@link LocalAgentDelegationRecord.observedModel}), filled by the
|
|
524
|
+
* core gateway when the broker itself names none. Display context for the
|
|
525
|
+
* cli-builtin layer: the CLI's compiled default names nothing, but "what
|
|
526
|
+
* ran last time" is exactly what a member still on that default would run
|
|
527
|
+
* again — so surfaces show it as the last-observed hint, never as
|
|
528
|
+
* {@link LocalAgentModelInfo.effective} (an observation is not a layer).
|
|
529
|
+
*/
|
|
530
|
+
lastObserved?: string;
|
|
531
|
+
/**
|
|
532
|
+
* The pickable model identifiers (deduped; empty when nothing names a
|
|
533
|
+
* model). The core gateway appends {@link LocalAgentModelInfo.lastObserved}
|
|
534
|
+
* as the final entry when no broker choice already lists it, so a member
|
|
535
|
+
* that only ever ran its CLI default still has a one-item menu.
|
|
536
|
+
*/
|
|
537
|
+
choices: readonly string[];
|
|
538
|
+
/** Rich, source-labelled directory; choices remains the legacy read face. */
|
|
539
|
+
directory?: LocalAgentModelDirectory;
|
|
540
|
+
/** Whether the harness's live driver is on for the member's rounds. */
|
|
541
|
+
live: boolean;
|
|
542
|
+
/**
|
|
543
|
+
* False while a round is in flight for the member: a model switch retires
|
|
544
|
+
* the member's resident runtime so the NEXT round respawns with the new
|
|
545
|
+
* model, and retiring a runtime mid-round would kill the run.
|
|
546
|
+
*/
|
|
547
|
+
switchable: boolean;
|
|
548
|
+
/** Why a switch is currently refused, when {@link LocalAgentModelInfo.switchable} is false. */
|
|
549
|
+
reason?: string;
|
|
550
|
+
}
|
|
551
|
+
/**
|
|
552
|
+
* A harness's model knob, registered with the harness and routed by the
|
|
553
|
+
* gateway: the settings card reads the memberless info, the member composer
|
|
554
|
+
* reads and switches per member. A harness without a broker keeps its old
|
|
555
|
+
* faces exactly (the card's free-text model input still writes plugin config).
|
|
556
|
+
*/
|
|
557
|
+
/** Stable execution identity, available before the first native session ID arrives. */
|
|
558
|
+
export interface MemberConfigurationAdapter {
|
|
559
|
+
/** Re-resolve defaults and validate before every whole-turn admission. */
|
|
560
|
+
prepare?(selection: LocalAgentMemberConfiguration): Promise<LocalAgentResolvedConfiguration>;
|
|
561
|
+
/** Read-only validation. Unknown native values can fail later at the control boundary. */
|
|
562
|
+
validate(selection: LocalAgentMemberConfiguration): Promise<void>;
|
|
563
|
+
/** No generation. Implementations must bound native controls and preserve session history. */
|
|
564
|
+
apply(selection: LocalAgentMemberConfiguration, previous: LocalAgentAppliedConfiguration, operationId: string): Promise<LocalAgentResolvedConfiguration>;
|
|
565
|
+
/** Reconcile before retry/recovery; unknown never authorizes another round. */
|
|
566
|
+
reconcile(state: LocalAgentMemberControlState): Promise<{
|
|
567
|
+
active: boolean;
|
|
568
|
+
matches: 'current' | 'operation' | 'unknown';
|
|
569
|
+
resolved: LocalAgentResolvedConfiguration;
|
|
570
|
+
}>;
|
|
571
|
+
}
|
|
572
|
+
export interface LocalAgentMemberBinding {
|
|
573
|
+
childSessionId: string;
|
|
574
|
+
provider: string;
|
|
575
|
+
parentSessionId: string;
|
|
576
|
+
cwd: string;
|
|
577
|
+
scope?: string;
|
|
578
|
+
model?: string;
|
|
579
|
+
effort?: string;
|
|
580
|
+
configurationLock?: string;
|
|
581
|
+
}
|
|
582
|
+
export interface LocalAgentModelBroker {
|
|
583
|
+
/** Native preparation for the core-owned configuration admission barrier. */
|
|
584
|
+
configurationAdapter?(binding: LocalAgentMemberBinding): MemberConfigurationAdapter;
|
|
585
|
+
/** Refresh only discovery; never starts a model turn or changes selection. */
|
|
586
|
+
modelDirectory?(childSessionId?: string, refresh?: boolean): LocalAgentModelDirectory | Promise<LocalAgentModelDirectory>;
|
|
587
|
+
/** Keeps an open picker current after a background probe. */
|
|
588
|
+
followModelDirectory?(childSessionId: string | undefined, signal: AbortSignal): AsyncIterable<LocalAgentModelDirectory>;
|
|
589
|
+
/**
|
|
590
|
+
* Read the model surface. With a member, `delegationModel` carries the
|
|
591
|
+
* delegation record's requested model (the record lives in the core; the
|
|
592
|
+
* broker ranks it between override and settings).
|
|
593
|
+
* @param childSessionId - the member to read, absent for the harness default.
|
|
594
|
+
* @param delegationModel - the delegation's recorded model, when it named one.
|
|
595
|
+
* @returns the layer-by-layer surface.
|
|
596
|
+
*/
|
|
597
|
+
modelInfo(childSessionId?: string, delegationModel?: string): LocalAgentModelInfo | Promise<LocalAgentModelInfo>;
|
|
598
|
+
/**
|
|
599
|
+
* Set (or clear, with undefined) a member's session-level model override.
|
|
600
|
+
* The override outranks the delegation's recorded model and the settings
|
|
601
|
+
* layer; it is in-memory and deliberately does not survive a host restart.
|
|
602
|
+
* When the member has a live runtime, setting a DIFFERENT model retires the
|
|
603
|
+
* runtime so the next round respawns with the new model; the same model is
|
|
604
|
+
* a no-op. Throws when a round is in flight for the member.
|
|
605
|
+
* @param childSessionId - the member to switch.
|
|
606
|
+
* @param model - the model identifier, or undefined to clear the override.
|
|
607
|
+
*/
|
|
608
|
+
setMemberModel(childSessionId: string, model: string | undefined): Promise<void>;
|
|
609
|
+
}
|
|
610
|
+
/**
|
|
611
|
+
* One member-to-member notification handed to the room gate — the frozen
|
|
612
|
+
* contract (`proposals/closed/2026-08-19-local-agent-member-channel.md` §3):
|
|
613
|
+
* `{ from, to, content, parentSessionId, provenance }`. The bridge cannot
|
|
614
|
+
* speak roster names for the SENDER (only room owns the roster), so `from`
|
|
615
|
+
* is the sender's dsh child session id and the full delegation view rides
|
|
616
|
+
* as `provenance`; room resolves both endpoints to roster names. The
|
|
617
|
+
* CLI-session resume handle never crosses.
|
|
618
|
+
*/
|
|
619
|
+
export interface LocalAgentMemberMessage {
|
|
620
|
+
/** The sending member's dsh child session id (resolved from its run token). */
|
|
621
|
+
from: string;
|
|
622
|
+
/**
|
|
623
|
+
* The raw `to` argument: a member's dsh child session id, or — only
|
|
624
|
+
* meaningful to a claiming room — a member name from the room roster.
|
|
625
|
+
*/
|
|
626
|
+
to: string;
|
|
627
|
+
/** The notification text. */
|
|
628
|
+
content: string;
|
|
629
|
+
/** The parent session both members belong to. */
|
|
630
|
+
parentSessionId: string;
|
|
631
|
+
/** The sender's delegation view, recorded as the delivery's provenance. */
|
|
632
|
+
provenance: Record<string, string>;
|
|
633
|
+
}
|
|
634
|
+
/**
|
|
635
|
+
* The room gate's answer to a member notification, passed back to the sender
|
|
636
|
+
* verbatim. Phase 1's gate is always human confirmation
|
|
637
|
+
* ('pending-confirm'); 'sent'/'busy' belong to the phase-2 auto gate. A
|
|
638
|
+
* DECLINE is not a value: the gate throws (the parent session is not a room
|
|
639
|
+
* this instance manages) and the family direct-sends.
|
|
640
|
+
*/
|
|
641
|
+
export type RoomMemberMessageReceipt = 'sent' | 'pending-confirm' | 'busy';
|
|
642
|
+
/**
|
|
643
|
+
* Duck-typed shape of the room service's member-message gate, probed via
|
|
644
|
+
* `ctx.get('room')` — deliberately NO import of any room package, so the
|
|
645
|
+
* optional local-agent → room edge needs no inter-plugin dependency and no
|
|
646
|
+
* independence-checker sanction: room absent or declining is invisible to the
|
|
647
|
+
* family path. Room implements this shape to own the dispatch gate.
|
|
648
|
+
*/
|
|
649
|
+
export type MemberRoomCommandName = 'room_plan' | 'room_read' | 'room_invite' | 'room_message';
|
|
650
|
+
export interface MemberRoomCommand {
|
|
651
|
+
name: MemberRoomCommandName;
|
|
652
|
+
arguments: Record<string, unknown>;
|
|
653
|
+
}
|
|
654
|
+
export interface RoomMemberMessageGate {
|
|
655
|
+
/** Explicit ownership prevents a rejected room operation falling through to direct execution. */
|
|
656
|
+
isRoom?(request: {
|
|
657
|
+
sessionId: string;
|
|
658
|
+
}): Promise<boolean>;
|
|
659
|
+
receiveMemberCommand?(actor: LocalAgentMemberRun, command: MemberRoomCommand): Promise<MemberMessageOutcome>;
|
|
660
|
+
receiveMemberMessage(message: LocalAgentMemberMessage): Promise<RoomMemberMessageReceipt>;
|
|
661
|
+
}
|
|
662
|
+
/**
|
|
663
|
+
* Outcome of one `member_message` delivery. Channel-level failures (unknown or
|
|
664
|
+
* expired token, unknown member) are `ok: false` tool errors;
|
|
665
|
+
* delivery verdicts — including a failed direct send — are receipts the sender
|
|
666
|
+
* can quote in its conclusion: `sent` / `pending-confirm` / `busy` /
|
|
667
|
+
* `error: <reason>` (or whatever a claiming room returns, passed verbatim).
|
|
668
|
+
*/
|
|
669
|
+
export type MemberMessageOutcome = {
|
|
670
|
+
readonly ok: true;
|
|
671
|
+
readonly receipt: string;
|
|
672
|
+
} | {
|
|
673
|
+
readonly ok: false;
|
|
674
|
+
readonly error: string;
|
|
675
|
+
};
|
|
676
|
+
/** Environment variable carrying the member-bridge socket path (spawn env → bridge). */
|
|
677
|
+
export declare const MEMBER_BRIDGE_SOCKET_ENV = "DSH_MEMBER_SOCKET";
|
|
678
|
+
/** Environment variable carrying the per-run member-bridge token. */
|
|
679
|
+
export declare const MEMBER_BRIDGE_TOKEN_ENV = "DSH_MEMBER_TOKEN";
|
|
680
|
+
/**
|
|
681
|
+
* One in-flight CLI run registered for the member channel: the per-run token's
|
|
682
|
+
* resolution target. The token is minted at run start (fresh or resume) and
|
|
683
|
+
* invalidated when the run settles — a member's identity is never
|
|
684
|
+
* self-reported. The token is the sole credential (host 0.1.5 removed the
|
|
685
|
+
* child pid the parentage cross-check used; the auth-hardening proposal
|
|
686
|
+
* tracks a stronger second factor).
|
|
687
|
+
*/
|
|
688
|
+
export interface LocalAgentMemberRun {
|
|
689
|
+
/** The dsh child session id of the run (its member identity). */
|
|
690
|
+
childSessionId: string;
|
|
691
|
+
/** The delegating parent session id. */
|
|
692
|
+
parentSessionId: string;
|
|
693
|
+
/** The `ctx.subagents` provider name running the CLI. */
|
|
694
|
+
provider: string;
|
|
695
|
+
}
|
|
696
|
+
/**
|
|
697
|
+
* What one delegation tool call intends for the provider's next `start()`.
|
|
698
|
+
* The tool stages exactly one intent per call before awaiting
|
|
699
|
+
* `ctx.subagents.start()`, and the provider consumes exactly one per start,
|
|
700
|
+
* so a per-(parent, provider) FIFO keeps fresh and resume rounds paired even
|
|
701
|
+
* under parallel delegation.
|
|
702
|
+
*/
|
|
703
|
+
export type LocalAgentDelegationIntent = {
|
|
704
|
+
readonly kind: 'fresh';
|
|
705
|
+
readonly onAdmitted?: () => void | Promise<void>;
|
|
706
|
+
readonly preparedMemberId?: string;
|
|
707
|
+
readonly effort?: string;
|
|
708
|
+
readonly configurationLock?: string;
|
|
709
|
+
/**
|
|
710
|
+
* The working directory the round's CLI process runs in, when the caller
|
|
711
|
+
* supplied one (the `cwd` call option riding the staged intent). Absent
|
|
712
|
+
* means the provider's default — the parent session's cwd.
|
|
713
|
+
*/
|
|
714
|
+
readonly cwd?: string;
|
|
715
|
+
/**
|
|
716
|
+
* Run the CLI inside this container instead of on the host (the `exec`
|
|
717
|
+
* call option riding the same staged intent). Absent means the host.
|
|
718
|
+
*/
|
|
719
|
+
readonly exec?: DelegationExecTarget;
|
|
720
|
+
/**
|
|
721
|
+
* The named scope whose scoped home the round runs against (the `scope`
|
|
722
|
+
* call option riding the same staged intent). Absent means the default
|
|
723
|
+
* scope — the provider then resolves the same directory it always did.
|
|
724
|
+
*/
|
|
725
|
+
readonly scope?: string;
|
|
726
|
+
/**
|
|
727
|
+
* The model this delegation requested (the `model` call option riding the
|
|
728
|
+
* same staged intent). The provider passes it to the CLI and RECORDS it,
|
|
729
|
+
* so every resume round of this delegation re-requests the same value.
|
|
730
|
+
* Absent means the provider falls back to its plugin config, then the
|
|
731
|
+
* scoped file, then the CLI default.
|
|
732
|
+
*/
|
|
733
|
+
readonly model?: string;
|
|
734
|
+
} | {
|
|
735
|
+
readonly kind: 'resume';
|
|
736
|
+
readonly onAdmitted?: () => void | Promise<void>;
|
|
737
|
+
/** The dsh child session id to continue (the resume handle). */
|
|
738
|
+
readonly childSessionId: string;
|
|
739
|
+
/** The CLI session id the resume command continues. */
|
|
740
|
+
readonly cliSessionId: string;
|
|
741
|
+
/**
|
|
742
|
+
* The working directory the caller asks the resume round to run in, when
|
|
743
|
+
* supplied. The provider compares it (or the parent-session default)
|
|
744
|
+
* against the recorded first-round cwd and fails loud on a mismatch.
|
|
745
|
+
*/
|
|
746
|
+
readonly cwd?: string;
|
|
747
|
+
/**
|
|
748
|
+
* Run the resume round's CLI inside this container. The caller repeats
|
|
749
|
+
* the first round's target; nothing recorded can verify that for it.
|
|
750
|
+
*/
|
|
751
|
+
readonly exec?: DelegationExecTarget;
|
|
752
|
+
/**
|
|
753
|
+
* The named scope the resume round runs against. Unlike the container
|
|
754
|
+
* target this one IS anchored: the record carries the first round's
|
|
755
|
+
* scope, and a round naming another (or none) is refused.
|
|
756
|
+
*/
|
|
757
|
+
readonly scope?: string;
|
|
758
|
+
};
|
|
759
|
+
/**
|
|
760
|
+
* One settled round's tool-call accounting, counted from the events the
|
|
761
|
+
* provider ALREADY parses for its transcript mirror — no second parse, no new
|
|
762
|
+
* readback channel. Per round, never cumulative.
|
|
763
|
+
*
|
|
764
|
+
* `byName` keys are whatever the harness's own CLI calls the tool, verbatim
|
|
765
|
+
* and un-normalized: codex reports its stream item types (`command_execution`,
|
|
766
|
+
* `web_search_call`), claude the `tool_use` name (`Bash`, `Read`, `TodoWrite`,
|
|
767
|
+
* an MCP tool's full name), kimi and dsh the tool name their own events carry.
|
|
768
|
+
* Cross-harness comparison is therefore `count` ONLY — the names are for a
|
|
769
|
+
* reader, and normalizing them would invent an equivalence the CLIs never
|
|
770
|
+
* agreed to. `byName` is absent when the events carried no usable name, and
|
|
771
|
+
* its values always sum to `count`.
|
|
772
|
+
*/
|
|
773
|
+
export interface LocalAgentToolCalls {
|
|
774
|
+
/** Tool calls the round made. Zero is a real observation — the round used none. */
|
|
775
|
+
readonly count: number;
|
|
776
|
+
/** Per-tool counts under the CLI's own names; absent when no name was available. */
|
|
777
|
+
readonly byName?: Readonly<Record<string, number>>;
|
|
778
|
+
}
|
|
779
|
+
/**
|
|
780
|
+
* One progress update for a delegation run. Providers report the data-bearing
|
|
781
|
+
* kinds ({@link reportRunProgress} on the registry — the facade only forwards);
|
|
782
|
+
* the facade itself emits the `heartbeat` kind while a facade-tracked run is
|
|
783
|
+
* in flight, so a caller can render "in progress" without any provider
|
|
784
|
+
* support.
|
|
785
|
+
*/
|
|
786
|
+
export type LocalAgentRunProgress = {
|
|
787
|
+
readonly kind: 'heartbeat';
|
|
788
|
+
/** Milliseconds since the facade started tracking the run. */
|
|
789
|
+
readonly elapsedMs: number;
|
|
790
|
+
} | {
|
|
791
|
+
readonly kind: 'mirror';
|
|
792
|
+
/** Total CLI transcript lines mirrored into the child session so far. */
|
|
793
|
+
readonly mirroredLines: number;
|
|
794
|
+
} | {
|
|
795
|
+
readonly kind: 'delta';
|
|
796
|
+
/** A live transcript increment (M3; not yet emitted by any provider). */
|
|
797
|
+
readonly text: string;
|
|
798
|
+
} | {
|
|
799
|
+
readonly kind: 'settled';
|
|
800
|
+
/** Native generation observation only, never copied from the requested effort. */
|
|
801
|
+
readonly observedEffort?: string;
|
|
802
|
+
/**
|
|
803
|
+
* The model identifier the provider observed in its own output stream for
|
|
804
|
+
* this round, when the stream carried one — absent otherwise, never
|
|
805
|
+
* guessed. The observed half of the evaluation's declared-vs-run model
|
|
806
|
+
* check; never enters any prompt or model-visible face.
|
|
807
|
+
*/
|
|
808
|
+
readonly observedModel?: string;
|
|
809
|
+
/**
|
|
810
|
+
* The CLI version the round ran, when the provider could read one back
|
|
811
|
+
* (its own stream or records first, the `--version` probe second).
|
|
812
|
+
*/
|
|
813
|
+
readonly cliVersion?: string;
|
|
814
|
+
/** The settled round's token usage, when the harness reported one. */
|
|
815
|
+
readonly usage?: TokenUsage;
|
|
816
|
+
/**
|
|
817
|
+
* The round's tool-call accounting, counted from the transcript events the
|
|
818
|
+
* provider already parsed. Absent when the harness reported none for this
|
|
819
|
+
* round — absence is "not observed", which is not the same fact as a
|
|
820
|
+
* `count: 0` round that ran no tools, so it is never filled in with zero.
|
|
821
|
+
*/
|
|
822
|
+
readonly toolCalls?: LocalAgentToolCalls;
|
|
823
|
+
};
|
|
824
|
+
/**
|
|
825
|
+
* Where one delegation round's CLI process runs when the caller has already
|
|
826
|
+
* acquired a container for it (a lab unit). Given a target, the provider
|
|
827
|
+
* spawns `docker exec -w <workdir> [-e NAME…] <container> <the same argv>`
|
|
828
|
+
* instead of the CLI directly; stdio stays piped and every downstream
|
|
829
|
+
* layer — stream parse, settle, readback, delegation record — is the host
|
|
830
|
+
* path's, unchanged.
|
|
831
|
+
*
|
|
832
|
+
* The scoped home stays a HOST directory, bind-mounted read-write into the
|
|
833
|
+
* unit: readback (codex's rollout, kimi's wire log, the sub-dsh session log)
|
|
834
|
+
* reads it straight off the host filesystem, and credential refreshes the CLI
|
|
835
|
+
* writes land back on the host. `env` is where the caller names those
|
|
836
|
+
* in-container paths (`CODEX_HOME`, `CLAUDE_CONFIG_DIR`, `KIMI_CODE_HOME`,
|
|
837
|
+
* `DSH_HOME`), which is why every provider REQUIRES its own scoped-home
|
|
838
|
+
* variable here: the host path it would otherwise forward means nothing
|
|
839
|
+
* inside the unit.
|
|
840
|
+
*
|
|
841
|
+
* Absent everywhere means the host path, byte-for-byte the behavior before
|
|
842
|
+
* this option existed.
|
|
843
|
+
*/
|
|
844
|
+
export interface DelegationExecTarget {
|
|
845
|
+
/**
|
|
846
|
+
* The container the round runs in — a name or id the caller acquired (lab's
|
|
847
|
+
* `UnitInfo.resource`). The family only ever runs `docker exec` against it:
|
|
848
|
+
* creating, inspecting, mounting and destroying it belong to the caller.
|
|
849
|
+
*/
|
|
850
|
+
readonly container: string;
|
|
851
|
+
/** Absolute in-container working directory (`docker exec -w`). */
|
|
852
|
+
readonly workdir: string;
|
|
853
|
+
/**
|
|
854
|
+
* Environment entries for the in-container process, overriding the
|
|
855
|
+
* provider's own per key. Values travel in the docker CLIENT's environment
|
|
856
|
+
* and reach the container through NAME-only `-e` flags, so nothing here
|
|
857
|
+
* lands in the host process table.
|
|
858
|
+
*/
|
|
859
|
+
readonly env?: Readonly<Record<string, string>>;
|
|
860
|
+
}
|
|
861
|
+
/**
|
|
862
|
+
* Call options for the public delegation facade (`LocalAgentRegistry.start` /
|
|
863
|
+
* `resume`). The interface is deliberately additive: later milestones extend
|
|
864
|
+
* it without changing the existing fields.
|
|
865
|
+
*/
|
|
866
|
+
export interface DelegationCallOptions {
|
|
867
|
+
/** Host-only durable input edge, executed at the provider admission boundary. */
|
|
868
|
+
readonly onAdmitted?: () => void | Promise<void>;
|
|
869
|
+
/** Reserved identity returned by prepareMember; never a caller-selected resume override. */
|
|
870
|
+
readonly preparedMemberId?: string;
|
|
871
|
+
/** Native reasoning value, fixed at creation unless changed through member controls. */
|
|
872
|
+
readonly effort?: string;
|
|
873
|
+
/** Frozen evaluation condition: rejects interactive configuration changes. */
|
|
874
|
+
readonly configurationLock?: string;
|
|
875
|
+
/**
|
|
876
|
+
* Child display label persisted with a session-backed child; omitted, the
|
|
877
|
+
* harness's own display name labels the delegation.
|
|
878
|
+
*/
|
|
879
|
+
readonly label?: string;
|
|
880
|
+
/**
|
|
881
|
+
* Caller-owned extra cancellation channel. It is fused with the facade's
|
|
882
|
+
* internal `cancel()` controller: aborting either cancels the run.
|
|
883
|
+
*/
|
|
884
|
+
readonly signal?: AbortSignal;
|
|
885
|
+
/**
|
|
886
|
+
* Per-call progress callback: receives the same {@link LocalAgentRunProgress}
|
|
887
|
+
* payloads the `localAgent/run-progress` cordis event carries, routed to the
|
|
888
|
+
* tracked run this call started. For callers that cannot conveniently
|
|
889
|
+
* subscribe to cordis events.
|
|
890
|
+
*/
|
|
891
|
+
readonly onProgress?: (event: LocalAgentRunProgress) => void;
|
|
892
|
+
/**
|
|
893
|
+
* `resume` only: when the child session is not live, reattach it from
|
|
894
|
+
* persistence (the default, true). Pass `false` to fail loud instead — the
|
|
895
|
+
* pre-facade behavior — leaving the absent session untouched.
|
|
896
|
+
*/
|
|
897
|
+
readonly reattach?: boolean;
|
|
898
|
+
/**
|
|
899
|
+
* The working directory the CLI round runs in. On a fresh delegation it
|
|
900
|
+
* replaces the parent session's cwd (the `resolveChildCwd` override — an
|
|
901
|
+
* orchestrator giving each cell its own directory); on a resume it must
|
|
902
|
+
* match the first round's recorded cwd or the call fails loud, since the
|
|
903
|
+
* CLI conversation continues in the directory its earlier rounds ran in.
|
|
904
|
+
* Absent everywhere means the parent session's cwd — the behavior before
|
|
905
|
+
* this option existed, unchanged.
|
|
906
|
+
*/
|
|
907
|
+
readonly cwd?: string;
|
|
908
|
+
/**
|
|
909
|
+
* Run the round's CLI inside an already-acquired container instead of on
|
|
910
|
+
* the host ({@link DelegationExecTarget}). The transport is the only thing
|
|
911
|
+
* it changes; a round with a target still parses, settles, reads back and
|
|
912
|
+
* records exactly as a host round does. On a resume, repeat the SAME target
|
|
913
|
+
* the first round used — the recorded first-round anchor is the host `cwd`,
|
|
914
|
+
* so a swapped container is a caller error the record cannot catch.
|
|
915
|
+
*/
|
|
916
|
+
readonly exec?: DelegationExecTarget;
|
|
917
|
+
/**
|
|
918
|
+
* Run the round against a NAMED scoped home of the harness
|
|
919
|
+
* (`<homesRoot>/<harness>@<scope>`) instead of the default one. A scope is
|
|
920
|
+
* a name matching `[a-z0-9-]`, never a path; the directory is materialized
|
|
921
|
+
* (0700, then the harness's own provisioning) the first time anything names
|
|
922
|
+
* it, and it holds its own credentials — nothing is copied from the default
|
|
923
|
+
* scope, so a fresh scope needs its own `/<harness> login --scope <name>`.
|
|
924
|
+
*
|
|
925
|
+
* On a resume the scope must be the one the first round recorded, absence
|
|
926
|
+
* included: continuing a CLI session against another account's credentials
|
|
927
|
+
* is refused rather than attempted.
|
|
928
|
+
*
|
|
929
|
+
* Absent everywhere means the default scope — byte for byte the behavior
|
|
930
|
+
* before scopes existed. A scoped round is exec-only (the live drivers bind
|
|
931
|
+
* the default scoped home) and, on kimi, carries no member channel (the
|
|
932
|
+
* bridge declaration is written into the default scope's `mcp.json`).
|
|
933
|
+
*/
|
|
934
|
+
readonly scope?: string;
|
|
935
|
+
/**
|
|
936
|
+
* The model this DELEGATION runs — `start` only. It outranks the harness's
|
|
937
|
+
* `model` plugin-config key, which in turn outranks the scoped
|
|
938
|
+
* configuration file and, last, the CLI's own default. Absent leaves that
|
|
939
|
+
* pre-existing order exactly as it was, so a caller that names no model
|
|
940
|
+
* gets byte-identical behavior.
|
|
941
|
+
*
|
|
942
|
+
* **Not accepted on `resume`.** A model belongs to the delegation, not to
|
|
943
|
+
* one of its rounds: the first round records what it requested and every
|
|
944
|
+
* later round of the same CLI session re-requests that same value (none,
|
|
945
|
+
* when the first round named none). Passing it to `resume` is a caller
|
|
946
|
+
* error and fails loud rather than switching a conversation's model
|
|
947
|
+
* mid-way — which the CLI would honour and the transcript would not show.
|
|
948
|
+
*
|
|
949
|
+
* A round with a model is exec-only, for the reason a scoped round is: the
|
|
950
|
+
* live drivers bind their model when the resident runtime spawns, and one
|
|
951
|
+
* runtime serves many rounds.
|
|
952
|
+
*
|
|
953
|
+
* dsh spells it `provider/model` (the shape `effectiveSettings.model`
|
|
954
|
+
* reports); the three CLI harnesses take whatever identifier their own CLI
|
|
955
|
+
* takes.
|
|
956
|
+
*/
|
|
957
|
+
readonly model?: string;
|
|
958
|
+
}
|
|
959
|
+
//# sourceMappingURL=types.d.ts.map
|