@uxnan/shared 0.0.17-alpha.20260926 → 0.0.18-alpha.20260926

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.md CHANGED
@@ -3,7 +3,7 @@
3
3
  ![TypeScript](https://img.shields.io/badge/TypeScript-ESM-3178C6?style=for-the-badge&logo=typescript&logoColor=white)
4
4
  ![Node.js](https://img.shields.io/badge/Node.js-%E2%89%A518-339933?style=for-the-badge&logo=nodedotjs&logoColor=white)
5
5
  ![JSON Schema](https://img.shields.io/badge/validation-Ajv-000000?style=for-the-badge&logo=json&logoColor=white)
6
- ![Contracts](https://img.shields.io/badge/72_methods_%7C_16_notifications-blue?style=for-the-badge)
6
+ ![Contracts](https://img.shields.io/badge/81_methods_%7C_21_notifications-blue?style=for-the-badge)
7
7
 
8
8
  Shared JSON-RPC and E2EE contracts for the [Uxnan](../README.md) ecosystem — the
9
9
  single source of truth every component agrees on. Consumed as a local workspace
@@ -13,7 +13,7 @@ equivalents (see
13
13
  [`architecture/02b-contracts-and-requirements.md`](../architecture/02b-contracts-and-requirements.md)
14
14
  §1 for the canonical contract list).
15
15
 
16
- > **Status:** implemented and stable — **81 JSON-RPC methods** + **22 streaming
16
+ > **Status:** implemented and stable — **81 JSON-RPC methods** + **21 streaming
17
17
  > notifications**, kept lock-step at build time with the `METHOD_NAMES` array and
18
18
  > the `StreamNotification` enum (a compile-time assertion in
19
19
  > `src/jsonrpc/method-registry.ts` fails the build on any drift). Changes are
@@ -124,7 +124,8 @@ export interface IAgentAdapter {
124
124
  * `activeTurnId` is the bridge turn currently in flight on the thread, so the
125
125
  * adapter can address the right run (and refuse if it has already moved on).
126
126
  * `turnId` is the queued turn the text came from — it does NOT start a run of
127
- * its own; it exists so the bridge can mark it `delivered`.
127
+ * its own: the bridge makes it the turn that carries the rest of this run,
128
+ * and keeps naming the run by `activeTurnId` in every call to the adapter.
128
129
  *
129
130
  * Returns **true only when the agent actually took the message**. Return
130
131
  * `false` (don't throw) for an ordinary "too late / not applicable" — the
@@ -56,7 +56,8 @@ export interface AgentCapabilities {
56
56
  * making it wait for the next one — what a CLI does when you type while it
57
57
  * works and it picks the message up at the next tool boundary. The bridge
58
58
  * hands such a turn straight to the adapter (`IAgentAdapter.steerTurn`)
59
- * rather than holding it, and marks it `delivered` (see `TurnStatus`).
59
+ * rather than holding it; the turn that was answering ends there and the new
60
+ * one carries the rest of the agent's run (architecture/02a §5.8.13).
60
61
  *
61
62
  * Optional; absent/false means the agent has no input channel mid-turn (a
62
63
  * one-shot CLI, or a protocol that serializes prompts per session), and its
@@ -169,17 +169,6 @@ export interface TurnSendResult {
169
169
  queued?: boolean;
170
170
  /** 1-based place in the queue when `queued` is true (1 = runs next). */
171
171
  queuePosition?: number;
172
- /**
173
- * True when the agent took the message **into the turn already running**
174
- * rather than making it wait (status `delivered`, see `TurnStatus`). It will
175
- * never run as a turn of its own — the reply belongs to the turn it joined —
176
- * so the client renders the user's message in place and stops offering to
177
- * edit or cancel it. Mutually exclusive with {@link queued}.
178
- *
179
- * Only ever true when the agent advertises `AgentCapabilities.steering`; on
180
- * every other agent a follow-up still comes back `queued`.
181
- */
182
- delivered?: boolean;
183
172
  }
184
173
  export interface QueueStateResult {
185
174
  /** Queued turn ids in drain order. */
@@ -27,11 +27,6 @@ export declare const StreamNotification: {
27
27
  readonly TurnAborted: "stream/turn/aborted";
28
28
  /** A queued turn was removed before it ever ran (status → `cancelled`). */
29
29
  readonly TurnCancelled: "stream/turn/cancelled";
30
- /**
31
- * A queued turn was handed to the agent **inside the turn already running**
32
- * instead of waiting for it (status → `delivered`).
33
- */
34
- readonly TurnDelivered: "stream/turn/delivered";
35
30
  /** The thread's message queue changed (queued, drained, cancelled, paused). */
36
31
  readonly QueueUpdated: "stream/queue/updated";
37
32
  /** The agent resolved an alias (e.g. `opus`) to a concrete model id for this turn. */
@@ -149,22 +144,6 @@ export interface TurnCancelledParams {
149
144
  threadId: string;
150
145
  turnId: string;
151
146
  }
152
- /**
153
- * A queued turn reached the agent **without waiting**: it was folded into the
154
- * turn that was already running (its status is now `delivered`), the way a CLI
155
- * picks up what you typed while it worked. It will never run as a turn of its
156
- * own — the answer is part of `intoTurnId`.
157
- *
158
- * The client keeps the user's message where it is and stops offering to edit or
159
- * cancel it: the agent already has it.
160
- */
161
- export interface TurnDeliveredParams {
162
- threadId: string;
163
- /** The queued turn that was handed over. */
164
- turnId: string;
165
- /** The running turn it was folded into; its reply covers both messages. */
166
- intoTurnId: string;
167
- }
168
147
  /**
169
148
  * The thread's message queue changed. Carries the WHOLE state rather than a
170
149
  * delta, so it is idempotent: a client that missed one (backgrounded, mid-
@@ -10,11 +10,6 @@ export const StreamNotification = {
10
10
  TurnAborted: 'stream/turn/aborted',
11
11
  /** A queued turn was removed before it ever ran (status → `cancelled`). */
12
12
  TurnCancelled: 'stream/turn/cancelled',
13
- /**
14
- * A queued turn was handed to the agent **inside the turn already running**
15
- * instead of waiting for it (status → `delivered`).
16
- */
17
- TurnDelivered: 'stream/turn/delivered',
18
13
  /** The thread's message queue changed (queued, drained, cancelled, paused). */
19
14
  QueueUpdated: 'stream/queue/updated',
20
15
  /** The agent resolved an alias (e.g. `opus`) to a concrete model id for this turn. */
@@ -1 +1 @@
1
- {"version":3,"file":"notifications.js","sourceRoot":"","sources":["../../../src/jsonrpc/notifications.ts"],"names":[],"mappings":"AAkBA,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,WAAW,EAAE,qBAAqB;IAClC,YAAY,EAAE,sBAAsB;IACpC,kFAAkF;IAClF,aAAa,EAAE,uBAAuB;IACtC,mFAAmF;IACnF,YAAY,EAAE,sBAAsB;IACpC,aAAa,EAAE,uBAAuB;IACtC,SAAS,EAAE,mBAAmB;IAC9B,WAAW,EAAE,qBAAqB;IAClC,2EAA2E;IAC3E,aAAa,EAAE,uBAAuB;IACtC;;;OAGG;IACH,aAAa,EAAE,uBAAuB;IACtC,+EAA+E;IAC/E,YAAY,EAAE,sBAAsB;IACpC,sFAAsF;IACtF,aAAa,EAAE,uBAAuB;IACtC;;;;OAIG;IACH,aAAa,EAAE,uBAAuB;IACtC,4BAA4B;IAC5B,aAAa,EAAE,uBAAuB;IACtC;;;;OAIG;IACH,WAAW,EAAE,qBAAqB;IAClC,oEAAoE;IACpE,gBAAgB,EAAE,0BAA0B;IAC5C,6EAA6E;IAC7E,gBAAgB,EAAE,0BAA0B;IAC5C,qDAAqD;IACrD,cAAc,EAAE,wBAAwB;IACxC,wEAAwE;IACxE,cAAc,EAAE,wBAAwB;IACxC,yEAAyE;IACzE,eAAe,EAAE,yBAAyB;IAC1C,0CAA0C;IAC1C,eAAe,EAAE,yBAAyB;IAC1C,uDAAuD;IACvD,cAAc,EAAE,wBAAwB;IACxC,qEAAqE;IACrE,aAAa,EAAE,uBAAuB;CAC9B,CAAC"}
1
+ {"version":3,"file":"notifications.js","sourceRoot":"","sources":["../../../src/jsonrpc/notifications.ts"],"names":[],"mappings":"AAkBA,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,WAAW,EAAE,qBAAqB;IAClC,YAAY,EAAE,sBAAsB;IACpC,kFAAkF;IAClF,aAAa,EAAE,uBAAuB;IACtC,mFAAmF;IACnF,YAAY,EAAE,sBAAsB;IACpC,aAAa,EAAE,uBAAuB;IACtC,SAAS,EAAE,mBAAmB;IAC9B,WAAW,EAAE,qBAAqB;IAClC,2EAA2E;IAC3E,aAAa,EAAE,uBAAuB;IACtC,+EAA+E;IAC/E,YAAY,EAAE,sBAAsB;IACpC,sFAAsF;IACtF,aAAa,EAAE,uBAAuB;IACtC;;;;OAIG;IACH,aAAa,EAAE,uBAAuB;IACtC,4BAA4B;IAC5B,aAAa,EAAE,uBAAuB;IACtC;;;;OAIG;IACH,WAAW,EAAE,qBAAqB;IAClC,oEAAoE;IACpE,gBAAgB,EAAE,0BAA0B;IAC5C,6EAA6E;IAC7E,gBAAgB,EAAE,0BAA0B;IAC5C,qDAAqD;IACrD,cAAc,EAAE,wBAAwB;IACxC,wEAAwE;IACxE,cAAc,EAAE,wBAAwB;IACxC,yEAAyE;IACzE,eAAe,EAAE,yBAAyB;IAC1C,0CAA0C;IAC1C,eAAe,EAAE,yBAAyB;IAC1C,uDAAuD;IACvD,cAAc,EAAE,wBAAwB;IACxC,qEAAqE;IACrE,aAAa,EAAE,uBAAuB;CAC9B,CAAC"}
@@ -86,7 +86,8 @@ export interface LocalControlMessageFrame {
86
86
  export type LocalControlFrame = LocalControlHelloFrame | LocalControlMessageFrame;
87
87
  /** Query parameters of the upgrade URL: `/control?client=<id>&resume=<seq>&instance=<id>`. */
88
88
  export interface LocalControlConnectParams {
89
- /** Stable name of the client (e.g. `desktop`); one live connection per name. */
89
+ /** Stable name of the client (e.g. `desktop-3f9a1c2b7d4e`, `cli`); one live
90
+ * connection per name. */
90
91
  client: string;
91
92
  /** Last notification `seq` the client applied (0 or absent on a fresh start). */
92
93
  resume?: number;
@@ -95,6 +96,17 @@ export interface LocalControlConnectParams {
95
96
  }
96
97
  /** Whether `id` is an acceptable local client name (lowercase, short, no separators). */
97
98
  export declare function isValidLocalClientId(id: string): boolean;
99
+ /**
100
+ * The name Uxnan Desktop's client ids start with. Each desktop **profile** — the
101
+ * installed app, a development build, a disposable `UXNAN_DATA_DIR` — connects
102
+ * under its own `desktop-<profile>` id, because the channel keeps one live
103
+ * connection per name: two desktops sharing one would supersede each other in
104
+ * an endless reconnect loop and trade the same outbound log, presence and
105
+ * tools back and forth (architecture/02a §5.8.15).
106
+ */
107
+ export declare const DESKTOP_LOCAL_CLIENT = "desktop";
108
+ /** Whether a local client id is Uxnan Desktop's (`desktop` or `desktop-<profile>`). */
109
+ export declare function isDesktopClientId(id: string): boolean;
98
110
  /**
99
111
  * The receiver id a local client is registered under in the bridge's session
100
112
  * registry. Prefixed so it can never collide with a paired phone's device id.
@@ -36,6 +36,20 @@ const CLIENT_ID_PATTERN = /^[a-z0-9][a-z0-9-]{0,31}$/;
36
36
  export function isValidLocalClientId(id) {
37
37
  return CLIENT_ID_PATTERN.test(id);
38
38
  }
39
+ /**
40
+ * The name Uxnan Desktop's client ids start with. Each desktop **profile** — the
41
+ * installed app, a development build, a disposable `UXNAN_DATA_DIR` — connects
42
+ * under its own `desktop-<profile>` id, because the channel keeps one live
43
+ * connection per name: two desktops sharing one would supersede each other in
44
+ * an endless reconnect loop and trade the same outbound log, presence and
45
+ * tools back and forth (architecture/02a §5.8.15).
46
+ */
47
+ export const DESKTOP_LOCAL_CLIENT = 'desktop';
48
+ /** Whether a local client id is Uxnan Desktop's (`desktop` or `desktop-<profile>`). */
49
+ export function isDesktopClientId(id) {
50
+ return (isValidLocalClientId(id) &&
51
+ (id === DESKTOP_LOCAL_CLIENT || id.startsWith(`${DESKTOP_LOCAL_CLIENT}-`)));
52
+ }
39
53
  /**
40
54
  * The receiver id a local client is registered under in the bridge's session
41
55
  * registry. Prefixed so it can never collide with a paired phone's device id.
@@ -1 +1 @@
1
- {"version":3,"file":"local-control.js","sourceRoot":"","sources":["../../../src/local-control/local-control.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,2DAA2D;AAC3D,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC;AAExC;;;;GAIG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,oBAAoB,CAAC;AAEvD,0CAA0C;AAC1C,MAAM,CAAC,MAAM,kBAAkB,GAAG,UAAU,CAAC;AAE7C;;;GAGG;AACH,MAAM,CAAC,MAAM,6BAA6B,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AAqE9D,MAAM,iBAAiB,GAAG,2BAA2B,CAAC;AAEtD,yFAAyF;AACzF,MAAM,UAAU,oBAAoB,CAAC,EAAU;IAC7C,OAAO,iBAAiB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACpC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,QAAgB;IAC9C,OAAO,SAAS,QAAQ,EAAE,CAAC;AAC7B,CAAC;AAuBD;;sCAEsC;AACtC,MAAM,CAAC,MAAM,kBAAkB,GAAG,aAAa,CAAC;AAEhD;;kBAEkB;AAClB,MAAM,UAAU,eAAe,CAAC,GAAW;IACzC,OAAO,kBAAkB,CAAC,GAAG,CAAC,CAAC;AACjC,CAAC;AAED;;yBAEyB;AACzB,MAAM,CAAC,MAAM,uBAAuB,GAAG,eAAe,CAAC;AAEvD,iFAAiF;AACjF,MAAM,UAAU,gBAAgB,CAAC,GAAW;IAC1C,OAAO,0DAA0D,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC9E,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,cAAc,CAAC,KAAa;IAC1C,OAAO,yBAAyB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAC/C,CAAC"}
1
+ {"version":3,"file":"local-control.js","sourceRoot":"","sources":["../../../src/local-control/local-control.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,2DAA2D;AAC3D,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC;AAExC;;;;GAIG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,oBAAoB,CAAC;AAEvD,0CAA0C;AAC1C,MAAM,CAAC,MAAM,kBAAkB,GAAG,UAAU,CAAC;AAE7C;;;GAGG;AACH,MAAM,CAAC,MAAM,6BAA6B,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AAsE9D,MAAM,iBAAiB,GAAG,2BAA2B,CAAC;AAEtD,yFAAyF;AACzF,MAAM,UAAU,oBAAoB,CAAC,EAAU;IAC7C,OAAO,iBAAiB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACpC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,SAAS,CAAC;AAE9C,uFAAuF;AACvF,MAAM,UAAU,iBAAiB,CAAC,EAAU;IAC1C,OAAO,CACL,oBAAoB,CAAC,EAAE,CAAC;QACxB,CAAC,EAAE,KAAK,oBAAoB,IAAI,EAAE,CAAC,UAAU,CAAC,GAAG,oBAAoB,GAAG,CAAC,CAAC,CAC3E,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,QAAgB;IAC9C,OAAO,SAAS,QAAQ,EAAE,CAAC;AAC7B,CAAC;AAuBD;;sCAEsC;AACtC,MAAM,CAAC,MAAM,kBAAkB,GAAG,aAAa,CAAC;AAEhD;;kBAEkB;AAClB,MAAM,UAAU,eAAe,CAAC,GAAW;IACzC,OAAO,kBAAkB,CAAC,GAAG,CAAC,CAAC;AACjC,CAAC;AAED;;yBAEyB;AACzB,MAAM,CAAC,MAAM,uBAAuB,GAAG,eAAe,CAAC;AAEvD,iFAAiF;AACjF,MAAM,UAAU,gBAAgB,CAAC,GAAW;IAC1C,OAAO,0DAA0D,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC9E,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,cAAc,CAAC,KAAa;IAC1C,OAAO,yBAAyB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAC/C,CAAC"}
@@ -133,8 +133,10 @@ export interface BridgeFeatures {
133
133
  messageQueue?: boolean;
134
134
  /**
135
135
  * The bridge can hand a queued turn to the agent **inside the turn already
136
- * running**, for agents whose CLI has an input channel mid-turn — it marks
137
- * that turn `delivered` and emits `stream/turn/delivered`. Absent/false → the
136
+ * running**, for agents whose CLI has an input channel mid-turn — the running
137
+ * turn completes and the handed-over one starts at once, carrying the rest of
138
+ * the agent's run (`stream/turn/completed` then `stream/turn/started`, as a
139
+ * queue that drained early). Absent/false → the
138
140
  * client must expect every follow-up to wait for the current turn to end, and
139
141
  * must not promise otherwise in its UI.
140
142
  *
@@ -17,14 +17,13 @@ export type MessageRole = 'user' | 'assistant' | 'system' | 'tool';
17
17
  * thread (never deleted) so the user's message stays visible with a "cancelled"
18
18
  * mark instead of silently vanishing; distinct from `aborted` precisely so a
19
19
  * client can tell "never ran" from "interrupted".
20
- * - `delivered` — it was QUEUED and the agent took it **into the turn already
21
- * running** (see `AgentCapabilities.steering`), so it will never run as a turn
22
- * of its own: the answer is part of the turn it was folded into
23
- * (`deliveredIntoTurnId`). Terminal and successful — distinct from `cancelled`
24
- * precisely because the message DID reach the agent; the user's bubble stays,
25
- * with no assistant reply hanging off it.
20
+ *
21
+ * A queued turn the agent takes **into the turn already running** (see
22
+ * `AgentCapabilities.steering`) goes straight to `streaming`: the running turn
23
+ * completes there and this one carries the rest of the agent's run, so what the
24
+ * agent says after taking the message is shown under it.
26
25
  */
27
- export type TurnStatus = 'queued' | 'pending' | 'streaming' | 'completed' | 'error' | 'aborted' | 'cancelled' | 'delivered';
26
+ export type TurnStatus = 'queued' | 'pending' | 'streaming' | 'completed' | 'error' | 'aborted' | 'cancelled';
28
27
  export type ThreadStatus = 'active' | 'idle' | 'archived';
29
28
  /**
30
29
  * Why a thread's message queue is held instead of draining:
@@ -81,12 +80,6 @@ export interface Turn {
81
80
  messages: Message[];
82
81
  createdAt: number;
83
82
  completedAt?: number;
84
- /**
85
- * For a `delivered` turn: the id of the turn its message was folded into.
86
- * The reply lives there, so a client renders this turn's user message in
87
- * place and expects no assistant message of its own. Absent otherwise.
88
- */
89
- deliveredIntoTurnId?: string;
90
83
  }
91
84
  /**
92
85
  * Per-thread access (approval) mode: how much the agent may do before it must
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uxnan/shared",
3
- "version": "0.0.17-alpha.20260926",
3
+ "version": "0.0.18-alpha.20260926",
4
4
  "description": "Shared JSON-RPC and E2EE contracts for the Uxnan ecosystem (bridge, relay, mobile).",
5
5
  "license": "MPL-2.0",
6
6
  "author": "Luis Donaldo Gamas Vazquez",