@byok-sdk/protocol 0.1.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ancienttwo
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 ADDED
@@ -0,0 +1,10 @@
1
+ # @byok-sdk/protocol
2
+
3
+ The frozen v1 BYOK device wire contract: envelope schemas, message payloads,
4
+ codecs, version negotiation, and golden fixtures.
5
+
6
+ ```ts
7
+ import { encodeEnvelope, decodeEnvelope } from '@byok-sdk/protocol';
8
+ ```
9
+
10
+ MIT licensed. Node.js 20 or newer.
@@ -0,0 +1,146 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * Normalized event shape that every runtime adapter (pi / claude / codex)
4
+ * translates its native JSONL output into. This is the interior of a
5
+ * `task.progress` payload's `events` array.
6
+ */
7
+ export declare const AgentEventSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
8
+ type: z.ZodLiteral<"progress">;
9
+ text: z.ZodString;
10
+ }, z.core.$strip>, z.ZodObject<{
11
+ type: z.ZodLiteral<"tool_use">;
12
+ tool: z.ZodString;
13
+ input: z.ZodOptional<z.ZodUnknown>;
14
+ }, z.core.$strip>, z.ZodObject<{
15
+ type: z.ZodLiteral<"tool_result">;
16
+ tool: z.ZodString;
17
+ output: z.ZodOptional<z.ZodUnknown>;
18
+ }, z.core.$strip>, z.ZodObject<{
19
+ type: z.ZodLiteral<"artifact">;
20
+ name: z.ZodString;
21
+ contentType: z.ZodString;
22
+ }, z.core.$strip>, z.ZodObject<{
23
+ type: z.ZodLiteral<"needs_approval">;
24
+ summary: z.ZodString;
25
+ }, z.core.$strip>, z.ZodObject<{
26
+ type: z.ZodLiteral<"turn_end">;
27
+ }, z.core.$strip>, z.ZodObject<{
28
+ type: z.ZodLiteral<"error">;
29
+ message: z.ZodString;
30
+ }, z.core.$strip>, z.ZodObject<{
31
+ type: z.ZodLiteral<"usage">;
32
+ inputTokens: z.ZodOptional<z.ZodNumber>;
33
+ cachedInputTokens: z.ZodOptional<z.ZodNumber>;
34
+ outputTokens: z.ZodOptional<z.ZodNumber>;
35
+ reasoningTokens: z.ZodOptional<z.ZodNumber>;
36
+ totalTokens: z.ZodOptional<z.ZodNumber>;
37
+ }, z.core.$strip>], "type">;
38
+ export type AgentEvent = z.infer<typeof AgentEventSchema>;
39
+ /**
40
+ * Known AgentEvent variant type discriminators — DERIVED directly from
41
+ * {@link AgentEventSchema}'s own discriminated-union variants via
42
+ * `z.toJSONSchema`, rather than hand-maintained as a second literal list.
43
+ * This used to be a standalone array kept in sync with the schema above by
44
+ * hand (dual authority); the freeze guard
45
+ * (`__tests__/freeze-guard.test.ts`'s "dual-authority cross-check") already
46
+ * asserted the two matched using this EXACT SAME `z.toJSONSchema` extraction
47
+ * mechanism, which is why deriving it this way is safe: the guard already
48
+ * proved this extraction produces the identical set the hand-written list
49
+ * held. With the derivation below, the two can no longer drift apart at
50
+ * all — there is only one authority now, {@link AgentEventSchema} itself.
51
+ * The freeze guard test is kept anyway (now definitionally true rather than
52
+ * a live check) as a regression net in case a future refactor reintroduces a
53
+ * hand-written list.
54
+ *
55
+ * Exported (not module-private) so {@link isKnownAgentEvent} /
56
+ * {@link partitionAgentEvents} (and the freeze guard) can check against the
57
+ * exact same set without each reaching into zod's discriminated-union
58
+ * internals directly. `z.toJSONSchema`'s output shape here (`.oneOf[].
59
+ * properties.type.const`) is a public, documented zod v4 API — not
60
+ * reaching into `._def`/internal fields — same as the freeze guard already
61
+ * relies on.
62
+ */
63
+ export declare const KNOWN_AGENT_EVENT_TYPES: readonly string[];
64
+ /**
65
+ * Pre-freeze compatibility widening (the freeze blocker this schema fixes):
66
+ * an unknown-type event — one a future runtime/protocol minor version
67
+ * introduces — parses as an opaque passthrough placeholder instead of
68
+ * hard-failing the entire `task.progress` batch it arrived in. Without this,
69
+ * `TaskProgressPayloadSchema.events: z.array(AgentEventSchema)` would throw
70
+ * on the whole array the moment one event had an unrecognized `type`, which
71
+ * made the wire's "additive new variants are non-breaking" promise false for
72
+ * the installed base — and unfixable post-freeze.
73
+ *
74
+ * The `.refine` guard is load-bearing, not decorative: it excludes every
75
+ * KNOWN type literal, so a *malformed* known variant (e.g. `progress`
76
+ * missing `text`) still fails validation instead of silently matching this
77
+ * fallback. Tolerance is only for unknown TYPES, never for malformed known
78
+ * ones — see {@link AgentEventOrUnknownSchema}, which is what actually
79
+ * combines this with {@link AgentEventSchema} for real use.
80
+ *
81
+ * Deliberately asymmetric with envelope-level control/security fields
82
+ * (`instruction`, `policy` — see `messages.ts`/`permission.ts`), which stay
83
+ * fail-closed on unknown shapes with no equivalent widening: this tolerance
84
+ * applies only to observability data (agent progress events), never to
85
+ * control/security surfaces. That asymmetry is the freeze rule.
86
+ */
87
+ export declare const UnknownAgentEventSchema: z.ZodObject<{
88
+ type: z.ZodString;
89
+ }, z.core.$loose>;
90
+ export type UnknownAgentEvent = z.infer<typeof UnknownAgentEventSchema>;
91
+ /**
92
+ * The actual element schema for `TaskProgressPayloadSchema.events`
93
+ * (`messages.ts`): a known, fully-typed {@link AgentEvent} OR an opaque
94
+ * unknown-type placeholder. `z.union` (not `discriminatedUnion`) is required
95
+ * here because the fallback branch matches on "not one of the known
96
+ * literals", which a discriminated union can't express directly.
97
+ */
98
+ export declare const AgentEventOrUnknownSchema: z.ZodUnion<readonly [z.ZodDiscriminatedUnion<[z.ZodObject<{
99
+ type: z.ZodLiteral<"progress">;
100
+ text: z.ZodString;
101
+ }, z.core.$strip>, z.ZodObject<{
102
+ type: z.ZodLiteral<"tool_use">;
103
+ tool: z.ZodString;
104
+ input: z.ZodOptional<z.ZodUnknown>;
105
+ }, z.core.$strip>, z.ZodObject<{
106
+ type: z.ZodLiteral<"tool_result">;
107
+ tool: z.ZodString;
108
+ output: z.ZodOptional<z.ZodUnknown>;
109
+ }, z.core.$strip>, z.ZodObject<{
110
+ type: z.ZodLiteral<"artifact">;
111
+ name: z.ZodString;
112
+ contentType: z.ZodString;
113
+ }, z.core.$strip>, z.ZodObject<{
114
+ type: z.ZodLiteral<"needs_approval">;
115
+ summary: z.ZodString;
116
+ }, z.core.$strip>, z.ZodObject<{
117
+ type: z.ZodLiteral<"turn_end">;
118
+ }, z.core.$strip>, z.ZodObject<{
119
+ type: z.ZodLiteral<"error">;
120
+ message: z.ZodString;
121
+ }, z.core.$strip>, z.ZodObject<{
122
+ type: z.ZodLiteral<"usage">;
123
+ inputTokens: z.ZodOptional<z.ZodNumber>;
124
+ cachedInputTokens: z.ZodOptional<z.ZodNumber>;
125
+ outputTokens: z.ZodOptional<z.ZodNumber>;
126
+ reasoningTokens: z.ZodOptional<z.ZodNumber>;
127
+ totalTokens: z.ZodOptional<z.ZodNumber>;
128
+ }, z.core.$strip>], "type">, z.ZodObject<{
129
+ type: z.ZodString;
130
+ }, z.core.$loose>]>;
131
+ export type AgentEventOrUnknown = z.infer<typeof AgentEventOrUnknownSchema>;
132
+ /**
133
+ * Type guard distinguishing a known, fully-typed {@link AgentEvent} from an
134
+ * {@link UnknownAgentEvent} passthrough placeholder.
135
+ */
136
+ export declare function isKnownAgentEvent(event: AgentEventOrUnknown): event is AgentEvent;
137
+ /**
138
+ * Split a `task.progress` events array into known (typed, actionable) and
139
+ * unknown (opaque, safe-to-skip) events. Consumers should process `known`
140
+ * and skip `unknown` rather than throwing on it — that's the point of the
141
+ * pre-freeze tolerance above.
142
+ */
143
+ export declare function partitionAgentEvents(events: readonly AgentEventOrUnknown[]): {
144
+ known: AgentEvent[];
145
+ unknown: UnknownAgentEvent[];
146
+ };
package/dist/blob.d.ts ADDED
@@ -0,0 +1,24 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * Canonical `contentHash` format (finding F9): `sha256:` followed by exactly
4
+ * 64 lowercase hex characters (a SHA-256 digest). Pinned here — the single
5
+ * source of truth both `BlobRefSchema` and `CreateBlobRequestSchema`
6
+ * (`http-api.ts`) validate against — rather than left as a bare `z.string()`
7
+ * that silently accepted any prefix (or none) and left the server to
8
+ * reconcile the mismatch with an ad hoc normalization step. No compat shim:
9
+ * the wire is pre-freeze (`v` stays `1`), so this is a straight tightening,
10
+ * not a migration.
11
+ */
12
+ export declare const CONTENT_HASH_RE: RegExp;
13
+ /**
14
+ * Reference to a large payload that was pushed out-of-band (presigned PUT) or
15
+ * is fetchable out-of-band (presigned GET), rather than inlined in an envelope.
16
+ */
17
+ export declare const BlobRefSchema: z.ZodObject<{
18
+ blobId: z.ZodString;
19
+ contentHash: z.ZodString;
20
+ size: z.ZodNumber;
21
+ contentType: z.ZodString;
22
+ url: z.ZodOptional<z.ZodString>;
23
+ }, z.core.$strip>;
24
+ export type BlobRef = z.infer<typeof BlobRefSchema>;
@@ -0,0 +1,116 @@
1
+ import type { z } from 'zod';
2
+ import { type Envelope } from './envelope';
3
+ import { MESSAGE_PAYLOAD_SCHEMAS, type MessageType } from './messages';
4
+ /**
5
+ * Validate an already-parsed JS value as an {@link Envelope}, narrowing
6
+ * `payload` by `type`. Throws {@link UnknownMessageTypeError} when `type`
7
+ * isn't a recognized message type (safe for the caller to skip/ignore), or
8
+ * {@link EnvelopeValidationError} when a recognized type fails schema
9
+ * validation.
10
+ */
11
+ export declare function parseMessage(data: unknown): Envelope;
12
+ /**
13
+ * Decode a single NDJSON line into a validated {@link Envelope}. Accepts a
14
+ * string or raw bytes (e.g. a WebSocket binary frame) — isomorphic, no
15
+ * stream handling required of the caller.
16
+ */
17
+ export declare function decodeEnvelope(line: string | Uint8Array): Envelope;
18
+ /** Encode an {@link Envelope} as a single-line NDJSON string (trailing `\n` included). */
19
+ export declare function encodeEnvelope(env: Envelope): string;
20
+ interface EnvelopeShapeOptions {
21
+ 'conn.hello': {
22
+ taskId?: string;
23
+ seq?: number;
24
+ };
25
+ 'conn.ack': {
26
+ taskId?: string;
27
+ seq: number;
28
+ };
29
+ 'task.offer': {
30
+ taskId: string;
31
+ seq: number;
32
+ };
33
+ 'task.approve': {
34
+ taskId: string;
35
+ seq: number;
36
+ };
37
+ 'task.reject': {
38
+ taskId: string;
39
+ seq: number;
40
+ };
41
+ 'task.cancel': {
42
+ taskId: string;
43
+ seq: number;
44
+ };
45
+ 'task.steer': {
46
+ taskId: string;
47
+ seq: number;
48
+ };
49
+ 'task.claim': {
50
+ taskId: string;
51
+ seq?: number;
52
+ };
53
+ 'task.started': {
54
+ taskId: string;
55
+ seq?: number;
56
+ };
57
+ 'task.decline': {
58
+ taskId: string;
59
+ seq?: number;
60
+ };
61
+ 'task.progress': {
62
+ taskId: string;
63
+ seq?: number;
64
+ };
65
+ 'task.artifact': {
66
+ taskId: string;
67
+ seq?: number;
68
+ };
69
+ 'task.await_approval': {
70
+ taskId: string;
71
+ seq?: number;
72
+ };
73
+ 'task.complete': {
74
+ taskId: string;
75
+ seq?: number;
76
+ };
77
+ 'task.fail': {
78
+ taskId: string;
79
+ seq?: number;
80
+ };
81
+ 'task.cancelled': {
82
+ taskId: string;
83
+ seq?: number;
84
+ };
85
+ 'task.approval_resolved': {
86
+ taskId: string;
87
+ seq?: number;
88
+ };
89
+ }
90
+ interface EnvelopeCommonOptions {
91
+ id?: string;
92
+ ts?: string;
93
+ v?: number;
94
+ /** Always optional regardless of `type` (docs/protocol.md §1.3). */
95
+ sessionRef?: string;
96
+ }
97
+ /** Public options shape for `createEnvelope<T>` — conditionally required `taskId`/`seq` per `EnvelopeShapeOptions[T]`, plus the always-optional common fields. Defaults to the full `MessageType` union (a loose, all-optional-ish shape) when `T` isn't pinned, which is also what `createEnvelope`'s own implementation uses internally to read `opts` without fighting the per-call-site conditional. */
98
+ export type CreateEnvelopeOptions<T extends MessageType = MessageType> = EnvelopeCommonOptions & EnvelopeShapeOptions[T];
99
+ /** `never` unless every key of `T` is optional — i.e. whether `createEnvelope`'s `opts` argument can be omitted entirely for a given message type. */
100
+ type RequiredKeys<T> = {
101
+ [K in keyof T]-?: object extends Pick<T, K> ? never : K;
102
+ }[keyof T];
103
+ /** The rest-parameter shape for `createEnvelope`'s 3rd argument: present-and-optional when `T` needs nothing, present-and-required when it needs `taskId` and/or `seq`. */
104
+ type CreateEnvelopeArgs<T extends MessageType> = RequiredKeys<EnvelopeShapeOptions[T]> extends never ? [opts?: CreateEnvelopeOptions<T>] : [opts: CreateEnvelopeOptions<T>];
105
+ type PayloadOf<T extends MessageType> = z.infer<(typeof MESSAGE_PAYLOAD_SCHEMAS)[T]>;
106
+ /**
107
+ * Build a well-formed {@link Envelope}, filling `v`/`id`/`ts` with defaults.
108
+ * `opts` (`taskId`/`seq`) is required or optional depending on `type` — see
109
+ * the module doc above — and the constructed envelope is validated against
110
+ * {@link EnvelopeSchema} before being returned, throwing
111
+ * {@link EnvelopeValidationError} if it doesn't satisfy the schema.
112
+ */
113
+ export declare function createEnvelope<T extends MessageType>(type: T, payload: PayloadOf<T>, ...rest: CreateEnvelopeArgs<T>): Extract<Envelope, {
114
+ type: T;
115
+ }>;
116
+ export {};
@@ -0,0 +1,333 @@
1
+ import { z } from 'zod';
2
+ import { type MessageType } from './messages';
3
+ /**
4
+ * The wire envelope: common transport fields plus a `payload` whose shape is
5
+ * determined by `type`. Unknown top-level fields are tolerated (stripped) for
6
+ * forward-compat; unknown `type` values do not match any branch below and are
7
+ * handled explicitly by {@link parseMessage} in `codec.ts`.
8
+ *
9
+ * Two cross-cutting requiredness rules, fixed at M1 (see docs/protocol.md
10
+ * "M0 -> M1 breaking changes"):
11
+ *
12
+ * - `task_id` is REQUIRED for every `task.*` type (they all route by task id)
13
+ * and stays optional for `conn.*` (M1 gap #1).
14
+ * - `seq` is REQUIRED for every type the *server* sends to the daemon — a
15
+ * per-device monotonic counter used as a redelivery cursor — and stays
16
+ * optional for daemon -> server types (M1 redelivery cursor; see
17
+ * `conn.hello.cursor` in `messages.ts`).
18
+ */
19
+ export declare const EnvelopeSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
20
+ v: z.ZodNumber;
21
+ id: z.ZodUUID;
22
+ ts: z.ZodISODateTime;
23
+ type: z.ZodLiteral<"conn.hello">;
24
+ task_id: z.ZodOptional<z.ZodString>;
25
+ session_ref: z.ZodOptional<z.ZodString>;
26
+ seq: z.ZodOptional<z.ZodNumber>;
27
+ payload: z.ZodObject<{
28
+ protocolVersions: z.ZodArray<z.ZodNumber>;
29
+ capabilities: z.ZodArray<z.ZodString>;
30
+ deviceId: z.ZodString;
31
+ productId: z.ZodString;
32
+ runtimes: z.ZodOptional<z.ZodArray<z.ZodObject<{
33
+ id: z.ZodEnum<{
34
+ claude: "claude";
35
+ codex: "codex";
36
+ pi: "pi";
37
+ }>;
38
+ version: z.ZodOptional<z.ZodString>;
39
+ authPresent: z.ZodOptional<z.ZodBoolean>;
40
+ capabilities: z.ZodOptional<z.ZodObject<{
41
+ steer: z.ZodOptional<z.ZodBoolean>;
42
+ resume: z.ZodOptional<z.ZodBoolean>;
43
+ approvalInteractive: z.ZodOptional<z.ZodBoolean>;
44
+ permissionModes: z.ZodOptional<z.ZodArray<z.ZodString>>;
45
+ }, z.core.$strip>>;
46
+ }, z.core.$strip>>>;
47
+ cursor: z.ZodOptional<z.ZodNumber>;
48
+ }, z.core.$strip>;
49
+ }, z.core.$strip>, z.ZodObject<{
50
+ v: z.ZodNumber;
51
+ id: z.ZodUUID;
52
+ ts: z.ZodISODateTime;
53
+ type: z.ZodLiteral<"conn.ack">;
54
+ task_id: z.ZodOptional<z.ZodString>;
55
+ session_ref: z.ZodOptional<z.ZodString>;
56
+ seq: z.ZodNumber;
57
+ payload: z.ZodObject<{
58
+ protocolVersion: z.ZodNumber;
59
+ capabilities: z.ZodArray<z.ZodString>;
60
+ serverTime: z.ZodISODateTime;
61
+ }, z.core.$strip>;
62
+ }, z.core.$strip>, z.ZodObject<{
63
+ v: z.ZodNumber;
64
+ id: z.ZodUUID;
65
+ ts: z.ZodISODateTime;
66
+ type: z.ZodLiteral<"task.offer">;
67
+ task_id: z.ZodString;
68
+ session_ref: z.ZodOptional<z.ZodString>;
69
+ seq: z.ZodNumber;
70
+ payload: z.ZodObject<{
71
+ instruction: z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
72
+ blobRef: z.ZodObject<{
73
+ blobId: z.ZodString;
74
+ contentHash: z.ZodString;
75
+ size: z.ZodNumber;
76
+ contentType: z.ZodString;
77
+ url: z.ZodOptional<z.ZodString>;
78
+ }, z.core.$strip>;
79
+ }, z.core.$strict>]>;
80
+ policy: z.ZodObject<{
81
+ mode: z.ZodEnum<{
82
+ auto: "auto";
83
+ confirm: "confirm";
84
+ plan: "plan";
85
+ readonly: "readonly";
86
+ }>;
87
+ allowTools: z.ZodOptional<z.ZodArray<z.ZodString>>;
88
+ denyTools: z.ZodOptional<z.ZodArray<z.ZodString>>;
89
+ workspaceRoot: z.ZodOptional<z.ZodString>;
90
+ network: z.ZodOptional<z.ZodBoolean>;
91
+ }, z.core.$strict>;
92
+ runtime: z.ZodOptional<z.ZodEnum<{
93
+ claude: "claude";
94
+ codex: "codex";
95
+ pi: "pi";
96
+ }>>;
97
+ sessionRef: z.ZodOptional<z.ZodString>;
98
+ workspaceHint: z.ZodOptional<z.ZodString>;
99
+ limits: z.ZodOptional<z.ZodObject<{
100
+ maxDurationMs: z.ZodOptional<z.ZodNumber>;
101
+ maxTokens: z.ZodOptional<z.ZodNumber>;
102
+ }, z.core.$strip>>;
103
+ }, z.core.$strip>;
104
+ }, z.core.$strip>, z.ZodObject<{
105
+ v: z.ZodNumber;
106
+ id: z.ZodUUID;
107
+ ts: z.ZodISODateTime;
108
+ type: z.ZodLiteral<"task.approve">;
109
+ task_id: z.ZodString;
110
+ session_ref: z.ZodOptional<z.ZodString>;
111
+ seq: z.ZodNumber;
112
+ payload: z.ZodObject<{
113
+ approvalId: z.ZodOptional<z.ZodString>;
114
+ }, z.core.$strip>;
115
+ }, z.core.$strip>, z.ZodObject<{
116
+ v: z.ZodNumber;
117
+ id: z.ZodUUID;
118
+ ts: z.ZodISODateTime;
119
+ type: z.ZodLiteral<"task.reject">;
120
+ task_id: z.ZodString;
121
+ session_ref: z.ZodOptional<z.ZodString>;
122
+ seq: z.ZodNumber;
123
+ payload: z.ZodObject<{
124
+ reason: z.ZodOptional<z.ZodString>;
125
+ approvalId: z.ZodOptional<z.ZodString>;
126
+ }, z.core.$strip>;
127
+ }, z.core.$strip>, z.ZodObject<{
128
+ v: z.ZodNumber;
129
+ id: z.ZodUUID;
130
+ ts: z.ZodISODateTime;
131
+ type: z.ZodLiteral<"task.cancel">;
132
+ task_id: z.ZodString;
133
+ session_ref: z.ZodOptional<z.ZodString>;
134
+ seq: z.ZodNumber;
135
+ payload: z.ZodObject<{
136
+ reason: z.ZodOptional<z.ZodString>;
137
+ }, z.core.$strip>;
138
+ }, z.core.$strip>, z.ZodObject<{
139
+ v: z.ZodNumber;
140
+ id: z.ZodUUID;
141
+ ts: z.ZodISODateTime;
142
+ type: z.ZodLiteral<"task.steer">;
143
+ task_id: z.ZodString;
144
+ session_ref: z.ZodOptional<z.ZodString>;
145
+ seq: z.ZodNumber;
146
+ payload: z.ZodObject<{
147
+ text: z.ZodString;
148
+ }, z.core.$strip>;
149
+ }, z.core.$strip>, z.ZodObject<{
150
+ v: z.ZodNumber;
151
+ id: z.ZodUUID;
152
+ ts: z.ZodISODateTime;
153
+ type: z.ZodLiteral<"task.claim">;
154
+ task_id: z.ZodString;
155
+ session_ref: z.ZodOptional<z.ZodString>;
156
+ seq: z.ZodOptional<z.ZodNumber>;
157
+ payload: z.ZodObject<{
158
+ deviceId: z.ZodString;
159
+ agentId: z.ZodOptional<z.ZodString>;
160
+ runtime: z.ZodOptional<z.ZodEnum<{
161
+ claude: "claude";
162
+ codex: "codex";
163
+ pi: "pi";
164
+ }>>;
165
+ capabilities: z.ZodOptional<z.ZodObject<{
166
+ steer: z.ZodOptional<z.ZodBoolean>;
167
+ resume: z.ZodOptional<z.ZodBoolean>;
168
+ approvalInteractive: z.ZodOptional<z.ZodBoolean>;
169
+ permissionModes: z.ZodOptional<z.ZodArray<z.ZodString>>;
170
+ }, z.core.$strip>>;
171
+ }, z.core.$strip>;
172
+ }, z.core.$strip>, z.ZodObject<{
173
+ v: z.ZodNumber;
174
+ id: z.ZodUUID;
175
+ ts: z.ZodISODateTime;
176
+ type: z.ZodLiteral<"task.started">;
177
+ task_id: z.ZodString;
178
+ session_ref: z.ZodOptional<z.ZodString>;
179
+ seq: z.ZodOptional<z.ZodNumber>;
180
+ payload: z.ZodObject<{}, z.core.$strip>;
181
+ }, z.core.$strip>, z.ZodObject<{
182
+ v: z.ZodNumber;
183
+ id: z.ZodUUID;
184
+ ts: z.ZodISODateTime;
185
+ type: z.ZodLiteral<"task.decline">;
186
+ task_id: z.ZodString;
187
+ session_ref: z.ZodOptional<z.ZodString>;
188
+ seq: z.ZodOptional<z.ZodNumber>;
189
+ payload: z.ZodObject<{
190
+ reason: z.ZodString;
191
+ retryable: z.ZodOptional<z.ZodBoolean>;
192
+ }, z.core.$strip>;
193
+ }, z.core.$strip>, z.ZodObject<{
194
+ v: z.ZodNumber;
195
+ id: z.ZodUUID;
196
+ ts: z.ZodISODateTime;
197
+ type: z.ZodLiteral<"task.progress">;
198
+ task_id: z.ZodString;
199
+ session_ref: z.ZodOptional<z.ZodString>;
200
+ seq: z.ZodOptional<z.ZodNumber>;
201
+ payload: z.ZodObject<{
202
+ seq: z.ZodNumber;
203
+ events: z.ZodArray<z.ZodUnion<readonly [z.ZodDiscriminatedUnion<[z.ZodObject<{
204
+ type: z.ZodLiteral<"progress">;
205
+ text: z.ZodString;
206
+ }, z.core.$strip>, z.ZodObject<{
207
+ type: z.ZodLiteral<"tool_use">;
208
+ tool: z.ZodString;
209
+ input: z.ZodOptional<z.ZodUnknown>;
210
+ }, z.core.$strip>, z.ZodObject<{
211
+ type: z.ZodLiteral<"tool_result">;
212
+ tool: z.ZodString;
213
+ output: z.ZodOptional<z.ZodUnknown>;
214
+ }, z.core.$strip>, z.ZodObject<{
215
+ type: z.ZodLiteral<"artifact">;
216
+ name: z.ZodString;
217
+ contentType: z.ZodString;
218
+ }, z.core.$strip>, z.ZodObject<{
219
+ type: z.ZodLiteral<"needs_approval">;
220
+ summary: z.ZodString;
221
+ }, z.core.$strip>, z.ZodObject<{
222
+ type: z.ZodLiteral<"turn_end">;
223
+ }, z.core.$strip>, z.ZodObject<{
224
+ type: z.ZodLiteral<"error">;
225
+ message: z.ZodString;
226
+ }, z.core.$strip>, z.ZodObject<{
227
+ type: z.ZodLiteral<"usage">;
228
+ inputTokens: z.ZodOptional<z.ZodNumber>;
229
+ cachedInputTokens: z.ZodOptional<z.ZodNumber>;
230
+ outputTokens: z.ZodOptional<z.ZodNumber>;
231
+ reasoningTokens: z.ZodOptional<z.ZodNumber>;
232
+ totalTokens: z.ZodOptional<z.ZodNumber>;
233
+ }, z.core.$strip>], "type">, z.ZodObject<{
234
+ type: z.ZodString;
235
+ }, z.core.$loose>]>>;
236
+ }, z.core.$strip>;
237
+ }, z.core.$strip>, z.ZodObject<{
238
+ v: z.ZodNumber;
239
+ id: z.ZodUUID;
240
+ ts: z.ZodISODateTime;
241
+ type: z.ZodLiteral<"task.artifact">;
242
+ task_id: z.ZodString;
243
+ session_ref: z.ZodOptional<z.ZodString>;
244
+ seq: z.ZodOptional<z.ZodNumber>;
245
+ payload: z.ZodObject<{
246
+ name: z.ZodString;
247
+ contentType: z.ZodString;
248
+ inline: z.ZodOptional<z.ZodString>;
249
+ blobRef: z.ZodOptional<z.ZodObject<{
250
+ blobId: z.ZodString;
251
+ contentHash: z.ZodString;
252
+ size: z.ZodNumber;
253
+ contentType: z.ZodString;
254
+ url: z.ZodOptional<z.ZodString>;
255
+ }, z.core.$strip>>;
256
+ }, z.core.$strip>;
257
+ }, z.core.$strip>, z.ZodObject<{
258
+ v: z.ZodNumber;
259
+ id: z.ZodUUID;
260
+ ts: z.ZodISODateTime;
261
+ type: z.ZodLiteral<"task.await_approval">;
262
+ task_id: z.ZodString;
263
+ session_ref: z.ZodOptional<z.ZodString>;
264
+ seq: z.ZodOptional<z.ZodNumber>;
265
+ payload: z.ZodObject<{
266
+ summary: z.ZodString;
267
+ approvalId: z.ZodOptional<z.ZodString>;
268
+ }, z.core.$strip>;
269
+ }, z.core.$strip>, z.ZodObject<{
270
+ v: z.ZodNumber;
271
+ id: z.ZodUUID;
272
+ ts: z.ZodISODateTime;
273
+ type: z.ZodLiteral<"task.complete">;
274
+ task_id: z.ZodString;
275
+ session_ref: z.ZodOptional<z.ZodString>;
276
+ seq: z.ZodOptional<z.ZodNumber>;
277
+ payload: z.ZodObject<{
278
+ summary: z.ZodString;
279
+ sessionRef: z.ZodString;
280
+ artifactRefs: z.ZodOptional<z.ZodArray<z.ZodObject<{
281
+ blobId: z.ZodString;
282
+ contentHash: z.ZodString;
283
+ size: z.ZodNumber;
284
+ contentType: z.ZodString;
285
+ url: z.ZodOptional<z.ZodString>;
286
+ }, z.core.$strip>>>;
287
+ }, z.core.$strip>;
288
+ }, z.core.$strip>, z.ZodObject<{
289
+ v: z.ZodNumber;
290
+ id: z.ZodUUID;
291
+ ts: z.ZodISODateTime;
292
+ type: z.ZodLiteral<"task.fail">;
293
+ task_id: z.ZodString;
294
+ session_ref: z.ZodOptional<z.ZodString>;
295
+ seq: z.ZodOptional<z.ZodNumber>;
296
+ payload: z.ZodObject<{
297
+ reason: z.ZodString;
298
+ retryable: z.ZodOptional<z.ZodBoolean>;
299
+ }, z.core.$strip>;
300
+ }, z.core.$strip>, z.ZodObject<{
301
+ v: z.ZodNumber;
302
+ id: z.ZodUUID;
303
+ ts: z.ZodISODateTime;
304
+ type: z.ZodLiteral<"task.cancelled">;
305
+ task_id: z.ZodString;
306
+ session_ref: z.ZodOptional<z.ZodString>;
307
+ seq: z.ZodOptional<z.ZodNumber>;
308
+ payload: z.ZodObject<{
309
+ reason: z.ZodOptional<z.ZodString>;
310
+ }, z.core.$strip>;
311
+ }, z.core.$strip>, z.ZodObject<{
312
+ v: z.ZodNumber;
313
+ id: z.ZodUUID;
314
+ ts: z.ZodISODateTime;
315
+ type: z.ZodLiteral<"task.approval_resolved">;
316
+ task_id: z.ZodString;
317
+ session_ref: z.ZodOptional<z.ZodString>;
318
+ seq: z.ZodOptional<z.ZodNumber>;
319
+ payload: z.ZodObject<{
320
+ approvalId: z.ZodString;
321
+ decision: z.ZodEnum<{
322
+ approve: "approve";
323
+ reject: "reject";
324
+ }>;
325
+ resolvedBy: z.ZodEnum<{
326
+ local: "local";
327
+ }>;
328
+ at: z.ZodISODateTime;
329
+ }, z.core.$strip>;
330
+ }, z.core.$strip>], "type">;
331
+ export type Envelope = z.infer<typeof EnvelopeSchema>;
332
+ /** `true` for every message type the server sends to the daemon (envelope `seq` is required for these). */
333
+ export declare function isServerToDaemonType(type: MessageType): boolean;
@@ -0,0 +1,25 @@
1
+ import type { ZodError } from 'zod';
2
+ /** Base class for all protocol decode/validation errors. */
3
+ export declare class ProtocolError extends Error {
4
+ constructor(message: string, options?: ErrorOptions);
5
+ }
6
+ /** The input was not valid JSON at all (only thrown by `decodeEnvelope`). */
7
+ export declare class EnvelopeParseError extends ProtocolError {
8
+ constructor(message: string, cause?: unknown);
9
+ }
10
+ /**
11
+ * The `type` field did not match any known message type. This is distinct
12
+ * from {@link EnvelopeValidationError} on purpose: a daemon/server on an
13
+ * older minor version should catch this specifically and skip the message
14
+ * instead of treating it as a bug, since a newer peer may have introduced an
15
+ * additive message type it doesn't understand yet.
16
+ */
17
+ export declare class UnknownMessageTypeError extends ProtocolError {
18
+ readonly type: unknown;
19
+ constructor(type: unknown);
20
+ }
21
+ /** The `type` field was recognized but the envelope/payload failed schema validation. */
22
+ export declare class EnvelopeValidationError extends ProtocolError {
23
+ readonly issues: ZodError;
24
+ constructor(message: string, issues: ZodError);
25
+ }