dsh-ntr 0.1.5 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +4 -2
- package/README.md +4 -2
- package/cordis.patch.yml +56 -28
- package/lib/bridge.js +378 -330
- package/lib/client/acp-ui.js +60 -32
- package/lib/client/index.js +17 -0
- package/lib/client/panel.js +257 -103
- package/lib/client/permission-dock.js +72 -0
- package/lib/client/session-controls.js +4 -4
- package/lib/client/store.js +88 -22
- package/lib/client.js +584 -226
- package/lib/client.js.map +1 -1
- package/lib/impl.js +128 -45
- package/lib/index.js +20 -52
- package/lib/types/bridge.d.ts +164 -214
- package/lib/types/client/panel.d.ts +59 -20
- package/lib/types/client/permission-dock.d.ts +31 -0
- package/lib/types/client/store.d.ts +52 -15
- package/lib/types/impl.d.ts +93 -27
- package/lib/types/protocol.d.ts +137 -52
- package/package.json +1 -1
- package/src/bridge.ts +433 -417
- package/src/client/acp-ui.tsx +60 -32
- package/src/client/index.tsx +18 -0
- package/src/client/panel.tsx +395 -197
- package/src/client/permission-dock.tsx +133 -0
- package/src/client/session-controls.tsx +4 -4
- package/src/client/store.ts +112 -36
- package/src/impl.ts +166 -82
- package/src/index.ts +20 -65
- package/src/protocol.ts +143 -52
package/lib/types/bridge.d.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import { type InitializeResponse } from '@agentclientprotocol/sdk';
|
|
10
10
|
import { EventHub, PermissionLedger, Transcript } from './impl.ts';
|
|
11
11
|
import type { AcpToolCallSnapshot, PendingPermissionView, RosterPresetRow } from './impl.ts';
|
|
12
|
-
import type { AcpAgentCatalog, AcpAgentDef, AcpApprovalDeadlines, AcpConversationChoice, AcpPresetStatus, AcpTagHalo, AgentCatalogResult, AgentProbeModelsResult, AgentTestResult, AgentView, ChatSessionView, ChatTranscriptView, DetectedAgentInfo, GenerateOptions, LlmAdapterLike, LlmModelInfo, LlmProviderInfo, LlmResolvedModelInfo, ApprovalDeadlineKey, PreparedAdapterCall, SessionView, StreamChunk } from './protocol.ts';
|
|
12
|
+
import type { AcpAgentCatalog, AcpAgentDef, AcpApprovalDeadlines, AcpConversationChoice, AcpPresetStatus, AcpTagHalo, AgentCatalogResult, AgentProbeModelsResult, AgentTestResult, AgentView, ChatSessionView, ChatTranscriptView, DetectedAgentInfo, GenerateOptions, LlmAdapterLike, LlmModelInfo, LlmProviderInfo, LlmResolvedModelInfo, ApprovalDeadlineKey, ApprovalDeadlinePatch, PermissionTimeoutMode, PreparedAdapterCall, SessionView, StreamChunk } from './protocol.ts';
|
|
13
13
|
interface LiveSession {
|
|
14
14
|
/** Host-minted id (stable across an agent restart; the ACP id is per-connection). */
|
|
15
15
|
sessionId: string;
|
|
@@ -39,9 +39,9 @@ interface LiveSession {
|
|
|
39
39
|
released: boolean;
|
|
40
40
|
/**
|
|
41
41
|
* Owning DSH chat session id, when the session was opened through the LLM
|
|
42
|
-
* adapter (`AcpLlmAdapter`).
|
|
43
|
-
*
|
|
44
|
-
*
|
|
42
|
+
* adapter (`AcpLlmAdapter`). It is what the conversation's permission card
|
|
43
|
+
* filters on (`permission/request.dshSessionId`); sessions started over the
|
|
44
|
+
* `/acp` RPC have no chat session, so their asks stay on the settings panel.
|
|
45
45
|
*/
|
|
46
46
|
dshSessionId?: string;
|
|
47
47
|
/**
|
|
@@ -95,50 +95,48 @@ interface LiveSession {
|
|
|
95
95
|
/** Reliability tuning, threaded from `AcpPluginConfig` through the manager. */
|
|
96
96
|
export interface BridgeTuning {
|
|
97
97
|
startupTimeoutMs?: number;
|
|
98
|
+
/**
|
|
99
|
+
* How long a permission ask may sit unanswered before the deadline fires.
|
|
100
|
+
*
|
|
101
|
+
* ONE window for every ask. Every ask is broadcast as `permission/request`
|
|
102
|
+
* and answered from the conversation's dock card; an `/acp`-RPC ask has no
|
|
103
|
+
* conversation, so the settings page mirrors the same ask as its answer
|
|
104
|
+
* entry. One question, one window: "is anyone there?" gets the same answer
|
|
105
|
+
* wherever the card is rendered, and two numbers made the settings card look
|
|
106
|
+
* like two unrelated knobs.
|
|
107
|
+
*
|
|
108
|
+
* `0` means 不等待, not "wait forever": `auto` settles the ask on arrival,
|
|
109
|
+
* `abort` floors the window to the shortest real wait (refusing every ask on
|
|
110
|
+
* arrival is a brick wall, not a timeout), and `wait` ignores the window
|
|
111
|
+
* entirely.
|
|
112
|
+
*
|
|
113
|
+
* The deadline is what keeps a turn from hanging on a question nobody
|
|
114
|
+
* answers: the card stays visible, but an unattended session would otherwise
|
|
115
|
+
* wait indefinitely. Past the deadline the ask is settled IN PLACE — never
|
|
116
|
+
* relocated: `abort` refuses it, `auto` answers it with the option the
|
|
117
|
+
* agent's own kinds imply (broadest grant first), and `wait` never expires
|
|
118
|
+
* it at all.
|
|
119
|
+
*
|
|
120
|
+
* Default 300000 ms. OmniTerm's equivalent is its reaper's permission-timeout
|
|
121
|
+
* threshold (30 min default, 30 s steps, hot-updatable).
|
|
122
|
+
*/
|
|
98
123
|
permissionTimeoutMs?: number;
|
|
99
124
|
/**
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
*
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
* ledger (`native-timeout`, see {@link LedgerFallbackReason}).
|
|
116
|
-
*
|
|
117
|
-
* A routed ask shows no card and broadcasts no frame — the composer takeover
|
|
118
|
-
* IS the answer entry — so a host that accepts the question and then never
|
|
119
|
-
* renders it (answerer absent from the browser scope, the conversation not
|
|
120
|
-
* mounted, a client that never received the remote event) leaves the turn
|
|
121
|
-
* hanging behind a question the user cannot see anywhere. The deadline is
|
|
122
|
-
* what makes that state bounded: past it the ask is withdrawn (the request
|
|
123
|
-
* signal is aborted, which settles the host ask `cancelled`) and re-asked on
|
|
124
|
-
* the ledger, where the chat line and the settings card are both visible.
|
|
125
|
-
*
|
|
126
|
-
* This is NOT an answer on the human's behalf: a human mid-decision simply
|
|
127
|
-
* finds the same question in the settings panel. `0` disables it (wait for
|
|
128
|
-
* the host forever).
|
|
129
|
-
*
|
|
130
|
-
* Default 300000 ms — the SAME window as
|
|
131
|
-
* {@link BridgeTuning.permissionFallbackTimeoutMs}, so "this route is not
|
|
132
|
-
* working" is one number instead of two arbitrary ones. A takeover that is
|
|
133
|
-
* going to render does so immediately, and the host replays a pending
|
|
134
|
-
* approval across reconnects (verified live on DSH web 0.2.1-rc.2), so the
|
|
135
|
-
* deadline only ever fires for a host that took the question and never showed
|
|
136
|
-
* it. OmniTerm's equivalent is its reaper's permission-timeout threshold
|
|
137
|
-
* (30 min default, 30 s steps, hot-updatable); this is the same knob without
|
|
138
|
-
* their `auto` tier, which this plugin does not have (every ask must reach a
|
|
139
|
-
* human).
|
|
140
|
-
*/
|
|
141
|
-
nativeApprovalTimeoutMs?: number;
|
|
125
|
+
* What an EXPIRED approval deadline does. `wait` (default) never expires
|
|
126
|
+
* the ask at all; `abort` refuses it; `auto` answers it with the option the
|
|
127
|
+
* agent's own kinds imply and lets the agent continue.
|
|
128
|
+
*
|
|
129
|
+
* Three values, and only three: an unanswered ask is either answered for
|
|
130
|
+
* you, kept waiting, or refused. Same vocabulary as OmniTerm dev's
|
|
131
|
+
* `acp_perm_timeout_mode`.
|
|
132
|
+
*
|
|
133
|
+
* The default is `wait`: a permission ask is a question FOR A HUMAN, and
|
|
134
|
+
* refusing it because the human looked away from the tab is worse than
|
|
135
|
+
* waiting — the ask stays visible the whole time, so nobody is left
|
|
136
|
+
* wondering what happened to it. `abort` and `auto` are opt-in for the
|
|
137
|
+
* deployments that would rather have a bounded turn.
|
|
138
|
+
*/
|
|
139
|
+
permissionTimeoutMode?: PermissionTimeoutMode;
|
|
142
140
|
cancelFallbackMs?: number;
|
|
143
141
|
/**
|
|
144
142
|
* How long to wait before resending a prompt the agent answered `cancelled`
|
|
@@ -172,54 +170,6 @@ export interface BridgeTuning {
|
|
|
172
170
|
*/
|
|
173
171
|
probeIdleTimeoutMs?: number;
|
|
174
172
|
}
|
|
175
|
-
/** One native approval question, as the host approval service defines it. */
|
|
176
|
-
export interface ApprovalRequestContext {
|
|
177
|
-
/** DSH chat session that owns the open turn the question is asked in. */
|
|
178
|
-
dshSessionId?: string;
|
|
179
|
-
agentId: string;
|
|
180
|
-
/** Human-facing identity of the operation awaiting a decision. */
|
|
181
|
-
toolName: string;
|
|
182
|
-
reason?: string;
|
|
183
|
-
signal?: AbortSignal;
|
|
184
|
-
}
|
|
185
|
-
/** Outcome vocabulary of the host approval chain (`'unavailable'` = no answerer). */
|
|
186
|
-
export type ApprovalOutcome = 'allowed-once' | 'rejected' | 'cancelled' | 'unavailable';
|
|
187
|
-
/**
|
|
188
|
-
* Why one permission ask rides the settings-panel ledger instead of the native
|
|
189
|
-
* approval chain, recorded when the ask is broadcast.
|
|
190
|
-
*
|
|
191
|
-
* `no-requester` and `no-chat-session` are the DESIGNED ledger paths: a host
|
|
192
|
-
* without the approval service, and an `/acp`-RPC session that has no chat
|
|
193
|
-
* turn to route into. `unavailable` is a DEGRADATION — the service is wired
|
|
194
|
-
* and this ask still failed, so the conversation the user is looking at will
|
|
195
|
-
* never show the question. `native-timeout` is the other degradation: the host
|
|
196
|
-
* TOOK the question and never answered it inside
|
|
197
|
-
* {@link BridgeTuning.nativeApprovalTimeoutMs}, so it is withdrawn and re-asked
|
|
198
|
-
* here. The last two decide both the chat line's wording (see
|
|
199
|
-
* `BridgeManager.streamPrompt`) and whether a deadline is armed
|
|
200
|
-
* ({@link AgentBridge.handlePermissionRequest}).
|
|
201
|
-
*/
|
|
202
|
-
export type LedgerFallbackReason = 'no-requester' | 'no-chat-session' | 'unavailable' | 'native-timeout';
|
|
203
|
-
/** One recorded ledger fallback: why the ask is here, and the deadline armed for it. */
|
|
204
|
-
export interface LedgerFallback {
|
|
205
|
-
reason: LedgerFallbackReason;
|
|
206
|
-
/** Effective deadline in ms; `0` = no timer (wait for the human). */
|
|
207
|
-
timeoutMs: number;
|
|
208
|
-
/**
|
|
209
|
-
* How long the NATIVE route waited before withdrawing this ask, on the
|
|
210
|
-
* `native-timeout` path only. The chat line reports it as an observed fact
|
|
211
|
-
* ("no answer in the conversation within Ns") rather than guessing a cause:
|
|
212
|
-
* a slow human and a host that never rendered the question look identical
|
|
213
|
-
* from here.
|
|
214
|
-
*/
|
|
215
|
-
waitedMs?: number;
|
|
216
|
-
}
|
|
217
|
-
/**
|
|
218
|
-
* Asks the host approval service (`ctx.approval`) for one decision. Never
|
|
219
|
-
* throws: any failure is reported as `'unavailable'` so the caller can fall
|
|
220
|
-
* back to the settings-panel ledger.
|
|
221
|
-
*/
|
|
222
|
-
export type ApprovalRequester = (request: ApprovalRequestContext) => Promise<ApprovalOutcome>;
|
|
223
173
|
/**
|
|
224
174
|
* Resolves the workspace root of one chat session (`dshSessionId` = DSH session
|
|
225
175
|
* id) — the sidebar's selected workspace, which the host's sandbox policy keeps
|
|
@@ -256,13 +206,8 @@ export declare class AgentBridge {
|
|
|
256
206
|
/** Observes `availableCommands` from any real session so the agent's slash menu survives its sessions. */
|
|
257
207
|
private readonly onCommands?;
|
|
258
208
|
/**
|
|
259
|
-
* Resolved per
|
|
260
|
-
* was built
|
|
261
|
-
*/
|
|
262
|
-
private readonly approvalRequester?;
|
|
263
|
-
/**
|
|
264
|
-
* Resolved per turn, like {@link approvalRequester}: the host entry may
|
|
265
|
-
* wire the access-mode mapping after this bridge was built.
|
|
209
|
+
* Resolved per turn: the host entry may wire the workspace reader after
|
|
210
|
+
* this bridge was built.
|
|
266
211
|
*/
|
|
267
212
|
private readonly sessionWorkspace?;
|
|
268
213
|
state: BridgeState;
|
|
@@ -279,29 +224,18 @@ export declare class AgentBridge {
|
|
|
279
224
|
/** Inflight `session/load` resumes, shared so concurrent prompts don't double-resume. */
|
|
280
225
|
private readonly resuming;
|
|
281
226
|
readonly permissions: PermissionLedger;
|
|
227
|
+
private readonly startupTimeoutMs;
|
|
282
228
|
/**
|
|
283
|
-
*
|
|
284
|
-
*
|
|
285
|
-
*
|
|
286
|
-
*
|
|
287
|
-
*
|
|
288
|
-
*
|
|
229
|
+
* The ONE approval deadline, and the mode it settles with. Neither is
|
|
230
|
+
* readonly, unlike their neighbors: the settings page rewrites them on a
|
|
231
|
+
* running bridge ({@link AgentBridge.applyApprovalDeadlines}), so a user
|
|
232
|
+
* editing the card does not have to kill every live session to see it take
|
|
233
|
+
* effect. Both are read per ask, so a reassignment reaches every ask from
|
|
234
|
+
* that moment on. An ask already holding an armed timer keeps the window it
|
|
235
|
+
* was started with.
|
|
289
236
|
*/
|
|
290
|
-
private
|
|
291
|
-
private
|
|
292
|
-
private readonly permissionTimeoutMs;
|
|
293
|
-
/**
|
|
294
|
-
* The two approval deadlines are NOT readonly, unlike their neighbors: the
|
|
295
|
-
* settings page rewrites them on a running bridge
|
|
296
|
-
* ({@link AgentBridge.applyApprovalDeadlines}), so a user editing the window
|
|
297
|
-
* does not have to kill every live session to see it take effect. Both are
|
|
298
|
-
* read per ask — `nativeApprovalTimeoutMs` when the native ask is armed,
|
|
299
|
-
* `permissionFallbackTimeoutMs` when the ledger ask's deadline is chosen — so
|
|
300
|
-
* a reassignment reaches every ask from that moment on. An ask already
|
|
301
|
-
* holding an armed timer keeps the window it was started with.
|
|
302
|
-
*/
|
|
303
|
-
private permissionFallbackTimeoutMs;
|
|
304
|
-
private nativeApprovalTimeoutMs;
|
|
237
|
+
private permissionTimeoutMs;
|
|
238
|
+
private permissionTimeoutMode;
|
|
305
239
|
private readonly cancelFallbackMs;
|
|
306
240
|
private readonly staleCancelRetryDelayMs;
|
|
307
241
|
private readonly staleCancelMaxRetries;
|
|
@@ -349,28 +283,23 @@ export declare class AgentBridge {
|
|
|
349
283
|
/** Observes `availableCommands` from any real session so the agent's slash menu survives its sessions. */
|
|
350
284
|
onCommands?: ((agentId: string, commands: NonNullable<SessionView["availableCommands"]>) => void) | undefined, tuning?: BridgeTuning,
|
|
351
285
|
/**
|
|
352
|
-
* Resolved per
|
|
353
|
-
* was built
|
|
354
|
-
*/
|
|
355
|
-
approvalRequester?: (() => ApprovalRequester | undefined) | undefined,
|
|
356
|
-
/**
|
|
357
|
-
* Resolved per turn, like {@link approvalRequester}: the host entry may
|
|
358
|
-
* wire the access-mode mapping after this bridge was built.
|
|
286
|
+
* Resolved per turn: the host entry may wire the workspace reader after
|
|
287
|
+
* this bridge was built.
|
|
359
288
|
*/
|
|
360
289
|
sessionWorkspace?: (() => SessionWorkspaceResolver | undefined) | undefined);
|
|
361
290
|
/**
|
|
362
|
-
* Adopt a rewritten approval deadline (the settings page's write).
|
|
363
|
-
*
|
|
364
|
-
* construction, because a live bridge cannot meaningfully adopt a
|
|
365
|
-
* or cancel window mid-flight.
|
|
291
|
+
* Adopt a rewritten approval deadline or mode (the settings page's write).
|
|
292
|
+
* Only the window and the mode live here; every other tuning value is
|
|
293
|
+
* fixed at construction, because a live bridge cannot meaningfully adopt a
|
|
294
|
+
* new startup or cancel window mid-flight.
|
|
366
295
|
*
|
|
367
296
|
* Deliberately does NOT re-arm anything: an ask that already holds a timer
|
|
368
297
|
* keeps the window it was started with, and the next ask reads the new value.
|
|
369
298
|
* Re-arming would move the goalposts under a human who is already deciding.
|
|
370
299
|
*/
|
|
371
300
|
applyApprovalDeadlines(patch: {
|
|
372
|
-
|
|
373
|
-
|
|
301
|
+
permissionTimeoutMs?: number;
|
|
302
|
+
mode?: PermissionTimeoutMode;
|
|
374
303
|
}): void;
|
|
375
304
|
/**
|
|
376
305
|
* The idle-release window this agent honors for its CHAT process: its own
|
|
@@ -585,43 +514,38 @@ export declare class AgentBridge {
|
|
|
585
514
|
stop(reason?: 'closed' | 'agent-stopped' | 'agent-died'): Promise<void>;
|
|
586
515
|
private killChild;
|
|
587
516
|
/**
|
|
588
|
-
* Answer one ACP permission ask.
|
|
589
|
-
*
|
|
590
|
-
* ledger
|
|
517
|
+
* Answer one ACP permission ask.
|
|
518
|
+
*
|
|
519
|
+
* EVERY ask is answered by a human through the plugin's own ledger. The
|
|
520
|
+
* host's approval chain is deliberately not involved: it could accept an ask
|
|
521
|
+
* and render nothing, leaving the turn hanging behind something invisible
|
|
522
|
+
* with no deadline (issue #2). The ledger broadcasts `permission/request`,
|
|
523
|
+
* the browser renders the answerable card, and `permission/respond` settles
|
|
524
|
+
* it.
|
|
525
|
+
*
|
|
526
|
+
* The host answers on its own only when a deadline expires: `abort` refuses,
|
|
527
|
+
* `auto` picks the option the agent's own kinds imply, `wait` never expires.
|
|
528
|
+
* The ACP request's own `signal` (the agent withdrawing the ask, or the
|
|
529
|
+
* connection dying) settles the same entry cancelled, so a withdrawn ask can
|
|
530
|
+
* never leave a stale card behind.
|
|
591
531
|
*/
|
|
592
532
|
private handlePermissionRequest;
|
|
593
533
|
/**
|
|
594
|
-
*
|
|
595
|
-
*
|
|
596
|
-
*
|
|
597
|
-
*
|
|
598
|
-
*/
|
|
599
|
-
|
|
600
|
-
/**
|
|
601
|
-
*
|
|
602
|
-
*
|
|
603
|
-
*
|
|
604
|
-
*
|
|
605
|
-
*
|
|
606
|
-
*
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
* broadcast at all, so a host that takes the question and never renders it
|
|
610
|
-
* would leave the turn hanging behind something invisible. Past the deadline
|
|
611
|
-
* the ask is WITHDRAWN (the signal handed to the host aborts, which settles
|
|
612
|
-
* its waterfall `cancelled` and drops the composer panel) and reported as
|
|
613
|
-
* `native-timeout`, sending the question to the ledger where the chat line
|
|
614
|
-
* and the settings card are both visible. Withdrawing is not answering: a
|
|
615
|
-
* human mid-decision finds the same question in the settings panel.
|
|
616
|
-
*
|
|
617
|
-
* No deadline is armed on the ledger paths (`no-requester`,
|
|
618
|
-
* `no-chat-session`): those never reach the host, and their answer entry is
|
|
619
|
-
* the settings panel the chat line already points at.
|
|
620
|
-
*
|
|
621
|
-
* @returns the agent-facing response, or the reason the ask must fall back
|
|
622
|
-
* to the ledger (see {@link LedgerFallbackReason}).
|
|
623
|
-
*/
|
|
624
|
-
private askNativeApproval;
|
|
534
|
+
* Announce and compute one ask's expiry. Shared by the 不等待 arrival path
|
|
535
|
+
* and the ledger deadline, so an expiry can never broadcast two notices or
|
|
536
|
+
* skip the `permission/expired` line: once the card is gone that frame is
|
|
537
|
+
* the conversation's only trace of what happened to the question.
|
|
538
|
+
*/
|
|
539
|
+
private announceExpiredAsk;
|
|
540
|
+
/**
|
|
541
|
+
* Settle the 不等待 (`auto` + zero window) ask on arrival: no ledger entry
|
|
542
|
+
* and no card — nothing asks a human, so there is nothing to show. `auto`
|
|
543
|
+
* answers with the option the agent's own kinds imply ({@link pickAutoOption})
|
|
544
|
+
* and the agent CONTINUES. A mode that cannot pick an option (no options, or
|
|
545
|
+
* none with a usable id) degrades to refusing: safer than guessing, and
|
|
546
|
+
* hanging is worse than either.
|
|
547
|
+
*/
|
|
548
|
+
private settleExpiredAsk;
|
|
625
549
|
private settlePermissions;
|
|
626
550
|
/** `fs/read_text_file` for agents that fetch workspace content through the
|
|
627
551
|
* client. Every path passes the same workspace guard as session cwds
|
|
@@ -638,7 +562,7 @@ export declare class AgentBridge {
|
|
|
638
562
|
* cannot reach into the agent's own process, so a second gate here would only
|
|
639
563
|
* be a fiction in front of a tool that can write without asking us at all.
|
|
640
564
|
* What the agent chooses to ask about goes to the human unchanged
|
|
641
|
-
* (`
|
|
565
|
+
* (`handlePermissionRequest`). See docs/acp-convergence.md §2.3.
|
|
642
566
|
*/
|
|
643
567
|
private handleFsWrite;
|
|
644
568
|
private guardFsPath;
|
|
@@ -659,6 +583,16 @@ export declare class AgentBridge {
|
|
|
659
583
|
* every step reads a key the agent itself declared.
|
|
660
584
|
*/
|
|
661
585
|
export declare function permissionToolName(toolCall: unknown): string;
|
|
586
|
+
/**
|
|
587
|
+
* One line saying WHAT the permission ask is about.
|
|
588
|
+
*
|
|
589
|
+
* WHY: without it the human only sees the tool's NAME ("Bash") and cannot
|
|
590
|
+
* judge the request; the ACP tool call already declares the missing half — its
|
|
591
|
+
* title and (for execute/edit tools) a short scalar in `rawInput`. Only ONE declared
|
|
592
|
+
* string is used, never a serialized object or a whole file, and the line is
|
|
593
|
+
* capped so a pathological input cannot fill the card or the audit log.
|
|
594
|
+
*/
|
|
595
|
+
export declare function permissionAskReason(toolCall: unknown): string;
|
|
662
596
|
/**
|
|
663
597
|
* This plugin's OWN state directory, `~/.dsh/dsh-ntr`.
|
|
664
598
|
*
|
|
@@ -916,7 +850,6 @@ type TurnSettle = {
|
|
|
916
850
|
declare class TurnStreamController {
|
|
917
851
|
private readonly manager;
|
|
918
852
|
private readonly hub;
|
|
919
|
-
private readonly bridge;
|
|
920
853
|
readonly session: LiveSession;
|
|
921
854
|
readonly turnId: string;
|
|
922
855
|
/** Releases the agent-side turn slot (the `beginTurn` settler; idempotent). */
|
|
@@ -932,7 +865,7 @@ declare class TurnStreamController {
|
|
|
932
865
|
private readonly pendingProse;
|
|
933
866
|
sawVisibleBlock: boolean;
|
|
934
867
|
private cursorSeq;
|
|
935
|
-
constructor(manager: BridgeManager, hub: EventHub,
|
|
868
|
+
constructor(manager: BridgeManager, hub: EventHub, session: LiveSession, turnId: string,
|
|
936
869
|
/** Releases the agent-side turn slot (the `beginTurn` settler; idempotent). */
|
|
937
870
|
endTurn: () => void, rollbackFirstTurn: (session: LiveSession) => void);
|
|
938
871
|
private readonly unsubscribe;
|
|
@@ -964,6 +897,15 @@ declare class TurnStreamController {
|
|
|
964
897
|
/** A segment opened on an already-settled turn: replay, then close it out. */
|
|
965
898
|
private finishAfterSettle;
|
|
966
899
|
private onEvent;
|
|
900
|
+
/**
|
|
901
|
+
* Put one narrative line into the turn's prose stream.
|
|
902
|
+
*
|
|
903
|
+
* A card-bearing segment must stay the segment's LAST block, so a line that
|
|
904
|
+
* arrives while a card is open is parked for the next segment instead of
|
|
905
|
+
* streaming below the card. A line that arrives between segments waits for
|
|
906
|
+
* the next one to open.
|
|
907
|
+
*/
|
|
908
|
+
private pushProse;
|
|
967
909
|
/** Events that arrive between segments: recorded, replayed into the next one. */
|
|
968
910
|
private recordWithoutSegment;
|
|
969
911
|
private noteUsage;
|
|
@@ -985,6 +927,15 @@ declare class TurnStreamController {
|
|
|
985
927
|
*/
|
|
986
928
|
private openToolCallBlock;
|
|
987
929
|
private permissionLine;
|
|
930
|
+
/**
|
|
931
|
+
* The line for an ask the plugin settled itself when its deadline fired.
|
|
932
|
+
*
|
|
933
|
+
* The composer card is disappearing, so the conversation has to say what
|
|
934
|
+
* happened to the question — otherwise the user is left with a vanished
|
|
935
|
+
* card and no explanation. The wording follows the outcome: refused, or
|
|
936
|
+
* answered with the option the agent's own kinds implied.
|
|
937
|
+
*/
|
|
938
|
+
private expiredLine;
|
|
988
939
|
}
|
|
989
940
|
/** Routes host session ids to their owning bridge. */
|
|
990
941
|
export declare class BridgeManager {
|
|
@@ -1001,17 +952,23 @@ export declare class BridgeManager {
|
|
|
1001
952
|
/** The agent tag's halo preference (persisted; see `persistAgents`). */
|
|
1002
953
|
private tagHaloSetting;
|
|
1003
954
|
/**
|
|
1004
|
-
* What `cordis.patch.yml` asked for
|
|
1005
|
-
*
|
|
955
|
+
* What `cordis.patch.yml` asked for — ONLY if it actually named the key.
|
|
956
|
+
* This is the deployment baseline, kept apart from
|
|
1006
957
|
* {@link deadlineOverride} so the settings card can tell the user which of
|
|
1007
|
-
* the two is winning. Empty when the file says nothing about
|
|
1008
|
-
*
|
|
958
|
+
* the two is winning. Empty when the file says nothing about the window, in
|
|
959
|
+
* which case {@link baselineDeadline} (the built-in default) applies.
|
|
1009
960
|
*/
|
|
1010
961
|
private configuredDeadlines;
|
|
1011
962
|
/**
|
|
1012
|
-
* The
|
|
1013
|
-
*
|
|
1014
|
-
*
|
|
963
|
+
* The mode the config file named, if it named one. Kept apart from the
|
|
964
|
+
* override for the same reason the window is: the card has to be able
|
|
965
|
+
* to say "the file asks for `abort`, the value set here wins".
|
|
966
|
+
*/
|
|
967
|
+
private configuredMode?;
|
|
968
|
+
/**
|
|
969
|
+
* The window before any override — the tuning this manager was constructed
|
|
970
|
+
* with, which already carries the validated config file (the host entry
|
|
971
|
+
* passes `validateConfig`'s output) or the built-in default.
|
|
1015
972
|
*
|
|
1016
973
|
* Held separately from {@link configuredDeadlines} because the two answer
|
|
1017
974
|
* different questions: this one is what RUNS when nothing is overridden, that
|
|
@@ -1019,17 +976,23 @@ export declare class BridgeManager {
|
|
|
1019
976
|
* and a bare default does not deserve). A manager built without tuning at all
|
|
1020
977
|
* — a direct consumer, a test — gets `DEFAULT_TUNING` here and stays correct.
|
|
1021
978
|
*/
|
|
1022
|
-
private
|
|
979
|
+
private baselineDeadline;
|
|
980
|
+
/**
|
|
981
|
+
* The mode before any override — the tuning this manager was constructed
|
|
982
|
+
* with, which already carries the validated config file (the host entry
|
|
983
|
+
* passes `validateConfig`'s output) or the built-in default.
|
|
984
|
+
*/
|
|
985
|
+
private baselineMode;
|
|
1023
986
|
/**
|
|
1024
987
|
* The settings page's own write, persisted with the agent registry. Merged
|
|
1025
|
-
* OVER {@link
|
|
988
|
+
* OVER {@link baselineDeadline}: once a user picks a window in the UI, that
|
|
1026
989
|
* pick is what runs, and the config file becomes the value the reset button
|
|
1027
990
|
* returns to. Without this split a hand-edited `cordis.patch.yml` and a UI
|
|
1028
991
|
* write would fight with no way to tell which one is live.
|
|
1029
992
|
*/
|
|
1030
993
|
private deadlineOverride;
|
|
1031
994
|
/**
|
|
1032
|
-
* Tuning in force, defaults applied. The
|
|
995
|
+
* Tuning in force, defaults applied. The approval deadline is rewritten
|
|
1033
996
|
* IN PLACE by {@link applyDeadlineTuning}, which is why this is a live object
|
|
1034
997
|
* rather than the constructor's parameter: every bridge built afterwards
|
|
1035
998
|
* inherits the rewrite instead of the boot-time value.
|
|
@@ -1058,20 +1021,10 @@ export declare class BridgeManager {
|
|
|
1058
1021
|
probeModelsOnBoot?: boolean, tuning?: BridgeTuning);
|
|
1059
1022
|
private makeBridge;
|
|
1060
1023
|
/**
|
|
1061
|
-
*
|
|
1062
|
-
*
|
|
1063
|
-
*
|
|
1064
|
-
*
|
|
1065
|
-
*/
|
|
1066
|
-
setApprovalRequester(requester?: ApprovalRequester): void;
|
|
1067
|
-
/** True when permission asks go to native approval instead of the settings panel. */
|
|
1068
|
-
approvalRouting(): boolean;
|
|
1069
|
-
/**
|
|
1070
|
-
* Every outstanding ledger ask, across all agents, with the view the browser
|
|
1071
|
-
* was shown it — the source of the reconnect replay (see
|
|
1072
|
-
* `permissionReplayFrames`). Asks on the native route are deliberately
|
|
1073
|
-
* absent: they broadcast nothing and their answer entry is the composer
|
|
1074
|
-
* takeover the client already holds.
|
|
1024
|
+
* Every outstanding ask, across all agents, with the view the browser was
|
|
1025
|
+
* shown it — the source of the reconnect replay (see
|
|
1026
|
+
* `permissionReplayFrames`). Every ACP ask is a ledger entry, so this is the
|
|
1027
|
+
* whole live set.
|
|
1075
1028
|
*/
|
|
1076
1029
|
pendingPermissionViews(): PendingPermissionView[];
|
|
1077
1030
|
/**
|
|
@@ -1598,7 +1551,7 @@ export declare class BridgeManager {
|
|
|
1598
1551
|
tagHalo(): AcpTagHalo;
|
|
1599
1552
|
setTagHalo(halo: AcpTagHalo): Promise<void>;
|
|
1600
1553
|
/**
|
|
1601
|
-
* Declare the deployment baseline for the
|
|
1554
|
+
* Declare the deployment baseline for the approval deadline — the values
|
|
1602
1555
|
* `cordis.patch.yml` names, passed RAW (only the keys it actually contains).
|
|
1603
1556
|
*
|
|
1604
1557
|
* Kept separate from the validated tuning because "the file says 600000" and
|
|
@@ -1608,19 +1561,21 @@ export declare class BridgeManager {
|
|
|
1608
1561
|
* also moves the baseline, so a manager built without tuning (a direct
|
|
1609
1562
|
* consumer, a test) still runs what the deployment asked for.
|
|
1610
1563
|
*/
|
|
1611
|
-
setConfiguredApprovalDeadlines(values: Partial<Record<ApprovalDeadlineKey, number
|
|
1564
|
+
setConfiguredApprovalDeadlines(values: Partial<Record<ApprovalDeadlineKey, number>>, mode?: PermissionTimeoutMode): void;
|
|
1612
1565
|
/**
|
|
1613
|
-
* The approval
|
|
1614
|
-
* user did not pick: the config-file baseline an override is outranking
|
|
1615
|
-
*
|
|
1616
|
-
*
|
|
1617
|
-
*
|
|
1566
|
+
* The approval deadline in force, plus the facts that explain a value the
|
|
1567
|
+
* user did not pick: the config-file baseline an override is outranking. The
|
|
1568
|
+
* mode rides along with its own baseline, for the same reason: the card has
|
|
1569
|
+
* to be able to say "the file asks for `abort`, the value set here wins".
|
|
1570
|
+
*
|
|
1571
|
+
* Everything is reported only when it is actually in play, so the card can
|
|
1572
|
+
* stay silent about a plain default.
|
|
1618
1573
|
*/
|
|
1619
1574
|
approvalDeadlines(): AcpApprovalDeadlines;
|
|
1620
1575
|
/**
|
|
1621
|
-
* Write
|
|
1622
|
-
* drop the persisted override with `reset` so the config-file baseline —
|
|
1623
|
-
* the built-in default — applies again.
|
|
1576
|
+
* Write the approval deadline, or the mode (omitted keys keep their value),
|
|
1577
|
+
* or drop the persisted override with `reset` so the config-file baseline —
|
|
1578
|
+
* or the built-in default — applies again.
|
|
1624
1579
|
*
|
|
1625
1580
|
* HOT: the rewrite reaches every live bridge, so a user changing the window
|
|
1626
1581
|
* never has to kill a running session to see it take effect (the one thing an
|
|
@@ -1628,10 +1583,10 @@ export declare class BridgeManager {
|
|
|
1628
1583
|
* write see the new window; an ask already holding a timer keeps the one it
|
|
1629
1584
|
* started with.
|
|
1630
1585
|
*/
|
|
1631
|
-
setApprovalDeadlines(patch:
|
|
1586
|
+
setApprovalDeadlines(patch: ApprovalDeadlinePatch, reset?: boolean): Promise<void>;
|
|
1632
1587
|
/**
|
|
1633
|
-
* Recompute the
|
|
1634
|
-
* the result to every live bridge.
|
|
1588
|
+
* Recompute the deadline and the mode from override → deployment baseline,
|
|
1589
|
+
* and push the result to every live bridge.
|
|
1635
1590
|
*/
|
|
1636
1591
|
private applyDeadlineTuning;
|
|
1637
1592
|
private readonly dshSessionMap;
|
|
@@ -1645,11 +1600,6 @@ export declare class BridgeManager {
|
|
|
1645
1600
|
private readonly chatAgentIntent;
|
|
1646
1601
|
/** Drop every chat-keyed record pointing at one host session (close/stop paths). */
|
|
1647
1602
|
private forgetDshSession;
|
|
1648
|
-
/**
|
|
1649
|
-
* Latest native-approval requester. Held as a field (read through a thunk by
|
|
1650
|
-
* each bridge) so a requester wired after bridges were built still applies.
|
|
1651
|
-
*/
|
|
1652
|
-
private approvalRequester?;
|
|
1653
1603
|
private sessionWorkspaceResolver?;
|
|
1654
1604
|
private acpModeResolver?;
|
|
1655
1605
|
/** Cached `chatSessionAcpMode` answers, keyed by DSH chat session id. */
|