dsh-ntr 0.1.6 → 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 +1 -1
- package/README.md +1 -1
- package/cordis.patch.yml +18 -18
- package/lib/bridge.js +120 -291
- package/lib/client/acp-ui.js +60 -32
- package/lib/client/index.js +17 -0
- package/lib/client/panel.js +37 -42
- package/lib/client/permission-dock.js +72 -0
- package/lib/client/session-controls.js +4 -4
- package/lib/client/store.js +58 -12
- package/lib/client.js +296 -95
- package/lib/client.js.map +1 -1
- package/lib/impl.js +30 -19
- package/lib/index.js +6 -51
- package/lib/types/bridge.d.ts +60 -154
- package/lib/types/client/panel.d.ts +12 -19
- package/lib/types/client/permission-dock.d.ts +31 -0
- package/lib/types/client/store.d.ts +42 -12
- package/lib/types/impl.d.ts +31 -17
- package/lib/types/protocol.d.ts +27 -12
- package/package.json +1 -1
- package/src/bridge.ts +147 -357
- package/src/client/acp-ui.tsx +60 -32
- package/src/client/index.tsx +18 -0
- package/src/client/panel.tsx +54 -58
- package/src/client/permission-dock.tsx +133 -0
- package/src/client/session-controls.tsx +4 -4
- package/src/client/store.ts +81 -25
- package/src/impl.ts +49 -29
- package/src/index.ts +6 -65
- package/src/protocol.ts +32 -12
package/lib/client/store.js
CHANGED
|
@@ -6,6 +6,39 @@
|
|
|
6
6
|
* Deliberately framework-free: React binds through `useSyncExternalStore`.
|
|
7
7
|
*/
|
|
8
8
|
import { DEFAULT_TAG_HALO } from "../protocol.js";
|
|
9
|
+
/** Returned by {@link deadlineRemainingLabel} once the deadline has passed. */
|
|
10
|
+
export const PERMISSION_DEADLINE_EXPIRED = 'expired';
|
|
11
|
+
/**
|
|
12
|
+
* The asks that belong to one DSH chat conversation. An ask without a
|
|
13
|
+
* `dshSessionId` belongs to no conversation (an `/acp` RPC-only session), so it
|
|
14
|
+
* is deliberately absent from every conversation's dock and stays in the
|
|
15
|
+
* settings-panel ledger.
|
|
16
|
+
*/
|
|
17
|
+
export function permissionsForSession(permissions, dshSessionId) {
|
|
18
|
+
return permissions.filter(permission => permission.dshSessionId === dshSessionId);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Compact countdown for one ask, or `undefined` when there is nothing to count
|
|
22
|
+
* down (`wait` mode, or a frame without a deadline). Pure so the formatting is
|
|
23
|
+
* testable without a clock: the dock passes `Date.now()` on its tick.
|
|
24
|
+
*
|
|
25
|
+
* Past the deadline the answer is {@link PERMISSION_DEADLINE_EXPIRED} rather
|
|
26
|
+
* than a negative number — the host has already armed the expiry, and a card
|
|
27
|
+
* counting backwards would claim a deadline that no longer exists.
|
|
28
|
+
*/
|
|
29
|
+
export function deadlineRemainingLabel(deadlineAt, now, mode) {
|
|
30
|
+
if (mode === 'wait' || deadlineAt === undefined)
|
|
31
|
+
return undefined;
|
|
32
|
+
const remainingMs = deadlineAt - now;
|
|
33
|
+
if (remainingMs <= 0)
|
|
34
|
+
return PERMISSION_DEADLINE_EXPIRED;
|
|
35
|
+
const seconds = Math.ceil(remainingMs / 1000);
|
|
36
|
+
if (seconds < 60)
|
|
37
|
+
return `${String(seconds)}s`;
|
|
38
|
+
const minutes = Math.floor(seconds / 60);
|
|
39
|
+
const rest = seconds % 60;
|
|
40
|
+
return rest === 0 ? `${String(minutes)}m` : `${String(minutes)}m ${String(rest)}s`;
|
|
41
|
+
}
|
|
9
42
|
const MAX_BLOCKS = 400;
|
|
10
43
|
/** How many observed ACP transcripts stay live at once (insertion-ordered eviction). */
|
|
11
44
|
const MAX_WATCHED = 6;
|
|
@@ -298,7 +331,6 @@ export class AcpStore {
|
|
|
298
331
|
seatCatalogs: {},
|
|
299
332
|
probingModels: {},
|
|
300
333
|
probeErrors: {},
|
|
301
|
-
approvalRouted: false,
|
|
302
334
|
chatSessions: [],
|
|
303
335
|
tagHalo: { ...DEFAULT_TAG_HALO },
|
|
304
336
|
approvalDeadlines: undefined,
|
|
@@ -501,7 +533,7 @@ export class AcpStore {
|
|
|
501
533
|
/** Snapshot refresh used on panel open and after a stream outage. */
|
|
502
534
|
async resync() {
|
|
503
535
|
try {
|
|
504
|
-
const [{ agents
|
|
536
|
+
const [{ agents }, { sessions }, defaultAgentInfo, detectResult, presetResult, tagHaloResult, deadlinesResult] = await Promise.all([
|
|
505
537
|
this.call('agents/list'),
|
|
506
538
|
this.call('sessions/list'),
|
|
507
539
|
// The plugin's OWN default ACP agent — a plugin-private id, not a DSH
|
|
@@ -526,7 +558,6 @@ export class AcpStore {
|
|
|
526
558
|
sessions,
|
|
527
559
|
defaultAgent: defaultAgentInfo?.agentId,
|
|
528
560
|
detectedAgents,
|
|
529
|
-
approvalRouted: approvalRouted === true,
|
|
530
561
|
preset: presetResult?.preset ?? undefined,
|
|
531
562
|
tagHalo: tagHaloResult?.halo ?? { ...DEFAULT_TAG_HALO },
|
|
532
563
|
approvalDeadlines: deadlinesResult.deadlines,
|
|
@@ -1244,12 +1275,21 @@ export class AcpStore {
|
|
|
1244
1275
|
this.flashError(error);
|
|
1245
1276
|
}
|
|
1246
1277
|
}
|
|
1278
|
+
/**
|
|
1279
|
+
* Answer one ask (`optionId` absent = cancel it). A failure is reported
|
|
1280
|
+
* through the store's notice — the settings panel renders that — AND
|
|
1281
|
+
* rethrown, so the conversation dock can pin the message next to the card
|
|
1282
|
+
* that failed instead of leaving it on a settings page the user may not
|
|
1283
|
+
* have open. Callers that cannot show it (the settings panel) swallow it
|
|
1284
|
+
* after the notice.
|
|
1285
|
+
*/
|
|
1247
1286
|
async respondPermission(requestId, optionId) {
|
|
1248
1287
|
try {
|
|
1249
1288
|
await this.call('permission/respond', { requestId, ...(optionId === undefined ? {} : { optionId }) });
|
|
1250
1289
|
}
|
|
1251
1290
|
catch (error) {
|
|
1252
1291
|
this.flashError(error);
|
|
1292
|
+
throw error;
|
|
1253
1293
|
}
|
|
1254
1294
|
}
|
|
1255
1295
|
async stopAgent(agentId) {
|
|
@@ -1601,16 +1641,22 @@ export class AcpStore {
|
|
|
1601
1641
|
return this.loadAgentCatalog(event.agentId);
|
|
1602
1642
|
}
|
|
1603
1643
|
case 'permission/request': {
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1644
|
+
// Verbatim copy of the host's frame. The host decides what the ask is
|
|
1645
|
+
// called (`toolName` / `reason`) and which options exist; deriving a
|
|
1646
|
+
// title from `toolCall` here used to make the card say something the
|
|
1647
|
+
// host never said.
|
|
1648
|
+
const permission = {
|
|
1649
|
+
requestId: event.requestId,
|
|
1650
|
+
sessionId: event.sessionId,
|
|
1651
|
+
...(event.dshSessionId === undefined ? {} : { dshSessionId: event.dshSessionId }),
|
|
1652
|
+
toolName: event.toolName,
|
|
1653
|
+
...(event.reason === undefined ? {} : { reason: event.reason }),
|
|
1654
|
+
options: event.options,
|
|
1655
|
+
timeoutMode: event.timeoutMode,
|
|
1656
|
+
...(event.deadlineAt === undefined ? {} : { deadlineAt: event.deadlineAt }),
|
|
1657
|
+
};
|
|
1607
1658
|
this.mutate({
|
|
1608
|
-
permissions: [...this.state.permissions.filter(p => p.requestId !== event.requestId),
|
|
1609
|
-
requestId: event.requestId,
|
|
1610
|
-
sessionId: event.sessionId,
|
|
1611
|
-
title,
|
|
1612
|
-
options: event.options,
|
|
1613
|
-
}],
|
|
1659
|
+
permissions: [...this.state.permissions.filter(p => p.requestId !== event.requestId), permission],
|
|
1614
1660
|
});
|
|
1615
1661
|
return Promise.resolve();
|
|
1616
1662
|
}
|