dsh-ntr 0.1.4 → 0.1.6
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 +22 -1
- package/README.md +22 -1
- package/cordis.patch.yml +65 -5
- package/lib/bridge.js +612 -54
- package/lib/client/panel.js +338 -10
- package/lib/client/store.js +77 -3
- package/lib/client.js +511 -28
- package/lib/client.js.map +1 -1
- package/lib/impl.js +290 -7
- package/lib/index.js +46 -3
- package/lib/types/bridge.d.ts +246 -22
- package/lib/types/client/panel.d.ts +92 -0
- package/lib/types/client/store.d.ts +28 -1
- package/lib/types/impl.d.ts +151 -2
- package/lib/types/protocol.d.ts +209 -4
- package/package.json +1 -1
- package/src/bridge.ts +673 -66
- package/src/client/panel.tsx +527 -10
- package/src/client/store.ts +87 -4
- package/src/impl.ts +338 -9
- package/src/index.ts +47 -5
- package/src/protocol.ts +211 -4
package/lib/client/store.js
CHANGED
|
@@ -301,6 +301,7 @@ export class AcpStore {
|
|
|
301
301
|
approvalRouted: false,
|
|
302
302
|
chatSessions: [],
|
|
303
303
|
tagHalo: { ...DEFAULT_TAG_HALO },
|
|
304
|
+
approvalDeadlines: undefined,
|
|
304
305
|
chatTranscripts: {},
|
|
305
306
|
acpMode: {},
|
|
306
307
|
chatBoundAgent: {},
|
|
@@ -500,7 +501,7 @@ export class AcpStore {
|
|
|
500
501
|
/** Snapshot refresh used on panel open and after a stream outage. */
|
|
501
502
|
async resync() {
|
|
502
503
|
try {
|
|
503
|
-
const [{ agents, approvalRouted }, { sessions }, defaultAgentInfo, detectResult, presetResult, tagHaloResult] = await Promise.all([
|
|
504
|
+
const [{ agents, approvalRouted }, { sessions }, defaultAgentInfo, detectResult, presetResult, tagHaloResult, deadlinesResult] = await Promise.all([
|
|
504
505
|
this.call('agents/list'),
|
|
505
506
|
this.call('sessions/list'),
|
|
506
507
|
// The plugin's OWN default ACP agent — a plugin-private id, not a DSH
|
|
@@ -511,6 +512,9 @@ export class AcpStore {
|
|
|
511
512
|
this.call('preset/status').catch(() => null),
|
|
512
513
|
// Host without the endpoint (older half) keeps the local default.
|
|
513
514
|
this.call('ui/tag-halo').catch(() => ({ halo: { ...DEFAULT_TAG_HALO } })),
|
|
515
|
+
// `undefined` (older half) hides the deadline card instead of showing a
|
|
516
|
+
// number this browser made up.
|
|
517
|
+
this.call('ui/approval-deadlines').catch(() => ({ deadlines: undefined })),
|
|
514
518
|
]);
|
|
515
519
|
const detectedAgents = (detectResult && detectResult.agents.length > 0)
|
|
516
520
|
? detectResult.agents
|
|
@@ -525,6 +529,7 @@ export class AcpStore {
|
|
|
525
529
|
approvalRouted: approvalRouted === true,
|
|
526
530
|
preset: presetResult?.preset ?? undefined,
|
|
527
531
|
tagHalo: tagHaloResult?.halo ?? { ...DEFAULT_TAG_HALO },
|
|
532
|
+
approvalDeadlines: deadlinesResult.deadlines,
|
|
528
533
|
});
|
|
529
534
|
const active = this.state.activeSessionId ?? sessions[0]?.sessionId;
|
|
530
535
|
if (active !== undefined)
|
|
@@ -785,12 +790,23 @@ export class AcpStore {
|
|
|
785
790
|
async loadTranscript(sessionId) {
|
|
786
791
|
try {
|
|
787
792
|
const { session, entries } = await this.call('transcript/get', { sessionId });
|
|
793
|
+
// An answer BEHIND what this page already folded must not replace it.
|
|
794
|
+
//
|
|
795
|
+
// The host fills its transcript from the notification pipeline, which the
|
|
796
|
+
// SDK paces independently of the `session/prompt` response, so a read
|
|
797
|
+
// taken right after a turn can still be missing the agent's last chunk.
|
|
798
|
+
// `lastSeq` already refuses to go backwards; the blocks must not either,
|
|
799
|
+
// or switching away and back inside that window truncates a turn the user
|
|
800
|
+
// just watched finish. A resumed session replays as fresh updates, but
|
|
801
|
+
// only after `session/reset` zeroes the watermark — so this cannot swallow
|
|
802
|
+
// a legitimate replay.
|
|
803
|
+
const seen = this.lastSeq.get(sessionId) ?? 0;
|
|
804
|
+
if ((entries.at(-1)?.seq ?? 0) < seen)
|
|
805
|
+
return;
|
|
788
806
|
const blocks = [];
|
|
789
807
|
for (const entry of entries) {
|
|
790
|
-
const before = blocks.length;
|
|
791
808
|
foldUpdate(blocks, entry.update);
|
|
792
809
|
this.lastSeq.set(sessionId, Math.max(this.lastSeq.get(sessionId) ?? 0, entry.seq));
|
|
793
|
-
void before;
|
|
794
810
|
}
|
|
795
811
|
const sessions = this.state.sessions.some(s => s.sessionId === sessionId)
|
|
796
812
|
? this.state.sessions.map(s => (s.sessionId === sessionId ? session : s))
|
|
@@ -1276,6 +1292,54 @@ export class AcpStore {
|
|
|
1276
1292
|
this.flashError(error);
|
|
1277
1293
|
}
|
|
1278
1294
|
}
|
|
1295
|
+
/**
|
|
1296
|
+
* Write the approval deadline (settings card), or drop the persisted override
|
|
1297
|
+
* with `reset` so the `cordis.patch.yml` baseline applies again.
|
|
1298
|
+
*
|
|
1299
|
+
* The host answer — the same route returns the deadlines now in force — is the
|
|
1300
|
+
* authority, so an optimistic write that the host trims (out of range, or
|
|
1301
|
+
* merged with a value this browser did not know about) self-corrects instead
|
|
1302
|
+
* of drifting from what runs.
|
|
1303
|
+
*/
|
|
1304
|
+
async setApprovalDeadline(ms) {
|
|
1305
|
+
await this.writeApprovalDeadlines({ permissionTimeoutMs: ms });
|
|
1306
|
+
}
|
|
1307
|
+
/**
|
|
1308
|
+
* Write the mode: what an expired deadline DOES (`abort` refuses, `auto`
|
|
1309
|
+
* answers with the option the agent's own kinds imply, `wait` never
|
|
1310
|
+
* expires). Same write path as the window, so the host answer stays
|
|
1311
|
+
* the authority.
|
|
1312
|
+
*/
|
|
1313
|
+
async setApprovalTimeoutMode(mode) {
|
|
1314
|
+
await this.writeApprovalDeadlines({ mode });
|
|
1315
|
+
}
|
|
1316
|
+
/** Drop the persisted override: the config file (or the built-in default) decides again. */
|
|
1317
|
+
async resetApprovalDeadlines() {
|
|
1318
|
+
await this.writeApprovalDeadlines({ reset: true });
|
|
1319
|
+
}
|
|
1320
|
+
async writeApprovalDeadlines(payload) {
|
|
1321
|
+
const previous = this.state.approvalDeadlines;
|
|
1322
|
+
// Optimistic only where it is safe: the effective numbers and the mode move
|
|
1323
|
+
// now, and the override facts (`configured`) stay as the host last reported
|
|
1324
|
+
// them. A failure restores the whole view.
|
|
1325
|
+
if (previous !== undefined) {
|
|
1326
|
+
this.mutate({
|
|
1327
|
+
approvalDeadlines: {
|
|
1328
|
+
...previous,
|
|
1329
|
+
...(payload.permissionTimeoutMs === undefined ? {} : { permissionTimeoutMs: payload.permissionTimeoutMs }),
|
|
1330
|
+
...(payload.mode === undefined ? {} : { mode: payload.mode }),
|
|
1331
|
+
},
|
|
1332
|
+
});
|
|
1333
|
+
}
|
|
1334
|
+
try {
|
|
1335
|
+
const result = await this.call('ui/approval-deadlines-set', payload);
|
|
1336
|
+
this.mutate({ approvalDeadlines: result.deadlines });
|
|
1337
|
+
}
|
|
1338
|
+
catch (error) {
|
|
1339
|
+
this.mutate({ approvalDeadlines: previous });
|
|
1340
|
+
this.flashError(error);
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1279
1343
|
/**
|
|
1280
1344
|
* Bind one DSH chat session to an ACP agent. This is the plugin's own
|
|
1281
1345
|
* conversation↔agent mapping: it does NOT write a DSH model route, so it
|
|
@@ -1554,6 +1618,16 @@ export class AcpStore {
|
|
|
1554
1618
|
this.mutate({ permissions: this.state.permissions.filter(p => p.requestId !== event.requestId) });
|
|
1555
1619
|
return Promise.resolve();
|
|
1556
1620
|
}
|
|
1621
|
+
case 'permission/synced': {
|
|
1622
|
+
// Terminator of the reconnect replay: the host just re-sent every
|
|
1623
|
+
// outstanding ask, and this set is the authoritative live list. Drop
|
|
1624
|
+
// anything this client still holds from before the disconnect — an ask
|
|
1625
|
+
// resolved while it was away is gone host-side, and answering it would
|
|
1626
|
+
// only earn `unknown_request`.
|
|
1627
|
+
const live = new Set(event.requestIds);
|
|
1628
|
+
this.mutate({ permissions: this.state.permissions.filter(p => live.has(p.requestId)) });
|
|
1629
|
+
return Promise.resolve();
|
|
1630
|
+
}
|
|
1557
1631
|
case 'chat/agent-locked': {
|
|
1558
1632
|
// The host locks a conversation at the START of its first ACP turn and
|
|
1559
1633
|
// says so at once; this frame is what makes the hand-off animation run
|