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.
@@ -561,6 +561,21 @@ export declare class EventHub {
561
561
  subscribe(sink: (event: AcpEvent) => void): () => void;
562
562
  broadcast(event: AcpEvent): void;
563
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
+ }
564
579
  export interface PendingPermission {
565
580
  requestId: string;
566
581
  sessionId: string;
@@ -570,17 +585,12 @@ export interface PendingPermission {
570
585
  * client receives (see `permissionReplayFrames`). Absent on entries created
571
586
  * without a view — those still answer, they just cannot be re-shown.
572
587
  */
573
- view?: {
574
- toolCall: unknown;
575
- options: PermissionOptionView[];
576
- };
588
+ view?: PendingPermissionFields;
577
589
  }
578
590
  /** One outstanding ask as a reconnecting client must be re-shown it. */
579
- export interface PendingPermissionView {
591
+ export interface PendingPermissionView extends PendingPermissionFields {
580
592
  requestId: string;
581
593
  sessionId: string;
582
- toolCall: unknown;
583
- options: PermissionOptionView[];
584
594
  }
585
595
  export declare class PermissionLedger {
586
596
  private readonly pending;
@@ -603,6 +613,16 @@ export declare class PermissionLedger {
603
613
  */
604
614
  pendingViews(): PendingPermissionView[];
605
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;
606
626
  /**
607
627
  * Frames that re-establish a freshly connected client's view of the pending
608
628
  * permission asks: every outstanding ask re-sent as its original
@@ -610,10 +630,10 @@ export declare class PermissionLedger {
610
630
  *
611
631
  * Why a replay at all: a `permission/request` is broadcast ONCE, when the ask is
612
632
  * created. A client that connects later — page reload, second tab, stream
613
- * outage — never receives it, and for a ledger ask (the `/acp` RPC path, or a
614
- * native route that degraded) that card is the ONLY answer entry, so the ask is
615
- * invisible to exactly the client that needs it. OmniTerm closes the same hole
616
- * with `PermissionManager::pending_events()` + a `PermissionsSynced` marker on
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
617
637
  * every WS connect.
618
638
  *
619
639
  * Why the terminator is mandatory: the replay is a snapshot, so an ask resolved
@@ -706,12 +726,6 @@ export interface AcpManager {
706
726
  * cache (newly published models appear, withdrawn ones drop out).
707
727
  */
708
728
  cachedAgentModels?(agentId: string): AgentProbeModelsResult | undefined;
709
- /**
710
- * True when ACP permission asks are routed to the host's native approval
711
- * service (answered in the chat composer) instead of the settings-panel
712
- * ledger. Read-only capability report for `agents/list`.
713
- */
714
- approvalRouting?(): boolean;
715
729
  /**
716
730
  * ACP agent sessions backing one DSH chat session (looked up by chat session
717
731
  * id), so the composer can offer the agent's OWN mode list. Empty when no
@@ -349,12 +349,35 @@ export type AcpEvent = {
349
349
  agentId: string;
350
350
  exposedModels: string[];
351
351
  autoDiscoverModels: boolean;
352
- } | {
352
+ }
353
+ /**
354
+ * One outstanding ask, broadcast ONCE when it is created and replayed as-is
355
+ * to every freshly connected client (see `permission/synced`).
356
+ *
357
+ * Every ACP permission ask carries everything the conversation card needs:
358
+ * who owns it (`dshSessionId`), what it is about (`toolCall` + the derived
359
+ * `toolName` / `reason`), the options to render verbatim, and the deadline in
360
+ * force. The host never answers on its own outside an expired `abort` /
361
+ * `auto` deadline, so this frame (and its replay) is the answer entry.
362
+ */
363
+ | {
353
364
  type: 'permission/request';
354
365
  requestId: string;
366
+ /** ACP session id the ask belongs to. */
355
367
  sessionId: string;
368
+ /** DSH chat session id; absent for `/acp` RPC-only sessions. */
369
+ dshSessionId?: string;
370
+ /** Raw ACP `ToolCallUpdate` the agent sent — never summarized away. */
356
371
  toolCall: unknown;
372
+ /** Host-derived human name (`permissionToolName`, `_meta` fallback included). */
373
+ toolName: string;
374
+ /** One-line detail from the agent's declared input, when it has any. */
375
+ reason?: string;
357
376
  options: PermissionOptionView[];
377
+ /** What an unanswered ask does when its deadline fires. */
378
+ timeoutMode: PermissionTimeoutMode;
379
+ /** Epoch ms of the deadline; absent in `wait` mode (no deadline). */
380
+ deadlineAt?: number;
358
381
  } | {
359
382
  type: 'permission/resolved';
360
383
  requestId: string;
@@ -366,11 +389,9 @@ export type AcpEvent = {
366
389
  * (`abort`), or answered with the option the agent's own kinds imply
367
390
  * (`auto`).
368
391
  *
369
- * A native ask (the composer takeover) never became a ledger entry, so this
370
- * frame is the ONLY trace of the expiry in the conversation: without it the
371
- * user would watch the composer card vanish with nothing said about it. The
372
- * ledger path does not need it — its card sits on the settings panel and the
373
- * chat line already pointed there.
392
+ * This is the conversation's record of the expiry: the card it was answering
393
+ * is gone, so without this frame the user would see it vanish with nothing
394
+ * said about it.
374
395
  */
375
396
  | {
376
397
  type: 'permission/expired';
@@ -495,12 +516,6 @@ export type RpcResult<T> = {
495
516
  };
496
517
  export interface AgentsListResult {
497
518
  agents: AgentView[];
498
- /**
499
- * True when permission asks are routed to the host approval chain and the
500
- * browser must stop rendering the settings-panel permission card. False (or
501
- * absent on an older host) keeps the degraded ledger path visible.
502
- */
503
- approvalRouted?: boolean;
504
519
  }
505
520
  export type SessionPromptResult = {
506
521
  accepted: true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-ntr",
3
- "version": "0.1.6",
3
+ "version": "0.2.0",
4
4
  "description": "Interactive Agent Client Protocol (ACP) client for the DSH web UI: attach any ACP-capable agent and stream its session.",
5
5
  "repository": "https://github.com/GDWhisper/dsh-ntr.git",
6
6
  "type": "module",