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
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
* React surface for ACP: the settings configuration section.
|
|
3
3
|
*
|
|
4
4
|
* Scope is deliberately narrow — this is a CONFIGURATION surface. Runtime
|
|
5
|
-
* interaction lives in the conversation: permission
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
5
|
+
* interaction lives in the conversation: every permission ask with a chat
|
|
6
|
+
* identity is answered on a card above that conversation's composer
|
|
7
|
+
* (`permission-dock.tsx`). The ledger card here is the cross-session mirror —
|
|
8
|
+
* asks whose conversation is not open, or which have no chat identity at all —
|
|
9
|
+
* and is where those are answered (see `permissionLedgerCard`).
|
|
10
10
|
*
|
|
11
11
|
* Each configured agent is one compact row (status + current model + badges)
|
|
12
12
|
* that expands into the full connection form and model catalog. The row stays
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* All business state lives in the injected AcpStore.
|
|
16
16
|
*/
|
|
17
17
|
import type { AcpStore, UiState } from './store.ts';
|
|
18
|
-
import type { AcpAgentDef, AgentSavePayload, AgentView, DetectedAgentInfo } from '../protocol.ts';
|
|
18
|
+
import type { AcpAgentDef, AcpApprovalDeadlines, AgentSavePayload, AgentView, DetectedAgentInfo, PermissionTimeoutMode } from '../protocol.ts';
|
|
19
19
|
interface WithStore {
|
|
20
20
|
acp: AcpStore;
|
|
21
21
|
}
|
|
@@ -30,22 +30,61 @@ export declare function payloadEquals(payload: AgentSavePayload, current: AcpAge
|
|
|
30
30
|
/**
|
|
31
31
|
* The settings panel's permission-ledger rule.
|
|
32
32
|
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
* (`approvalRouted` → a read-only count, no buttons) hid the only answer
|
|
40
|
-
* entry exactly when the native route had degraded to unavailable, and the
|
|
41
|
-
* ask then hung until its timeout.
|
|
42
|
-
*
|
|
43
|
-
* `fellBackFromConversation` is the case the card has to explain: the host
|
|
44
|
-
* can route asks, and this one still did not make it.
|
|
33
|
+
* Pending asks are shown in the conversation card while that conversation is
|
|
34
|
+
* open; this card is the cross-session mirror for the ones it cannot show —
|
|
35
|
+
* asks belonging to another conversation, and `/acp` RPC-only asks with no
|
|
36
|
+
* chat identity at all. It therefore appears whenever ANY ask is pending: a
|
|
37
|
+
* pending entry with no conversation card would otherwise have no answer
|
|
38
|
+
* surface anywhere.
|
|
45
39
|
*/
|
|
46
|
-
export declare function permissionLedgerCard(ui: Pick<UiState, '
|
|
40
|
+
export declare function permissionLedgerCard(ui: Pick<UiState, 'permissions'>): {
|
|
47
41
|
visible: boolean;
|
|
48
|
-
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* The shortest REAL wait — the first rung above 不等待.
|
|
45
|
+
*
|
|
46
|
+
* Hand-copied from the host half (`APPROVAL_DEADLINE_MIN_WAIT_MS` in
|
|
47
|
+
* `src/impl.ts`): the browser half may not import the host module, so the
|
|
48
|
+
* equivalence is pinned by a test instead (see `tests/acp-view.spec.ts`).
|
|
49
|
+
*/
|
|
50
|
+
export declare const DEADLINE_MIN_WAIT_MS = 30000;
|
|
51
|
+
/**
|
|
52
|
+
* The window the card shows under a mode.
|
|
53
|
+
*
|
|
54
|
+
* 不等待 (`0`) is the **auto** mode's rung: it answers with the option the
|
|
55
|
+
* agent's own kinds imply, so the agent CONTINUES. `abort` has no such rung —
|
|
56
|
+
* refusing every ask the instant it arrives stops the agent at every gate it
|
|
57
|
+
* meets, which is not a timeout but a wall. So under `abort` the window floors
|
|
58
|
+
* to the shortest real wait, and the card never shows a zero it cannot run.
|
|
59
|
+
*
|
|
60
|
+
* `undefined` (the `wait` mode) stays undefined: there is no window at all.
|
|
61
|
+
*/
|
|
62
|
+
export declare function deadlineValueFor(mode: PermissionTimeoutMode, ms: number | undefined): number | undefined;
|
|
63
|
+
/**
|
|
64
|
+
* The rung the slider shows for a window. Exact hits keep their rung; anything
|
|
65
|
+
* else takes the nearest one. A tie goes to the SHORTER wait: the thumb must
|
|
66
|
+
* never promise more patience than the window has.
|
|
67
|
+
*/
|
|
68
|
+
export declare function deadlineSliderIndex(ms: number, mode?: PermissionTimeoutMode): number;
|
|
69
|
+
/**
|
|
70
|
+
* The facts the card has to show, decided from what the host reported.
|
|
71
|
+
*
|
|
72
|
+
* The card carries TWO controls, and they are orthogonal: one window (how
|
|
73
|
+
* long) and one mode (what happens). An override can sit on either of them,
|
|
74
|
+
* so each is decided on its own.
|
|
75
|
+
*
|
|
76
|
+
* `overridden` is what gates the reset button. A baseline that AGREES with the
|
|
77
|
+
* running value is not an override, and offering a reset for it would be a
|
|
78
|
+
* button that does nothing (observed live).
|
|
79
|
+
*/
|
|
80
|
+
export declare function approvalDeadlineFacts(deadlines: AcpApprovalDeadlines): {
|
|
81
|
+
/** The config-file window the card's window outranks — undefined when
|
|
82
|
+
* nothing is being outranked. */
|
|
83
|
+
baseline: number | undefined;
|
|
84
|
+
/** The config-file mode the card's mode outranks, when it differs. */
|
|
85
|
+
modeBaseline: PermissionTimeoutMode | undefined;
|
|
86
|
+
/** A reset is only honest when something is actually being outranked. */
|
|
87
|
+
overridden: boolean;
|
|
49
88
|
};
|
|
50
89
|
/** Settings configuration section for ACP agents. */
|
|
51
90
|
export declare function AcpSettingsSection(props: WithStore): import("react").JSX.Element;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pending ACP permission asks for the staged DSH conversation, rendered as
|
|
3
|
+
* composer-width entries above the composer card (`conversation.input.dock`).
|
|
4
|
+
* The slot itself spans the conversation column; the dock mirrors the host's
|
|
5
|
+
* InputBar width formula so the cards line up with the input box.
|
|
6
|
+
*
|
|
7
|
+
* The host broadcasts every ask as `permission/request`, carrying the DSH chat
|
|
8
|
+
* session id when the ask belongs to a conversation. This dock is the primary
|
|
9
|
+
* answer surface: it renders exactly the asks whose `dshSessionId` is the
|
|
10
|
+
* conversation it was mounted for, and nothing otherwise. There is no
|
|
11
|
+
* ACP-mode gate — the frame set itself is the gate, so an ordinary
|
|
12
|
+
* conversation shows this card if and only if it actually has an ask.
|
|
13
|
+
*
|
|
14
|
+
* The ask's description and options are the agent's own: buttons appear in the
|
|
15
|
+
* agent's order with the agent's names (including "Always Allow"), and
|
|
16
|
+
* `kind` is not rendered. A separate cancel button answers with no option,
|
|
17
|
+
* which the host forwards as `cancelled`. No Enter/Escape handlers: the
|
|
18
|
+
* composer owns its keyboard, and these are ordinary focusable buttons.
|
|
19
|
+
*
|
|
20
|
+
* Multiple asks stack in ARRIVAL order, oldest first: the question ignored
|
|
21
|
+
* longest stays at the top. The list is capped in height by CSS and scrolls.
|
|
22
|
+
* Asks without a chat identity never appear here — they stay in the
|
|
23
|
+
* settings-panel ledger, the cross-session mirror.
|
|
24
|
+
*/
|
|
25
|
+
import type { AcpStore } from './store.ts';
|
|
26
|
+
export interface PermissionDockProps {
|
|
27
|
+
/** Injected face: the store plus the chat session id this dock belongs to. */
|
|
28
|
+
acp: AcpStore;
|
|
29
|
+
sessionId: string;
|
|
30
|
+
}
|
|
31
|
+
export declare function PermissionDock({ acp, sessionId }: PermissionDockProps): import("react").JSX.Element | null;
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Deliberately framework-free: React binds through `useSyncExternalStore`.
|
|
7
7
|
*/
|
|
8
|
-
import type { AcpApprovalDeadlines, AcpConversationChoice, AcpPresetStatus, AcpTagHalo, AgentCatalogResult, AgentProbeModelsResult, AgentSavePayload, AgentTestResult, AgentView, ChatSessionView,
|
|
8
|
+
import type { AcpApprovalDeadlines, AcpConversationChoice, AcpPresetStatus, AcpTagHalo, AgentCatalogResult, AgentProbeModelsResult, AgentSavePayload, AgentTestResult, AgentView, ChatSessionView, DetectedAgentInfo, PermissionTimeoutMode, SessionView } from '../protocol.ts';
|
|
9
9
|
/** One slash command the ACP agent declared, whole row (the menu draws name, description and `input.hint`). */
|
|
10
10
|
export type AgentCommand = NonNullable<ChatSessionView['availableCommands']>[number];
|
|
11
11
|
/** The unary RPC face (narrowed `ctx.connection.rpc.call`). */
|
|
@@ -84,16 +84,49 @@ export type Block = {
|
|
|
84
84
|
tone: 'info' | 'error';
|
|
85
85
|
text: string;
|
|
86
86
|
};
|
|
87
|
+
/**
|
|
88
|
+
* One permission ask waiting for a human. Every field is copied VERBATIM from
|
|
89
|
+
* the host's `permission/request` frame: the host owns what the ask is called
|
|
90
|
+
* (`toolName` / `reason`), which options the agent offered (order included),
|
|
91
|
+
* and whether the ask carries a deadline. The browser does not derive a title
|
|
92
|
+
* from `toolCall` and does not second-guess the agent's option labels.
|
|
93
|
+
*/
|
|
87
94
|
export interface PendingPermission {
|
|
88
95
|
requestId: string;
|
|
96
|
+
/** ACP session id. */
|
|
89
97
|
sessionId: string;
|
|
90
|
-
|
|
98
|
+
/** DSH chat session id; absent for `/acp` RPC-only sessions (settings-only ask). */
|
|
99
|
+
dshSessionId?: string;
|
|
100
|
+
toolName: string;
|
|
101
|
+
reason?: string;
|
|
91
102
|
options: Array<{
|
|
92
103
|
optionId: string;
|
|
93
104
|
name: string;
|
|
94
105
|
kind: string;
|
|
95
106
|
}>;
|
|
107
|
+
timeoutMode: PermissionTimeoutMode;
|
|
108
|
+
/** Epoch ms; absent in `wait` mode. */
|
|
109
|
+
deadlineAt?: number;
|
|
96
110
|
}
|
|
111
|
+
/** Returned by {@link deadlineRemainingLabel} once the deadline has passed. */
|
|
112
|
+
export declare const PERMISSION_DEADLINE_EXPIRED = "expired";
|
|
113
|
+
/**
|
|
114
|
+
* The asks that belong to one DSH chat conversation. An ask without a
|
|
115
|
+
* `dshSessionId` belongs to no conversation (an `/acp` RPC-only session), so it
|
|
116
|
+
* is deliberately absent from every conversation's dock and stays in the
|
|
117
|
+
* settings-panel ledger.
|
|
118
|
+
*/
|
|
119
|
+
export declare function permissionsForSession(permissions: readonly PendingPermission[], dshSessionId: string): PendingPermission[];
|
|
120
|
+
/**
|
|
121
|
+
* Compact countdown for one ask, or `undefined` when there is nothing to count
|
|
122
|
+
* down (`wait` mode, or a frame without a deadline). Pure so the formatting is
|
|
123
|
+
* testable without a clock: the dock passes `Date.now()` on its tick.
|
|
124
|
+
*
|
|
125
|
+
* Past the deadline the answer is {@link PERMISSION_DEADLINE_EXPIRED} rather
|
|
126
|
+
* than a negative number — the host has already armed the expiry, and a card
|
|
127
|
+
* counting backwards would claim a deadline that no longer exists.
|
|
128
|
+
*/
|
|
129
|
+
export declare function deadlineRemainingLabel(deadlineAt: number | undefined, now: number, mode: PermissionTimeoutMode): string | undefined;
|
|
97
130
|
/**
|
|
98
131
|
* One observed ACP transcript, keyed by ACP session id. This is the ACP view
|
|
99
132
|
* tab's data source: the native chat plane can only render text/reasoning
|
|
@@ -164,17 +197,6 @@ export interface UiState {
|
|
|
164
197
|
* that never answers, …) reads as a dead button.
|
|
165
198
|
*/
|
|
166
199
|
probeErrors: Record<string, string>;
|
|
167
|
-
/**
|
|
168
|
-
* Host reports that it HAS the native approval chain (permission asks are
|
|
169
|
-
* answered in the chat composer). This is a capability report, not a
|
|
170
|
-
* per-ask promise: an individual ask still falls back to the settings-panel
|
|
171
|
-
* ledger when the service is unavailable this time, or when the session has
|
|
172
|
-
* no chat identity behind it (`AgentBridge.askNativeApproval`). The panel
|
|
173
|
-
* therefore answers ANY pending ask, whatever this flag says — see
|
|
174
|
-
* `permissionLedgerCard` in panel.tsx. False on an older host or when the
|
|
175
|
-
* approval service is missing, in which case the ledger is the only path.
|
|
176
|
-
*/
|
|
177
|
-
approvalRouted: boolean;
|
|
178
200
|
/**
|
|
179
201
|
* ACP agent sessions backing the chat session the composer control sits in.
|
|
180
202
|
* Only meaningful together with {@link chatSessionsFor}: the control reads it
|
|
@@ -556,6 +578,14 @@ export declare class AcpStore {
|
|
|
556
578
|
/** Mirror the running flag onto a transcript the ACP view is showing. */
|
|
557
579
|
private markWatchedRunning;
|
|
558
580
|
setConfigOption(configId: string, value: string): Promise<void>;
|
|
581
|
+
/**
|
|
582
|
+
* Answer one ask (`optionId` absent = cancel it). A failure is reported
|
|
583
|
+
* through the store's notice — the settings panel renders that — AND
|
|
584
|
+
* rethrown, so the conversation dock can pin the message next to the card
|
|
585
|
+
* that failed instead of leaving it on a settings page the user may not
|
|
586
|
+
* have open. Callers that cannot show it (the settings panel) swallow it
|
|
587
|
+
* after the notice.
|
|
588
|
+
*/
|
|
559
589
|
respondPermission(requestId: string, optionId?: string): Promise<void>;
|
|
560
590
|
stopAgent(agentId: string): Promise<void>;
|
|
561
591
|
/**
|
|
@@ -572,7 +602,7 @@ export declare class AcpStore {
|
|
|
572
602
|
*/
|
|
573
603
|
setTagHalo(halo: AcpTagHalo): Promise<void>;
|
|
574
604
|
/**
|
|
575
|
-
* Write
|
|
605
|
+
* Write the approval deadline (settings card), or drop the persisted override
|
|
576
606
|
* with `reset` so the `cordis.patch.yml` baseline applies again.
|
|
577
607
|
*
|
|
578
608
|
* The host answer — the same route returns the deadlines now in force — is the
|
|
@@ -580,7 +610,14 @@ export declare class AcpStore {
|
|
|
580
610
|
* merged with a value this browser did not know about) self-corrects instead
|
|
581
611
|
* of drifting from what runs.
|
|
582
612
|
*/
|
|
583
|
-
setApprovalDeadline(
|
|
613
|
+
setApprovalDeadline(ms: number): Promise<void>;
|
|
614
|
+
/**
|
|
615
|
+
* Write the mode: what an expired deadline DOES (`abort` refuses, `auto`
|
|
616
|
+
* answers with the option the agent's own kinds imply, `wait` never
|
|
617
|
+
* expires). Same write path as the window, so the host answer stays
|
|
618
|
+
* the authority.
|
|
619
|
+
*/
|
|
620
|
+
setApprovalTimeoutMode(mode: PermissionTimeoutMode): Promise<void>;
|
|
584
621
|
/** Drop the persisted override: the config file (or the built-in default) decides again. */
|
|
585
622
|
resetApprovalDeadlines(): Promise<void>;
|
|
586
623
|
private writeApprovalDeadlines;
|
package/lib/types/impl.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* fakes.
|
|
6
6
|
*/
|
|
7
7
|
import { randomUUID } from './ids.ts';
|
|
8
|
-
import type { AcpAgentCatalog, AcpAgentDef, AcpApprovalDeadlines, AcpConversationChoice, AcpEvent, AcpMcpServerDef, AcpPluginConfig, AcpPresetStatus, AcpTagHalo, AgentCatalogResult, AgentProbeModelsResult, AgentTestResult, AgentView, ApprovalDeadlineKey, ChatSessionView, ChatTranscriptView, DetectedAgentInfo, PermissionOptionView, PermissionOutcome, RpcResult } from './protocol.ts';
|
|
8
|
+
import type { AcpAgentCatalog, AcpAgentDef, AcpApprovalDeadlines, AcpConversationChoice, AcpEvent, AcpMcpServerDef, AcpPluginConfig, AcpPresetStatus, AcpTagHalo, AgentCatalogResult, AgentProbeModelsResult, AgentTestResult, AgentView, ApprovalDeadlineKey, ApprovalDeadlinePatch, ChatSessionView, ChatTranscriptView, DetectedAgentInfo, PermissionOptionView, PermissionOutcome, PermissionTimeoutMode, RpcResult } from './protocol.ts';
|
|
9
9
|
export { randomUUID };
|
|
10
10
|
/** Structured failure the RPC layer converts to `{ok:false, error}` and `apply()` surfaces at boot. */
|
|
11
11
|
export declare class AcpError extends Error {
|
|
@@ -35,6 +35,36 @@ export declare function validateTagHalo(value: unknown): AcpTagHalo;
|
|
|
35
35
|
* longer would be a mis-typed number rather than a deliberate window.
|
|
36
36
|
*/
|
|
37
37
|
export declare const APPROVAL_DEADLINE_MAX_MS = 86400000;
|
|
38
|
+
/**
|
|
39
|
+
* The shortest REAL wait: the first rung of the card's ladder (30 seconds).
|
|
40
|
+
*
|
|
41
|
+
* The floor for a mode that must not wait zero — see
|
|
42
|
+
* {@link normalizeApprovalWindow}.
|
|
43
|
+
*/
|
|
44
|
+
export declare const APPROVAL_DEADLINE_MIN_WAIT_MS = 30000;
|
|
45
|
+
/**
|
|
46
|
+
* Make a window legal for the mode it runs under.
|
|
47
|
+
*
|
|
48
|
+
* `0` means 不等待 — the ask is settled the moment it arrives instead of
|
|
49
|
+
* waiting. That is the **auto** mode's business: `auto` answers with the
|
|
50
|
+
* option the agent's own kinds imply, so the agent CONTINUES, and an
|
|
51
|
+
* unattended deployment gets a sane default instead of a hang.
|
|
52
|
+
*
|
|
53
|
+
* Under `abort` the same zero would refuse every ask the instant it arrived:
|
|
54
|
+
* the agent stops at every gate it meets, which is not a timeout, it is a
|
|
55
|
+
* brick wall. So `abort` never offers 不等待, and a window of `0` reaching it
|
|
56
|
+
* (a config file, a state file written by an older build, a mode switch that
|
|
57
|
+
* left the window behind) normalizes to the shortest real wait.
|
|
58
|
+
*
|
|
59
|
+
* `wait` ignores the window entirely — it never expires.
|
|
60
|
+
*/
|
|
61
|
+
export declare function normalizeApprovalWindow(mode: PermissionTimeoutMode, ms: number): number;
|
|
62
|
+
/**
|
|
63
|
+
* The order `auto` mode picks an option in, by the kind the agent declared:
|
|
64
|
+
* the broadest grant first, then the narrow one, then the refusals, then
|
|
65
|
+
* whatever the agent offered instead. See {@link pickAutoOption}.
|
|
66
|
+
*/
|
|
67
|
+
export declare const AUTO_OPTION_KIND_PRIORITY: readonly ["allow_always", "allow_once", "reject_once", "reject_always"];
|
|
38
68
|
/**
|
|
39
69
|
* Validate ONE approval deadline, shared by every layer that accepts one.
|
|
40
70
|
*
|
|
@@ -48,10 +78,32 @@ export declare const APPROVAL_DEADLINE_MAX_MS = 86400000;
|
|
|
48
78
|
export declare function validateApprovalDeadlineMs(raw: unknown, label: string, code: 'bad_config' | 'bad_payload', hint: string): number;
|
|
49
79
|
/**
|
|
50
80
|
* Validate a settings-page deadline write. Omitted keys are simply absent from
|
|
51
|
-
* the result — the manager merges, so a card that only
|
|
52
|
-
*
|
|
81
|
+
* the result — the manager merges, so a card that only moved the mode must not
|
|
82
|
+
* reset the number. `reset` is not a deadline and is dropped here.
|
|
53
83
|
*/
|
|
54
84
|
export declare function validateApprovalDeadlines(value: unknown): Partial<Record<ApprovalDeadlineKey, number>>;
|
|
85
|
+
/**
|
|
86
|
+
* Validate the mode a write asks for. Three values, and the list is the whole
|
|
87
|
+
* vocabulary: an unanswered ask is either answered for you, kept waiting, or
|
|
88
|
+
* refused. Anything else is a typo, not a new mode.
|
|
89
|
+
*/
|
|
90
|
+
export declare function validatePermissionTimeoutMode(raw: unknown, code: 'bad_config' | 'bad_payload'): PermissionTimeoutMode;
|
|
91
|
+
/**
|
|
92
|
+
* The option an expired deadline answers with in `auto` mode.
|
|
93
|
+
*
|
|
94
|
+
* The pick is by the option's KIND, because that is what the agent declared
|
|
95
|
+
* about the choice it is offering: `allow_always` first (one grant covers the
|
|
96
|
+
* rest of the run), then `allow_once` (the smaller grant), then the reject
|
|
97
|
+
* kinds (refuse but let the agent continue), and finally the first option
|
|
98
|
+
* whatever it is — an agent may offer only a custom kind, and refusing to
|
|
99
|
+
* answer at all would hang the turn.
|
|
100
|
+
*
|
|
101
|
+
* `undefined` means NO option could be read (no options, or none with a usable
|
|
102
|
+
* id). The caller then degrades to `abort`: guessing an option is worse than
|
|
103
|
+
* refusing, and hanging is worse than either. OmniTerm dev picks the same way
|
|
104
|
+
* (`pick_auto_option`, `src/acp/permission.rs`).
|
|
105
|
+
*/
|
|
106
|
+
export declare function pickAutoOption(options: readonly PermissionOptionView[]): PermissionOptionView | undefined;
|
|
55
107
|
/**
|
|
56
108
|
* Validate one agent's `mcpServers` list (the plugin's normalized shape, NOT
|
|
57
109
|
* the ACP wire shape).
|
|
@@ -509,6 +561,21 @@ export declare class EventHub {
|
|
|
509
561
|
subscribe(sink: (event: AcpEvent) => void): () => void;
|
|
510
562
|
broadcast(event: AcpEvent): void;
|
|
511
563
|
}
|
|
564
|
+
/** Everything a `permission/request` frame carries besides the ids the ledger owns. */
|
|
565
|
+
export interface PendingPermissionFields {
|
|
566
|
+
/** DSH chat session id; absent for `/acp` RPC-only sessions. */
|
|
567
|
+
dshSessionId?: string;
|
|
568
|
+
toolCall: unknown;
|
|
569
|
+
/** Host-derived human name (`permissionToolName`). */
|
|
570
|
+
toolName: string;
|
|
571
|
+
/** One-line detail from the agent's declared input, when it has any. */
|
|
572
|
+
reason?: string;
|
|
573
|
+
options: PermissionOptionView[];
|
|
574
|
+
/** What an unanswered ask does when its deadline fires. */
|
|
575
|
+
timeoutMode: PermissionTimeoutMode;
|
|
576
|
+
/** Epoch ms of the deadline; absent in `wait` mode (no deadline). */
|
|
577
|
+
deadlineAt?: number;
|
|
578
|
+
}
|
|
512
579
|
export interface PendingPermission {
|
|
513
580
|
requestId: string;
|
|
514
581
|
sessionId: string;
|
|
@@ -518,17 +585,12 @@ export interface PendingPermission {
|
|
|
518
585
|
* client receives (see `permissionReplayFrames`). Absent on entries created
|
|
519
586
|
* without a view — those still answer, they just cannot be re-shown.
|
|
520
587
|
*/
|
|
521
|
-
view?:
|
|
522
|
-
toolCall: unknown;
|
|
523
|
-
options: PermissionOptionView[];
|
|
524
|
-
};
|
|
588
|
+
view?: PendingPermissionFields;
|
|
525
589
|
}
|
|
526
590
|
/** One outstanding ask as a reconnecting client must be re-shown it. */
|
|
527
|
-
export interface PendingPermissionView {
|
|
591
|
+
export interface PendingPermissionView extends PendingPermissionFields {
|
|
528
592
|
requestId: string;
|
|
529
593
|
sessionId: string;
|
|
530
|
-
toolCall: unknown;
|
|
531
|
-
options: PermissionOptionView[];
|
|
532
594
|
}
|
|
533
595
|
export declare class PermissionLedger {
|
|
534
596
|
private readonly pending;
|
|
@@ -551,6 +613,16 @@ export declare class PermissionLedger {
|
|
|
551
613
|
*/
|
|
552
614
|
pendingViews(): PendingPermissionView[];
|
|
553
615
|
}
|
|
616
|
+
/**
|
|
617
|
+
* The `permission/request` frame for one ask.
|
|
618
|
+
*
|
|
619
|
+
* ONE builder for both the live broadcast and the reconnect replay, so the two
|
|
620
|
+
* can never drift: a client that reconnects must receive exactly the frame the
|
|
621
|
+
* live one carried (its conversation card filters on `dshSessionId` and shows
|
|
622
|
+
* `toolName` / `deadlineAt`, so a replay missing them would silently drop the
|
|
623
|
+
* ask).
|
|
624
|
+
*/
|
|
625
|
+
export declare function permissionRequestFrame(view: PendingPermissionView): AcpEvent;
|
|
554
626
|
/**
|
|
555
627
|
* Frames that re-establish a freshly connected client's view of the pending
|
|
556
628
|
* permission asks: every outstanding ask re-sent as its original
|
|
@@ -558,10 +630,10 @@ export declare class PermissionLedger {
|
|
|
558
630
|
*
|
|
559
631
|
* Why a replay at all: a `permission/request` is broadcast ONCE, when the ask is
|
|
560
632
|
* created. A client that connects later — page reload, second tab, stream
|
|
561
|
-
* outage — never receives it, and
|
|
562
|
-
*
|
|
563
|
-
*
|
|
564
|
-
*
|
|
633
|
+
* outage — never receives it, and that card (conversation or settings ledger)
|
|
634
|
+
* is the ONLY answer entry for the ask, so the ask is invisible to exactly the
|
|
635
|
+
* client that needs it. OmniTerm closes the same hole with
|
|
636
|
+
* `PermissionManager::pending_events()` + a `PermissionsSynced` marker on
|
|
565
637
|
* every WS connect.
|
|
566
638
|
*
|
|
567
639
|
* Why the terminator is mandatory: the replay is a snapshot, so an ask resolved
|
|
@@ -607,18 +679,18 @@ export interface AcpManager {
|
|
|
607
679
|
/** Write the halo preference (persisted with the agent registry). */
|
|
608
680
|
setTagHalo?(halo: AcpTagHalo): Promise<void>;
|
|
609
681
|
/**
|
|
610
|
-
* The approval
|
|
611
|
-
* (the `cordis.patch.yml` baseline it outranks
|
|
612
|
-
*
|
|
613
|
-
*
|
|
682
|
+
* The approval deadline in force, with the facts that explain an override
|
|
683
|
+
* (the `cordis.patch.yml` baseline it outranks). `undefined` on a host half
|
|
684
|
+
* from before the card existed, which hides the settings card instead of
|
|
685
|
+
* guessing a value.
|
|
614
686
|
*/
|
|
615
687
|
approvalDeadlines?(): AcpApprovalDeadlines | undefined;
|
|
616
688
|
/**
|
|
617
|
-
* Write
|
|
618
|
-
* drop the persisted override with `reset` so the config-file baseline
|
|
689
|
+
* Write the approval deadline, or the mode (omitted keys keep their value),
|
|
690
|
+
* or drop the persisted override with `reset` so the config-file baseline
|
|
619
691
|
* applies again. Applies to the running bridges without a restart.
|
|
620
692
|
*/
|
|
621
|
-
setApprovalDeadlines?(patch:
|
|
693
|
+
setApprovalDeadlines?(patch: ApprovalDeadlinePatch, reset?: boolean): Promise<void>;
|
|
622
694
|
detectAgents?(): Promise<DetectedAgentInfo[]>;
|
|
623
695
|
saveAgent?(def: AcpAgentDef): Promise<void>;
|
|
624
696
|
deleteAgent?(agentId: string): Promise<void>;
|
|
@@ -654,12 +726,6 @@ export interface AcpManager {
|
|
|
654
726
|
* cache (newly published models appear, withdrawn ones drop out).
|
|
655
727
|
*/
|
|
656
728
|
cachedAgentModels?(agentId: string): AgentProbeModelsResult | undefined;
|
|
657
|
-
/**
|
|
658
|
-
* True when ACP permission asks are routed to the host's native approval
|
|
659
|
-
* service (answered in the chat composer) instead of the settings-panel
|
|
660
|
-
* ledger. Read-only capability report for `agents/list`.
|
|
661
|
-
*/
|
|
662
|
-
approvalRouting?(): boolean;
|
|
663
729
|
/**
|
|
664
730
|
* ACP agent sessions backing one DSH chat session (looked up by chat session
|
|
665
731
|
* id), so the composer can offer the agent's OWN mode list. Empty when no
|