dsh-loop-engine-allinone 0.0.0-stage → 0.4.1
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 +119 -0
- package/CONTRIBUTING.md +78 -0
- package/LICENSE +23 -0
- package/NOTICE +45 -0
- package/README.md +223 -2
- package/README.zh.md +179 -0
- package/SECURITY.md +39 -0
- package/cordis.patch.yml +3 -0
- package/lib/client.js +2166 -0
- package/lib/index.js +11621 -0
- package/lib/invariant.js +453 -0
- package/lib/types/agent-preset-ids.d.ts +276 -0
- package/lib/types/client/LoopEngineBadge.d.ts +71 -0
- package/lib/types/client/LoopEngineComposerSelect.d.ts +126 -0
- package/lib/types/client/LoopEngineSection.d.ts +35 -0
- package/lib/types/client/engine-marks.d.ts +87 -0
- package/lib/types/client/index.d.ts +39 -0
- package/lib/types/client/locales.d.ts +182 -0
- package/lib/types/client/reload.d.ts +135 -0
- package/lib/types/client/session-engine.d.ts +508 -0
- package/lib/types/client/store.d.ts +103 -0
- package/lib/types/client/turn-status.d.ts +234 -0
- package/lib/types/client/use-session-engine.d.ts +74 -0
- package/lib/types/commands.d.ts +77 -0
- package/lib/types/compat.d.ts +25 -0
- package/lib/types/driver-acp/agent.d.ts +289 -0
- package/lib/types/driver-acp/approval.d.ts +27 -0
- package/lib/types/driver-acp/capabilities.d.ts +200 -0
- package/lib/types/driver-acp/client.d.ts +297 -0
- package/lib/types/driver-acp/executable.d.ts +53 -0
- package/lib/types/driver-acp/mapping.d.ts +76 -0
- package/lib/types/driver-acp/profile.d.ts +126 -0
- package/lib/types/driver-acp/runtime.d.ts +45 -0
- package/lib/types/driver-acp/subprocess.d.ts +19 -0
- package/lib/types/driver-acp/terminal.d.ts +98 -0
- package/lib/types/driver-acp/transport.d.ts +43 -0
- package/lib/types/driver-acp/types.d.ts +152 -0
- package/lib/types/driver-cli/capability.d.ts +42 -0
- package/lib/types/driver-cli/cli-agent.d.ts +258 -0
- package/lib/types/driver-cli/cli-loop.d.ts +46 -0
- package/lib/types/driver-cli/flatten.d.ts +35 -0
- package/lib/types/driver-cli/frames.d.ts +128 -0
- package/lib/types/driver-cli/ndjson.d.ts +106 -0
- package/lib/types/driver-cli/session-sink.d.ts +333 -0
- package/lib/types/driver-cli/stream-json.d.ts +61 -0
- package/lib/types/driver-cli/text.d.ts +66 -0
- package/lib/types/driver-cli/transport.d.ts +84 -0
- package/lib/types/driver-core/agents-md-skill-provider.d.ts +72 -0
- package/lib/types/driver-core/assistant-stream.d.ts +68 -0
- package/lib/types/driver-core/context-files.d.ts +62 -0
- package/lib/types/driver-core/host-servers.d.ts +119 -0
- package/lib/types/driver-core/hosted-engine-runtime.d.ts +190 -0
- package/lib/types/driver-core/hosted-tool-vocabulary.d.ts +78 -0
- package/lib/types/driver-core/idle-child.d.ts +57 -0
- package/lib/types/driver-core/image-access.d.ts +25 -0
- package/lib/types/driver-core/inbox.d.ts +104 -0
- package/lib/types/driver-core/model-handover.d.ts +134 -0
- package/lib/types/driver-core/ownership.d.ts +41 -0
- package/lib/types/driver-core/permission-knobs.d.ts +26 -0
- package/lib/types/driver-core/prompt.d.ts +64 -0
- package/lib/types/driver-core/session-lifetime.d.ts +62 -0
- package/lib/types/driver-core/session-model.d.ts +128 -0
- package/lib/types/driver-core/skill-inject.d.ts +59 -0
- package/lib/types/driver-core/step-close.d.ts +49 -0
- package/lib/types/driver-core/step-images.d.ts +62 -0
- package/lib/types/driver-core/step-tools.d.ts +43 -0
- package/lib/types/driver-core/system-head.d.ts +43 -0
- package/lib/types/engine-agy/agent.d.ts +67 -0
- package/lib/types/engine-agy/frames.d.ts +90 -0
- package/lib/types/engine-agy/loop.d.ts +64 -0
- package/lib/types/engine-agy/process.d.ts +173 -0
- package/lib/types/engine-claude/agent.d.ts +186 -0
- package/lib/types/engine-claude/loop.d.ts +73 -0
- package/lib/types/engine-claude/mapping.d.ts +108 -0
- package/lib/types/engine-claude/permission.d.ts +41 -0
- package/lib/types/engine-claude/process.d.ts +59 -0
- package/lib/types/engine-claude/sdk.d.ts +67 -0
- package/lib/types/engine-claude/types.d.ts +28 -0
- package/lib/types/engine-codex/agent.d.ts +216 -0
- package/lib/types/engine-codex/appserver/client.d.ts +80 -0
- package/lib/types/engine-codex/appserver/mapping.d.ts +67 -0
- package/lib/types/engine-codex/appserver/thread.d.ts +66 -0
- package/lib/types/engine-codex/appserver/types.d.ts +225 -0
- package/lib/types/engine-codex/loop.d.ts +75 -0
- package/lib/types/engine-codex/model-handover.d.ts +56 -0
- package/lib/types/engine-codex/permission.d.ts +126 -0
- package/lib/types/engine-codex/skills.d.ts +28 -0
- package/lib/types/engine-codex/types.d.ts +36 -0
- package/lib/types/engine-drivers.d.ts +132 -0
- package/lib/types/engine-grok/agent.d.ts +24 -0
- package/lib/types/engine-grok/loop.d.ts +61 -0
- package/lib/types/engine-grok/process.d.ts +46 -0
- package/lib/types/engine-grok/profile.d.ts +82 -0
- package/lib/types/engine-hermes/agent.d.ts +47 -0
- package/lib/types/engine-hermes/loop.d.ts +56 -0
- package/lib/types/engine-hermes/process.d.ts +92 -0
- package/lib/types/engine-kimi/acp/client.d.ts +123 -0
- package/lib/types/engine-kimi/acp/mapping.d.ts +73 -0
- package/lib/types/engine-kimi/acp/types.d.ts +143 -0
- package/lib/types/engine-kimi/agent.d.ts +24 -0
- package/lib/types/engine-kimi/commands.d.ts +44 -0
- package/lib/types/engine-kimi/loop.d.ts +62 -0
- package/lib/types/engine-kimi/model-handover.d.ts +32 -0
- package/lib/types/engine-kimi/permission.d.ts +28 -0
- package/lib/types/engine-kimi/process.d.ts +47 -0
- package/lib/types/engine-kimi/profile.d.ts +19 -0
- package/lib/types/engine-kimi/skills.d.ts +52 -0
- package/lib/types/engine-kimi/types.d.ts +25 -0
- package/lib/types/engine-minimaxcode/agent.d.ts +24 -0
- package/lib/types/engine-minimaxcode/loop.d.ts +83 -0
- package/lib/types/engine-minimaxcode/process.d.ts +53 -0
- package/lib/types/engine-minimaxcode/profile.d.ts +72 -0
- package/lib/types/engine-of-session.d.ts +97 -0
- package/lib/types/engine-pi/agent.d.ts +230 -0
- package/lib/types/engine-pi/loop.d.ts +72 -0
- package/lib/types/engine-pi/model-handover.d.ts +84 -0
- package/lib/types/engine-pi/permission.d.ts +47 -0
- package/lib/types/engine-pi/rpc/client.d.ts +107 -0
- package/lib/types/engine-pi/rpc/mapping.d.ts +37 -0
- package/lib/types/engine-pi/rpc/types.d.ts +249 -0
- package/lib/types/engine-pi/skills.d.ts +47 -0
- package/lib/types/engine-pi/types.d.ts +36 -0
- package/lib/types/engine-qoderclicn/agent.d.ts +59 -0
- package/lib/types/engine-qoderclicn/loop.d.ts +51 -0
- package/lib/types/engine-qoderclicn/process.d.ts +113 -0
- package/lib/types/engine-registry.d.ts +584 -0
- package/lib/types/engine-remote.d.ts +192 -0
- package/lib/types/engine-surface.d.ts +36 -0
- package/lib/types/engine-zcode/agent.d.ts +50 -0
- package/lib/types/engine-zcode/loop.d.ts +47 -0
- package/lib/types/engine-zcode/process.d.ts +111 -0
- package/lib/types/index.d.ts +205 -0
- package/lib/types/invariant.d.ts +26 -0
- package/lib/types/model-selection-reset.d.ts +271 -0
- package/lib/types/namespace.d.ts +25 -0
- package/lib/types/patch-manager.d.ts +104 -0
- package/lib/types/preset.d.ts +254 -0
- package/lib/types/provider-route.d.ts +245 -0
- package/lib/types/router-loop.d.ts +458 -0
- package/lib/types/session-engine-store.d.ts +138 -0
- package/lib/types/settings.d.ts +60 -0
- package/lib/types/skills.d.ts +85 -0
- package/package.json +152 -6
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shared ACP loop Agent: drives one session through turn and step boundaries
|
|
3
|
+
* over a persistent ACP child (Agent Client Protocol over stdio), speaking one
|
|
4
|
+
* stateless `session/new` + `session/prompt` per dsh step.
|
|
5
|
+
*
|
|
6
|
+
* Three engines run on this one body — Kimi Code (`kimi acp`), Grok Build
|
|
7
|
+
* (`grok agent stdio`) and MiniMax Code (`mcode acp`) — because the whole
|
|
8
|
+
* turn/step machine, the durable-log projection, the streamed-update mapping and
|
|
9
|
+
* the reverse-RPC approval answer are ENGINE-INDEPENDENT. Everything that is not
|
|
10
|
+
* travels in {@link AcpEngineProfile} as data and four small hooks: the argv, the
|
|
11
|
+
* declared protocol version, how a model is selected, how a session is opened,
|
|
12
|
+
* and (for kimi only) the dsh endpoint handover.
|
|
13
|
+
*
|
|
14
|
+
* The dsh session log is the sole source of model context and the prompt is a
|
|
15
|
+
* pure serialization of the durable history, so the transcript stays
|
|
16
|
+
* Model-visible ⟺ logged. Each engine owns its system prompt and tools natively;
|
|
17
|
+
* the ACP child is spawned through the dsh subprocess seam (the only available
|
|
18
|
+
* privilege boundary) and tool approvals are answered from the session's dsh
|
|
19
|
+
* approval knobs (an `ask` policy degrades to denial, since the unattended
|
|
20
|
+
* runtime has no human to ask).
|
|
21
|
+
*
|
|
22
|
+
* @module dsh-loop-engine-allinone/driver-acp/agent
|
|
23
|
+
*/
|
|
24
|
+
import type { Agent, AgentCancelCause, AgentOptions, AgentStatus, CancelOptions, InboxTarget } from '@deepseek-ai/dsh-agent';
|
|
25
|
+
import type { Scope } from '@deepseek-ai/dsh-scope';
|
|
26
|
+
import type { Session, SessionId, UserMessage } from '@deepseek-ai/dsh-session';
|
|
27
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
28
|
+
import type { AcpEngineProfile, AcpResolvedConfig } from './profile.ts';
|
|
29
|
+
import { DriverInbox } from '../driver-core/inbox.ts';
|
|
30
|
+
import type { AcpSpawnCapability } from './transport.ts';
|
|
31
|
+
/**
|
|
32
|
+
* Provider route label this driver logs into request/header snapshots and
|
|
33
|
+
* message provenance — the ONE route every hosted engine shares
|
|
34
|
+
* ({@link HOSTED_ROUTE_LABEL}), so every hosted engine selects `external/default`
|
|
35
|
+
* and the model menu carries a single group instead of one per engine.
|
|
36
|
+
*/
|
|
37
|
+
export declare const PROVIDER = "external";
|
|
38
|
+
/** Drives one session through turn and step boundaries on an ACP engine. */
|
|
39
|
+
export declare class AcpEngineAgent implements Agent {
|
|
40
|
+
private loopCtx;
|
|
41
|
+
readonly id: SessionId;
|
|
42
|
+
readonly options: AgentOptions;
|
|
43
|
+
readonly session: Session;
|
|
44
|
+
private readonly config;
|
|
45
|
+
private readonly spawn;
|
|
46
|
+
private readonly profile;
|
|
47
|
+
readonly inbox: DriverInbox;
|
|
48
|
+
private phase;
|
|
49
|
+
private activityDone;
|
|
50
|
+
/** The agent-scoped registration boundary; the lifecycle owner unwinds it after the driver exits. */
|
|
51
|
+
readonly scope: Scope;
|
|
52
|
+
readonly ctx: Context;
|
|
53
|
+
/** Fused dispatcher, built once in the constructor so hot-path dispatches never allocate. */
|
|
54
|
+
private readonly dispatch;
|
|
55
|
+
/** Whether this loop instance has appended its initial/resume request anchor. */
|
|
56
|
+
private requestHeaderLogged;
|
|
57
|
+
/** Agent-lifecycle-local counter naming each streamed attempt. */
|
|
58
|
+
private streamAttempts;
|
|
59
|
+
/** Lazily created ACP client, reused across steps and released on scope teardown. */
|
|
60
|
+
private acp;
|
|
61
|
+
/** The last `initialize` result, where an agent publishes its auth methods. */
|
|
62
|
+
private initializeResult;
|
|
63
|
+
/** The spawn spec the cached client was built from; a change forces a respawn. */
|
|
64
|
+
private lastSpec;
|
|
65
|
+
/**
|
|
66
|
+
* The environment the last handover produced, memoized by its own content so
|
|
67
|
+
* an unchanged endpoint reuses ONE object: `specsEqual` compares environments
|
|
68
|
+
* by reference, and a fresh object per step would otherwise respawn the child
|
|
69
|
+
* every step.
|
|
70
|
+
*/
|
|
71
|
+
private handoverEnvCache;
|
|
72
|
+
/**
|
|
73
|
+
* Countdown that shuts the child down once this agent has been idle past the
|
|
74
|
+
* configured window, so an idle session stops paying for a process nothing
|
|
75
|
+
* is driving. Armed and cancelled by {@link setPhase}.
|
|
76
|
+
*/
|
|
77
|
+
private readonly idleChild;
|
|
78
|
+
/**
|
|
79
|
+
* Resolves the read-only path behind each image block of the prompt. The
|
|
80
|
+
* attachment service is read lazily on every call, so a host that composes it
|
|
81
|
+
* after this agent still reaches the transcript.
|
|
82
|
+
*/
|
|
83
|
+
private readonly imageAccess;
|
|
84
|
+
constructor(loopCtx: Context, id: SessionId, options: AgentOptions, session: Session, config: AcpResolvedConfig, spawn: AcpSpawnCapability, profile: AcpEngineProfile);
|
|
85
|
+
/** Shut the cached child down; the next step's {@link acpClient} spawns a fresh one. */
|
|
86
|
+
private closeChild;
|
|
87
|
+
get status(): AgentStatus;
|
|
88
|
+
/** Commit a phase and publish its externally visible status transition. */
|
|
89
|
+
private setPhase;
|
|
90
|
+
send(message: UserMessage, target: InboxTarget, wakeup: boolean): void;
|
|
91
|
+
/**
|
|
92
|
+
* Queue a message for the next turn and wake the driver.
|
|
93
|
+
* @param input - the user message to deliver.
|
|
94
|
+
*/
|
|
95
|
+
followup(input: UserMessage): void;
|
|
96
|
+
/**
|
|
97
|
+
* Queue a message for the running step and wake the driver.
|
|
98
|
+
* @param input - the user message to deliver.
|
|
99
|
+
*/
|
|
100
|
+
steer(input: UserMessage): void;
|
|
101
|
+
/**
|
|
102
|
+
* Queue a message for the running step without waking the driver.
|
|
103
|
+
* @param input - the user message to deliver.
|
|
104
|
+
*/
|
|
105
|
+
inject(input: UserMessage): void;
|
|
106
|
+
cancel(cause: AgentCancelCause, options?: CancelOptions): void;
|
|
107
|
+
/**
|
|
108
|
+
* Run a maintenance job while the agent is idle.
|
|
109
|
+
* @param job - the maintenance operation, receiving the phase abort signal.
|
|
110
|
+
* @returns the maintenance result.
|
|
111
|
+
*/
|
|
112
|
+
runMaintenance<T>(job: (signal: AbortSignal) => Promise<T>): Promise<T>;
|
|
113
|
+
/**
|
|
114
|
+
* Start one driver, or latch its wake behind maintenance or an aborted
|
|
115
|
+
* activity. A wake sent while idle always opens its turn boundary, even
|
|
116
|
+
* when its message was cleared; only a latched replay is suppressed when
|
|
117
|
+
* the queue no longer holds the wake.
|
|
118
|
+
* @param wakeAfterAbort - the {@link send} classification, captured before
|
|
119
|
+
* the inbox insertion so a reentrant cancel cannot reclassify it.
|
|
120
|
+
*/
|
|
121
|
+
private wakeDriver;
|
|
122
|
+
whenIdle(): Promise<void>;
|
|
123
|
+
/** Report one failure at its live boundary, then preserve it for driver containment. */
|
|
124
|
+
private throwError;
|
|
125
|
+
private kick;
|
|
126
|
+
private preStep;
|
|
127
|
+
/**
|
|
128
|
+
* Scan the step's user messages for `/name` skill gestures, load each
|
|
129
|
+
* matching skill, and inject the rendered skill content into the message
|
|
130
|
+
* batch. This mirrors what dsh-tool-skill does for the in-process engine.
|
|
131
|
+
* @param messages - the current step's message batch.
|
|
132
|
+
* @param signal - cancellation signal (aborted loads are silently dropped).
|
|
133
|
+
* @returns the original batch when no skill was invoked, or an extended
|
|
134
|
+
* batch with injected skill-content messages appended.
|
|
135
|
+
*/
|
|
136
|
+
private injectSkills;
|
|
137
|
+
/** Open one turn before claiming its first proposed step. */
|
|
138
|
+
private turn;
|
|
139
|
+
/** Model label recorded in the request header for one lifecycle. */
|
|
140
|
+
private modelLabel;
|
|
141
|
+
/** Append the request header snapshot once per loop instance. */
|
|
142
|
+
private assertRequestHeader;
|
|
143
|
+
/** Whether two spawn specs describe the same ACP child. */
|
|
144
|
+
private specsEqual;
|
|
145
|
+
/**
|
|
146
|
+
* The client-side capability bridges this engine declared, bound to the host.
|
|
147
|
+
*
|
|
148
|
+
* Built fresh per client because the terminal bridge closes over the session's
|
|
149
|
+
* working directory, which is per agent. An engine that declares no bridges gets
|
|
150
|
+
* an empty object, and the client then announces no capabilities at all.
|
|
151
|
+
* @param cwd - the session's working directory, the default for a terminal.
|
|
152
|
+
* @returns the handlers the client answers reverse RPCs with.
|
|
153
|
+
*/
|
|
154
|
+
private reverseHandlers;
|
|
155
|
+
/** Return the cached ACP client, respawning when the spec or process changed. */
|
|
156
|
+
private acpClient;
|
|
157
|
+
/** Build the ACP child's argv/cwd/env for the persistent child. */
|
|
158
|
+
private spawnSpec;
|
|
159
|
+
/**
|
|
160
|
+
* The child environment for one step, with the engine's own dsh-endpoint
|
|
161
|
+
* handover applied when its profile declares one.
|
|
162
|
+
*
|
|
163
|
+
* Memoized by content so an unchanged endpoint yields the SAME object across
|
|
164
|
+
* steps (see {@link handoverEnvCache}): `specsEqual` compares environments by
|
|
165
|
+
* reference, and a fresh object per step would respawn the child every step.
|
|
166
|
+
*
|
|
167
|
+
* `handedOver` is what tells the step whether to select a model itself: an
|
|
168
|
+
* engine whose profile translates the endpoint into its own environment has
|
|
169
|
+
* already been told which model to use, and asking it again by the raw dsh
|
|
170
|
+
* model id would ask for an alias its env-model path never defined.
|
|
171
|
+
* @returns the environment, and whether a dsh endpoint travelled in it.
|
|
172
|
+
*/
|
|
173
|
+
private childEnvironment;
|
|
174
|
+
/**
|
|
175
|
+
* Open the ACP session one step drives.
|
|
176
|
+
*
|
|
177
|
+
* The profile's hook is what makes an engine's own handshake differences
|
|
178
|
+
* expressible without touching this body: grok's `session/new` refuses an
|
|
179
|
+
* unauthenticated client (which its hook answers by authenticating with the
|
|
180
|
+
* method id `initialize` published), while MiniMax Code needs no such step.
|
|
181
|
+
* @param client - the connected ACP client.
|
|
182
|
+
* @param cwd - the session's working directory.
|
|
183
|
+
* @returns the ACP session id.
|
|
184
|
+
*/
|
|
185
|
+
private openSession;
|
|
186
|
+
/**
|
|
187
|
+
* Run one `kimi acp` step for the current session history and map the streamed updates.
|
|
188
|
+
* @param messages - the messages this step delivers; only their images ride
|
|
189
|
+
* with the prompt as ACP image blocks, and only they.
|
|
190
|
+
*/
|
|
191
|
+
private step;
|
|
192
|
+
/** Per-step accumulation state for streamed assistant blocks and tool calls. */
|
|
193
|
+
private blocks;
|
|
194
|
+
/**
|
|
195
|
+
* Announced calls still waiting for the update that carries their input,
|
|
196
|
+
* keyed by call id, holding the latest content snapshot seen meanwhile (a
|
|
197
|
+
* frame can report output before it reports input). `toolContent` holds the
|
|
198
|
+
* calls already logged, so a call is in exactly one of the two.
|
|
199
|
+
*/
|
|
200
|
+
private pendingCalls;
|
|
201
|
+
/** Whether the current step published any assistant message at all. */
|
|
202
|
+
private producedOutput;
|
|
203
|
+
/**
|
|
204
|
+
* Tool results logged into the currently open step. A result means the
|
|
205
|
+
* segment that requested the call is finished, so the next assistant content
|
|
206
|
+
* opens the next step (see {@link beginSegment}).
|
|
207
|
+
*/
|
|
208
|
+
private stepSettledTools;
|
|
209
|
+
/**
|
|
210
|
+
* The current step's calls, and which of them already carry a result.
|
|
211
|
+
*
|
|
212
|
+
* Kimi announces a call and reports its outcome on separate ACP updates, so
|
|
213
|
+
* "which calls of THIS step are still open" is state the driver has to keep.
|
|
214
|
+
* Both halves of the Session V4 rule read it: a call may not be given a second
|
|
215
|
+
* result in one step, and a step may not end while a call it announced has no
|
|
216
|
+
* result at all — the closing pass writes the missing outcome instead
|
|
217
|
+
* ({@link closeStep}).
|
|
218
|
+
*
|
|
219
|
+
* Reset per step (see {@link step}), never on an internal segment rotation:
|
|
220
|
+
* call ids are unique for the life of the session, so retaining the marks only
|
|
221
|
+
* widens duplicate suppression and can never hide a legitimate result.
|
|
222
|
+
*/
|
|
223
|
+
private readonly stepTools;
|
|
224
|
+
/**
|
|
225
|
+
* Tool calls the current segment has already received input for, awaiting the
|
|
226
|
+
* segment's single assistant message. A model turn that announces several
|
|
227
|
+
* calls before any result must land them all in ONE message — the chat node
|
|
228
|
+
* keys by `${turn}:${step}` and replaces blocks on every message, so a second
|
|
229
|
+
* message would overwrite the first one's reasoning/text (and tool-call head).
|
|
230
|
+
* The list is flushed once, when the segment closes (its first settled result).
|
|
231
|
+
*/
|
|
232
|
+
private segmentCalls;
|
|
233
|
+
/** Latest content snapshot per logged tool call (see {@link applyUpdate}). */
|
|
234
|
+
private toolContent;
|
|
235
|
+
/** The live attempt framing the assistant message being assembled right now. */
|
|
236
|
+
private live;
|
|
237
|
+
private blockRef;
|
|
238
|
+
private ensureBlock;
|
|
239
|
+
/**
|
|
240
|
+
* Rotate to the next step when the segment that ran a tool has finished, so
|
|
241
|
+
* each assistant segment lands in its own step.
|
|
242
|
+
*
|
|
243
|
+
* Called as new assistant content begins. A step holding a settled tool
|
|
244
|
+
* result means the previous segment is complete, and the content about to be
|
|
245
|
+
* applied belongs to the next one. Rotating here (rather than when a call is
|
|
246
|
+
* announced) keeps calls that were announced before any result — one model
|
|
247
|
+
* turn — in a single step.
|
|
248
|
+
*
|
|
249
|
+
* A step that still OWES a result does not rotate: Kimi announces several
|
|
250
|
+
* calls in one turn and settles them one at a time, and rotating on the first
|
|
251
|
+
* outcome would strand the rest in a step that can never resolve them
|
|
252
|
+
* (`step/end leaves unresolved tool call …`). Those calls run one model turn
|
|
253
|
+
* later instead, which is what they belong to, and a call the engine never
|
|
254
|
+
* answers is closed by {@link closeStep} rather than by moving on.
|
|
255
|
+
*/
|
|
256
|
+
private beginSegment;
|
|
257
|
+
/** Apply one streamed update to the open step's blocks and stream. */
|
|
258
|
+
private applyUpdate;
|
|
259
|
+
/** The live attempt framing the current segment, opened on its first chunk. */
|
|
260
|
+
private currentStream;
|
|
261
|
+
/**
|
|
262
|
+
* Flush the open segment's single assistant message and every `tool/call` it
|
|
263
|
+
* accumulated, in the load-bearing order assistant/message → tool/call. One
|
|
264
|
+
* segment produces exactly ONE message even when the model announced several
|
|
265
|
+
* calls before any result, because the chat view keys an assistant node by
|
|
266
|
+
* `${turn}:${step}` and replaces its blocks on every message — a second
|
|
267
|
+
* message in the same step would overwrite the first one's reasoning/text and
|
|
268
|
+
* leave only the last bare tool-call head visible.
|
|
269
|
+
* @param phase - the open step the segment belongs to.
|
|
270
|
+
*/
|
|
271
|
+
private flushSegment;
|
|
272
|
+
/**
|
|
273
|
+
* Flush the accumulated assistant blocks into one durable assistant/message
|
|
274
|
+
* carrying the exact stream the attempt published live, optionally closing it
|
|
275
|
+
* with the tool-call block(s) that ended the segment.
|
|
276
|
+
*
|
|
277
|
+
* Every flush settles its own attempt (and so its own live `end` frame): a
|
|
278
|
+
* step emits one message per assistant segment, and the client pairs a durable
|
|
279
|
+
* message with the attempt whose `end` cites it, so two messages may not share
|
|
280
|
+
* one attempt.
|
|
281
|
+
* @param phase - the open step the message belongs to.
|
|
282
|
+
* @param toolCalls - the calls that closed this segment, rendered as trailing
|
|
283
|
+
* `tool-call` content blocks. A segment with no blocks of its own — a step
|
|
284
|
+
* whose only activity was a tool call — still emits this message, so its
|
|
285
|
+
* `tool/call` and `tool/result` events have a parent to pair with.
|
|
286
|
+
*/
|
|
287
|
+
private flushAssistant;
|
|
288
|
+
}
|
|
289
|
+
//# sourceMappingURL=agent.d.ts.map
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shared tool-approval fold for ACP engines.
|
|
3
|
+
*
|
|
4
|
+
* An ACP agent surfaces a tool it wants to run as the reverse-RPC
|
|
5
|
+
* `session/request_permission`, which the client MUST answer; the dsh harness has
|
|
6
|
+
* no interactive approval callback in the unattended runtime, so the fold has to
|
|
7
|
+
* decide. It mirrors the codex/Pi bridges: an `ask` policy degrades to a denial
|
|
8
|
+
* (the only safe answer when no human is present), and anything else (`never`, or
|
|
9
|
+
* a knob-less session) auto-approves.
|
|
10
|
+
*
|
|
11
|
+
* It lives here rather than in one engine's directory because all three ACP
|
|
12
|
+
* engines arrive at the same gate; an engine whose own permission switch already
|
|
13
|
+
* bounds the tool can still override the fold through its profile's `approve`.
|
|
14
|
+
*
|
|
15
|
+
* @module dsh-loop-engine-allinone/driver-acp/approval
|
|
16
|
+
*/
|
|
17
|
+
import type { Session } from '@deepseek-ai/dsh-session';
|
|
18
|
+
/**
|
|
19
|
+
* Whether the driver should answer `session/request_permission` with approval for
|
|
20
|
+
* one query. An `ask` policy denies; anything else auto-approves. The sandbox
|
|
21
|
+
* stance is not consulted: the engine's own tool policy bounds what a tool does,
|
|
22
|
+
* and the ACP approval is the host's gate, signalled by `approval/policy`.
|
|
23
|
+
* @param session - the session whose durable knobs are read.
|
|
24
|
+
* @returns whether tool requests are approved.
|
|
25
|
+
*/
|
|
26
|
+
export declare function acpDefaultApproval(session: Session): boolean;
|
|
27
|
+
//# sourceMappingURL=approval.d.ts.map
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ACP client-side capabilities: the reverse-RPC methods an agent may call on US.
|
|
3
|
+
*
|
|
4
|
+
* ACP is bidirectional. Besides the requests a client sends (`initialize`,
|
|
5
|
+
* `session/new`, `session/prompt`), an agent may call BACK into the client for
|
|
6
|
+
* things it cannot do itself. Which ones it calls is the agent's decision, and a
|
|
7
|
+
* client that answers `methodNotFound` is a client that agent cannot fully use.
|
|
8
|
+
*
|
|
9
|
+
* This module is the measured list, not the specification's full menu. Two
|
|
10
|
+
* engines were driven with real tasks and every reverse RPC they issued was
|
|
11
|
+
* recorded with `scripts/probe/acp-handshake.mjs` against the real CLIs:
|
|
12
|
+
*
|
|
13
|
+
* - `session/request_permission` — already implemented since the kimi driver;
|
|
14
|
+
* - `fs/read_text_file` — Grok Build asks the client to read a file for it;
|
|
15
|
+
* - `terminal/create` (+ its lifecycle calls) — Grok Build asks the client to
|
|
16
|
+
* RUN a command and report its output.
|
|
17
|
+
*
|
|
18
|
+
* MiniMax Code asks for none of these: it executes its own tools and reports the
|
|
19
|
+
* result, which is why its tool calls work without any of this.
|
|
20
|
+
*
|
|
21
|
+
* ## Why the terminal bridge is the interesting one
|
|
22
|
+
*
|
|
23
|
+
* A command the AGENT wants run must not become a second, weaker execution path
|
|
24
|
+
* than the one dsh's own `bash` tool uses. So the bridge does not spawn anything
|
|
25
|
+
* itself: it resolves the harness's `shell` service (`ctx.shell`), which owns the
|
|
26
|
+
* sandbox policy, the timeout policy and the output caps. A grok tool call and a
|
|
27
|
+
* dsh `bash` call therefore run through the SAME executor with the SAME
|
|
28
|
+
* confinement — which is also what makes the approval question answerable: the
|
|
29
|
+
* reverse-RPC permission gate stays the single place a human's policy is applied.
|
|
30
|
+
*
|
|
31
|
+
* @module dsh-loop-engine-allinone/driver-acp/capabilities
|
|
32
|
+
*/
|
|
33
|
+
import type { Readable, Writable } from 'node:stream';
|
|
34
|
+
/**
|
|
35
|
+
* One reverse-RPC request frame, as it arrived.
|
|
36
|
+
*
|
|
37
|
+
* `id` is a number for every request an agent sends (JSON-RPC allows strings, but
|
|
38
|
+
* all three measured engines use integers); the client echoes it back verbatim.
|
|
39
|
+
*/
|
|
40
|
+
export interface AcpReverseRequest {
|
|
41
|
+
readonly id: number;
|
|
42
|
+
readonly method: string;
|
|
43
|
+
readonly params: unknown;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* What the client can do for an agent, as the agent was TOLD it can.
|
|
47
|
+
*
|
|
48
|
+
* The values travel in `initialize`'s `clientCapabilities`. Declaring a capability
|
|
49
|
+
* is a promise: an agent reads it to decide whether to send the request at all, so
|
|
50
|
+
* declaring one this client cannot honour is worse than declaring none — the
|
|
51
|
+
* agent would plan a tool call that then fails.
|
|
52
|
+
*/
|
|
53
|
+
export interface AcpClientCapabilities {
|
|
54
|
+
/** Whether `fs/read_text_file` and `fs/write_text_file` are answerable. */
|
|
55
|
+
readonly fs: {
|
|
56
|
+
readonly readTextFile: boolean;
|
|
57
|
+
readonly writeTextFile: boolean;
|
|
58
|
+
};
|
|
59
|
+
/** Whether the `terminal/*` lifecycle is answerable. */
|
|
60
|
+
readonly terminal: boolean;
|
|
61
|
+
}
|
|
62
|
+
/** The capabilities a client announces when it bridges every measured reverse RPC. */
|
|
63
|
+
export declare const FULL_CLIENT_CAPABILITIES: AcpClientCapabilities;
|
|
64
|
+
/**
|
|
65
|
+
* The capabilities a client announces when it bridges nothing.
|
|
66
|
+
*
|
|
67
|
+
* Kept as a named constant because it is a real, supported configuration: the
|
|
68
|
+
* kimi driver has always announced this, and a deployment that would rather not
|
|
69
|
+
* let an engine run commands can keep it.
|
|
70
|
+
*/
|
|
71
|
+
export declare const NO_CLIENT_CAPABILITIES: AcpClientCapabilities;
|
|
72
|
+
/**
|
|
73
|
+
* Whether one ACP method name is a capability this client implements.
|
|
74
|
+
*
|
|
75
|
+
* The list is closed on purpose: it is exactly the measured set (plus the
|
|
76
|
+
* terminal lifecycle ACP defines around `terminal/create`). Anything else stays
|
|
77
|
+
* on the `methodNotFound` path, so an engine's private extension never gets a
|
|
78
|
+
* half-answer from a client that only meant to support the standard ones.
|
|
79
|
+
* @param method - the ACP method name from a reverse-RPC request.
|
|
80
|
+
* @returns whether {@link AcpReverseHandlers} can answer it.
|
|
81
|
+
*/
|
|
82
|
+
export declare function isCapabilityMethod(method: string): boolean;
|
|
83
|
+
/**
|
|
84
|
+
* One live terminal the client owns on the agent's behalf.
|
|
85
|
+
*
|
|
86
|
+
* The agent creates it with a command, then polls/awaits it, then releases it. The
|
|
87
|
+
* client is responsible for the whole lifetime: an agent that never releases one
|
|
88
|
+
* must not leak a process, so the client closes every open terminal when its own
|
|
89
|
+
* process goes away.
|
|
90
|
+
*/
|
|
91
|
+
export interface AcpTerminal {
|
|
92
|
+
/** Output accumulated so far, plus whether the command has finished. */
|
|
93
|
+
snapshot(): AcpTerminalSnapshot;
|
|
94
|
+
/** Resolve when the command finishes, or reject when it could not start. */
|
|
95
|
+
waitForExit(): Promise<AcpTerminalExit>;
|
|
96
|
+
/** Terminate the command (idempotent) and drop the terminal. */
|
|
97
|
+
release(): void;
|
|
98
|
+
}
|
|
99
|
+
/** The `terminal/output` answer: what the command has produced so far. */
|
|
100
|
+
export interface AcpTerminalSnapshot {
|
|
101
|
+
/** Everything written to stdout so far. */
|
|
102
|
+
readonly output: string;
|
|
103
|
+
/** Whether the command has finished (and so whether `output` is final). */
|
|
104
|
+
readonly exited: boolean;
|
|
105
|
+
/** Exit code, present once `exited`. */
|
|
106
|
+
readonly exitCode?: number;
|
|
107
|
+
}
|
|
108
|
+
/** The `terminal/wait_for_exit` answer. */
|
|
109
|
+
export interface AcpTerminalExit {
|
|
110
|
+
/** Exit code; a signal-killed or timed-out command reports a non-zero code. */
|
|
111
|
+
readonly exitCode: number;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* The seam a driver hands the client so it can answer the measured reverse RPCs.
|
|
115
|
+
*
|
|
116
|
+
* Every method is optional: a client built without a bridge answers
|
|
117
|
+
* `methodNotFound` for it, which is the honest answer and what the kimi driver
|
|
118
|
+
* has always done. The `terminal` capability is announced ONLY when
|
|
119
|
+
* {@link AcpReverseHandlers.spawnTerminal} is present, so the declaration and the
|
|
120
|
+
* implementation cannot drift apart.
|
|
121
|
+
*/
|
|
122
|
+
export interface AcpReverseHandlers {
|
|
123
|
+
/**
|
|
124
|
+
* Read one text file for the agent.
|
|
125
|
+
*
|
|
126
|
+
* The path arrives absolute (measured: grok sends the resolved path). It is
|
|
127
|
+
* read through the harness filesystem service when one is available, so dsh's
|
|
128
|
+
* observation policy applies; a plain `node:fs` read is the fallback for a
|
|
129
|
+
* composition without that service.
|
|
130
|
+
* @param path - the absolute path the agent asked for.
|
|
131
|
+
* @returns the file's decoded text.
|
|
132
|
+
*/
|
|
133
|
+
readTextFile?(path: string): Promise<string>;
|
|
134
|
+
/**
|
|
135
|
+
* Start one command on the agent's behalf.
|
|
136
|
+
*
|
|
137
|
+
* Implementations MUST route through the harness's own executor (or an
|
|
138
|
+
* equivalently confined path) rather than spawning freely: this is the
|
|
139
|
+
* difference between "the engine can use tools" and "the engine bypasses the
|
|
140
|
+
* sandbox".
|
|
141
|
+
* @param command - the shell command line the agent asked to run.
|
|
142
|
+
* @param options - the agent's requested environment and working directory.
|
|
143
|
+
* @returns the live terminal handle.
|
|
144
|
+
*/
|
|
145
|
+
spawnTerminal?(command: string, options: AcpTerminalSpawnOptions): Promise<AcpTerminal>;
|
|
146
|
+
}
|
|
147
|
+
/** What an agent may ask for when creating a terminal. */
|
|
148
|
+
export interface AcpTerminalSpawnOptions {
|
|
149
|
+
/** Environment entries the agent wants layered over the child's own. */
|
|
150
|
+
readonly env?: Record<string, string>;
|
|
151
|
+
/** Working directory, when the agent named one. */
|
|
152
|
+
readonly cwd?: string;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* One `terminal/create` params payload, as the agent sent it.
|
|
156
|
+
*
|
|
157
|
+
* ACP spells the environment as a LIST of `{name, value}` pairs, not a map —
|
|
158
|
+
* measured on grok, which sends 14 entries including its own `GROK_SESSION_ID`.
|
|
159
|
+
*/
|
|
160
|
+
export interface AcpTerminalCreateParams {
|
|
161
|
+
readonly sessionId?: string;
|
|
162
|
+
readonly command?: unknown;
|
|
163
|
+
readonly env?: unknown;
|
|
164
|
+
readonly cwd?: unknown;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Read the command out of `terminal/create` params, or undefined when absent.
|
|
168
|
+
* @param params - the reverse-RPC params.
|
|
169
|
+
* @returns the command line, when the frame carried a non-empty one.
|
|
170
|
+
*/
|
|
171
|
+
export declare function terminalCommandOf(params: unknown): string | undefined;
|
|
172
|
+
/**
|
|
173
|
+
* Read the agent's environment request out of `terminal/create` params.
|
|
174
|
+
*
|
|
175
|
+
* The wire shape is a list of pairs; a malformed entry is skipped rather than
|
|
176
|
+
* failing the call, because a command with a partial environment is still a
|
|
177
|
+
* command the agent asked for, while a thrown error would fail the whole turn
|
|
178
|
+
* over a field the agent did not need.
|
|
179
|
+
* @param params - the reverse-RPC params.
|
|
180
|
+
* @returns the entries as a map, or undefined when the frame carried none.
|
|
181
|
+
*/
|
|
182
|
+
export declare function terminalEnvOf(params: unknown): Record<string, string> | undefined;
|
|
183
|
+
/**
|
|
184
|
+
* Read the working directory out of `terminal/create` params.
|
|
185
|
+
* @param params - the reverse-RPC params.
|
|
186
|
+
* @returns the directory, when the frame carried a non-empty one.
|
|
187
|
+
*/
|
|
188
|
+
export declare function terminalCwdOf(params: unknown): string | undefined;
|
|
189
|
+
/**
|
|
190
|
+
* The stream triple an ACP child exposes, as the capability layer needs it.
|
|
191
|
+
*
|
|
192
|
+
* Declared structurally rather than imported from `./transport.ts` so a test can
|
|
193
|
+
* build one without the rest of the transport contract.
|
|
194
|
+
*/
|
|
195
|
+
export interface AcpStdio {
|
|
196
|
+
readonly stdin: Writable;
|
|
197
|
+
readonly stdout: Readable;
|
|
198
|
+
readonly stderr: Readable;
|
|
199
|
+
}
|
|
200
|
+
//# sourceMappingURL=capabilities.d.ts.map
|