@openclaw/gateway-protocol 0.0.0 → 2026.7.2-beta.5

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/CHANGELOG.md ADDED
@@ -0,0 +1,144 @@
1
+ # Changelog — @openclaw/gateway-protocol
2
+
3
+ Wire-protocol and schema contracts for the OpenClaw Gateway (WebSocket JSON-RPC-style
4
+ frames, handshake, and method/event payload schemas). Protocol version is negotiated
5
+ per connection via `minProtocol`/`maxProtocol`. This log covers the wire protocol
6
+ version and the additive schema surface. Dates are authoring dates (2026).
7
+
8
+ ## Unreleased
9
+
10
+ - Add semantic `agent` / `system` roster kinds negotiated through the `agent-kind` client capability.
11
+ - Rename structured-question item `id` to `questionId` and flatten keyed answer arrays.
12
+ - Slim worker and session-catalog payloads to the active wire contract.
13
+ - Remove dead protocol surfaces and add since-vintage metadata to retained schemas and methods.
14
+
15
+ ## Protocol v4 (current)
16
+
17
+ Introduced 2026-05-07 (commit `330ba1f`). The stable, current wire version.
18
+
19
+ Wire contract:
20
+ - Frame envelopes: `req` / `res` / `event`, discriminated on `type`.
21
+ - Handshake: client sends `ConnectParams` (advertises `minProtocol`/`maxProtocol`,
22
+ client identity, `caps`, `commands`, `permissions`, `role`/`scopes`, optional signed
23
+ `device` identity, and an `auth` bag: token / bootstrapToken / deviceToken / password /
24
+ approvalRuntimeToken / agentRuntimeIdentityToken).
25
+ - Server replies `hello-ok` with the negotiated `protocol`, server identity, the live
26
+ `features` map (`methods`, `events`, `capabilities`), initial `snapshot`, minted `auth`
27
+ device tokens, and connection `policy` (maxPayload / maxBufferedBytes / tickIntervalMs).
28
+ - Server events: `tick` heartbeat and `shutdown` notice; event frames may carry `seq`
29
+ and `stateVersion` for ordered state sync.
30
+
31
+ Changed vs v3:
32
+ - `hello-ok` handshake replaced the single `canvasHostUrl` string with a
33
+ `pluginSurfaceUrls` map (canvas generalized into arbitrary plugin surfaces). This
34
+ field change is the breaking bump behind v4.
35
+ - Later additively extended (no bump) with `controlUiTabs` (plugin-declared Control UI
36
+ tabs) and multi-`deviceTokens` in the handshake auth block.
37
+
38
+ Compatibility window (`version.ts`):
39
+ - `MIN_CLIENT_PROTOCOL_VERSION = 4` — general clients must speak v4.
40
+ - `MIN_NODE_PROTOCOL_VERSION = 3` and `MIN_PROBE_PROTOCOL_VERSION = 3` — authenticated
41
+ nodes and lightweight probes are accepted at N-1 (v3) to stay manageable during
42
+ rolling upgrades. Added 2026-07-06 (#101109).
43
+ - A transient v5 (2026-05-16, `07f05e9`) renamed `inboundTurnKind` -> `inboundEventKind`;
44
+ it was reverted the next day (2026-05-17, `ad155fb`, "restore v4 message action
45
+ protocol"). v5 never shipped as a stable ceiling; v4 remains current.
46
+
47
+ ## Protocol v3
48
+
49
+ Baseline wire version. Present since repo genesis (2026-04-21) as an inline literal in
50
+ `protocol-schemas.ts`; extracted into `version.ts` unchanged on 2026-05-04 (`2949171`).
51
+ The first externally-relevant version — there is no 2->3 bump in tracked history.
52
+
53
+ Established the still-current shape: `req`/`res`/`event` frame envelopes, the
54
+ `ConnectParams` -> `hello-ok` handshake with protocol negotiation, `snapshot` state sync,
55
+ and the founding method/event families: sessions, agent chat, cron, devices, nodes,
56
+ channels, config, commands, logs-chat, exec-approvals, plugin-approvals, secrets, push,
57
+ wizard. (v3 `hello-ok` carried `canvasHostUrl`; v4 replaced it — see above.)
58
+
59
+ ## Schema surface history
60
+
61
+ Additive method/event/schema families added over time. Pure refactors, test-only, and
62
+ docs commits are omitted. The package was extracted from `src/gateway/protocol` to
63
+ `packages/gateway-protocol` on 2026-05-29 (#87797); paths before that date lived under
64
+ the old tree.
65
+
66
+ ### 2026-04 (v3 baseline era)
67
+
68
+ - Ship baseline families: frames/handshake, sessions, agent chat, cron, devices, nodes,
69
+ channels, config, commands, logs-chat, exec-approvals, plugin-approvals, secrets, push,
70
+ wizard, snapshot, primitives, agents-models-skills.
71
+ - Add WhatsApp `replyToMode` quoting (#62305).
72
+ - Add browser realtime Talk and transports — origin of the talk/voice families.
73
+ - Add Control UI PWA web push support (#44590).
74
+ - Add plugins and artifacts schema modules.
75
+ - Add OpenClaw SDK package and authenticated iOS background presence beacon (#73330).
76
+
77
+ ### 2026-05
78
+
79
+ - Add environments discovery RPCs (#74867) and task-ledger RPCs (#74847) — tasks family.
80
+ - Add unified Talk gateway sessions, realtime active-run control, and typed `sessionKey`
81
+ on the wake protocol.
82
+ - Add SDK `tools.invoke` RPC; extend cron with agentId filtering (#77602), run
83
+ diagnostics (#75928), and direct job lookup.
84
+ - Add Skill Workshop gateway methods: proposal files, revision requests, persisted origin.
85
+ - Add core session goals (#87469).
86
+ - Add heartbeat flag on agent event broadcast (#80610), warm-MCP effective inventory, and
87
+ plugin approval action metadata.
88
+ - Harden auth/device identity: bind approval access to requester metadata (#81380);
89
+ require approval for setup-code device pairing (#81292); scope Talk session to resolver.
90
+ - Introduce and revert transient protocol v5 (`07f05e9` / `ad155fb`).
91
+
92
+ ### 2026-06
93
+
94
+ Enhancement-only month (no new schema modules):
95
+ - Extend cron with command jobs, compact list responses (#93395), and an on-exit
96
+ schedule kind that fires when a watched command exits.
97
+ - Forward-port fast-Talks auto mode (#85104); add session workspace rail (#92856).
98
+
99
+ ### 2026-07 (largest expansion)
100
+
101
+ - Add terminal family: `terminal.*` RPC methods/events, detach/reattach with output
102
+ replay, `terminal.list`/`terminal.text`, and file uploads into terminals (#107364).
103
+ - Add managed git worktrees: lifecycle create/provision/snapshot/restore/GC (#100535),
104
+ new-session-in-worktree (#100788), session worktree targeting and branch listing
105
+ (#103432); add read-only `agents.workspace` browsing RPCs (#100738).
106
+ - Add audit family: metadata-only message audit events (#103903), native-search audit
107
+ correlation (#98704), and audit-activity schema.
108
+ - Add `tts.speak` returning synthesized audio inline (#100770).
109
+ - Add cooperative host suspension / gateway-suspend prepare/status/resume RPCs (#103618).
110
+ - Add durable approvals: persisted operator approvals (#103579), typed cross-surface
111
+ approval actions (#103679), approval-id, and the durable-approvals stack (#104837).
112
+ - Add cloud-workers stack: durable environments + lifecycle RPCs (#104401), worker bundle
113
+ + SSH bootstrap + admission handshake (#104532), authenticated worker protocol with
114
+ minted credentials (#104688), durable transcript commit (#104809), live-event streaming
115
+ (#105275), inference proxy (#105719), and session placement/dispatch (#106332).
116
+ - Add session catalog: sessions-catalog + sessions-create with external-session
117
+ pagination unification (#104717).
118
+ - Add fs family: `sessions.files.set` hash-CAS writes (#104757) and gateway/node folder
119
+ browsing (#105114).
120
+ - Add node-hosted plugins — dynamic tools, MCP servers, skills (#90431) — plus node
121
+ invoke/presence protocol schemas.
122
+ - Add migrations family: log-migration protocol schemas and Codex/Claude memory import
123
+ (#106406).
124
+ - Add durable device rename for human-friendly device names (#94517).
125
+ - Add follow-up task suggestions (#102422) and task-suggestions schema.
126
+ - Add cron event triggers via polled condition-watcher scripts (#101195) and native
127
+ mobile Automations parity (#106355).
128
+ - Add system-agent conversational onboarding (#99935); rename `crestodian.*` methods to
129
+ `openclaw.chat` / `openclaw.setup.*` (2026-07-14, `a6a0716`).
130
+ - Add typed structured questions / `ask_user` with live option cards (#109922, #110242)
131
+ and the questions schema module.
132
+ - Add ui-command / screen-tool Control UI layout control and capability-gated
133
+ `show_widget` inline web chat widgets (#101840).
134
+ - Add direct watch/watchOS node connect to Gateway (#102893); widen node/probe protocol
135
+ acceptance to N-1 (#101109).
136
+
137
+ ## Notes for external versioning
138
+
139
+ - Post-v4 changes are additive except: the transient v5 rename (reverted, net-zero) and
140
+ the 2026-07-14 `crestodian.*` -> `openclaw.*` method rename. The renamed feature was
141
+ introduced only 9 days earlier (2026-07-05, #99935), never left the v4 window, and
142
+ predates public publish, so no released method name changed under v4.
143
+ - `schema/types.ts` was removed 2026-07-11 (#103679); it re-exported compile-time type
144
+ aliases only and has no wire impact.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 OpenClaw Foundation
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,158 @@
1
- # OpenClaw Gateway Protocol
1
+ # `@openclaw/gateway-protocol`
2
2
 
3
- Reserved package name. Release artifacts are published by OpenClaw release automation.
3
+ Typed schemas, inferred TypeScript types, and runtime validators for the OpenClaw
4
+ Gateway WebSocket protocol.
5
+
6
+ The current wire protocol is version 4. General clients must use v4; authenticated
7
+ node clients and lightweight probes may use the N-1 window during rolling upgrades.
8
+ See the [Gateway protocol specification](https://docs.openclaw.ai/gateway/protocol)
9
+ for transport, authentication, roles, scopes, and complete frame examples.
10
+
11
+ ## Versioning
12
+
13
+ Package versions follow the OpenClaw calendar release train:
14
+ `YYYY.M.PATCH`, with the same prerelease suffix when applicable. A package version
15
+ therefore identifies the OpenClaw source release that produced the schemas; it is
16
+ not the wire protocol number.
17
+
18
+ The wire protocol integer is versioned separately. Its current value is exported
19
+ as `PROTOCOL_VERSION` from `@openclaw/gateway-protocol/version`. Gateway protocol
20
+ changes are additive first. An incompatible wire change requires an explicit
21
+ protocol-version decision and coordinated client follow-through. See
22
+ [`CHANGELOG.md`](./CHANGELOG.md) for the wire and schema history.
23
+
24
+ ## Install
25
+
26
+ ```bash
27
+ npm install @openclaw/gateway-protocol
28
+ ```
29
+
30
+ ## Entry points
31
+
32
+ - `@openclaw/gateway-protocol` exports runtime validators, selected schemas, error
33
+ formatting, and their TypeScript types. This is the main TypeBox-backed entry.
34
+ - `@openclaw/gateway-protocol/schema` exports the TypeBox schema graph, including
35
+ the `ProtocolSchemas` registry used by generators.
36
+ - `@openclaw/gateway-protocol/frame-guards` exports dependency-free structural
37
+ guards for gateway event and response envelopes.
38
+ - `@openclaw/gateway-protocol/client-info` exports client ID, mode, and capability
39
+ registries plus normalization helpers.
40
+ - `@openclaw/gateway-protocol/connect-error-details` exports structured connect
41
+ error readers and recovery metadata.
42
+ - `@openclaw/gateway-protocol/gateway-error-details` exports helpers for reading
43
+ structured details from general gateway errors.
44
+ - `@openclaw/gateway-protocol/startup-unavailable` exports startup retry constants
45
+ and helpers.
46
+ - `@openclaw/gateway-protocol/version` exports the current and minimum accepted
47
+ protocol versions.
48
+
49
+ The `frame-guards`, `client-info`, `connect-error-details`, `gateway-error-details`,
50
+ `startup-unavailable`, and `version` entry points are TypeBox-free. Prefer them when
51
+ a browser bundle only needs envelope dispatch, handshake constants, or reconnect
52
+ policy. This also avoids runtime compilation in CSP-sensitive consumers. The root
53
+ and `schema` entry points provide the full validation surface and depend on TypeBox.
54
+
55
+ ## Validate an inbound frame
56
+
57
+ The compiled validators are callable type guards. Their `errors` property contains
58
+ the most recent validation errors.
59
+
60
+ ```ts
61
+ import { formatValidationErrors, validateRequestFrame } from "@openclaw/gateway-protocol";
62
+
63
+ const frame: unknown = JSON.parse(inboundText);
64
+
65
+ if (!validateRequestFrame(frame)) {
66
+ throw new Error(formatValidationErrors(validateRequestFrame.errors));
67
+ }
68
+
69
+ console.log(frame.id, frame.method);
70
+ ```
71
+
72
+ `validateRequestFrame` validates the request envelope. Dispatch code must also use
73
+ the validator for the selected method's `params`; the root entry point exports those
74
+ validators as `validate*Params` functions.
75
+
76
+ ## Guard an event without TypeBox
77
+
78
+ Use the lightweight guards when code only needs safe frame discrimination. They
79
+ check dispatch-critical envelope fields and intentionally allow additive payload
80
+ fields.
81
+
82
+ ```ts
83
+ import { isGatewayEventFrame } from "@openclaw/gateway-protocol/frame-guards";
84
+
85
+ const frame: unknown = JSON.parse(inboundText);
86
+
87
+ if (isGatewayEventFrame(frame)) {
88
+ console.log(frame.event, frame.seq);
89
+ }
90
+ ```
91
+
92
+ ## Build handshake version and capability fields
93
+
94
+ Protocol levels and client capabilities live in TypeBox-free entry points.
95
+
96
+ ```ts
97
+ import { GATEWAY_CLIENT_CAPS } from "@openclaw/gateway-protocol/client-info";
98
+ import { MIN_CLIENT_PROTOCOL_VERSION, PROTOCOL_VERSION } from "@openclaw/gateway-protocol/version";
99
+
100
+ const handshake = {
101
+ minProtocol: MIN_CLIENT_PROTOCOL_VERSION,
102
+ maxProtocol: PROTOCOL_VERSION,
103
+ caps: [GATEWAY_CLIENT_CAPS.TOOL_EVENTS],
104
+ };
105
+ ```
106
+
107
+ Nodes and probes use `MIN_NODE_PROTOCOL_VERSION` and
108
+ `MIN_PROBE_PROTOCOL_VERSION`, respectively. A capability advertises client support;
109
+ it does not grant authorization.
110
+
111
+ ## Contract notes
112
+
113
+ ### Session identifiers
114
+
115
+ Several identifier names coexist because they identify different things:
116
+
117
+ - `key` is the established logical session selector used by most `sessions.*`
118
+ CRUD, send, subscription, patch, reset, delete, compaction, and usage methods.
119
+ A key can be canonicalized or resolved within an agent's session store.
120
+ - `sessionKey` names the same logical routing identity where the contract needs to
121
+ make that meaning explicit. `chat.*`, session file and diff APIs, transcript
122
+ branch/rewind/fork APIs, agent events, and channel delivery payloads use this
123
+ spelling.
124
+ - `sessionId` is the opaque stored transcript or runtime instance ID. Session
125
+ results may return it beside a key. Talk, terminal, worker, and selected channel
126
+ protocols also use `sessionId` for their own concrete session instances; do not
127
+ substitute a logical session key there.
128
+
129
+ Follow each method schema rather than converting fields based on their spelling.
130
+ `sessions.resolve` is the explicit bridge when a caller has a key, raw session ID,
131
+ label, or parent/agent scope.
132
+
133
+ ### Intentionally open fields
134
+
135
+ The schema graph is strict by default, but roughly 60 fields intentionally use
136
+ `Type.Unknown()` passthroughs. The main clusters are transport-owned channel
137
+ payloads, logs-chat message and attachment passthrough, worker and node tool
138
+ arguments/results, and the dynamic `config.schema` response. Frame `params`,
139
+ `payload`, and error `details` are also open at the envelope layer because the
140
+ selected method, event, or error code owns their concrete shape.
141
+
142
+ Do not treat these fields as validated domain objects. Narrow them at their owner
143
+ boundary before reading nested values.
144
+
145
+ ### Machine-readable schema
146
+
147
+ `protocol.schema.json` ships in the npm tarball as the generated machine-readable
148
+ contract. It contains the frame union, named schema definitions, and core method
149
+ metadata. It is generated during `prepack` and is not committed to the repository.
150
+
151
+ ### Method discovery
152
+
153
+ The `hello-ok.features.methods` list is conservative discovery, not a complete
154
+ enumeration of every callable method. It reflects the methods the connected
155
+ Gateway intentionally advertises. Core-internal, role-specific, plugin-provided,
156
+ or otherwise non-advertised methods can have valid schemas without appearing in
157
+ that list. Clients should use discovery to enable optional UI, not to reject an
158
+ otherwise documented method contract.
@@ -0,0 +1,94 @@
1
+ //#region src/client-info.d.ts
2
+ /** Canonical client ids accepted in gateway hello/connect payloads. */
3
+ declare const GATEWAY_CLIENT_IDS: {
4
+ readonly WEBCHAT_UI: "webchat-ui";
5
+ readonly CONTROL_UI: "openclaw-control-ui";
6
+ readonly BROWSER_COPILOT: "openclaw-browser-copilot";
7
+ readonly TUI: "openclaw-tui";
8
+ readonly WEBCHAT: "webchat";
9
+ readonly CLI: "cli";
10
+ readonly GATEWAY_CLIENT: "gateway-client";
11
+ readonly MACOS_APP: "openclaw-macos";
12
+ readonly LINUX_APP: "openclaw-linux";
13
+ readonly IOS_APP: "openclaw-ios";
14
+ readonly WATCHOS_APP: "openclaw-watchos";
15
+ readonly ANDROID_APP: "openclaw-android";
16
+ readonly NODE_HOST: "node-host";
17
+ readonly WORKER: "openclaw-worker";
18
+ readonly TEST: "test";
19
+ readonly FINGERPRINT: "fingerprint";
20
+ readonly PROBE: "openclaw-probe";
21
+ };
22
+ /** Stable gateway client ids used on the wire during hello/connect handshakes. */
23
+ type GatewayClientId = (typeof GATEWAY_CLIENT_IDS)[keyof typeof GATEWAY_CLIENT_IDS];
24
+ declare const GATEWAY_CLIENT_NAMES: {
25
+ readonly WEBCHAT_UI: "webchat-ui";
26
+ readonly CONTROL_UI: "openclaw-control-ui";
27
+ readonly BROWSER_COPILOT: "openclaw-browser-copilot";
28
+ readonly TUI: "openclaw-tui";
29
+ readonly WEBCHAT: "webchat";
30
+ readonly CLI: "cli";
31
+ readonly GATEWAY_CLIENT: "gateway-client";
32
+ readonly MACOS_APP: "openclaw-macos";
33
+ readonly LINUX_APP: "openclaw-linux";
34
+ readonly IOS_APP: "openclaw-ios";
35
+ readonly WATCHOS_APP: "openclaw-watchos";
36
+ readonly ANDROID_APP: "openclaw-android";
37
+ readonly NODE_HOST: "node-host";
38
+ readonly WORKER: "openclaw-worker";
39
+ readonly TEST: "test";
40
+ readonly FINGERPRINT: "fingerprint";
41
+ readonly PROBE: "openclaw-probe";
42
+ };
43
+ /** Compatibility alias for internal callers that still use "name" terminology. */
44
+ type GatewayClientName = GatewayClientId;
45
+ /** Coarse modes let policy group clients without matching every product id. */
46
+ declare const GATEWAY_CLIENT_MODES: {
47
+ readonly WEBCHAT: "webchat";
48
+ readonly CLI: "cli";
49
+ readonly UI: "ui";
50
+ readonly BACKEND: "backend";
51
+ readonly NODE: "node";
52
+ readonly WORKER: "worker";
53
+ readonly PROBE: "probe";
54
+ readonly TEST: "test";
55
+ };
56
+ /** Coarse client category used for gateway policy and diagnostics. */
57
+ type GatewayClientMode = (typeof GATEWAY_CLIENT_MODES)[keyof typeof GATEWAY_CLIENT_MODES];
58
+ /** Client metadata sent during gateway connection setup. */
59
+ type GatewayClientInfo = {
60
+ /** Stable product/client identifier from `GATEWAY_CLIENT_IDS`. */id: GatewayClientId; /** Human-readable label for diagnostics; not used for policy decisions. */
61
+ displayName?: string; /** Client app or package version reported by the connecting process. */
62
+ version: string; /** Runtime platform string, such as `darwin`, `ios`, `android`, or `web`. */
63
+ platform: string; /** Optional device family used by native clients for display and routing hints. */
64
+ deviceFamily?: string; /** Native hardware/model identifier when available. */
65
+ modelIdentifier?: string; /** Coarse category from `GATEWAY_CLIENT_MODES` for policy and diagnostics. */
66
+ mode: GatewayClientMode; /** Per-installation or per-process id used to distinguish same-product clients. */
67
+ instanceId?: string;
68
+ };
69
+ /** Capability flags a client may advertise during the gateway handshake. */
70
+ declare const GATEWAY_CLIENT_CAPS: {
71
+ readonly AGENT_KIND: "agent-kind";
72
+ readonly APPROVALS: "approvals";
73
+ readonly EXEC_APPROVALS: "exec-approvals";
74
+ readonly INLINE_WIDGETS: "inline-widgets";
75
+ readonly RUN_TOOL_BINDINGS: "run-tool-bindings";
76
+ readonly SESSION_SCOPED_EVENTS: "session-scoped-events";
77
+ readonly PLUGIN_APPROVALS: "plugin-approvals";
78
+ readonly TASK_SUGGESTIONS: "task-suggestions";
79
+ readonly TERMINAL_OFFSET_SEQ: "terminal-offset-seq";
80
+ readonly TOOL_EVENTS: "tool-events";
81
+ readonly UI_COMMANDS: "ui-commands";
82
+ };
83
+ /** Optional capability advertised by clients during gateway handshake. */
84
+ type GatewayClientCap = (typeof GATEWAY_CLIENT_CAPS)[keyof typeof GATEWAY_CLIENT_CAPS];
85
+ /** Normalizes untrusted client ids and rejects unknown values. */
86
+ declare function normalizeGatewayClientId(raw?: string | null): GatewayClientId | undefined;
87
+ /** Normalizes legacy client-name fields through the canonical client-id registry. */
88
+ declare function normalizeGatewayClientName(raw?: string | null): GatewayClientName | undefined;
89
+ /** Normalizes untrusted client modes and rejects unknown values. */
90
+ declare function normalizeGatewayClientMode(raw?: string | null): GatewayClientMode | undefined;
91
+ /** Checks a client-advertised capability list without treating missing caps as errors. */
92
+ declare function hasGatewayClientCap(caps: string[] | null | undefined, cap: GatewayClientCap): boolean;
93
+ //#endregion
94
+ export { GATEWAY_CLIENT_CAPS, GATEWAY_CLIENT_IDS, GATEWAY_CLIENT_MODES, GATEWAY_CLIENT_NAMES, GatewayClientCap, GatewayClientId, GatewayClientInfo, GatewayClientMode, GatewayClientName, hasGatewayClientCap, normalizeGatewayClientId, normalizeGatewayClientMode, normalizeGatewayClientName };
@@ -0,0 +1,82 @@
1
+ //#region src/client-info.ts
2
+ /**
3
+ * Shared gateway client identity contract.
4
+ *
5
+ * These values cross the WebSocket handshake boundary, so additions must stay
6
+ * aligned with protocol schemas and server policy checks.
7
+ */
8
+ function normalizeOptionalLowercaseString(raw) {
9
+ if (typeof raw !== "string") return;
10
+ return raw.trim().toLowerCase() || void 0;
11
+ }
12
+ /** Canonical client ids accepted in gateway hello/connect payloads. */
13
+ const GATEWAY_CLIENT_IDS = {
14
+ WEBCHAT_UI: "webchat-ui",
15
+ CONTROL_UI: "openclaw-control-ui",
16
+ BROWSER_COPILOT: "openclaw-browser-copilot",
17
+ TUI: "openclaw-tui",
18
+ WEBCHAT: "webchat",
19
+ CLI: "cli",
20
+ GATEWAY_CLIENT: "gateway-client",
21
+ MACOS_APP: "openclaw-macos",
22
+ LINUX_APP: "openclaw-linux",
23
+ IOS_APP: "openclaw-ios",
24
+ WATCHOS_APP: "openclaw-watchos",
25
+ ANDROID_APP: "openclaw-android",
26
+ NODE_HOST: "node-host",
27
+ WORKER: "openclaw-worker",
28
+ TEST: "test",
29
+ FINGERPRINT: "fingerprint",
30
+ PROBE: "openclaw-probe"
31
+ };
32
+ const GATEWAY_CLIENT_NAMES = GATEWAY_CLIENT_IDS;
33
+ /** Coarse modes let policy group clients without matching every product id. */
34
+ const GATEWAY_CLIENT_MODES = {
35
+ WEBCHAT: "webchat",
36
+ CLI: "cli",
37
+ UI: "ui",
38
+ BACKEND: "backend",
39
+ NODE: "node",
40
+ WORKER: "worker",
41
+ PROBE: "probe",
42
+ TEST: "test"
43
+ };
44
+ /** Capability flags a client may advertise during the gateway handshake. */
45
+ const GATEWAY_CLIENT_CAPS = {
46
+ AGENT_KIND: "agent-kind",
47
+ APPROVALS: "approvals",
48
+ EXEC_APPROVALS: "exec-approvals",
49
+ INLINE_WIDGETS: "inline-widgets",
50
+ RUN_TOOL_BINDINGS: "run-tool-bindings",
51
+ SESSION_SCOPED_EVENTS: "session-scoped-events",
52
+ PLUGIN_APPROVALS: "plugin-approvals",
53
+ TASK_SUGGESTIONS: "task-suggestions",
54
+ TERMINAL_OFFSET_SEQ: "terminal-offset-seq",
55
+ TOOL_EVENTS: "tool-events",
56
+ UI_COMMANDS: "ui-commands"
57
+ };
58
+ const GATEWAY_CLIENT_ID_SET = new Set(Object.values(GATEWAY_CLIENT_IDS));
59
+ const GATEWAY_CLIENT_MODE_SET = new Set(Object.values(GATEWAY_CLIENT_MODES));
60
+ /** Normalizes untrusted client ids and rejects unknown values. */
61
+ function normalizeGatewayClientId(raw) {
62
+ const normalized = normalizeOptionalLowercaseString(raw);
63
+ if (!normalized) return;
64
+ return GATEWAY_CLIENT_ID_SET.has(normalized) ? normalized : void 0;
65
+ }
66
+ /** Normalizes legacy client-name fields through the canonical client-id registry. */
67
+ function normalizeGatewayClientName(raw) {
68
+ return normalizeGatewayClientId(raw);
69
+ }
70
+ /** Normalizes untrusted client modes and rejects unknown values. */
71
+ function normalizeGatewayClientMode(raw) {
72
+ const normalized = normalizeOptionalLowercaseString(raw);
73
+ if (!normalized) return;
74
+ return GATEWAY_CLIENT_MODE_SET.has(normalized) ? normalized : void 0;
75
+ }
76
+ /** Checks a client-advertised capability list without treating missing caps as errors. */
77
+ function hasGatewayClientCap(caps, cap) {
78
+ if (!Array.isArray(caps)) return false;
79
+ return caps.includes(cap);
80
+ }
81
+ //#endregion
82
+ export { GATEWAY_CLIENT_CAPS, GATEWAY_CLIENT_IDS, GATEWAY_CLIENT_MODES, GATEWAY_CLIENT_NAMES, hasGatewayClientCap, normalizeGatewayClientId, normalizeGatewayClientMode, normalizeGatewayClientName };
@@ -0,0 +1,114 @@
1
+ //#region src/connect-error-details.d.ts
2
+ /** Structured connect-error codes carried in gateway error `details.code`. */
3
+ declare const ConnectErrorDetailCodes: {
4
+ readonly AUTH_REQUIRED: "AUTH_REQUIRED";
5
+ readonly AUTH_UNAUTHORIZED: "AUTH_UNAUTHORIZED";
6
+ readonly AUTH_TOKEN_MISSING: "AUTH_TOKEN_MISSING";
7
+ readonly AUTH_TOKEN_MISMATCH: "AUTH_TOKEN_MISMATCH";
8
+ readonly AUTH_TOKEN_NOT_CONFIGURED: "AUTH_TOKEN_NOT_CONFIGURED";
9
+ readonly AUTH_PASSWORD_MISSING: "AUTH_PASSWORD_MISSING";
10
+ readonly AUTH_PASSWORD_MISMATCH: "AUTH_PASSWORD_MISMATCH";
11
+ readonly AUTH_PASSWORD_NOT_CONFIGURED: "AUTH_PASSWORD_NOT_CONFIGURED";
12
+ readonly AUTH_BOOTSTRAP_TOKEN_INVALID: "AUTH_BOOTSTRAP_TOKEN_INVALID";
13
+ readonly AUTH_DEVICE_TOKEN_MISMATCH: "AUTH_DEVICE_TOKEN_MISMATCH";
14
+ readonly AUTH_SCOPE_MISMATCH: "AUTH_SCOPE_MISMATCH";
15
+ readonly AUTH_RATE_LIMITED: "AUTH_RATE_LIMITED";
16
+ readonly AUTH_TAILSCALE_IDENTITY_MISSING: "AUTH_TAILSCALE_IDENTITY_MISSING";
17
+ readonly AUTH_TAILSCALE_PROXY_MISSING: "AUTH_TAILSCALE_PROXY_MISSING";
18
+ readonly AUTH_TAILSCALE_WHOIS_FAILED: "AUTH_TAILSCALE_WHOIS_FAILED";
19
+ readonly AUTH_TAILSCALE_IDENTITY_MISMATCH: "AUTH_TAILSCALE_IDENTITY_MISMATCH";
20
+ readonly CONTROL_UI_ORIGIN_NOT_ALLOWED: "CONTROL_UI_ORIGIN_NOT_ALLOWED";
21
+ readonly PROTOCOL_MISMATCH: "PROTOCOL_MISMATCH";
22
+ readonly CONTROL_UI_DEVICE_IDENTITY_REQUIRED: "CONTROL_UI_DEVICE_IDENTITY_REQUIRED";
23
+ readonly DEVICE_IDENTITY_REQUIRED: "DEVICE_IDENTITY_REQUIRED";
24
+ readonly DEVICE_AUTH_INVALID: "DEVICE_AUTH_INVALID";
25
+ readonly DEVICE_AUTH_DEVICE_ID_MISMATCH: "DEVICE_AUTH_DEVICE_ID_MISMATCH";
26
+ readonly DEVICE_AUTH_SIGNATURE_EXPIRED: "DEVICE_AUTH_SIGNATURE_EXPIRED";
27
+ readonly DEVICE_AUTH_NONCE_REQUIRED: "DEVICE_AUTH_NONCE_REQUIRED";
28
+ readonly DEVICE_AUTH_NONCE_MISMATCH: "DEVICE_AUTH_NONCE_MISMATCH";
29
+ readonly DEVICE_AUTH_SIGNATURE_INVALID: "DEVICE_AUTH_SIGNATURE_INVALID";
30
+ readonly DEVICE_AUTH_PUBLIC_KEY_INVALID: "DEVICE_AUTH_PUBLIC_KEY_INVALID";
31
+ readonly PAIRING_REQUIRED: "PAIRING_REQUIRED";
32
+ readonly CLIENT_VERSION_MISMATCH: "CLIENT_VERSION_MISMATCH";
33
+ };
34
+ type ConnectErrorDetailCode = (typeof ConnectErrorDetailCodes)[keyof typeof ConnectErrorDetailCodes];
35
+ /** Pairing-specific reasons clients can display and use for reconnect policy. */
36
+ declare const ConnectPairingRequiredReasons: {
37
+ readonly NOT_PAIRED: "not-paired";
38
+ readonly ROLE_UPGRADE: "role-upgrade";
39
+ readonly SCOPE_UPGRADE: "scope-upgrade";
40
+ readonly METADATA_UPGRADE: "metadata-upgrade";
41
+ };
42
+ type ConnectPairingRequiredReason = (typeof ConnectPairingRequiredReasons)[keyof typeof ConnectPairingRequiredReasons];
43
+ /** Suggested client-side recovery action for structured connect errors. */
44
+ type ConnectRecoveryNextStep = "retry_with_device_token" | "update_auth_configuration" | "update_auth_credentials" | "wait_then_retry" | "review_auth_configuration";
45
+ /** Optional retry guidance extracted from gateway connect-error details. */
46
+ type ConnectErrorRecoveryAdvice = {
47
+ canRetryWithDeviceToken?: boolean;
48
+ recommendedNextStep?: ConnectRecoveryNextStep;
49
+ };
50
+ /** Full structured details for pairing-required connect failures. */
51
+ type PairingConnectErrorDetails = {
52
+ code: typeof ConnectErrorDetailCodes.PAIRING_REQUIRED;
53
+ reason?: ConnectPairingRequiredReason;
54
+ requestId?: string;
55
+ remediationHint?: string;
56
+ recommendedNextStep?: ConnectRecoveryNextStep;
57
+ retryable?: boolean;
58
+ pauseReconnect?: boolean;
59
+ deviceId?: string;
60
+ requestedRole?: string;
61
+ requestedScopes?: string[];
62
+ approvedRoles?: string[];
63
+ approvedScopes?: string[];
64
+ };
65
+ /** Compact pairing-required subset used by reconnect/status surfaces. */
66
+ type ConnectPairingRequiredDetails = Pick<PairingConnectErrorDetails, "reason" | "requestId">;
67
+ /** Maps internal auth failure reasons to public connect-error detail codes. */
68
+ declare function resolveAuthConnectErrorDetailCode(reason: string | undefined): ConnectErrorDetailCode;
69
+ /** Maps device-auth verifier reasons to public connect-error detail codes. */
70
+ declare function resolveDeviceAuthConnectErrorDetailCode(reason: string | undefined): ConnectErrorDetailCode;
71
+ /** Reads a non-empty detail code from an untrusted error details payload. */
72
+ declare function readConnectErrorDetailCode(details: unknown): string | null;
73
+ /** Extracts normalized retry advice from untrusted connect-error details. */
74
+ declare function readConnectErrorRecoveryAdvice(details: unknown): ConnectErrorRecoveryAdvice;
75
+ /** Normalizes pairing request ids before echoing them in close reasons or UI text. */
76
+ declare function normalizePairingConnectRequestId(value: unknown): string | undefined;
77
+ /** Human-readable requirement summary for a pairing-required reason. */
78
+ declare function describePairingConnectRequirement(reason: ConnectPairingRequiredReason | undefined): string;
79
+ /** Builds the gateway close/error message for a pairing-required connect failure. */
80
+ declare function buildPairingConnectErrorMessage(reason: ConnectPairingRequiredReason | undefined): string;
81
+ /** Short user-facing recovery title for pairing-required connect failures. */
82
+ declare function buildPairingConnectRecoveryTitle(reason: ConnectPairingRequiredReason | undefined): string;
83
+ /** Builds sanitized structured details for a pairing-required connect failure. */
84
+ declare function buildPairingConnectErrorDetails(params: {
85
+ reason: ConnectPairingRequiredReason | undefined;
86
+ requestId?: string;
87
+ remediationHint?: string;
88
+ recommendedNextStep?: ConnectRecoveryNextStep;
89
+ retryable?: boolean;
90
+ pauseReconnect?: boolean;
91
+ deviceId?: string;
92
+ requestedRole?: string;
93
+ requestedScopes?: string[];
94
+ approvedRoles?: string[];
95
+ approvedScopes?: string[];
96
+ }): PairingConnectErrorDetails;
97
+ /** Builds a sanitized close reason string for WebSocket pairing rejections. */
98
+ declare function buildPairingConnectCloseReason(params: {
99
+ reason: ConnectPairingRequiredReason | undefined;
100
+ requestId?: string;
101
+ }): string;
102
+ /** Reads and backfills pairing-required details from an untrusted details object. */
103
+ declare function readPairingConnectErrorDetails(details: unknown): PairingConnectErrorDetails | null;
104
+ /** Parses legacy/string-only pairing-required messages into structured details. */
105
+ declare function readConnectPairingRequiredMessage(message: string | null | undefined): ConnectPairingRequiredDetails | null;
106
+ /** Formats pairing-required details into the canonical user-facing message. */
107
+ declare function formatConnectPairingRequiredMessage(details: unknown): string;
108
+ /** Formats connect errors using structured details before falling back to raw messages. */
109
+ declare function formatConnectErrorMessage(params: {
110
+ message?: string;
111
+ details?: unknown;
112
+ }): string;
113
+ //#endregion
114
+ export { ConnectErrorDetailCodes, ConnectPairingRequiredDetails, ConnectPairingRequiredReason, buildPairingConnectCloseReason, buildPairingConnectErrorDetails, buildPairingConnectErrorMessage, buildPairingConnectRecoveryTitle, describePairingConnectRequirement, formatConnectErrorMessage, formatConnectPairingRequiredMessage, normalizePairingConnectRequestId, readConnectErrorDetailCode, readConnectErrorRecoveryAdvice, readConnectPairingRequiredMessage, readPairingConnectErrorDetails, resolveAuthConnectErrorDetailCode, resolveDeviceAuthConnectErrorDetailCode };