@uxnan/shared 0.0.9-alpha.20260721 → 0.0.11-alpha.20260729

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/66_methods_%7C_8_notifications-blue?style=for-the-badge)
6
+ ![Contracts](https://img.shields.io/badge/68_methods_%7C_10_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 — **66 JSON-RPC methods** + **8 streaming
16
+ > **Status:** implemented and stable — **68 JSON-RPC methods** + **10 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
@@ -49,7 +49,7 @@ flowchart TB
49
49
 
50
50
  | Area | Exports |
51
51
  |---|---|
52
- | JSON-RPC | envelope types + constructors (`makeRequest`, `makeNotification`, `makeResponse`, `makeErrorResponse`), error codes (`JsonRpcErrorCode` + Uxnan-specific `-32000..-32008`), `RpcError`, typed method registry (`JsonRpcMethodRegistry` + `METHOD_NAMES`), `isKnownMethod` |
52
+ | JSON-RPC | envelope types + constructors (`makeRequest`, `makeNotification`, `makeResponse`, `makeErrorResponse`), error codes (`JsonRpcErrorCode` + Uxnan-specific `-32000..-32009`), `RpcError`, typed method registry (`JsonRpcMethodRegistry` + `METHOD_NAMES`), `isKnownMethod` |
53
53
  | Streaming | `StreamNotification` enum + param types (`TurnStartedParams`, `MessageDeltaParams`, `ThinkingDeltaParams`, `ContentBlockParams`, `TurnCompletedParams`, `TurnUsage`, `TurnErrorParams`, `TurnAbortedParams`, `ModelResolvedParams`) |
54
54
  | E2EE | handshake messages (`clientHello` / `serverHello` / `clientAuth` / `ready`), `buildHandshakeTranscript`, `SecureEnvelope`, `PairingPayload` v2 (`relay` optional + `hosts: string[]`) with `Base64(utf8(JSON))` QR encoding |
55
55
  | Models | thread / turn / message (with `MessageContent` polymorphic blocks), git, workspace (incl. `browseDirs` + `exists`), project, auth, session/trust (`BridgeStatus` incl. `latestVersion?`/`updateAvailable?`), bridge-owned profile metrics (complete-ledger export/import), approval, question (interactive multiple-choice) |
@@ -82,6 +82,31 @@ export interface IAgentAdapter {
82
82
  respondApproval?(threadId: string, approvalId: string, decision: ApprovalDecision): Promise<void>;
83
83
  /** Subscribe to streaming events. Returns an unsubscribe function. */
84
84
  onEvent(listener: (event: AgentStreamEvent) => void): () => void;
85
+ /**
86
+ * True when this adapter delivers `SendTurnOptions.attachments` to its CLI
87
+ * **itself** (a native image input), so the bridge must not materialize them
88
+ * to files nor append a path note to the prompt.
89
+ *
90
+ * Default (unset/false) is the CLI-agnostic path: the bridge writes each
91
+ * attachment into the agent's working directory and references it, which
92
+ * works for every agent whose file tools can open an image. An adapter opts
93
+ * in here when its protocol carries images natively AND its file tools
94
+ * cannot (Zero: ACP `promptCapabilities.image` accepts an inline image
95
+ * block, while its `read_file` is line-oriented text only).
96
+ */
97
+ handlesAttachments?(): boolean;
98
+ /**
99
+ * The directory this adapter runs a turn in when the turn carries no `cwd`
100
+ * of its own (its configured {@link AgentConfig.cwd}, else the daemon's
101
+ * process directory). Optional; adapters that cannot report one leave it
102
+ * unset.
103
+ *
104
+ * The bridge needs it to place per-turn files — image attachments in
105
+ * particular — **inside** the directory the CLI is actually sandboxed to:
106
+ * every supported agent refuses to open a path outside its workspace, so a
107
+ * file written anywhere else is unreachable no matter how it is referenced.
108
+ */
109
+ defaultCwd?(): string | undefined;
85
110
  /** List the models this agent's CLI reports as available (optional). */
86
111
  listModels?(): Promise<AgentModel[]>;
87
112
  /**
@@ -91,8 +91,8 @@ export interface AgentModelOption {
91
91
  * A selectable model an agent reports, returned by `agent/models`.
92
92
  *
93
93
  * `id` is the wire value passed back to the agent for routing — a stable alias
94
- * for Claude Code (`opus`/`sonnet`/`haiku`), a `provider/model` id for OpenCode,
95
- * or a concrete model id for Codex. `displayName`, `description`, `version` and
94
+ * for Claude Code (`fable`/`opus`/`sonnet`/`haiku`), a `provider/model` id for
95
+ * OpenCode, or a concrete model id for Codex. `displayName`, `description`, `version` and
96
96
  * `isDefault` are presentation hints; consumers must tolerate any of them being
97
97
  * absent (older bridges report bare id strings).
98
98
  */
@@ -105,7 +105,7 @@ export interface AgentModel {
105
105
  description?: string;
106
106
  /**
107
107
  * Concrete underlying version when `id` is an alias that resolves to a
108
- * moving target — e.g. Claude Code's `opus` → `claude-opus-4-8`. Surfaced so
108
+ * moving target — e.g. Claude Code's `opus` → `claude-opus-5`. Surfaced so
109
109
  * the user can see which exact model an alias currently maps to.
110
110
  */
111
111
  version?: string;
@@ -125,11 +125,12 @@ export interface AgentModel {
125
125
  */
126
126
  contextWindow?: number;
127
127
  /**
128
- * Marks a moving-target "latest" alias (Claude Code's `opus`/`sonnet`/`haiku`,
129
- * each of which always routes to the newest version of that tier the account
130
- * can use — see {@link version} for the resolved concrete id). Concrete/pinned
131
- * models leave this absent. Presentation-only: lets a client offer to
132
- * hide the aliases and show exact versions only, without hardcoding ids.
128
+ * Marks a moving-target "latest" alias (Claude Code's
129
+ * `fable`/`opus`/`sonnet`/`haiku`, each of which always routes to the newest
130
+ * version of that tier the account can use — see {@link version} for the
131
+ * resolved concrete id). Concrete/pinned models leave this absent.
132
+ * Presentation-only: lets a client offer to hide the aliases and show exact
133
+ * versions only, without hardcoding ids.
133
134
  */
134
135
  isLatestAlias?: boolean;
135
136
  }
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * JSON-RPC 2.0 error codes used across the Uxnan ecosystem.
3
3
  *
4
- * Standard codes (-32700..-32603) plus Uxnan-specific codes (-32000..-32008).
4
+ * Standard codes (-32700..-32603) plus Uxnan-specific codes (-32000..-32009).
5
5
  * Source: architecture/02b-contracts-and-requirements.md.
6
6
  */
7
7
  export declare const JsonRpcErrorCode: {
@@ -19,6 +19,12 @@ export declare const JsonRpcErrorCode: {
19
19
  readonly SessionExpired: -32006;
20
20
  readonly ConfirmationRequired: -32007;
21
21
  readonly ResourceNotFound: -32008;
22
+ /**
23
+ * A turn is already in flight on this thread and the caller asked NOT to
24
+ * queue behind it (`turn/send` with `queue: false`). Only ever raised on an
25
+ * explicit opt-out — the default is to queue.
26
+ */
27
+ readonly AgentBusy: -32009;
22
28
  };
23
29
  export type JsonRpcErrorCode = (typeof JsonRpcErrorCode)[keyof typeof JsonRpcErrorCode];
24
30
  /** Human-readable default messages for each error code. */
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * JSON-RPC 2.0 error codes used across the Uxnan ecosystem.
3
3
  *
4
- * Standard codes (-32700..-32603) plus Uxnan-specific codes (-32000..-32008).
4
+ * Standard codes (-32700..-32603) plus Uxnan-specific codes (-32000..-32009).
5
5
  * Source: architecture/02b-contracts-and-requirements.md.
6
6
  */
7
7
  export const JsonRpcErrorCode = {
@@ -21,6 +21,12 @@ export const JsonRpcErrorCode = {
21
21
  SessionExpired: -32006,
22
22
  ConfirmationRequired: -32007,
23
23
  ResourceNotFound: -32008,
24
+ /**
25
+ * A turn is already in flight on this thread and the caller asked NOT to
26
+ * queue behind it (`turn/send` with `queue: false`). Only ever raised on an
27
+ * explicit opt-out — the default is to queue.
28
+ */
29
+ AgentBusy: -32009,
24
30
  };
25
31
  /** Human-readable default messages for each error code. */
26
32
  export const JSON_RPC_ERROR_MESSAGES = {
@@ -38,6 +44,7 @@ export const JSON_RPC_ERROR_MESSAGES = {
38
44
  [JsonRpcErrorCode.SessionExpired]: 'Session expired',
39
45
  [JsonRpcErrorCode.ConfirmationRequired]: 'Confirmation required',
40
46
  [JsonRpcErrorCode.ResourceNotFound]: 'Resource not found',
47
+ [JsonRpcErrorCode.AgentBusy]: 'Agent busy',
41
48
  };
42
49
  /**
43
50
  * Error carrying a JSON-RPC error code, so handlers can throw a typed error
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../src/jsonrpc/errors.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,wBAAwB;IACxB,UAAU,EAAE,CAAC,KAAK;IAClB,cAAc,EAAE,CAAC,KAAK;IACtB,cAAc,EAAE,CAAC,KAAK;IACtB,aAAa,EAAE,CAAC,KAAK;IACrB,aAAa,EAAE,CAAC,KAAK;IACrB,iBAAiB;IACjB,WAAW,EAAE,CAAC,KAAK;IACnB,sBAAsB,EAAE,CAAC,KAAK;IAC9B,eAAe,EAAE,CAAC,KAAK;IACvB,kBAAkB,EAAE,CAAC,KAAK;IAC1B,qBAAqB,EAAE,CAAC,KAAK;IAC7B,yBAAyB,EAAE,CAAC,KAAK;IACjC,cAAc,EAAE,CAAC,KAAK;IACtB,oBAAoB,EAAE,CAAC,KAAK;IAC5B,gBAAgB,EAAE,CAAC,KAAK;CAChB,CAAC;AAIX,2DAA2D;AAC3D,MAAM,CAAC,MAAM,uBAAuB,GAAqC;IACvE,CAAC,gBAAgB,CAAC,UAAU,CAAC,EAAE,aAAa;IAC5C,CAAC,gBAAgB,CAAC,cAAc,CAAC,EAAE,iBAAiB;IACpD,CAAC,gBAAgB,CAAC,cAAc,CAAC,EAAE,kBAAkB;IACrD,CAAC,gBAAgB,CAAC,aAAa,CAAC,EAAE,gBAAgB;IAClD,CAAC,gBAAgB,CAAC,aAAa,CAAC,EAAE,gBAAgB;IAClD,CAAC,gBAAgB,CAAC,WAAW,CAAC,EAAE,cAAc;IAC9C,CAAC,gBAAgB,CAAC,sBAAsB,CAAC,EAAE,yBAAyB;IACpE,CAAC,gBAAgB,CAAC,eAAe,CAAC,EAAE,mBAAmB;IACvD,CAAC,gBAAgB,CAAC,kBAAkB,CAAC,EAAE,sBAAsB;IAC7D,CAAC,gBAAgB,CAAC,qBAAqB,CAAC,EAAE,yBAAyB;IACnE,CAAC,gBAAgB,CAAC,yBAAyB,CAAC,EAAE,6BAA6B;IAC3E,CAAC,gBAAgB,CAAC,cAAc,CAAC,EAAE,iBAAiB;IACpD,CAAC,gBAAgB,CAAC,oBAAoB,CAAC,EAAE,uBAAuB;IAChE,CAAC,gBAAgB,CAAC,gBAAgB,CAAC,EAAE,oBAAoB;CAC1D,CAAC;AASF;;;GAGG;AACH,MAAM,OAAO,QAAS,SAAQ,KAAK;IACxB,IAAI,CAAS;IACb,IAAI,CAAW;IAExB,YAAY,IAAY,EAAE,OAAgB,EAAE,IAAc;QACxD,MAAM,QAAQ,GAAG,OAAO,IAAI,uBAAuB,CAAC,IAAwB,CAAC,IAAI,cAAc,CAAC;QAChG,KAAK,CAAC,QAAQ,CAAC,CAAC;QAChB,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;QACvB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACnB,CAAC;IACH,CAAC;IAED,aAAa;QACX,MAAM,GAAG,GAAuB,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC;QAC3E,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YAC5B,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QACvB,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IAED,MAAM,CAAC,cAAc,CAAC,MAAc;QAClC,OAAO,IAAI,QAAQ,CAAC,gBAAgB,CAAC,cAAc,EAAE,qBAAqB,MAAM,EAAE,CAAC,CAAC;IACtF,CAAC;IAED,MAAM,CAAC,aAAa,CAAC,MAAe,EAAE,IAAc;QAClD,MAAM,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,mBAAmB,MAAM,EAAE,CAAC,CAAC,CAAC,gBAAgB,CAAC;QACxE,OAAO,IAAI,QAAQ,CAAC,gBAAgB,CAAC,aAAa,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IACrE,CAAC;CACF"}
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../src/jsonrpc/errors.ts"],"names":[],"mappings":"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"}
@@ -3,5 +3,5 @@
3
3
  * compile-time {@link JsonRpcMethodRegistry} via the assertion below.
4
4
  */
5
5
  import type { JsonRpcMethodName } from './methods.js';
6
- export declare const METHOD_NAMES: readonly ["thread/list", "thread/read", "thread/start", "thread/resume", "thread/fork", "thread/setModel", "thread/rename", "thread/setAccessMode", "thread/archive", "thread/unarchive", "thread/delete", "turn/list", "turn/read", "turn/send", "turn/cancel", "git/status", "git/diff", "git/commit", "git/push", "git/pull", "git/checkout", "git/createBranch", "git/createWorktree", "git/stage", "git/unstage", "git/discard", "git/createPr", "git/undoCommit", "git/branches", "git/switchBranch", "git/revert", "git/deleteBranch", "git/removeWorktree", "git/log", "git/commitShow", "workspace/readFile", "workspace/readImage", "workspace/list", "workspace/searchFiles", "workspace/browseDirs", "workspace/checkpoint", "workspace/diffCheckpoint", "workspace/applyCheckpoint", "workspace/applyPatch", "workspace/exists", "project/list", "project/resolve", "agent/list", "agent/models", "agent/commands", "agent/usageStats", "metrics/get", "metrics/export", "metrics/import", "auth/status", "auth/login", "auth/logout", "notifications/register", "notifications/update", "notifications/unregister", "bridge/status", "bridge/generatePairingQr", "bridge/connectedPhones", "bridge/disconnectPhone", "bridge/trustedDevices", "bridge/removeTrustedDevice"];
6
+ export declare const METHOD_NAMES: readonly ["thread/list", "thread/read", "thread/start", "thread/resume", "thread/fork", "thread/setModel", "thread/rename", "thread/setAccessMode", "thread/archive", "thread/unarchive", "thread/delete", "turn/list", "turn/read", "turn/send", "turn/cancel", "queue/resume", "queue/clear", "git/status", "git/diff", "git/commit", "git/push", "git/pull", "git/checkout", "git/createBranch", "git/createWorktree", "git/stage", "git/unstage", "git/discard", "git/createPr", "git/undoCommit", "git/branches", "git/switchBranch", "git/revert", "git/deleteBranch", "git/removeWorktree", "git/log", "git/commitShow", "workspace/readFile", "workspace/readImage", "workspace/list", "workspace/searchFiles", "workspace/browseDirs", "workspace/checkpoint", "workspace/diffCheckpoint", "workspace/applyCheckpoint", "workspace/applyPatch", "workspace/exists", "project/list", "project/resolve", "agent/list", "agent/models", "agent/commands", "agent/usageStats", "metrics/get", "metrics/export", "metrics/import", "auth/status", "auth/login", "auth/logout", "notifications/register", "notifications/update", "notifications/unregister", "bridge/status", "bridge/generatePairingQr", "bridge/connectedPhones", "bridge/disconnectPhone", "bridge/trustedDevices", "bridge/removeTrustedDevice"];
7
7
  export declare function isKnownMethod(method: string): method is JsonRpcMethodName;
@@ -15,6 +15,9 @@ export const METHOD_NAMES = [
15
15
  'turn/read',
16
16
  'turn/send',
17
17
  'turn/cancel',
18
+ // Message queue
19
+ 'queue/resume',
20
+ 'queue/clear',
18
21
  // Git
19
22
  'git/status',
20
23
  'git/diff',
@@ -1 +1 @@
1
- {"version":3,"file":"method-registry.js","sourceRoot":"","sources":["../../../src/jsonrpc/method-registry.ts"],"names":[],"mappings":"AAMA,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,kBAAkB;IAClB,aAAa;IACb,aAAa;IACb,cAAc;IACd,eAAe;IACf,aAAa;IACb,iBAAiB;IACjB,eAAe;IACf,sBAAsB;IACtB,gBAAgB;IAChB,kBAAkB;IAClB,eAAe;IACf,WAAW;IACX,WAAW;IACX,WAAW;IACX,aAAa;IACb,MAAM;IACN,YAAY;IACZ,UAAU;IACV,YAAY;IACZ,UAAU;IACV,UAAU;IACV,cAAc;IACd,kBAAkB;IAClB,oBAAoB;IACpB,WAAW;IACX,aAAa;IACb,aAAa;IACb,cAAc;IACd,gBAAgB;IAChB,cAAc;IACd,kBAAkB;IAClB,YAAY;IACZ,kBAAkB;IAClB,oBAAoB;IACpB,SAAS;IACT,gBAAgB;IAChB,YAAY;IACZ,oBAAoB;IACpB,qBAAqB;IACrB,gBAAgB;IAChB,uBAAuB;IACvB,sBAAsB;IACtB,sBAAsB;IACtB,0BAA0B;IAC1B,2BAA2B;IAC3B,sBAAsB;IACtB,kBAAkB;IAClB,WAAW;IACX,cAAc;IACd,iBAAiB;IACjB,SAAS;IACT,YAAY;IACZ,cAAc;IACd,gBAAgB;IAChB,kBAAkB;IAClB,yEAAyE;IACzE,aAAa;IACb,gBAAgB;IAChB,gBAAgB;IAChB,OAAO;IACP,aAAa;IACb,YAAY;IACZ,aAAa;IACb,uBAAuB;IACvB,wBAAwB;IACxB,sBAAsB;IACtB,0BAA0B;IAC1B,iBAAiB;IACjB,eAAe;IACf,0BAA0B;IAC1B,wBAAwB;IACxB,wBAAwB;IACxB,uBAAuB;IACvB,4BAA4B;CACpB,CAAC;AAQX,MAAM,sBAAsB,GAAqB,IAAI,CAAC;AACtD,MAAM,sBAAsB,GAAqB,IAAI,CAAC;AACtD,KAAK,sBAAsB,CAAC;AAC5B,KAAK,sBAAsB,CAAC;AAE5B,MAAM,eAAe,GAAwB,IAAI,GAAG,CAAC,YAAY,CAAC,CAAC;AAEnE,MAAM,UAAU,aAAa,CAAC,MAAc;IAC1C,OAAO,eAAe,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;AACrC,CAAC"}
1
+ {"version":3,"file":"method-registry.js","sourceRoot":"","sources":["../../../src/jsonrpc/method-registry.ts"],"names":[],"mappings":"AAMA,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,kBAAkB;IAClB,aAAa;IACb,aAAa;IACb,cAAc;IACd,eAAe;IACf,aAAa;IACb,iBAAiB;IACjB,eAAe;IACf,sBAAsB;IACtB,gBAAgB;IAChB,kBAAkB;IAClB,eAAe;IACf,WAAW;IACX,WAAW;IACX,WAAW;IACX,aAAa;IACb,gBAAgB;IAChB,cAAc;IACd,aAAa;IACb,MAAM;IACN,YAAY;IACZ,UAAU;IACV,YAAY;IACZ,UAAU;IACV,UAAU;IACV,cAAc;IACd,kBAAkB;IAClB,oBAAoB;IACpB,WAAW;IACX,aAAa;IACb,aAAa;IACb,cAAc;IACd,gBAAgB;IAChB,cAAc;IACd,kBAAkB;IAClB,YAAY;IACZ,kBAAkB;IAClB,oBAAoB;IACpB,SAAS;IACT,gBAAgB;IAChB,YAAY;IACZ,oBAAoB;IACpB,qBAAqB;IACrB,gBAAgB;IAChB,uBAAuB;IACvB,sBAAsB;IACtB,sBAAsB;IACtB,0BAA0B;IAC1B,2BAA2B;IAC3B,sBAAsB;IACtB,kBAAkB;IAClB,WAAW;IACX,cAAc;IACd,iBAAiB;IACjB,SAAS;IACT,YAAY;IACZ,cAAc;IACd,gBAAgB;IAChB,kBAAkB;IAClB,yEAAyE;IACzE,aAAa;IACb,gBAAgB;IAChB,gBAAgB;IAChB,OAAO;IACP,aAAa;IACb,YAAY;IACZ,aAAa;IACb,uBAAuB;IACvB,wBAAwB;IACxB,sBAAsB;IACtB,0BAA0B;IAC1B,iBAAiB;IACjB,eAAe;IACf,0BAA0B;IAC1B,wBAAwB;IACxB,wBAAwB;IACxB,uBAAuB;IACvB,4BAA4B;CACpB,CAAC;AAQX,MAAM,sBAAsB,GAAqB,IAAI,CAAC;AACtD,MAAM,sBAAsB,GAAqB,IAAI,CAAC;AACtD,KAAK,sBAAsB,CAAC;AAC5B,KAAK,sBAAsB,CAAC;AAE5B,MAAM,eAAe,GAAwB,IAAI,GAAG,CAAC,YAAY,CAAC,CAAC;AAEnE,MAAM,UAAU,aAAa,CAAC,MAAc;IAC1C,OAAO,eAAe,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;AACrC,CAAC"}
@@ -5,7 +5,7 @@
5
5
  * Source: architecture/02b-contracts-and-requirements.md and
6
6
  * uxnandesktop/architecture/02e-bridge-integration.md §4.4.
7
7
  */
8
- import type { AccessMode, Thread, ThreadList, Turn, TurnList } from '../models/thread.js';
8
+ import type { AccessMode, QueuePausedReason, Thread, ThreadList, Turn, TurnList } from '../models/thread.js';
9
9
  import type { GitBranchList, GitBranchResult, GitCommitDetails, GitCommitResult, GitCommitShowParams, GitDiff, GitLogParams, GitLogResult, GitPrResult, GitPullResult, GitPushResult, GitRepoStatus, GitWorktreeResult } from '../models/git.js';
10
10
  import type { ApplyResult, BrowseResult, Checkpoint, CheckpointDiff, FileContent, ImageContent, PatchChange, TurnAttachment, WorkspaceExistsResult, WorkspaceListing, SearchFilesParams, WorkspaceSearchResult } from '../models/workspace.js';
11
11
  import type { AuthStatus, Project } from '../models/project.js';
@@ -91,6 +91,20 @@ export interface TurnSendParams {
91
91
  * required.
92
92
  */
93
93
  command?: AgentCommandInvocation;
94
+ /**
95
+ * What to do when a turn is ALREADY in flight on this thread (the CLIs' own
96
+ * "queue a follow-up while it works" behaviour):
97
+ * - `true` — queue it explicitly; it starts when the queue drains to it.
98
+ * - `false` — reject with `AgentBusy` instead of queueing (a client that
99
+ * wants to handle the busy case itself).
100
+ * - absent — **queue it anyway**. Queueing is the safe default because the
101
+ * bridge can only drive ONE turn per thread: half the agents run one-shot
102
+ * per turn (`claude -p --resume`, gemini, pi, antigravity) and a second
103
+ * concurrent turn would put two CLI processes on the same session.
104
+ *
105
+ * Ignored when no turn is in flight — the turn simply starts.
106
+ */
107
+ queue?: boolean;
94
108
  }
95
109
  export interface ThreadSetModelParams {
96
110
  threadId: string;
@@ -108,6 +122,22 @@ export interface ThreadSetAccessModeParams {
108
122
  }
109
123
  export interface TurnSendResult {
110
124
  turnId: string;
125
+ /**
126
+ * True when the turn was QUEUED behind an in-flight one instead of starting
127
+ * now. The `turnId` is real either way (the turn is stored, and `turn/cancel`
128
+ * takes it off the queue); it just has status `queued` until it runs.
129
+ */
130
+ queued?: boolean;
131
+ /** 1-based place in the queue when `queued` is true (1 = runs next). */
132
+ queuePosition?: number;
133
+ }
134
+ export interface QueueStateResult {
135
+ /** Queued turn ids in drain order. */
136
+ queuedTurnIds: string[];
137
+ /** True while draining is held after a stop/failure. */
138
+ paused: boolean;
139
+ /** Why it is held; absent when it is not paused. */
140
+ pausedReason?: QueuePausedReason;
111
141
  }
112
142
  export interface GitCommitParams {
113
143
  cwd: string;
@@ -342,6 +372,20 @@ export interface JsonRpcMethodRegistry {
342
372
  };
343
373
  result: void;
344
374
  };
375
+ /** Resumes draining after a stop/failure held the queue. */
376
+ 'queue/resume': {
377
+ params: {
378
+ threadId: string;
379
+ };
380
+ result: QueueStateResult;
381
+ };
382
+ /** Drops every queued turn (each → `cancelled`) and clears the paused state. */
383
+ 'queue/clear': {
384
+ params: {
385
+ threadId: string;
386
+ };
387
+ result: QueueStateResult;
388
+ };
345
389
  'git/status': {
346
390
  params: {
347
391
  cwd: string;
@@ -3,6 +3,7 @@
3
3
  *
4
4
  * Source: architecture/02b-contracts-and-requirements.md (streaming events).
5
5
  */
6
+ import type { QueuePausedReason } from '../models/thread.js';
6
7
  export declare const StreamNotification: {
7
8
  readonly TurnStarted: "stream/turn/started";
8
9
  readonly MessageDelta: "stream/message/delta";
@@ -13,6 +14,10 @@ export declare const StreamNotification: {
13
14
  readonly TurnCompleted: "stream/turn/completed";
14
15
  readonly TurnError: "stream/turn/error";
15
16
  readonly TurnAborted: "stream/turn/aborted";
17
+ /** A queued turn was removed before it ever ran (status → `cancelled`). */
18
+ readonly TurnCancelled: "stream/turn/cancelled";
19
+ /** The thread's message queue changed (queued, drained, cancelled, paused). */
20
+ readonly QueueUpdated: "stream/queue/updated";
16
21
  /** The agent resolved an alias (e.g. `opus`) to a concrete model id for this turn. */
17
22
  readonly ModelResolved: "stream/model/resolved";
18
23
  };
@@ -89,6 +94,32 @@ export interface TurnAbortedParams {
89
94
  threadId: string;
90
95
  turnId: string;
91
96
  }
97
+ /**
98
+ * A queued turn was removed before it ever started (its status is now
99
+ * `cancelled`). The turn is NOT deleted — the user's message stays in the
100
+ * thread, marked as cancelled, so the history shows what was asked and dropped.
101
+ */
102
+ export interface TurnCancelledParams {
103
+ threadId: string;
104
+ turnId: string;
105
+ }
106
+ /**
107
+ * The thread's message queue changed. Carries the WHOLE state rather than a
108
+ * delta, so it is idempotent: a client that missed one (backgrounded, mid-
109
+ * reconnect) converges on the next one it receives instead of drifting.
110
+ *
111
+ * A client that sees an id it does not know about yet (another device queued
112
+ * it) resyncs the thread the same way it does for any unknown turn.
113
+ */
114
+ export interface QueueUpdatedParams {
115
+ threadId: string;
116
+ /** Queued turn ids in drain order; empty when the queue just emptied. */
117
+ queuedTurnIds: string[];
118
+ /** True while draining is held after a stop/failure (see `TurnList.queuePaused`). */
119
+ paused: boolean;
120
+ /** Why it is held; absent when it is not paused. */
121
+ pausedReason?: QueuePausedReason;
122
+ }
92
123
  export interface ModelResolvedParams {
93
124
  threadId: string;
94
125
  turnId: string;
@@ -1,8 +1,3 @@
1
- /**
2
- * Bridge → phone streaming notifications (JSON-RPC notifications, no `id`).
3
- *
4
- * Source: architecture/02b-contracts-and-requirements.md (streaming events).
5
- */
6
1
  export const StreamNotification = {
7
2
  TurnStarted: 'stream/turn/started',
8
3
  MessageDelta: 'stream/message/delta',
@@ -13,6 +8,10 @@ export const StreamNotification = {
13
8
  TurnCompleted: 'stream/turn/completed',
14
9
  TurnError: 'stream/turn/error',
15
10
  TurnAborted: 'stream/turn/aborted',
11
+ /** A queued turn was removed before it ever ran (status → `cancelled`). */
12
+ TurnCancelled: 'stream/turn/cancelled',
13
+ /** The thread's message queue changed (queued, drained, cancelled, paused). */
14
+ QueueUpdated: 'stream/queue/updated',
16
15
  /** The agent resolved an alias (e.g. `opus`) to a concrete model id for this turn. */
17
16
  ModelResolved: 'stream/model/resolved',
18
17
  };
@@ -1 +1 @@
1
- {"version":3,"file":"notifications.js","sourceRoot":"","sources":["../../../src/jsonrpc/notifications.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,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,sFAAsF;IACtF,aAAa,EAAE,uBAAuB;CAC9B,CAAC"}
1
+ {"version":3,"file":"notifications.js","sourceRoot":"","sources":["../../../src/jsonrpc/notifications.ts"],"names":[],"mappings":"AAOA,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;CAC9B,CAAC"}
@@ -37,4 +37,27 @@ export interface BridgeStatus {
37
37
  * without querying npm itself. Absent/false when unknown or up to date.
38
38
  */
39
39
  updateAvailable?: boolean;
40
+ /**
41
+ * Optional capabilities this bridge supports, so a newer client offers a
42
+ * feature only where it actually works instead of inferring it from the
43
+ * version string. **Absent means "assume none"** — an older bridge simply
44
+ * omits the field and the client falls back to the pre-feature behaviour.
45
+ *
46
+ * This matters because guessing wrong is not a cosmetic degradation: a client
47
+ * that offers to queue a follow-up against a bridge that cannot queue makes it
48
+ * start a second CONCURRENT turn, which corrupts the agent session (two CLI
49
+ * processes on one `--resume`; OpenCode retires the running turn outright).
50
+ */
51
+ features?: BridgeFeatures;
52
+ }
53
+ /** Optional, additive bridge capabilities advertised on {@link BridgeStatus}. */
54
+ export interface BridgeFeatures {
55
+ /**
56
+ * The bridge queues a `turn/send` that arrives while a turn is in flight
57
+ * (stored as a `queued` turn, drained on completion) instead of starting it
58
+ * concurrently, and implements `queue/resume` / `queue/clear`. Absent/false →
59
+ * the client must NOT offer to queue: sending during a live turn is unsafe on
60
+ * that bridge.
61
+ */
62
+ messageQueue?: boolean;
40
63
  }
@@ -5,8 +5,31 @@
5
5
  * and are kept in sync manually (see 02e-bridge-integration.md §4.2).
6
6
  */
7
7
  export type MessageRole = 'user' | 'assistant' | 'system' | 'tool';
8
- export type TurnStatus = 'pending' | 'streaming' | 'completed' | 'error' | 'aborted';
8
+ /**
9
+ * Lifecycle of a turn.
10
+ * - `queued` — accepted while another turn was in flight; it holds its place in
11
+ * the thread's queue and starts on its own once the queue drains to it. The
12
+ * user message is already stored, the assistant one is still empty.
13
+ * - `pending` / `streaming` — the agent is producing it.
14
+ * - `completed` / `error` — it ran and ended.
15
+ * - `aborted` — it was RUNNING and the user stopped it mid-flight.
16
+ * - `cancelled` — it was QUEUED and removed before it ever started. Kept in the
17
+ * thread (never deleted) so the user's message stays visible with a "cancelled"
18
+ * mark instead of silently vanishing; distinct from `aborted` precisely so a
19
+ * client can tell "never ran" from "interrupted".
20
+ */
21
+ export type TurnStatus = 'queued' | 'pending' | 'streaming' | 'completed' | 'error' | 'aborted' | 'cancelled';
9
22
  export type ThreadStatus = 'active' | 'idle' | 'archived';
23
+ /**
24
+ * Why a thread's message queue is held instead of draining:
25
+ * - `turnAborted` — the user stopped the running turn.
26
+ * - `turnError` — the running turn failed.
27
+ *
28
+ * In both cases the remaining queued turns are kept, not discarded: the user
29
+ * stopped (or the agent broke) for a reason, and firing the follow-ups at it
30
+ * anyway is the one outcome nobody wants. Draining resumes on `queue/resume`.
31
+ */
32
+ export type QueuePausedReason = 'turnAborted' | 'turnError';
10
33
  export interface Message {
11
34
  id: string;
12
35
  turnId: string;
@@ -101,4 +124,20 @@ export interface TurnList {
101
124
  * stopped tracking while backgrounded, instead of treating the turn as ended.
102
125
  */
103
126
  activeTurnId?: string;
127
+ /**
128
+ * The thread's queued turns (status `queued`), in the order they will run.
129
+ * Like {@link activeTurnId} this is LIVE bridge state the phone re-attaches to
130
+ * on resync — a client restores its "queued" bubbles from it without having to
131
+ * scan the returned page (the queue may sit outside a narrow `turn/list` page).
132
+ * Absent/empty when nothing is queued.
133
+ */
134
+ queuedTurnIds?: string[];
135
+ /**
136
+ * True when the queue is HELD: the previous turn was stopped by the user or
137
+ * failed, so the bridge stopped draining and is waiting for an explicit
138
+ * `queue/resume` (or `queue/clear`). Queued turns stay queued meanwhile.
139
+ */
140
+ queuePaused?: boolean;
141
+ /** Why the queue is held; absent when it is not paused. */
142
+ queuePausedReason?: QueuePausedReason;
104
143
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uxnan/shared",
3
- "version": "0.0.9-alpha.20260721",
3
+ "version": "0.0.11-alpha.20260729",
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",