dsh-ntr 0.3.1 → 0.3.3
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 +3 -1
- package/README.md +3 -1
- package/cordis.patch.yml +17 -1
- package/lib/bridge.js +2436 -82
- package/lib/client/acp-ui.js +103 -3
- package/lib/client/acp-view.js +24 -4
- package/lib/client/activity-pill.js +156 -0
- package/lib/client/branch-action.js +149 -0
- package/lib/client/composer-mode.js +48 -13
- package/lib/client/context-meter.js +86 -0
- package/lib/client/index.js +279 -16
- package/lib/client/model-seat.js +8 -2
- package/lib/client/panel.js +96 -29
- package/lib/client/released-banner.js +66 -0
- package/lib/client/session-controls.js +38 -13
- package/lib/client/slash-isolation.js +2 -2
- package/lib/client/store.js +380 -13
- package/lib/client.js +1364 -70
- package/lib/client.js.map +1 -1
- package/lib/impl.js +1229 -8
- package/lib/index.js +34 -0
- package/lib/protocol.js +19 -0
- package/lib/tps.js +32 -0
- package/lib/types/bridge.d.ts +850 -18
- package/lib/types/client/acp-ui.d.ts +34 -2
- package/lib/types/client/acp-view.d.ts +13 -2
- package/lib/types/client/activity-pill.d.ts +81 -0
- package/lib/types/client/branch-action.d.ts +47 -0
- package/lib/types/client/composer-mode.d.ts +28 -0
- package/lib/types/client/context-meter.d.ts +51 -0
- package/lib/types/client/model-seat.d.ts +21 -0
- package/lib/types/client/released-banner.d.ts +34 -0
- package/lib/types/client/slash-isolation.d.ts +1 -1
- package/lib/types/client/store.d.ts +108 -1
- package/lib/types/impl.d.ts +94 -7
- package/lib/types/protocol.d.ts +145 -0
- package/lib/types/tps.d.ts +17 -0
- package/package.json +2 -1
- package/src/bridge.ts +2754 -86
- package/src/client/acp-ui.tsx +123 -2
- package/src/client/acp-view.tsx +30 -5
- package/src/client/activity-pill.tsx +201 -0
- package/src/client/branch-action.tsx +178 -0
- package/src/client/composer-mode.tsx +59 -13
- package/src/client/context-meter.tsx +128 -0
- package/src/client/index.tsx +298 -15
- package/src/client/model-seat.tsx +28 -3
- package/src/client/panel.tsx +157 -40
- package/src/client/primitives.d.ts +27 -0
- package/src/client/released-banner.tsx +97 -0
- package/src/client/session-controls.tsx +34 -11
- package/src/client/slash-isolation.ts +2 -1
- package/src/client/store.ts +412 -14
- package/src/impl.ts +1251 -13
- package/src/index.ts +33 -0
- package/src/protocol.ts +145 -0
- package/src/tps.ts +40 -0
package/lib/types/bridge.d.ts
CHANGED
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
* Connection RPC channel (authenticated) into `BridgeManager`, which is the
|
|
7
7
|
* only object allowed to spawn a configured command.
|
|
8
8
|
*/
|
|
9
|
-
import { type InitializeResponse } from '@agentclientprotocol/sdk';
|
|
9
|
+
import { type ContentBlock, 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, AcpReleaseCause, AcpTagHalo, AgentCatalogResult, AgentProbeModelsResult, AgentTestResult, AgentView, ChatSessionView, ChatTranscriptView, DetectedAgentInfo, GenerateOptions, LlmAdapterLike, LlmModelInfo, LlmProviderInfo, LlmResolvedModelInfo, ApprovalDeadlineKey, ApprovalDeadlinePatch, PermissionTimeoutMode, PluginUpdateState, PreparedAdapterCall, SessionView, StreamChunk } from './protocol.ts';
|
|
12
|
+
import type { AcpAgentCatalog, AcpAgentDef, AcpApprovalDeadlines, AcpConversationChoice, AcpPresetStatus, AcpReleaseCause, AcpTagHalo, AgentCatalogResult, AgentProbeModelsResult, AgentTestResult, AgentView, ChatSessionView, ChatTranscriptView, DetectedAgentInfo, GenerateOptions, LlmAdapterLike, LlmModelInfo, LlmProviderInfo, LlmResolvedModelInfo, AcpIdleRelease, ApprovalDeadlineKey, ApprovalDeadlinePatch, PermissionTimeoutMode, PluginUpdateState, PreparedAdapterCall, SessionView, StreamChunk } from './protocol.ts';
|
|
13
13
|
import { UpdateService } from './update.ts';
|
|
14
14
|
/**
|
|
15
15
|
* Version of this installed package, read from the shipped `package.json` —
|
|
@@ -41,10 +41,42 @@ interface LiveSession {
|
|
|
41
41
|
availableCommands?: SessionView['availableCommands'];
|
|
42
42
|
plan?: unknown[];
|
|
43
43
|
usage?: SessionView['usage'];
|
|
44
|
+
/**
|
|
45
|
+
* The plugin's own measurable burst aggregate over this ACP session's turns
|
|
46
|
+
* (see `src/tps.ts`). Persisted with the chat-session record so the dock's
|
|
47
|
+
* rate survives a host restart; absent until a turn contributes a measurable
|
|
48
|
+
* burst (>= 1 inter-chunk gap).
|
|
49
|
+
*/
|
|
50
|
+
burst?: SessionView['burst'];
|
|
44
51
|
running: boolean;
|
|
52
|
+
/**
|
|
53
|
+
* The HOST's prompt slot for this session: true from the moment a turn
|
|
54
|
+
* starts until the agent answers it (or the host aborts the turn, or the
|
|
55
|
+
* turn is discarded). Together with the {@link settled} gate — which STAYS
|
|
56
|
+
* claimed after an abort until the agent acknowledges the cancel — this is
|
|
57
|
+
* what `claimTurn` arbitrates, NOT `running`.
|
|
58
|
+
*
|
|
59
|
+
* The three are deliberately different:
|
|
60
|
+
* - an ambient wait keeps `running` true (the background drain is still the
|
|
61
|
+
* session's work, so nothing may release the process) while this slot is
|
|
62
|
+
* already FREE (the agent answered; only the drain keeps talking);
|
|
63
|
+
* - an aborted turn drops THIS flag at once (our face of the turn ends) but
|
|
64
|
+
* leaves `settled` in place, so the next prompt still waits out
|
|
65
|
+
* `turnSettleMs` for the agent instead of racing a concurrent prompt.
|
|
66
|
+
*/
|
|
67
|
+
promptInFlight?: boolean;
|
|
45
68
|
closed: boolean;
|
|
46
69
|
/** The agent process died; the session awaits a `session/load` resume. */
|
|
47
70
|
released: boolean;
|
|
71
|
+
/**
|
|
72
|
+
* The agent-side session has history while the LOCAL buffer is empty: this
|
|
73
|
+
* record was restored from disk after a host restart (the replay has not
|
|
74
|
+
* landed yet), or a resume cleared the buffer ahead of the replay. The ACP
|
|
75
|
+
* view then says history replays on restore instead of "nothing yet". Set
|
|
76
|
+
* by `doResumeSession` (captured before the reset) and the restore path; the
|
|
77
|
+
* serializer ORs it with the live buffer length.
|
|
78
|
+
*/
|
|
79
|
+
hasHistory?: boolean;
|
|
48
80
|
/**
|
|
49
81
|
* When the agent last showed ANY sign of life on this session: an update, a
|
|
50
82
|
* permission ask (arrival or answer), or a turn start. The stall watchdog
|
|
@@ -142,8 +174,70 @@ interface LiveSession {
|
|
|
142
174
|
* the controller's own `retire` when it still points at it.
|
|
143
175
|
*/
|
|
144
176
|
turnController?: TurnStreamController;
|
|
177
|
+
/**
|
|
178
|
+
* A background turn the agent started on its OWN initiative (CodeBuddy's
|
|
179
|
+
* multitask drains: a sub-agent completion notice starts a run the client
|
|
180
|
+
* never asked for). Its first `session_info_update` announces the request id
|
|
181
|
+
* every later update of that run carries in `_meta`; a `session/prompt`
|
|
182
|
+
* answered instantly with no conversation request id was folded into this
|
|
183
|
+
* drain and its real answer arrives as the drain's updates.
|
|
184
|
+
*
|
|
185
|
+
* Only lifetime events clear it (close / release / resume / stop): a drain
|
|
186
|
+
* may outlive the turn that was steered into it, and clearing on a normal
|
|
187
|
+
* settle would misclassify the NEXT steered prompt.
|
|
188
|
+
*/
|
|
189
|
+
ambient?: AmbientTurn;
|
|
190
|
+
/**
|
|
191
|
+
* Request ids whose `agent/notice` already went out, so a repeated
|
|
192
|
+
* announcement of the same background run is not logged twice.
|
|
193
|
+
*/
|
|
194
|
+
ambientNotices?: Set<string>;
|
|
195
|
+
/**
|
|
196
|
+
* Background drains this session already STOPPED waiting on (an ambient wait
|
|
197
|
+
* that closed on silence, or a record that outlived `ambientStaleMs`). The
|
|
198
|
+
* record is gone from {@link ambient}, but the drain itself may still be
|
|
199
|
+
* running: a queued message or a long tool makes it speak again while the
|
|
200
|
+
* NEXT turn is on air. Without this list the late text would teach that turn
|
|
201
|
+
* the drain's id (`noteTurnRequestId` / `adoptTurnRequestId`) and fold the
|
|
202
|
+
* background run's answer into the user's answer (a double answer).
|
|
203
|
+
*
|
|
204
|
+
* An update or re-announcement carrying a listed id REVIVES it
|
|
205
|
+
* (`reviveAmbientId` + a rebuilt {@link ambient}): the run is alive, so its
|
|
206
|
+
* next steered prompt must receive the drain's text. Only while the record
|
|
207
|
+
* stays retired is its text transcript-only. Timestamps let the zero-output
|
|
208
|
+
* report recognise a prompt folded into a recently quieted drain
|
|
209
|
+
* (`hasRecentRetiredAmbient`). Bounded ({@link RETIRED_AMBIENT_MAX}), oldest
|
|
210
|
+
* evicted first; every lifecycle boundary clears the list
|
|
211
|
+
* (`clearAmbientState`).
|
|
212
|
+
*/
|
|
213
|
+
retiredAmbients?: RetiredAmbient[];
|
|
145
214
|
transcript: Transcript;
|
|
146
215
|
}
|
|
216
|
+
/** One unsolicited (background) turn the agent is running, as announced in `_meta`. */
|
|
217
|
+
interface AmbientTurn {
|
|
218
|
+
/** `codebuddy.ai/requestId` of the drain; every update of the run carries it. */
|
|
219
|
+
requestId: string;
|
|
220
|
+
startedAt: number;
|
|
221
|
+
/**
|
|
222
|
+
* When this drain last showed any sign of life (announcement or an update
|
|
223
|
+
* carrying its request id). {@link BridgeTuning.ambientStaleMs} expires the
|
|
224
|
+
* record from here, so a drain that died silently does not capture a later
|
|
225
|
+
* normal turn as steered.
|
|
226
|
+
*/
|
|
227
|
+
lastActivityAt: number;
|
|
228
|
+
/** Whether the drain already streamed prose (text or thought) into the transcript. */
|
|
229
|
+
sawOutput: boolean;
|
|
230
|
+
}
|
|
231
|
+
/** A drain id the session stopped waiting on, and when it was retired. */
|
|
232
|
+
interface RetiredAmbient {
|
|
233
|
+
requestId: string;
|
|
234
|
+
/**
|
|
235
|
+
* When the wait was retired. `hasRecentRetiredAmbient` uses it to tell a
|
|
236
|
+
* prompt folded into a just-quieted drain from one that ended on its own;
|
|
237
|
+
* `adoptTurnRequestId` / `noteTurnRequestId` ignore the id at any age.
|
|
238
|
+
*/
|
|
239
|
+
retiredAt: number;
|
|
240
|
+
}
|
|
147
241
|
/** Reliability tuning, threaded from `AcpPluginConfig` through the manager. */
|
|
148
242
|
export interface BridgeTuning {
|
|
149
243
|
startupTimeoutMs?: number;
|
|
@@ -238,6 +332,65 @@ export interface BridgeTuning {
|
|
|
238
332
|
* probe-only process has no stake in. `0` disables it.
|
|
239
333
|
*/
|
|
240
334
|
probeIdleTimeoutMs?: number;
|
|
335
|
+
/**
|
|
336
|
+
* How long an ambient wait (see `TurnStreamController.awaitAmbient`) stays
|
|
337
|
+
* open after the LAST session update before the turn closes itself.
|
|
338
|
+
*
|
|
339
|
+
* A drain's end is its silence — the SDK's schema rejects the `session_end`
|
|
340
|
+
* CodeBuddy never sends anyway — but "silence" must be measured in the
|
|
341
|
+
* agent's own cadence, not in a chat-app-paced one. Measured against
|
|
342
|
+
* CodeBuddy 2.179.0: the gaps between drain ticks (the
|
|
343
|
+
* `model_requesting`→`model_streaming` hand-off of a real model call) run
|
|
344
|
+
* 1.0–1.4s, so any window below that closes the turn BEFORE the answer
|
|
345
|
+
* exists. 10s leaves room for two slow hand-offs without holding the turn
|
|
346
|
+
* open for a drain that actually finished.
|
|
347
|
+
*
|
|
348
|
+
* Deliberately NOT keyed on an `agentPhase` idle flag: a drain goes idle
|
|
349
|
+
* between queued messages too, so that signal closes the turn in the middle
|
|
350
|
+
* of its own work.
|
|
351
|
+
*/
|
|
352
|
+
ambientQuietMs?: number;
|
|
353
|
+
/**
|
|
354
|
+
* The hard ceiling on ONE ambient wait: a background run that keeps ticking
|
|
355
|
+
* for minutes without finishing must not hold the conversation's turn open
|
|
356
|
+
* forever. At the ceiling the turn says where the rest of the output will
|
|
357
|
+
* land and closes.
|
|
358
|
+
*/
|
|
359
|
+
ambientMaxMs?: number;
|
|
360
|
+
/**
|
|
361
|
+
* The hard ceiling on the WORKER phase of a detached-worker hold (see
|
|
362
|
+
* `TurnStreamController.holdForBackgroundDispatch`): the turn dispatched one
|
|
363
|
+
* or more background workers through `Agent` tool calls, the agent answered
|
|
364
|
+
* the prompt, and at least one worker has not reported back yet. The worker
|
|
365
|
+
* phase has NO quiet window on purpose — measured against CodeBuddy 2.179.0,
|
|
366
|
+
* a detached worker's own updates are 13–24s apart while it is perfectly
|
|
367
|
+
* alive — so only EVERY known dispatch reporting (completed / failed) or
|
|
368
|
+
* this ceiling can end it; a drain announcement is recorded but arms no
|
|
369
|
+
* clock before then. Once they have all reported, the drain's own
|
|
370
|
+
* `ambientQuietMs` / `ambientMaxMs` windows take over.
|
|
371
|
+
*
|
|
372
|
+
* Default 600000 ms (10 minutes).
|
|
373
|
+
*/
|
|
374
|
+
backgroundTaskMaxMs?: number;
|
|
375
|
+
/**
|
|
376
|
+
* How long a PARKED detached-worker hold may wait for the host to open the
|
|
377
|
+
* driver step that carries the worker's report. The prompt response landed
|
|
378
|
+
* between two steps (the host is about to execute the `Agent` card), and a
|
|
379
|
+
* host that never opens the continuation step must not hang the turn. The
|
|
380
|
+
* host normally opens it at once, so this is a safety bound, not a cadence —
|
|
381
|
+
* deliberately far larger than one model hand-off.
|
|
382
|
+
*
|
|
383
|
+
* Default 30000 ms (30 seconds).
|
|
384
|
+
*/
|
|
385
|
+
backgroundParkedGraceMs?: number;
|
|
386
|
+
/**
|
|
387
|
+
* How long an announced background drain may go without ANY update before a
|
|
388
|
+
* later instant prompt stops treating itself as folded into it. A drain that
|
|
389
|
+
* went silent for minutes is over even though nothing told us; keeping the
|
|
390
|
+
* record would misclassify the next normal turn as steered. Checked lazily
|
|
391
|
+
* when a prompt response arrives (see `streamPrompt`), never on a timer.
|
|
392
|
+
*/
|
|
393
|
+
ambientStaleMs?: number;
|
|
241
394
|
}
|
|
242
395
|
/**
|
|
243
396
|
* Resolves the workspace root of one chat session (`dshSessionId` = DSH session
|
|
@@ -279,6 +432,20 @@ export declare class AgentBridge {
|
|
|
279
432
|
* this bridge was built.
|
|
280
433
|
*/
|
|
281
434
|
private readonly sessionWorkspace?;
|
|
435
|
+
/**
|
|
436
|
+
* Told whenever the SESSION RECORDS change shape (a release parks them, a
|
|
437
|
+
* resume revives them): the manager persists `chatSessions` through it, so
|
|
438
|
+
* a parked conversation stays resumable across a host restart (see
|
|
439
|
+
* `BridgeManager.loadPersistedChatSessions`).
|
|
440
|
+
*/
|
|
441
|
+
private readonly onSessionsDirty?;
|
|
442
|
+
/**
|
|
443
|
+
* Told whenever a fresh `initialize` reports the agent's
|
|
444
|
+
* `sessionCapabilities.fork` bit — the LAST KNOWN fork capability the
|
|
445
|
+
* manager persists and serves to the browser (the bridge's own
|
|
446
|
+
* `initResult` dies with the process; the manager's copy survives it).
|
|
447
|
+
*/
|
|
448
|
+
private readonly onForkCapability?;
|
|
282
449
|
state: BridgeState;
|
|
283
450
|
initResult?: InitializeResponse;
|
|
284
451
|
lastError?: string;
|
|
@@ -309,7 +476,7 @@ export declare class AgentBridge {
|
|
|
309
476
|
private readonly staleCancelRetryDelayMs;
|
|
310
477
|
private readonly staleCancelMaxRetries;
|
|
311
478
|
private readonly turnSettleMs;
|
|
312
|
-
private
|
|
479
|
+
private defaultIdleTimeoutMs;
|
|
313
480
|
private readonly probeIdleTimeoutMs;
|
|
314
481
|
/** Armed while READY; fires the idle-release check. */
|
|
315
482
|
private idleTimer?;
|
|
@@ -330,6 +497,13 @@ export declare class AgentBridge {
|
|
|
330
497
|
* that a caller is waiting on the child it is about to kill.
|
|
331
498
|
*/
|
|
332
499
|
private pendingCreates;
|
|
500
|
+
/**
|
|
501
|
+
* `session/fork` requests in flight (the adopt path of a branched
|
|
502
|
+
* conversation). Same teardown hazard as {@link pendingCreates}: until the
|
|
503
|
+
* forked session is registered, nothing else would stop an idle release
|
|
504
|
+
* from killing the child out from under the caller.
|
|
505
|
+
*/
|
|
506
|
+
private pendingForks;
|
|
333
507
|
/**
|
|
334
508
|
* Whether a REAL session has ever been registered on this bridge. Probes
|
|
335
509
|
* bypass session bookkeeping, so a bridge that has never hosted anything was
|
|
@@ -366,7 +540,21 @@ export declare class AgentBridge {
|
|
|
366
540
|
* Resolved per turn: the host entry may wire the workspace reader after
|
|
367
541
|
* this bridge was built.
|
|
368
542
|
*/
|
|
369
|
-
sessionWorkspace?: (() => SessionWorkspaceResolver | undefined) | undefined
|
|
543
|
+
sessionWorkspace?: (() => SessionWorkspaceResolver | undefined) | undefined,
|
|
544
|
+
/**
|
|
545
|
+
* Told whenever the SESSION RECORDS change shape (a release parks them, a
|
|
546
|
+
* resume revives them): the manager persists `chatSessions` through it, so
|
|
547
|
+
* a parked conversation stays resumable across a host restart (see
|
|
548
|
+
* `BridgeManager.loadPersistedChatSessions`).
|
|
549
|
+
*/
|
|
550
|
+
onSessionsDirty?: (() => void) | undefined,
|
|
551
|
+
/**
|
|
552
|
+
* Told whenever a fresh `initialize` reports the agent's
|
|
553
|
+
* `sessionCapabilities.fork` bit — the LAST KNOWN fork capability the
|
|
554
|
+
* manager persists and serves to the browser (the bridge's own
|
|
555
|
+
* `initResult` dies with the process; the manager's copy survives it).
|
|
556
|
+
*/
|
|
557
|
+
onForkCapability?: ((agentId: string, supported: boolean) => void) | undefined);
|
|
370
558
|
/**
|
|
371
559
|
* Adopt a rewritten approval deadline or mode (the settings page's write).
|
|
372
560
|
* Only the window and the mode live here; every other tuning value is
|
|
@@ -381,6 +569,18 @@ export declare class AgentBridge {
|
|
|
381
569
|
permissionTimeoutMs?: number;
|
|
382
570
|
mode?: PermissionTimeoutMode;
|
|
383
571
|
}): void;
|
|
572
|
+
/**
|
|
573
|
+
* Adopt a rewritten host-default idle window (the settings page's write).
|
|
574
|
+
*
|
|
575
|
+
* Only the DEFAULT moves: an agent that names its own `idleTimeoutMs` keeps
|
|
576
|
+
* it (that promise is about the agent, not about the deployment), and a
|
|
577
|
+
* probe-only child keeps the short probe window.
|
|
578
|
+
*
|
|
579
|
+
* The timer is re-armed so the new window governs from NOW: an already
|
|
580
|
+
* armed timer still holds the old window, so a shortened window would not
|
|
581
|
+
* take effect until the old one elapsed.
|
|
582
|
+
*/
|
|
583
|
+
applyIdleRelease(defaultIdleTimeoutMs: number): void;
|
|
384
584
|
/**
|
|
385
585
|
* The idle-release window this agent honors for its CHAT process: its own
|
|
386
586
|
* field, else the host default. This is the number the panel prefills and
|
|
@@ -446,6 +646,11 @@ export declare class AgentBridge {
|
|
|
446
646
|
* same session — forbidden by the protocol and rejected (or worse, mishandled)
|
|
447
647
|
* by the agent. Waiting turns a protocol error into a short delay.
|
|
448
648
|
*
|
|
649
|
+
* The gate is {@link LiveSession.promptInFlight} + the settle promise, NOT
|
|
650
|
+
* {@link LiveSession.running}: an ambient turn keeps `running` true for the
|
|
651
|
+
* whole drain while its prompt slot is already free, so `running` would
|
|
652
|
+
* refuse every message the user sends during a background task.
|
|
653
|
+
*
|
|
449
654
|
* @param session - the session asking for the slot.
|
|
450
655
|
* @returns whether the slot was claimed, and why not when it was not.
|
|
451
656
|
*/
|
|
@@ -455,9 +660,12 @@ export declare class AgentBridge {
|
|
|
455
660
|
ok: false;
|
|
456
661
|
reason: 'busy' | 'unsettled';
|
|
457
662
|
}>;
|
|
663
|
+
/** Whether a turn currently holds the agent prompt slot (see `promptInFlight`). */
|
|
664
|
+
private promptSlotHeld;
|
|
458
665
|
/**
|
|
459
|
-
* Mark the agent-side turn as in flight
|
|
460
|
-
*
|
|
666
|
+
* Mark the agent-side turn as in flight — claim the agent prompt slot and
|
|
667
|
+
* open the settle gate — and hand back the settler its stream owner must
|
|
668
|
+
* call once the agent is actually done with it.
|
|
461
669
|
*
|
|
462
670
|
* @param session - the session whose turn is starting.
|
|
463
671
|
* @returns the settler (idempotent; also used by connection loss).
|
|
@@ -466,7 +674,8 @@ export declare class AgentBridge {
|
|
|
466
674
|
/**
|
|
467
675
|
* Open a session's settle gate without owning its turn stream: the turn's
|
|
468
676
|
* stream is gone (a resume, a release) but the next prompt still waits on
|
|
469
|
-
* this promise, and nothing is left that could ever resolve it.
|
|
677
|
+
* this promise, and nothing is left that could ever resolve it. The prompt
|
|
678
|
+
* slot itself goes with it: whatever held it is gone too.
|
|
470
679
|
*/
|
|
471
680
|
private openSettleGate;
|
|
472
681
|
view(): AgentView;
|
|
@@ -559,6 +768,25 @@ export declare class AgentBridge {
|
|
|
559
768
|
* only shape that keeps every session of that process consistent.
|
|
560
769
|
*/
|
|
561
770
|
probeConfigOptions(): Promise<unknown[]>;
|
|
771
|
+
/**
|
|
772
|
+
* Fork one live session through the agent's own `session/fork` (the
|
|
773
|
+
* UNSTABLE fourth session-creation point): the agent copies its internal
|
|
774
|
+
* state into a NEW ACP session that remembers the parent's context — the
|
|
775
|
+
* second half of a branched conversation, after the host's `sessions.fork`
|
|
776
|
+
* has already built the DSH session tree. The child is addressed by the
|
|
777
|
+
* CHILD DSH conversation id, so its first message reaches an agent that
|
|
778
|
+
* actually remembers the parent.
|
|
779
|
+
*
|
|
780
|
+
* The parent must be live on this connection — a released parent has no
|
|
781
|
+
* process to fork from (the browser blocks that click; this is the hard
|
|
782
|
+
* stop behind it). The ACP request carries the SAME `mcpServers` list as
|
|
783
|
+
* `session/new` / `session/load` (one list per process; an empty list would
|
|
784
|
+
* evict the live sessions' MCP scope) and is bounded by the startup timeout
|
|
785
|
+
* like every session-creation step. Counted in {@link pendingForks} while
|
|
786
|
+
* in flight so an idle release cannot kill the child out from under the
|
|
787
|
+
* caller.
|
|
788
|
+
*/
|
|
789
|
+
forkSession(parent: LiveSession, dshSessionId: string): Promise<LiveSession>;
|
|
562
790
|
/**
|
|
563
791
|
* Fire one prompt turn. Returns after the agent settles the turn; the caller
|
|
564
792
|
* (manager) keeps the UI informed through broadcast events, so the RPC that
|
|
@@ -571,9 +799,25 @@ export declare class AgentBridge {
|
|
|
571
799
|
* {@link BridgeTuning.staleCancelRetryDelayMs}, out-waiting the cleanup
|
|
572
800
|
* window, bounded by {@link BridgeTuning.staleCancelMaxRetries}; a genuinely
|
|
573
801
|
* requested cancel (`cancelRequestedAtTurn` still names this generation)
|
|
574
|
-
* returns as-is
|
|
575
|
-
|
|
576
|
-
|
|
802
|
+
* returns as-is — checked BOTH before the delay and after it, so a second
|
|
803
|
+
* stop click inside the retry window does not resend the prompt the user
|
|
804
|
+
* just stopped.
|
|
805
|
+
*
|
|
806
|
+
* `turnGen` is the {@link LiveSession.turnSeq} this prompt was sent for. A
|
|
807
|
+
* stale-cancel resend is only allowed while that generation still owns the
|
|
808
|
+
* session: once a successor turn began (the cancel fallback finalized this
|
|
809
|
+
* one and the user moved on), resending would race a second `session/prompt`
|
|
810
|
+
* into the successor's turn. The late answer is returned as-is instead.
|
|
811
|
+
*
|
|
812
|
+
* The RESPONSE's `_meta` travels with the stop reason: it is the only place
|
|
813
|
+
* an agent says whether the prompt actually queued a conversation turn (see
|
|
814
|
+
* `CodebuddyMeta.conversationRequestId`), which decides whether an instant
|
|
815
|
+
* answer was the turn ending or a fold into a background drain.
|
|
816
|
+
*/
|
|
817
|
+
runPrompt(session: LiveSession, prompt: string | ContentBlock[], turnId: string, turnGen: number): Promise<{
|
|
818
|
+
stopReason: string;
|
|
819
|
+
meta?: Record<string, unknown>;
|
|
820
|
+
}>;
|
|
577
821
|
cancel(session: LiveSession): Promise<void>;
|
|
578
822
|
/**
|
|
579
823
|
* An agent that ignores the `session/cancel` notification must not wedge the
|
|
@@ -591,10 +835,19 @@ export declare class AgentBridge {
|
|
|
591
835
|
private armCancelFallback;
|
|
592
836
|
/**
|
|
593
837
|
* Force the local end of one turn whose agent never acknowledged it: clear
|
|
594
|
-
* `running`, open the settle gate, and
|
|
595
|
-
* ''` (there is no agent response to attribute it to). Shared by the cancel
|
|
838
|
+
* `running`, open the settle gate, and end the turn. Shared by the cancel
|
|
596
839
|
* fallback and the stall watchdog.
|
|
597
840
|
*
|
|
841
|
+
* A live controller owns the turn's streaming face — its segment queue, its
|
|
842
|
+
* ambient clocks and its `turn/end` id. Ending the turn behind its back
|
|
843
|
+
* would leave the driver step parked on an open queue, and the controller's
|
|
844
|
+
* own timers would later broadcast a SECOND `turn/end` for a turn already
|
|
845
|
+
* closed. So when the session still points at a current controller, the
|
|
846
|
+
* force goes THROUGH it (`forceFinalize`), which runs the same closing work
|
|
847
|
+
* as a normal settle exactly once. Only a turn with no live controller (an
|
|
848
|
+
* `/acp`-RPC turn, or one already retired) falls back to the local broadcast
|
|
849
|
+
* with `turnId: ''` — there is no stream whose id could be named.
|
|
850
|
+
*
|
|
598
851
|
* Generation-guarded: a turn that already ended — or that a later turn
|
|
599
852
|
* superseded — is not this call's to finalize. Returns whether it acted; a
|
|
600
853
|
* `false` means the caller was late and must leave the session alone.
|
|
@@ -617,6 +870,12 @@ export declare class AgentBridge {
|
|
|
617
870
|
takeReleased(sessionId: string): LiveSession | undefined;
|
|
618
871
|
/** Drop a released session record for good (user closed it or the agent was stopped). */
|
|
619
872
|
dropReleased(sessionId: string): boolean;
|
|
873
|
+
/**
|
|
874
|
+
* Register a session record restored from disk as already parked: a host
|
|
875
|
+
* restart killed every process, so it comes back exactly as `releaseAll`
|
|
876
|
+
* would have left it — resumable via `session/load`, never live.
|
|
877
|
+
*/
|
|
878
|
+
parkReleased(session: LiveSession): void;
|
|
620
879
|
get releasedList(): LiveSession[];
|
|
621
880
|
hasReleased(sessionId: string): LiveSession | undefined;
|
|
622
881
|
inflightResume(sessionId: string): Promise<LiveSession> | undefined;
|
|
@@ -941,6 +1200,14 @@ interface SegmentState {
|
|
|
941
1200
|
activeToolCall: AcpToolCallSnapshot | undefined;
|
|
942
1201
|
/** Whether this segment opened at least one tool-call block. */
|
|
943
1202
|
hasCard: boolean;
|
|
1203
|
+
/**
|
|
1204
|
+
* The segment belongs to an ambient wait: the agent folded our prompt into a
|
|
1205
|
+
* background drain it started itself. Tool traffic is recorded (so the
|
|
1206
|
+
* transcript and any parked tool-output waiter get it) but opens NO card —
|
|
1207
|
+
* a card would make the host loop execute a tool for a turn whose answer has
|
|
1208
|
+
* not even started streaming yet.
|
|
1209
|
+
*/
|
|
1210
|
+
ambient: boolean;
|
|
944
1211
|
finished: boolean;
|
|
945
1212
|
}
|
|
946
1213
|
/** How one segment's stream ends, in `StreamChunk.finish` terms. */
|
|
@@ -990,13 +1257,111 @@ declare class TurnStreamController {
|
|
|
990
1257
|
private readonly rollbackFirstTurn;
|
|
991
1258
|
private segment;
|
|
992
1259
|
private settled;
|
|
1260
|
+
/** Whether the turn's closing work already ran (see `closeTurn`). */
|
|
1261
|
+
private closedTurn;
|
|
993
1262
|
private disposed;
|
|
994
1263
|
private readonly seenCalls;
|
|
995
1264
|
private readonly announcedKinds;
|
|
996
1265
|
/** Narrative lines (permission asks) that arrived while no segment was open. */
|
|
997
1266
|
private readonly pendingProse;
|
|
998
1267
|
sawVisibleBlock: boolean;
|
|
1268
|
+
/**
|
|
1269
|
+
* Whether THIS turn streamed its own prose (text or reasoning), i.e. an
|
|
1270
|
+
* answer that is not the background drain's. Tool cards do not count — a
|
|
1271
|
+
* drain that only ran tools delivered no words to the conversation.
|
|
1272
|
+
*/
|
|
1273
|
+
private sawAgentProse;
|
|
1274
|
+
/**
|
|
1275
|
+
* Whether any `agent_message_chunk` text reached the plane. Reasoning does
|
|
1276
|
+
* not count: a steer whose drain only thought is still a turn with no answer
|
|
1277
|
+
* text, and must say so when it closes.
|
|
1278
|
+
*/
|
|
1279
|
+
private sawVisibleText;
|
|
1280
|
+
/**
|
|
1281
|
+
* The CodeBuddy request id this turn has adopted as its own (from the
|
|
1282
|
+
* prompt RESPONSE's `conversationRequestId`, or from the first update that
|
|
1283
|
+
* named one). Used to keep a background drain's prose out of the user's
|
|
1284
|
+
* answer; `undefined` means "unknown" and nothing is split (fail-open).
|
|
1285
|
+
*/
|
|
1286
|
+
private turnRequestId?;
|
|
1287
|
+
/**
|
|
1288
|
+
* Updates the session's background drain already streamed while this turn's
|
|
1289
|
+
* OWN request id was still unknown. They are NOT folded on arrival: a drain
|
|
1290
|
+
* tick that lands before the prompt response could otherwise teach the turn
|
|
1291
|
+
* the drain's id (`noteTurnRequestId`) and mix the drain's text into the
|
|
1292
|
+
* user's answer. Withheld here (cursor already advanced — the transcript
|
|
1293
|
+
* keeps a copy) until the response decides what this turn is:
|
|
1294
|
+
* - a normal turn (response carries a conversation id) discards them — they
|
|
1295
|
+
* were never this turn's answer;
|
|
1296
|
+
* - a steered turn flushes them into the segment, in order, once
|
|
1297
|
+
* `awaitAmbient` marks the segment ambient (tool cards suppressed).
|
|
1298
|
+
*/
|
|
1299
|
+
private readonly pendingAmbientUpdates;
|
|
1300
|
+
private pendingAmbientChars;
|
|
1301
|
+
/**
|
|
1302
|
+
* Whether the bounded buffer dropped at least one update. The flush then
|
|
1303
|
+
* says so in the conversation instead of silently losing the front of the
|
|
1304
|
+
* drain's output (constraint 29: ACP payloads are never dropped silently).
|
|
1305
|
+
*/
|
|
1306
|
+
private pendingAmbientTruncated;
|
|
1307
|
+
/** The ambient wait in progress, if the agent folded this prompt into a drain. */
|
|
1308
|
+
private awaitingAmbient?;
|
|
1309
|
+
/**
|
|
1310
|
+
* The prompt response named no conversation turn while a drain this session
|
|
1311
|
+
* retired RECENTLY may still be running without re-announcing. The turn still
|
|
1312
|
+
* settles at once (a drain record that no update revived is not waited on),
|
|
1313
|
+
* but its zero-output report says the turn may have been folded into that run
|
|
1314
|
+
* instead of blaming the agent and suggesting a resend.
|
|
1315
|
+
*/
|
|
1316
|
+
private recentRetiredDrain;
|
|
1317
|
+
/** The outcome of a settle whose closing work is waiting one macrotask (see `settle`). */
|
|
1318
|
+
private pendingOutcome?;
|
|
1319
|
+
private closeScheduled;
|
|
999
1320
|
private cursorSeq;
|
|
1321
|
+
/**
|
|
1322
|
+
* Burst tracker for this turn, fed from the LIVE hub frames only. Segment
|
|
1323
|
+
* replay must not touch it: every event is already counted once when it
|
|
1324
|
+
* arrived, and replay would double the numerator and the gaps.
|
|
1325
|
+
*/
|
|
1326
|
+
private readonly tps;
|
|
1327
|
+
private tpsSeq;
|
|
1328
|
+
private lastTpsPushAt;
|
|
1329
|
+
/**
|
|
1330
|
+
* The ambient quiet/ceiling windows, read once from the manager: they are
|
|
1331
|
+
* deployment tuning, not per-tick facts, and small test values must be stable
|
|
1332
|
+
* for the whole turn (see {@link BridgeTuning.ambientQuietMs} for the
|
|
1333
|
+
* measured CodeBuddy tick cadence these defaults come from).
|
|
1334
|
+
*/
|
|
1335
|
+
private readonly ambientTuning;
|
|
1336
|
+
/**
|
|
1337
|
+
* The detached workers this turn dispatched (see {@link BackgroundDispatch}),
|
|
1338
|
+
* by `toolCallId`, recorded from the wire while the turn is still streaming —
|
|
1339
|
+
* typically in the first step, before the prompt response lands. A turn may
|
|
1340
|
+
* dispatch several, and the hold waits for ALL of them.
|
|
1341
|
+
*/
|
|
1342
|
+
private readonly dispatches;
|
|
1343
|
+
/** The detached-worker hold in progress, once a segment carries it. */
|
|
1344
|
+
private backgroundHold?;
|
|
1345
|
+
/**
|
|
1346
|
+
* A hold parked because the prompt response landed while NO driver segment
|
|
1347
|
+
* was open (the host is between steps, about to execute the Agent card). It
|
|
1348
|
+
* begins when the next `startSegment` opens — or closes after a bounded
|
|
1349
|
+
* grace so a host that never opens one does not hang the turn.
|
|
1350
|
+
*/
|
|
1351
|
+
private pendingBackgroundHold?;
|
|
1352
|
+
/**
|
|
1353
|
+
* The hold's own windows, read once at construction like the ambient pair:
|
|
1354
|
+
* `taskMaxMs` is the worker phase's ceiling, `graceMs` the parked hold's
|
|
1355
|
+
* bounded wait for a driver step (see {@link BridgeTuning.backgroundTaskMaxMs}
|
|
1356
|
+
* and {@link BridgeTuning.backgroundParkedGraceMs}).
|
|
1357
|
+
*/
|
|
1358
|
+
private readonly backgroundTuning;
|
|
1359
|
+
/**
|
|
1360
|
+
* When this controller was built. Only a drain announced AFTER this moment
|
|
1361
|
+
* may be adopted by the hold: an `ambient` record that predates the turn is
|
|
1362
|
+
* some earlier run's, and folding its text would mix two answers.
|
|
1363
|
+
*/
|
|
1364
|
+
private readonly constructedAt;
|
|
1000
1365
|
constructor(manager: BridgeManager, hub: EventHub, session: LiveSession, turnId: string,
|
|
1001
1366
|
/** Releases the agent-side turn slot (the `beginTurn` settler; idempotent). */
|
|
1002
1367
|
endTurn: () => void, rollbackFirstTurn: (session: LiveSession) => void);
|
|
@@ -1021,11 +1386,270 @@ declare class TurnStreamController {
|
|
|
1021
1386
|
startSegment(): SegmentState;
|
|
1022
1387
|
/** Finish the segment on air (if any). Used by cancel paths and teardown. */
|
|
1023
1388
|
finishActive(reason: SegmentFinishReason): void;
|
|
1389
|
+
/** Whether this turn already streamed prose of its own (not a drain's). */
|
|
1390
|
+
get hasAgentProse(): boolean;
|
|
1391
|
+
/**
|
|
1392
|
+
* Keep the turn open for a background drain the agent folded our prompt into.
|
|
1393
|
+
*
|
|
1394
|
+
* THE SHAPE: the agent answered `session/prompt` at once (CodeBuddy returns
|
|
1395
|
+
* `end_turn` in ~14ms when it merges the message into a drain it is already
|
|
1396
|
+
* running) and the real answer streams afterwards under the DRAIN's request
|
|
1397
|
+
* id. Closing on the response would cut the turn before its text exists —
|
|
1398
|
+
* which is exactly the bug this state exists for. So:
|
|
1399
|
+
*
|
|
1400
|
+
* - the agent-side prompt slot is released (`endTurn`: the response HAS
|
|
1401
|
+
* landed) but the turn is NOT settled: `settled` means "the turn is over",
|
|
1402
|
+
* and it is not — no `turn/end`, no `running = false`, no segment close;
|
|
1403
|
+
* - the segment keeps folding the session's updates, with cards suppressed
|
|
1404
|
+
* (see `SegmentState.ambient`): the host must not execute a background
|
|
1405
|
+
* tool for a turn whose answer has not begun;
|
|
1406
|
+
* - any update on the session resets the quiet clock, and the drain's end is
|
|
1407
|
+
* its silence (a `session_end` is rejected by the SDK's schema and never
|
|
1408
|
+
* reaches us — never wait for one); a hard ceiling closes what never ends.
|
|
1409
|
+
*/
|
|
1410
|
+
awaitAmbient(requestId: string, stopReason: string): void;
|
|
1411
|
+
/**
|
|
1412
|
+
* The prompt response named no conversation turn while a drain the session
|
|
1413
|
+
* retired recently may still be running (and did not re-announce): the turn
|
|
1414
|
+
* cannot be held open, but a zero-output report must not blame the agent.
|
|
1415
|
+
* See `hasRecentRetiredAmbient` for the window.
|
|
1416
|
+
*/
|
|
1417
|
+
noteRecentRetiredDrain(): void;
|
|
1418
|
+
/**
|
|
1419
|
+
* Whether this turn dispatched a detached worker whose answer is still
|
|
1420
|
+
* outstanding. Read when the prompt response lands: such a turn is NOT over
|
|
1421
|
+
* even though the agent answered it (see {@link holdForBackgroundDispatch}).
|
|
1422
|
+
*
|
|
1423
|
+
* Two ways a dispatch keeps the turn open:
|
|
1424
|
+
* - it has not reported back yet (the worker is still running), or
|
|
1425
|
+
* - it DID report — before the response, even — but it was structurally
|
|
1426
|
+
* detached and emitted worker updates: the drain that carries its report
|
|
1427
|
+
* is still coming.
|
|
1428
|
+
* A synchronous `Agent` / `Task` call (completed, no `isBackground`, no
|
|
1429
|
+
* worker updates) matches neither and closes normally.
|
|
1430
|
+
*/
|
|
1431
|
+
get dispatchedDetachedWorker(): boolean;
|
|
1432
|
+
/** The hold this turn is inside — adopted, or parked waiting for a step. */
|
|
1433
|
+
private activeBackgroundHold;
|
|
1434
|
+
/** Whether every KNOWN dispatch has reported (no dispatch at all counts as done). */
|
|
1435
|
+
private allDispatchesCompleted;
|
|
1436
|
+
/**
|
|
1437
|
+
* Hold the turn open for the detached worker(s) its `Agent` tool calls
|
|
1438
|
+
* dispatched — the second controlled ambient-family exception (constraint
|
|
1439
|
+
* 23), not a new idle watchdog.
|
|
1440
|
+
*
|
|
1441
|
+
* THE SHAPE (measured against CodeBuddy 2.179.0, multitask on): the
|
|
1442
|
+
* coordinator opens an `Agent` call that hands work to a background worker,
|
|
1443
|
+
* says "已派…", and the `session/prompt` RESPONSE arrives normally while the
|
|
1444
|
+
* worker keeps running. The worker's own updates (requestId B +
|
|
1445
|
+
* `parentToolCallId`) never touch this session's message; when it finishes,
|
|
1446
|
+
* the agent starts a background DRAIN (requestId A — the response's own
|
|
1447
|
+
* `conversationRequestId`) whose steps carry the report and the final answer.
|
|
1448
|
+
* Nothing tells the client the worker is running: no `session_end` (the SDK's
|
|
1449
|
+
* schema rejects it) and an `agentPhase` idle tick even fires BETWEEN the
|
|
1450
|
+
* worker and the drain. So the response cannot close the turn.
|
|
1451
|
+
*
|
|
1452
|
+
* The hold:
|
|
1453
|
+
* - releases the agent-side prompt slot (`endTurn`: the response landed)
|
|
1454
|
+
* while keeping `running` and the driver step open;
|
|
1455
|
+
* - marks the segment background, so EVERY tool call for the rest of the
|
|
1456
|
+
* hold — the workers', the drain's TaskOutput — stays transcript-only and
|
|
1457
|
+
* the final message carries text with no card in front of it;
|
|
1458
|
+
* - waits in two phases: the worker phase only has the
|
|
1459
|
+
* {@link BridgeTuning.backgroundTaskMaxMs} ceiling (a detached worker goes
|
|
1460
|
+
* silent for tens of seconds while it works) and ends only when ALL known
|
|
1461
|
+
* dispatches reported, after which the drain phase uses the ambient
|
|
1462
|
+
* windows.
|
|
1463
|
+
*/
|
|
1464
|
+
holdForBackgroundDispatch(stopReason: string, requestId?: string): void;
|
|
1465
|
+
/** Give a (possibly parked) hold a driver segment: cards off for the rest of it. */
|
|
1466
|
+
private adoptBackgroundHold;
|
|
1467
|
+
/**
|
|
1468
|
+
* The worker phase is over once EVERY known dispatch reported: the drain
|
|
1469
|
+
* that carries their reports is (about to be) running, so the existing
|
|
1470
|
+
* ambient windows take over, measured from HERE.
|
|
1471
|
+
*
|
|
1472
|
+
* An announced run is accepted immediately, but while any dispatch is still
|
|
1473
|
+
* incomplete it only records the id and switches the hold's primary run —
|
|
1474
|
+
* arming the quiet clock there would let a drain that spoke early cut the
|
|
1475
|
+
* turn off before its other worker ever reports.
|
|
1476
|
+
*/
|
|
1477
|
+
private startBackgroundDrain;
|
|
1478
|
+
/**
|
|
1479
|
+
* A dispatch that lands while the drain phase already began means the
|
|
1480
|
+
* coordinator handed out NEW work: the worker phase is back, and with it the
|
|
1481
|
+
* absence of a quiet window.
|
|
1482
|
+
*/
|
|
1483
|
+
private enterBackgroundWorkerPhase;
|
|
1484
|
+
/** Push the drain phase's quiet clock out: every update is evidence of work. */
|
|
1485
|
+
private armBackgroundQuiet;
|
|
1486
|
+
/**
|
|
1487
|
+
* Any session update restarts the drain phase's quiet clock. Only an
|
|
1488
|
+
* ADOPTED hold has one: a parked hold's wait belongs to its grace timer, and
|
|
1489
|
+
* the worker phase never has a clock at all.
|
|
1490
|
+
*/
|
|
1491
|
+
private noteBackgroundHoldActivity;
|
|
1492
|
+
/** The drained run went quiet: the turn ends with the stop reason the response gave. */
|
|
1493
|
+
private onBackgroundQuiet;
|
|
1494
|
+
/** The hold outlived its ceiling (worker or drain): the run is still going. */
|
|
1495
|
+
private onBackgroundHard;
|
|
1496
|
+
/**
|
|
1497
|
+
* No driver step opened inside the grace: the host is not coming back for
|
|
1498
|
+
* this turn's Agent card. Close it the way a quiet ambient ending does — the
|
|
1499
|
+
* deferral line, never the zero-output blame — and keep ONE drain record:
|
|
1500
|
+
* the worker may still report into a run no turn is waiting on.
|
|
1501
|
+
*
|
|
1502
|
+
* A parked hold has NO segment by definition, so the chat plane has nowhere
|
|
1503
|
+
* to put the deferral line: it goes to the settings log (`agent/notice`)
|
|
1504
|
+
* instead of being parked on prose nobody will read.
|
|
1505
|
+
*/
|
|
1506
|
+
private onBackgroundGraceExpired;
|
|
1507
|
+
/**
|
|
1508
|
+
* Close a detached-worker hold exactly once. A turn that folded NO answer
|
|
1509
|
+
* text from its held runs says where the rest of the output will land (the
|
|
1510
|
+
* deferral wording, never the silent-failure blame); the parent's interim
|
|
1511
|
+
* acknowledgement does not count as that answer.
|
|
1512
|
+
*
|
|
1513
|
+
* Ownership of the drain ids at close (`releaseBackgroundHoldIds`): a quiet
|
|
1514
|
+
* close retires every accepted id and drops the session record; a hard or
|
|
1515
|
+
* grace close keeps exactly ONE record, because the run is provably still
|
|
1516
|
+
* going and a later update or re-announcement must revive it normally.
|
|
1517
|
+
*/
|
|
1518
|
+
private closeBackgroundHold;
|
|
1519
|
+
/** Write a hold's deferral line into the settings log (`agent/notice`). */
|
|
1520
|
+
private broadcastBackgroundDeferral;
|
|
1521
|
+
/** Drop a hold's clocks. The hold object itself stays for its `retired` mark. */
|
|
1522
|
+
private clearBackgroundHoldTimers;
|
|
1523
|
+
/**
|
|
1524
|
+
* Drop every hold clock (teardown / a forced ending). `retire` also gives up
|
|
1525
|
+
* every accepted run id — a SUCCESSOR turn must never fold the abandoned
|
|
1526
|
+
* hold's drain or worker text into its own answer.
|
|
1527
|
+
*/
|
|
1528
|
+
private clearBackgroundHold;
|
|
1529
|
+
/**
|
|
1530
|
+
* Settle what happens to the session's drain records when a hold closes.
|
|
1531
|
+
*
|
|
1532
|
+
* `hard: false` (quiet close, cancel, teardown): the run is no longer waited
|
|
1533
|
+
* on, so EVERY accepted id is retired — its late text stays transcript-only
|
|
1534
|
+
* for whatever turn runs next — and the session record is dropped if it
|
|
1535
|
+
* names one of them.
|
|
1536
|
+
*
|
|
1537
|
+
* `hard: true` (ceiling / parked grace): the run is provably still going.
|
|
1538
|
+
* Exactly ONE record survives — the session's current drain if it has one,
|
|
1539
|
+
* else the hold's primary run — with its activity clock refreshed; the other
|
|
1540
|
+
* accepted ids are retired. A hold whose primary id had no session record
|
|
1541
|
+
* CREATES one, so a successor's `noteTurnRequestId` cannot adopt that id.
|
|
1542
|
+
* Every accepted id therefore ends up either retired or owned by
|
|
1543
|
+
* `session.ambient`; never both unowned and unretired.
|
|
1544
|
+
*/
|
|
1545
|
+
private releaseBackgroundHoldIds;
|
|
1546
|
+
/**
|
|
1547
|
+
* Fold the withheld drain updates that belong to this hold into its segment.
|
|
1548
|
+
* The rest of the buffer was some OTHER run's text: it is dropped here (the
|
|
1549
|
+
* transcript keeps it), never leaked into this turn's answer.
|
|
1550
|
+
*/
|
|
1551
|
+
private flushBackgroundHoldUpdates;
|
|
1552
|
+
/**
|
|
1553
|
+
* Follow the detached-worker signals of one live update.
|
|
1554
|
+
*
|
|
1555
|
+
* Only CodeBuddy sends them (`codebuddyMetaOf` answers `undefined` for every
|
|
1556
|
+
* other agent, so nothing here can fire for them):
|
|
1557
|
+
* - an `Agent` / `Task` `tool_call` records the dispatch — the FIRST send
|
|
1558
|
+
* needs that CodeBuddy shape;
|
|
1559
|
+
* - the SAME call re-sent with `isBackground: true`, or any update naming it
|
|
1560
|
+
* as its `parentToolCallId`, marks the work DETACHED;
|
|
1561
|
+
* - that call reporting completed / failed is read BY ID: the real
|
|
1562
|
+
* completion frame carries NO `_meta` at all, so requiring the CodeBuddy
|
|
1563
|
+
* shape here would miss every real completion;
|
|
1564
|
+
* - any update carrying `parentToolCallId` is the worker's OWN turn — it
|
|
1565
|
+
* proves the worker alive but never belongs to this session's answer;
|
|
1566
|
+
* - an `unsolicitedTurn` announcement is the drain that carries the report.
|
|
1567
|
+
*/
|
|
1568
|
+
private trackBackgroundDispatch;
|
|
1569
|
+
/**
|
|
1570
|
+
* A dispatch just reported: once the LAST one has, the drain phase starts.
|
|
1571
|
+
* Until then the turn stays in its worker phase, with no quiet clock.
|
|
1572
|
+
*/
|
|
1573
|
+
private noteDispatchProgress;
|
|
1574
|
+
/** Any session update is evidence the drain still works: push the quiet clock out. */
|
|
1575
|
+
private noteAmbientActivity;
|
|
1576
|
+
/** The drain went quiet: the turn ends with the stop reason the response gave. */
|
|
1577
|
+
private onAmbientQuiet;
|
|
1578
|
+
/** The drain outlived the ceiling: say where the rest will land, then close. */
|
|
1579
|
+
private onAmbientHard;
|
|
1580
|
+
/**
|
|
1581
|
+
* Close an ambient wait exactly once. A turn that produced no answer TEXT
|
|
1582
|
+
* says so: the drain's output is in the ACP transcript view, and this
|
|
1583
|
+
* bubble must not be empty (the zero-output report, reworded for a turn that
|
|
1584
|
+
* was deferred rather than failed).
|
|
1585
|
+
*/
|
|
1586
|
+
private finishAmbient;
|
|
1587
|
+
/** Drop the ambient wait's clocks (the turn is being closed by someone else). */
|
|
1588
|
+
private clearAmbientWait;
|
|
1589
|
+
/**
|
|
1590
|
+
* Withhold one background-drain update until the prompt response says what
|
|
1591
|
+
* this turn is. The cursor advances past it anyway: it is already in the
|
|
1592
|
+
* transcript, and replaying it into a later segment would emit it twice.
|
|
1593
|
+
* Bounded (see `PENDING_AMBIENT_MAX_*`) — the transcript still keeps every
|
|
1594
|
+
* entry the buffer drops.
|
|
1595
|
+
*/
|
|
1596
|
+
private bufferAmbientUpdate;
|
|
1597
|
+
/** Fold the withheld drain updates into the now-ambient segment, once, in order. */
|
|
1598
|
+
private flushPendingAmbient;
|
|
1599
|
+
/** Drop withheld drain updates: this turn is not the drain, or it is closing. */
|
|
1600
|
+
private clearPendingAmbient;
|
|
1024
1601
|
/** The agent settled its `session/prompt`. Ends the turn's streaming face. */
|
|
1025
1602
|
settle(outcome: TurnSettle): void;
|
|
1026
|
-
/**
|
|
1603
|
+
/**
|
|
1604
|
+
* Force the local end of this turn — the cancel fallback and any watchdog
|
|
1605
|
+
* that reached the controller. Runs the SAME closing work as a normal settle
|
|
1606
|
+
* (`closeTurn`): the ambient clocks stop, the on-air segment closes (so the
|
|
1607
|
+
* driver step parked on its queue ends), exactly one `turn/end` goes out
|
|
1608
|
+
* with THIS turn's id, the session stops running and the prompt slot is
|
|
1609
|
+
* released. Idempotent: a turn that already closed ignores the call.
|
|
1610
|
+
*/
|
|
1611
|
+
forceFinalize(outcome: TurnSettle): void;
|
|
1612
|
+
/**
|
|
1613
|
+
* Run the deferred closing work now, when a driver step is about to open on
|
|
1614
|
+
* an already settled turn: the continuation step must see the closed turn
|
|
1615
|
+
* (and the session NOT left `running` across the macrotask boundary).
|
|
1616
|
+
* Idempotent — the scheduled tick then finds nothing left to do.
|
|
1617
|
+
*/
|
|
1618
|
+
private flushScheduledClose;
|
|
1619
|
+
/**
|
|
1620
|
+
* Fold the notifications that reached the transcript but not the segment:
|
|
1621
|
+
* the tail a response raced past. Stops AT a cutting event without consuming
|
|
1622
|
+
* it (same rule as `startSegment`) — its next segment is not this turn's.
|
|
1623
|
+
*/
|
|
1624
|
+
private drainTail;
|
|
1625
|
+
/**
|
|
1626
|
+
* End the turn exactly once: burst metrics, ONE `turn/end`, the provisional
|
|
1627
|
+
* lock, the closing text, the segment, the session flags, the prompt slot.
|
|
1628
|
+
*
|
|
1629
|
+
* `ambient.line` marks a controlled ambient ending: the given line closes
|
|
1630
|
+
* the segment when the turn carried no answer text, instead of the generic
|
|
1631
|
+
* zero-output warning (which would blame the agent for a turn the drain
|
|
1632
|
+
* simply had not finished yet). `ambient.keepAmbient` says the ending was the
|
|
1633
|
+
* CEILING, not silence: see the drain-record comment below.
|
|
1634
|
+
*/
|
|
1635
|
+
private closeTurn;
|
|
1636
|
+
/**
|
|
1637
|
+
* Cancelled by the user: finish the on-air segment and release the turn's
|
|
1638
|
+
* slots. An AMBIENT turn has already released the prompt slot (its response
|
|
1639
|
+
* landed) and holds no segment the cancel fallback could close, so the
|
|
1640
|
+
* cancel closes the turn itself with the cancelled stop.
|
|
1641
|
+
*/
|
|
1027
1642
|
abortActiveSegment(): void;
|
|
1028
|
-
/**
|
|
1643
|
+
/**
|
|
1644
|
+
* Stop observing and release the manager's index. Idempotent.
|
|
1645
|
+
*
|
|
1646
|
+
* The segment is CLOSED here (aborted) before unsubscribing: a driver step
|
|
1647
|
+
* parked on its queue (`yield* segment.queue`) only ends when the queue
|
|
1648
|
+
* closes, and a controller replaced by the conversation's next turn would
|
|
1649
|
+
* otherwise leave that step hanging forever. An already finished segment is a
|
|
1650
|
+
* no-op, and the session's flags are left alone — a superseded turn must not
|
|
1651
|
+
* mark the newer turn's session idle.
|
|
1652
|
+
*/
|
|
1029
1653
|
dispose(): void;
|
|
1030
1654
|
/**
|
|
1031
1655
|
* Stop observing and release the manager's index WITHOUT touching the
|
|
@@ -1037,7 +1661,19 @@ declare class TurnStreamController {
|
|
|
1037
1661
|
private finishSegment;
|
|
1038
1662
|
/** A segment opened on an already-settled turn: replay, then close it out. */
|
|
1039
1663
|
private finishAfterSettle;
|
|
1664
|
+
/**
|
|
1665
|
+
* The "protocol says success, the bubble is empty" line. When a recently
|
|
1666
|
+
* retired drain may have taken the prompt without re-announcing itself, say
|
|
1667
|
+
* that instead of blaming the agent: the output exists in the transcript.
|
|
1668
|
+
*/
|
|
1669
|
+
private zeroOutputLine;
|
|
1040
1670
|
private onEvent;
|
|
1671
|
+
/** Feed one live update into the turn's burst tracker. Never called by replay. */
|
|
1672
|
+
private trackTps;
|
|
1673
|
+
/** Throttled live aggregate so the dock's speed moves during a long turn. */
|
|
1674
|
+
private pushLiveTps;
|
|
1675
|
+
/** Close the turn's metrics: persist the measurable part, then broadcast it. */
|
|
1676
|
+
private finalizeTps;
|
|
1041
1677
|
/**
|
|
1042
1678
|
* Put one narrative line into the turn's prose stream.
|
|
1043
1679
|
*
|
|
@@ -1051,12 +1687,33 @@ declare class TurnStreamController {
|
|
|
1051
1687
|
private recordWithoutSegment;
|
|
1052
1688
|
/** Record a tool call's output (idempotent) and wake any waiter. */
|
|
1053
1689
|
private noteToolRecord;
|
|
1690
|
+
/**
|
|
1691
|
+
* Whether this update belongs to a background drain — the one the session is
|
|
1692
|
+
* waiting on, or one whose wait already closed (retired) but whose run can
|
|
1693
|
+
* still speak.
|
|
1694
|
+
*/
|
|
1695
|
+
private isAmbientOwned;
|
|
1696
|
+
/** Learn this turn's own CodeBuddy request id from the first update naming one. */
|
|
1697
|
+
private noteTurnRequestId;
|
|
1698
|
+
/** Adopt the request id the prompt RESPONSE declared for this turn. */
|
|
1699
|
+
adoptTurnRequestId(requestId: string): void;
|
|
1700
|
+
/**
|
|
1701
|
+
* Prose of the session's background drain while this turn runs on its OWN
|
|
1702
|
+
* request id: it belongs to the background run, not to the user's answer.
|
|
1703
|
+
* Fail-open by design — an unknown turn id splits nothing, and a turn that
|
|
1704
|
+
* was itself folded into the drain keeps its prose.
|
|
1705
|
+
*/
|
|
1706
|
+
private isForeignAmbientText;
|
|
1054
1707
|
/**
|
|
1055
1708
|
* The turn's whole fold, shared by live events and segment replay. Visible
|
|
1056
1709
|
* non-tool events END a card-bearing segment instead of joining it — that is
|
|
1057
1710
|
* the segmentation rule that puts each card between two assistant messages.
|
|
1058
1711
|
* Returns whether the event was consumed into this segment: the cutting
|
|
1059
1712
|
* event is left unconsumed (and thus behind the cursor) for the next one.
|
|
1713
|
+
*
|
|
1714
|
+
* `seq` is the update's transcript position when the caller has one: the
|
|
1715
|
+
* detached-worker hold uses it to count only the answer text that arrived
|
|
1716
|
+
* AFTER the hold began (a parked hold replays the parent's earlier ack).
|
|
1060
1717
|
*/
|
|
1061
1718
|
private foldInto;
|
|
1062
1719
|
private emitProse;
|
|
@@ -1131,6 +1788,29 @@ export declare class BridgeManager {
|
|
|
1131
1788
|
* write would fight with no way to tell which one is live.
|
|
1132
1789
|
*/
|
|
1133
1790
|
private deadlineOverride;
|
|
1791
|
+
/**
|
|
1792
|
+
* The idle-release window the settings page picked, in ms, persisted
|
|
1793
|
+
* with the agent registry. `undefined` means the deployment baseline runs.
|
|
1794
|
+
*
|
|
1795
|
+
* Merged OVER {@link baselineIdleTimeoutMs} exactly like
|
|
1796
|
+
* {@link deadlineOverride} over {@link baselineDeadline}: a UI pick is what
|
|
1797
|
+
* runs, and `cordis.patch.yml` becomes the value the reset button returns
|
|
1798
|
+
* to. Without this split the two sources would fight with no way to tell
|
|
1799
|
+
* which one is live.
|
|
1800
|
+
*/
|
|
1801
|
+
private idleReleaseOverride?;
|
|
1802
|
+
/**
|
|
1803
|
+
* The window `cordis.patch.yml` names, when it names one. Kept apart
|
|
1804
|
+
* from the override for the same reason the deadline keeps its baseline
|
|
1805
|
+
* apart: the settings card has to be able to say "the file asks for
|
|
1806
|
+
* 10 minutes, the value set here wins".
|
|
1807
|
+
*/
|
|
1808
|
+
private configuredIdleTimeoutMs?;
|
|
1809
|
+
/**
|
|
1810
|
+
* The idle-release window before any override — the tuning this manager was
|
|
1811
|
+
* constructed with (the deployment baseline, or the built-in 30 minutes).
|
|
1812
|
+
*/
|
|
1813
|
+
private baselineIdleTimeoutMs;
|
|
1134
1814
|
/**
|
|
1135
1815
|
* Tuning in force, defaults applied. The approval deadline is rewritten
|
|
1136
1816
|
* IN PLACE by {@link applyDeadlineTuning}, which is why this is a live object
|
|
@@ -1150,6 +1830,28 @@ export declare class BridgeManager {
|
|
|
1150
1830
|
private readonly turnStreams;
|
|
1151
1831
|
/** The segmented turn a continuation step of one chat session must resume. */
|
|
1152
1832
|
turnStreamFor(dshSessionId: string): TurnStreamController | undefined;
|
|
1833
|
+
/**
|
|
1834
|
+
* The ambient windows a turn's controller reads once at construction (see
|
|
1835
|
+
* {@link BridgeTuning.ambientQuietMs}). Exposed as a plain snapshot so the
|
|
1836
|
+
* controller never pokes at the manager's tuning object, and tests can tune
|
|
1837
|
+
* the cadence through the constructor like every other window.
|
|
1838
|
+
*/
|
|
1839
|
+
get ambientTuning(): {
|
|
1840
|
+
quietMs: number;
|
|
1841
|
+
maxMs: number;
|
|
1842
|
+
};
|
|
1843
|
+
/**
|
|
1844
|
+
* The detached-worker hold's windows, read once at controller construction
|
|
1845
|
+
* for the same reason as {@link ambientTuning}: `taskMaxMs` is the worker
|
|
1846
|
+
* phase's only clock (see {@link BridgeTuning.backgroundTaskMaxMs}) and
|
|
1847
|
+
* `graceMs` the bounded wait a PARKED hold gives the host to open its
|
|
1848
|
+
* continuation step (see {@link BridgeTuning.backgroundParkedGraceMs}). The
|
|
1849
|
+
* drain phase reuses {@link ambientTuning}.
|
|
1850
|
+
*/
|
|
1851
|
+
get backgroundTuning(): {
|
|
1852
|
+
taskMaxMs: number;
|
|
1853
|
+
graceMs: number;
|
|
1854
|
+
};
|
|
1153
1855
|
/** Index a turn's controller, retiring whatever turn it supersedes. */
|
|
1154
1856
|
rememberTurnStream(dshSessionId: string, controller: TurnStreamController): void;
|
|
1155
1857
|
/** Drop a controller from the index (its own `dispose` calls back into this). */
|
|
@@ -1197,6 +1899,10 @@ export declare class BridgeManager {
|
|
|
1197
1899
|
* sessions and headless deployments.
|
|
1198
1900
|
*/
|
|
1199
1901
|
setSessionWorkspaceResolver(resolver?: SessionWorkspaceResolver): void;
|
|
1902
|
+
/**
|
|
1903
|
+
* Set the attachment service or context used to resolve image attachments in user prompts.
|
|
1904
|
+
*/
|
|
1905
|
+
setAttachmentResolver(resolver?: unknown): void;
|
|
1200
1906
|
/**
|
|
1201
1907
|
* Wire the ACP-mode probe: given a DSH session id, report whether that
|
|
1202
1908
|
* session runs the ACP agent preset. The host half reads it back from the
|
|
@@ -1238,6 +1944,29 @@ export declare class BridgeManager {
|
|
|
1238
1944
|
* keeps the two controls from reading as duplicates.
|
|
1239
1945
|
*/
|
|
1240
1946
|
chatSessions(dshSessionId: string): ChatSessionView[];
|
|
1947
|
+
/**
|
|
1948
|
+
* Adopt the ACP side of a branched conversation: the browser has already
|
|
1949
|
+
* asked the HOST to fork the DSH session tree (`sessions.fork`), and this
|
|
1950
|
+
* runs the agent's own `session/fork` of the parent conversation's live ACP
|
|
1951
|
+
* session so the child actually remembers the parent context — then binds
|
|
1952
|
+
* the child to it (the fourth session-creation point, with every discipline
|
|
1953
|
+
* the other three carry).
|
|
1954
|
+
*
|
|
1955
|
+
* Failure is deliberately all-or-nothing: nothing is written for the child
|
|
1956
|
+
* until the agent has answered, so a failed adopt leaves the child an
|
|
1957
|
+
* ordinary copied-history conversation whose first message starts a fresh
|
|
1958
|
+
* `session/new` (today's behavior) — never a half-bound record.
|
|
1959
|
+
*
|
|
1960
|
+
* @param childDshSessionId - the DSH session the host fork created.
|
|
1961
|
+
* @param parentDshSessionId - the conversation being branched; its ACP
|
|
1962
|
+
* session must be LIVE on this connection (a released parent has no
|
|
1963
|
+
* process to fork from — the browser blocks the click, this is the hard
|
|
1964
|
+
* stop behind it).
|
|
1965
|
+
*/
|
|
1966
|
+
adoptForkedSession(childDshSessionId: string, parentDshSessionId: string): Promise<{
|
|
1967
|
+
sessionId: string;
|
|
1968
|
+
acpSessionId: string;
|
|
1969
|
+
}>;
|
|
1241
1970
|
/**
|
|
1242
1971
|
* Bind one DSH chat session to an ACP agent.
|
|
1243
1972
|
*
|
|
@@ -1391,11 +2120,26 @@ export declare class BridgeManager {
|
|
|
1391
2120
|
*/
|
|
1392
2121
|
chatTranscripts(dshSessionId: string, afterSeq?: number): ChatTranscriptView[];
|
|
1393
2122
|
/**
|
|
1394
|
-
* Switch one chat-bound session's ACP mode.
|
|
1395
|
-
*
|
|
1396
|
-
*
|
|
2123
|
+
* Switch one chat-bound session's ACP mode.
|
|
2124
|
+
*
|
|
2125
|
+
* A released session is deliberately NOT resumed for it (user decision,
|
|
2126
|
+
* 2026-10-30 — the composer's axes are "what the next turn should use", and
|
|
2127
|
+
* the process comes back only when the user sends a message or hits the
|
|
2128
|
+
* released banner's resume button). The pick is recorded and
|
|
2129
|
+
* `applyChatChoice` applies it on resume, exactly like `setChatSessionModel`
|
|
2130
|
+
* / `setChatSessionEffort` / `setChatSessionConfig`; only a LIVE session is
|
|
2131
|
+
* told immediately (`session/set_mode`).
|
|
1397
2132
|
*/
|
|
1398
2133
|
setChatSessionMode(dshSessionId: string, modeId: string, agentId?: string): Promise<void>;
|
|
2134
|
+
/**
|
|
2135
|
+
* Bring one chat conversation's released session back on a fresh process on
|
|
2136
|
+
* the USER's explicit gesture (the "恢复进程" banner) — the same work the
|
|
2137
|
+
* next prompt performs lazily (`getOrCreateDshSession`'s released branch),
|
|
2138
|
+
* minus the create path: a banner button must never materialize a session
|
|
2139
|
+
* the conversation has not had yet. A live target passes through untouched,
|
|
2140
|
+
* so a double click (or a click racing the next message) spawns nothing.
|
|
2141
|
+
*/
|
|
2142
|
+
resumeChatSession(dshSessionId: string): Promise<void>;
|
|
1399
2143
|
/**
|
|
1400
2144
|
* Wire the harness's preset roster (`ctx.agentPresets`) into `presetStatus`.
|
|
1401
2145
|
*
|
|
@@ -1450,8 +2194,37 @@ export declare class BridgeManager {
|
|
|
1450
2194
|
* missing agent does not erase the choice it was made for.
|
|
1451
2195
|
*/
|
|
1452
2196
|
private loadPersistedChoices;
|
|
2197
|
+
/**
|
|
2198
|
+
* Fold one settled ACP turn's measurable burst into the session aggregate and
|
|
2199
|
+
* persist it. A turn with no measured inter-chunk gap contributes neither
|
|
2200
|
+
* side: adding its tokens with zero time would inflate the session rate.
|
|
2201
|
+
*/
|
|
2202
|
+
noteSessionBurst(session: LiveSession, outputTokens: number, burstMs: number): void;
|
|
1453
2203
|
private persistAgents;
|
|
2204
|
+
/**
|
|
2205
|
+
* Restore chat session records from disk as PARKED sessions.
|
|
2206
|
+
*
|
|
2207
|
+
* Every process died with the host, so nothing comes back live — but the
|
|
2208
|
+
* agent-side sessions did not. Restoring each record as `released` is what
|
|
2209
|
+
* makes the released banner, its resume button and the "next message
|
|
2210
|
+
* resumes it" promise survive a restart (the alternative is the next
|
|
2211
|
+
* message quietly starting a NEW, context-less session — constraint 10's
|
|
2212
|
+
* "released ≠ destroyed" lost at the process boundary). Tolerant, one row
|
|
2213
|
+
* at a time: a single corrupt entry must not cost every other conversation
|
|
2214
|
+
* its context. Records whose agent is no longer configured are dropped —
|
|
2215
|
+
* there is nothing to resume ON, and the next write omits them.
|
|
2216
|
+
*/
|
|
2217
|
+
private loadPersistedChatSessions;
|
|
1454
2218
|
private readonly catalogs;
|
|
2219
|
+
/**
|
|
2220
|
+
* Each agent's LAST KNOWN `sessionCapabilities.fork` bit, persisted with the
|
|
2221
|
+
* model catalog. A live `initialize` result outranks it whenever one exists
|
|
2222
|
+
* (see {@link canForkNow}) — this copy answers when the process is gone
|
|
2223
|
+
* (a released session's branch button must not flash into "supported" just
|
|
2224
|
+
* because `initResult` died with it, nor read `view().agentCapabilities`,
|
|
2225
|
+
* which a release clears).
|
|
2226
|
+
*/
|
|
2227
|
+
private readonly forkCaps;
|
|
1455
2228
|
/**
|
|
1456
2229
|
* The slash commands each agent declared in its most recent session, cached
|
|
1457
2230
|
* per agent and persisted alongside the model catalog.
|
|
@@ -1520,6 +2293,21 @@ export declare class BridgeManager {
|
|
|
1520
2293
|
* restatement would make every open composer re-read `agent/catalog`.
|
|
1521
2294
|
*/
|
|
1522
2295
|
private absorbCommands;
|
|
2296
|
+
/**
|
|
2297
|
+
* Record a fresh `initialize`'s `sessionCapabilities.fork` verdict (the
|
|
2298
|
+
* bridge reports it right after the handshake). Persisted with the model
|
|
2299
|
+
* catalog; no broadcast — the browser learns it through `session/chat-list`
|
|
2300
|
+
* rows, and its branch button deliberately runs on a last-known basis (the
|
|
2301
|
+
* agent's own next `initialize` is the refresh).
|
|
2302
|
+
*/
|
|
2303
|
+
private noteForkCapability;
|
|
2304
|
+
/**
|
|
2305
|
+
* Whether an agent's fork can be attempted RIGHT NOW: a live `initialize`
|
|
2306
|
+
* answer wins (the process is up, its handshake is current); only when the
|
|
2307
|
+
* process is gone does the LAST KNOWN persisted bit answer. `false` covers
|
|
2308
|
+
* "never seen" — an unknown agent simply never gets a fork attempt.
|
|
2309
|
+
*/
|
|
2310
|
+
private canForkNow;
|
|
1523
2311
|
/**
|
|
1524
2312
|
* Append models this observation newly introduced to the agent's selection.
|
|
1525
2313
|
*
|
|
@@ -1698,7 +2486,7 @@ export declare class BridgeManager {
|
|
|
1698
2486
|
startSession(agentId: string, cwd: string | undefined): Promise<{
|
|
1699
2487
|
sessionId: string;
|
|
1700
2488
|
}>;
|
|
1701
|
-
prompt(sessionId: string,
|
|
2489
|
+
prompt(sessionId: string, prompt: string | ContentBlock[]): Promise<{
|
|
1702
2490
|
turnId: string;
|
|
1703
2491
|
}>;
|
|
1704
2492
|
/** The late answer landed (or failed): the turn is no longer an orphan. */
|
|
@@ -1736,6 +2524,18 @@ export declare class BridgeManager {
|
|
|
1736
2524
|
/** The agent tag's halo preference (composer display, plugin-private). */
|
|
1737
2525
|
tagHalo(): AcpTagHalo;
|
|
1738
2526
|
setTagHalo(halo: AcpTagHalo): Promise<void>;
|
|
2527
|
+
/**
|
|
2528
|
+
* Declare the deployment baseline for the idle-release window — the value
|
|
2529
|
+
* `cordis.patch.yml` names, passed RAW (only when it names one).
|
|
2530
|
+
*
|
|
2531
|
+
* Kept separate from the validated tuning for the same reason the
|
|
2532
|
+
* deadline keeps its baseline apart: "the file says 10 minutes" and
|
|
2533
|
+
* "the file says nothing, so 30 minutes applies" are different facts.
|
|
2534
|
+
* The first is the reset target and shows as the fact being overridden;
|
|
2535
|
+
* the second is a built-in default. Called once by the host entry,
|
|
2536
|
+
* before any bridge exists.
|
|
2537
|
+
*/
|
|
2538
|
+
setConfiguredIdleRelease(idleTimeoutMs: number | undefined): void;
|
|
1739
2539
|
/**
|
|
1740
2540
|
* Declare the deployment baseline for the approval deadline — the values
|
|
1741
2541
|
* `cordis.patch.yml` names, passed RAW (only the keys it actually contains).
|
|
@@ -1758,6 +2558,37 @@ export declare class BridgeManager {
|
|
|
1758
2558
|
* stay silent about a plain default.
|
|
1759
2559
|
*/
|
|
1760
2560
|
approvalDeadlines(): AcpApprovalDeadlines;
|
|
2561
|
+
/**
|
|
2562
|
+
* The idle-release window in force, plus the facts that explain a value the
|
|
2563
|
+
* user did not pick: the deployment baseline an override is outranking.
|
|
2564
|
+
*
|
|
2565
|
+
* `defaultIdleTimeoutMs` is the window a RESET returns to — the deployment
|
|
2566
|
+
* baseline when the config file names one, else the built-in default. It is
|
|
2567
|
+
* NOT a hardcoded constant: a deployment that names 10 minutes must reset
|
|
2568
|
+
* to 10 minutes, not to the built-in 30.
|
|
2569
|
+
*
|
|
2570
|
+
* The config file's own number is reported only when the file names one, so
|
|
2571
|
+
* the settings card can stay silent about a plain default.
|
|
2572
|
+
*/
|
|
2573
|
+
idleRelease(): AcpIdleRelease;
|
|
2574
|
+
/**
|
|
2575
|
+
* Write the idle-release window, or drop the persisted override with
|
|
2576
|
+
* `reset` so the deployment baseline applies again.
|
|
2577
|
+
*
|
|
2578
|
+
* HOT: the rewrite reaches every live bridge, so a user changing the window
|
|
2579
|
+
* never has to kill a running session to see it take effect.
|
|
2580
|
+
*/
|
|
2581
|
+
setIdleRelease(idleTimeoutMs: number | undefined, reset?: boolean): Promise<void>;
|
|
2582
|
+
/**
|
|
2583
|
+
* Recompute the idle-release window from override → deployment baseline, and
|
|
2584
|
+
* push the result to every live bridge.
|
|
2585
|
+
*/
|
|
2586
|
+
private applyIdleReleaseTuning;
|
|
2587
|
+
/**
|
|
2588
|
+
* The idle-release window that runs, in ms: the settings page's pick, else
|
|
2589
|
+
* the deployment baseline.
|
|
2590
|
+
*/
|
|
2591
|
+
private idleTimeoutMs;
|
|
1761
2592
|
/**
|
|
1762
2593
|
* Write the approval deadline, or the mode (omitted keys keep their value),
|
|
1763
2594
|
* or drop the persisted override with `reset` so the config-file baseline —
|
|
@@ -1788,6 +2619,7 @@ export declare class BridgeManager {
|
|
|
1788
2619
|
private forgetDshSession;
|
|
1789
2620
|
private sessionWorkspaceResolver?;
|
|
1790
2621
|
private acpModeResolver?;
|
|
2622
|
+
private attachmentResolver?;
|
|
1791
2623
|
/** Cached `chatSessionAcpMode` answers, keyed by DSH chat session id. */
|
|
1792
2624
|
private readonly acpModeCache;
|
|
1793
2625
|
/** In-flight cold reads behind `chatSessionAcpModeAnswer`, keyed by session id. */
|