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.
Files changed (143) hide show
  1. package/CHANGELOG.md +119 -0
  2. package/CONTRIBUTING.md +78 -0
  3. package/LICENSE +23 -0
  4. package/NOTICE +45 -0
  5. package/README.md +223 -2
  6. package/README.zh.md +179 -0
  7. package/SECURITY.md +39 -0
  8. package/cordis.patch.yml +3 -0
  9. package/lib/client.js +2166 -0
  10. package/lib/index.js +11621 -0
  11. package/lib/invariant.js +453 -0
  12. package/lib/types/agent-preset-ids.d.ts +276 -0
  13. package/lib/types/client/LoopEngineBadge.d.ts +71 -0
  14. package/lib/types/client/LoopEngineComposerSelect.d.ts +126 -0
  15. package/lib/types/client/LoopEngineSection.d.ts +35 -0
  16. package/lib/types/client/engine-marks.d.ts +87 -0
  17. package/lib/types/client/index.d.ts +39 -0
  18. package/lib/types/client/locales.d.ts +182 -0
  19. package/lib/types/client/reload.d.ts +135 -0
  20. package/lib/types/client/session-engine.d.ts +508 -0
  21. package/lib/types/client/store.d.ts +103 -0
  22. package/lib/types/client/turn-status.d.ts +234 -0
  23. package/lib/types/client/use-session-engine.d.ts +74 -0
  24. package/lib/types/commands.d.ts +77 -0
  25. package/lib/types/compat.d.ts +25 -0
  26. package/lib/types/driver-acp/agent.d.ts +289 -0
  27. package/lib/types/driver-acp/approval.d.ts +27 -0
  28. package/lib/types/driver-acp/capabilities.d.ts +200 -0
  29. package/lib/types/driver-acp/client.d.ts +297 -0
  30. package/lib/types/driver-acp/executable.d.ts +53 -0
  31. package/lib/types/driver-acp/mapping.d.ts +76 -0
  32. package/lib/types/driver-acp/profile.d.ts +126 -0
  33. package/lib/types/driver-acp/runtime.d.ts +45 -0
  34. package/lib/types/driver-acp/subprocess.d.ts +19 -0
  35. package/lib/types/driver-acp/terminal.d.ts +98 -0
  36. package/lib/types/driver-acp/transport.d.ts +43 -0
  37. package/lib/types/driver-acp/types.d.ts +152 -0
  38. package/lib/types/driver-cli/capability.d.ts +42 -0
  39. package/lib/types/driver-cli/cli-agent.d.ts +258 -0
  40. package/lib/types/driver-cli/cli-loop.d.ts +46 -0
  41. package/lib/types/driver-cli/flatten.d.ts +35 -0
  42. package/lib/types/driver-cli/frames.d.ts +128 -0
  43. package/lib/types/driver-cli/ndjson.d.ts +106 -0
  44. package/lib/types/driver-cli/session-sink.d.ts +333 -0
  45. package/lib/types/driver-cli/stream-json.d.ts +61 -0
  46. package/lib/types/driver-cli/text.d.ts +66 -0
  47. package/lib/types/driver-cli/transport.d.ts +84 -0
  48. package/lib/types/driver-core/agents-md-skill-provider.d.ts +72 -0
  49. package/lib/types/driver-core/assistant-stream.d.ts +68 -0
  50. package/lib/types/driver-core/context-files.d.ts +62 -0
  51. package/lib/types/driver-core/host-servers.d.ts +119 -0
  52. package/lib/types/driver-core/hosted-engine-runtime.d.ts +190 -0
  53. package/lib/types/driver-core/hosted-tool-vocabulary.d.ts +78 -0
  54. package/lib/types/driver-core/idle-child.d.ts +57 -0
  55. package/lib/types/driver-core/image-access.d.ts +25 -0
  56. package/lib/types/driver-core/inbox.d.ts +104 -0
  57. package/lib/types/driver-core/model-handover.d.ts +134 -0
  58. package/lib/types/driver-core/ownership.d.ts +41 -0
  59. package/lib/types/driver-core/permission-knobs.d.ts +26 -0
  60. package/lib/types/driver-core/prompt.d.ts +64 -0
  61. package/lib/types/driver-core/session-lifetime.d.ts +62 -0
  62. package/lib/types/driver-core/session-model.d.ts +128 -0
  63. package/lib/types/driver-core/skill-inject.d.ts +59 -0
  64. package/lib/types/driver-core/step-close.d.ts +49 -0
  65. package/lib/types/driver-core/step-images.d.ts +62 -0
  66. package/lib/types/driver-core/step-tools.d.ts +43 -0
  67. package/lib/types/driver-core/system-head.d.ts +43 -0
  68. package/lib/types/engine-agy/agent.d.ts +67 -0
  69. package/lib/types/engine-agy/frames.d.ts +90 -0
  70. package/lib/types/engine-agy/loop.d.ts +64 -0
  71. package/lib/types/engine-agy/process.d.ts +173 -0
  72. package/lib/types/engine-claude/agent.d.ts +186 -0
  73. package/lib/types/engine-claude/loop.d.ts +73 -0
  74. package/lib/types/engine-claude/mapping.d.ts +108 -0
  75. package/lib/types/engine-claude/permission.d.ts +41 -0
  76. package/lib/types/engine-claude/process.d.ts +59 -0
  77. package/lib/types/engine-claude/sdk.d.ts +67 -0
  78. package/lib/types/engine-claude/types.d.ts +28 -0
  79. package/lib/types/engine-codex/agent.d.ts +216 -0
  80. package/lib/types/engine-codex/appserver/client.d.ts +80 -0
  81. package/lib/types/engine-codex/appserver/mapping.d.ts +67 -0
  82. package/lib/types/engine-codex/appserver/thread.d.ts +66 -0
  83. package/lib/types/engine-codex/appserver/types.d.ts +225 -0
  84. package/lib/types/engine-codex/loop.d.ts +75 -0
  85. package/lib/types/engine-codex/model-handover.d.ts +56 -0
  86. package/lib/types/engine-codex/permission.d.ts +126 -0
  87. package/lib/types/engine-codex/skills.d.ts +28 -0
  88. package/lib/types/engine-codex/types.d.ts +36 -0
  89. package/lib/types/engine-drivers.d.ts +132 -0
  90. package/lib/types/engine-grok/agent.d.ts +24 -0
  91. package/lib/types/engine-grok/loop.d.ts +61 -0
  92. package/lib/types/engine-grok/process.d.ts +46 -0
  93. package/lib/types/engine-grok/profile.d.ts +82 -0
  94. package/lib/types/engine-hermes/agent.d.ts +47 -0
  95. package/lib/types/engine-hermes/loop.d.ts +56 -0
  96. package/lib/types/engine-hermes/process.d.ts +92 -0
  97. package/lib/types/engine-kimi/acp/client.d.ts +123 -0
  98. package/lib/types/engine-kimi/acp/mapping.d.ts +73 -0
  99. package/lib/types/engine-kimi/acp/types.d.ts +143 -0
  100. package/lib/types/engine-kimi/agent.d.ts +24 -0
  101. package/lib/types/engine-kimi/commands.d.ts +44 -0
  102. package/lib/types/engine-kimi/loop.d.ts +62 -0
  103. package/lib/types/engine-kimi/model-handover.d.ts +32 -0
  104. package/lib/types/engine-kimi/permission.d.ts +28 -0
  105. package/lib/types/engine-kimi/process.d.ts +47 -0
  106. package/lib/types/engine-kimi/profile.d.ts +19 -0
  107. package/lib/types/engine-kimi/skills.d.ts +52 -0
  108. package/lib/types/engine-kimi/types.d.ts +25 -0
  109. package/lib/types/engine-minimaxcode/agent.d.ts +24 -0
  110. package/lib/types/engine-minimaxcode/loop.d.ts +83 -0
  111. package/lib/types/engine-minimaxcode/process.d.ts +53 -0
  112. package/lib/types/engine-minimaxcode/profile.d.ts +72 -0
  113. package/lib/types/engine-of-session.d.ts +97 -0
  114. package/lib/types/engine-pi/agent.d.ts +230 -0
  115. package/lib/types/engine-pi/loop.d.ts +72 -0
  116. package/lib/types/engine-pi/model-handover.d.ts +84 -0
  117. package/lib/types/engine-pi/permission.d.ts +47 -0
  118. package/lib/types/engine-pi/rpc/client.d.ts +107 -0
  119. package/lib/types/engine-pi/rpc/mapping.d.ts +37 -0
  120. package/lib/types/engine-pi/rpc/types.d.ts +249 -0
  121. package/lib/types/engine-pi/skills.d.ts +47 -0
  122. package/lib/types/engine-pi/types.d.ts +36 -0
  123. package/lib/types/engine-qoderclicn/agent.d.ts +59 -0
  124. package/lib/types/engine-qoderclicn/loop.d.ts +51 -0
  125. package/lib/types/engine-qoderclicn/process.d.ts +113 -0
  126. package/lib/types/engine-registry.d.ts +584 -0
  127. package/lib/types/engine-remote.d.ts +192 -0
  128. package/lib/types/engine-surface.d.ts +36 -0
  129. package/lib/types/engine-zcode/agent.d.ts +50 -0
  130. package/lib/types/engine-zcode/loop.d.ts +47 -0
  131. package/lib/types/engine-zcode/process.d.ts +111 -0
  132. package/lib/types/index.d.ts +205 -0
  133. package/lib/types/invariant.d.ts +26 -0
  134. package/lib/types/model-selection-reset.d.ts +271 -0
  135. package/lib/types/namespace.d.ts +25 -0
  136. package/lib/types/patch-manager.d.ts +104 -0
  137. package/lib/types/preset.d.ts +254 -0
  138. package/lib/types/provider-route.d.ts +245 -0
  139. package/lib/types/router-loop.d.ts +458 -0
  140. package/lib/types/session-engine-store.d.ts +138 -0
  141. package/lib/types/settings.d.ts +60 -0
  142. package/lib/types/skills.d.ts +85 -0
  143. 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