@byok-sdk/protocol 0.3.0 → 0.4.1

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
@@ -14,4 +14,4 @@ the additive field and silently executing a runtime-only offer.
14
14
  import { encodeEnvelope, decodeEnvelope } from '@byok-sdk/protocol';
15
15
  ```
16
16
 
17
- MIT licensed. Node.js 22.19.0 or newer.
17
+ MIT licensed. Node.js 22.22.0 or newer.
@@ -45,6 +45,7 @@ export declare const EnvelopeSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
45
45
  permissionModes: z.ZodOptional<z.ZodArray<z.ZodString>>;
46
46
  }, z.core.$strip>>;
47
47
  }, z.core.$strip>>>;
48
+ configuredToolsets: z.ZodOptional<z.ZodArray<z.ZodString>>;
48
49
  cursor: z.ZodOptional<z.ZodNumber>;
49
50
  }, z.core.$strip>;
50
51
  }, z.core.$strip>, z.ZodObject<{
@@ -99,6 +99,7 @@ export declare const EventsPollResponseSchema: z.ZodObject<{
99
99
  permissionModes: z.ZodOptional<z.ZodArray<z.ZodString>>;
100
100
  }, z.core.$strip>>;
101
101
  }, z.core.$strip>>>;
102
+ configuredToolsets: z.ZodOptional<z.ZodArray<z.ZodString>>;
102
103
  cursor: z.ZodOptional<z.ZodNumber>;
103
104
  }, z.core.$strip>;
104
105
  }, z.core.$strip>, z.ZodObject<{
@@ -457,6 +458,7 @@ export declare const EventsPollResponseSchema: z.ZodObject<{
457
458
  }, z.core.$strip>;
458
459
  }, z.core.$strip>], "type">>;
459
460
  cursor: z.ZodNumber;
461
+ capabilities: z.ZodOptional<z.ZodArray<z.ZodString>>;
460
462
  }, z.core.$strip>;
461
463
  export type EventsPollResponse = z.infer<typeof EventsPollResponseSchema>;
462
464
  /**
@@ -498,6 +500,7 @@ export declare const MessagesSendRequestSchema: z.ZodObject<{
498
500
  permissionModes: z.ZodOptional<z.ZodArray<z.ZodString>>;
499
501
  }, z.core.$strip>>;
500
502
  }, z.core.$strip>>>;
503
+ configuredToolsets: z.ZodOptional<z.ZodArray<z.ZodString>>;
501
504
  cursor: z.ZodOptional<z.ZodNumber>;
502
505
  }, z.core.$strip>;
503
506
  }, z.core.$strip>, z.ZodObject<{
package/dist/index.d.ts CHANGED
@@ -8,7 +8,7 @@ export { AgentEventSchema, UnknownAgentEventSchema, AgentEventOrUnknownSchema, K
8
8
  export type { AgentEvent, UnknownAgentEvent, AgentEventOrUnknown } from './agent-event';
9
9
  export { TASK_STATES, TASK_TRANSITIONS, canTransition } from './task-state';
10
10
  export type { TaskState } from './task-state';
11
- export { MESSAGE_TYPES, MESSAGE_PAYLOAD_SCHEMAS, SERVER_TO_DAEMON_TYPES, DAEMON_TO_SERVER_TYPES, RuntimeIdSchema, RuntimeInfoSchema, RuntimeCapabilitiesSchema, DispatchSelectionSchema, ToolsetIdSchema, RequiredToolsetsSchema, ConnHelloPayloadSchema, ConnAckPayloadSchema, TaskOfferPayloadSchema, TaskOfferWithToolsetsPayloadSchema, TaskApprovePayloadSchema, TaskRejectPayloadSchema, TaskCancelPayloadSchema, TaskSteerPayloadSchema, TaskClaimPayloadSchema, TaskStartedPayloadSchema, TaskDeclinePayloadSchema, TaskProgressPayloadSchema, TaskArtifactPayloadSchema, TaskAwaitApprovalPayloadSchema, TaskCompletePayloadSchema, TaskFailPayloadSchema, TaskCancelledPayloadSchema, TaskApprovalResolvedPayloadSchema, RESULT_DOCUMENT_MAX_BYTES, checkResultDocument, } from './messages';
11
+ export { MESSAGE_TYPES, MESSAGE_PAYLOAD_SCHEMAS, SERVER_TO_DAEMON_TYPES, DAEMON_TO_SERVER_TYPES, RuntimeIdSchema, RuntimeInfoSchema, RuntimeCapabilitiesSchema, DispatchSelectionSchema, ToolsetIdSchema, ConfiguredToolsetsSchema, RequiredToolsetsSchema, CONFIGURED_TOOLSETS_MAX_ITEMS, ConnHelloPayloadSchema, ConnAckPayloadSchema, TaskOfferPayloadSchema, TaskOfferWithToolsetsPayloadSchema, TaskApprovePayloadSchema, TaskRejectPayloadSchema, TaskCancelPayloadSchema, TaskSteerPayloadSchema, TaskClaimPayloadSchema, TaskStartedPayloadSchema, TaskDeclinePayloadSchema, TaskProgressPayloadSchema, TaskArtifactPayloadSchema, TaskAwaitApprovalPayloadSchema, TaskCompletePayloadSchema, TaskFailPayloadSchema, TaskCancelledPayloadSchema, TaskApprovalResolvedPayloadSchema, RESULT_DOCUMENT_MAX_BYTES, checkResultDocument, } from './messages';
12
12
  export type { ResultDocumentCheck, MessageType, RuntimeId, RuntimeInfo, RuntimeCapabilities, DispatchSelection, ToolsetId, ConnHelloPayload, ConnAckPayload, TaskOfferPayload, TaskOfferWithToolsetsPayload, TaskApprovePayload, TaskRejectPayload, TaskCancelPayload, TaskSteerPayload, TaskClaimPayload, TaskStartedPayload, TaskDeclinePayload, TaskProgressPayload, TaskArtifactPayload, TaskAwaitApprovalPayload, TaskCompletePayload, TaskFailPayload, TaskCancelledPayload, TaskApprovalResolvedPayload, } from './messages';
13
13
  export { EnvelopeSchema, isServerToDaemonType } from './envelope';
14
14
  export type { Envelope } from './envelope';
package/dist/index.js CHANGED
@@ -115,6 +115,18 @@ var RuntimeInfoSchema = z.object({
115
115
  /** Optional: older daemons omit this entirely (pre-freeze addition). */
116
116
  capabilities: RuntimeCapabilitiesSchema.optional()
117
117
  });
118
+ var CONFIGURED_TOOLSETS_MAX_ITEMS = 64;
119
+ var ToolsetIdSchema = z.string().min(1).max(128).regex(/^[a-z0-9]+(?:[._-][a-z0-9]+)*$/u, "toolset ids must be lowercase logical identifiers");
120
+ function rejectDuplicateToolsets(ids, ctx, label) {
121
+ const seen = /* @__PURE__ */ new Set();
122
+ for (const [index, id] of ids.entries()) {
123
+ if (seen.has(id)) {
124
+ ctx.addIssue({ code: "custom", path: [index], message: `duplicate ${label} toolset: ${id}` });
125
+ }
126
+ seen.add(id);
127
+ }
128
+ }
129
+ var ConfiguredToolsetsSchema = z.array(ToolsetIdSchema).max(CONFIGURED_TOOLSETS_MAX_ITEMS).superRefine((ids, ctx) => rejectDuplicateToolsets(ids, ctx, "configured"));
118
130
  var ConnHelloPayloadSchema = z.object({
119
131
  protocolVersions: z.array(z.number().int()),
120
132
  capabilities: z.array(z.string()),
@@ -122,6 +134,8 @@ var ConnHelloPayloadSchema = z.object({
122
134
  productId: z.string(),
123
135
  /** Runtimes detected on this device (M1 gap #4; replaces `agents`). */
124
136
  runtimes: z.array(RuntimeInfoSchema).optional(),
137
+ /** Validated logical IDs configured on this device; definitions and secrets stay local. */
138
+ configuredToolsets: ConfiguredToolsetsSchema.optional(),
125
139
  /**
126
140
  * Last `seq` this device has seen from the server (M1 redelivery cursor).
127
141
  * Omitted on a device's first-ever connection. The server replays any
@@ -163,20 +177,7 @@ var TaskOfferPayloadSchema = z.object({
163
177
  maxTokens: z.number().int().positive().optional()
164
178
  }).optional()
165
179
  });
166
- var ToolsetIdSchema = z.string().min(1).max(128).regex(/^[a-z0-9]+(?:[._-][a-z0-9]+)*$/u, "toolset ids must be lowercase logical identifiers");
167
- var RequiredToolsetsSchema = z.array(ToolsetIdSchema).min(1).max(16).superRefine((ids, ctx) => {
168
- const seen = /* @__PURE__ */ new Set();
169
- for (const [index, id] of ids.entries()) {
170
- if (seen.has(id)) {
171
- ctx.addIssue({
172
- code: "custom",
173
- path: [index],
174
- message: `duplicate required toolset: ${id}`
175
- });
176
- }
177
- seen.add(id);
178
- }
179
- });
180
+ var RequiredToolsetsSchema = z.array(ToolsetIdSchema).min(1).max(16).superRefine((ids, ctx) => rejectDuplicateToolsets(ids, ctx, "required"));
180
181
  var TaskOfferWithToolsetsPayloadSchema = TaskOfferPayloadSchema.extend({
181
182
  requiredToolsets: RequiredToolsetsSchema
182
183
  }).strict();
@@ -508,7 +509,14 @@ var EventsPollQuerySchema = z.object({
508
509
  });
509
510
  var EventsPollResponseSchema = z.object({
510
511
  events: z.array(EnvelopeSchema),
511
- cursor: z.number().int()
512
+ cursor: z.number().int(),
513
+ /**
514
+ * Server capabilities for THIS long-poll response. This is the HTTP
515
+ * transport's equivalent of `conn.ack.capabilities`: a new daemon treats
516
+ * absence as no advertised capabilities, so old responders remain
517
+ * fail-closed for gated daemon -> server fields and messages.
518
+ */
519
+ capabilities: z.array(z.string()).optional()
512
520
  });
513
521
  var MAX_MESSAGES_PER_BATCH = 256;
514
522
  var MessagesSendRequestSchema = z.object({
@@ -556,6 +564,6 @@ function byokBlobContentPath(blobId) {
556
564
  return `/byok/blobs/${blobId}/content`;
557
565
  }
558
566
 
559
- export { AgentEventOrUnknownSchema, AgentEventSchema, BYOK_ACTIVITY_PATH, BYOK_BLOBS_PATH, BYOK_BLOB_CONTENT_ROUTE, BYOK_BLOB_FINALIZE_ROUTE, BYOK_BLOB_URL_ROUTE, BYOK_BOARD_CLAIM_ROUTE, BYOK_BOARD_PATH, BYOK_BOARD_STATUS_ROUTE, BYOK_BOARD_STREAM_PATH, BYOK_BOARD_UNCLAIM_ROUTE, BYOK_CAPABILITIES_PATH, BYOK_CHALLENGE_PATH, BYOK_EVENTS_PATH, BYOK_MESSAGES_PATH, BYOK_PAIR_PATH, BYOK_PRESENCE_PATH, BYOK_RECORDS_PATH, BYOK_RECORD_ROUTE, BYOK_SKILL_PACKS_PATH, BYOK_SKILL_PACK_FILE_ROUTE, BYOK_TOKEN_PATH, BYOK_WS_PATH, BlobDownloadUrlResponseSchema, BlobRefSchema, CAPABILITY_FLAGS, CONTENT_HASH_RE, ChallengeRequestSchema, ChallengeResponseSchema, ConnAckPayloadSchema, ConnHelloPayloadSchema, CreateBlobRequestSchema, CreateBlobResponseSchema, DAEMON_TO_SERVER_TYPES, DispatchSelectionSchema, EnvelopeParseError, EnvelopeSchema, EnvelopeValidationError, EventsPollQuerySchema, EventsPollResponseSchema, KNOWN_AGENT_EVENT_TYPES, MAX_MESSAGES_PER_BATCH, MESSAGE_PAYLOAD_SCHEMAS, MESSAGE_TYPES, MessagesSendRequestSchema, MessagesSendResponseSchema, PERMISSION_MODES, PROTOCOL_VERSION, PairRequestSchema, PairResponseSchema, PermissionPolicySchema, ProtocolError, RESULT_DOCUMENT_MAX_BYTES, RequiredToolsetsSchema, RuntimeCapabilitiesSchema, RuntimeIdSchema, RuntimeInfoSchema, SERVER_TO_DAEMON_TYPES, TASK_STATES, TASK_TRANSITIONS, TaskApprovalResolvedPayloadSchema, TaskApprovePayloadSchema, TaskArtifactPayloadSchema, TaskAwaitApprovalPayloadSchema, TaskCancelPayloadSchema, TaskCancelledPayloadSchema, TaskClaimPayloadSchema, TaskCompletePayloadSchema, TaskDeclinePayloadSchema, TaskFailPayloadSchema, TaskOfferPayloadSchema, TaskOfferWithToolsetsPayloadSchema, TaskProgressPayloadSchema, TaskRejectPayloadSchema, TaskStartedPayloadSchema, TaskSteerPayloadSchema, TokenRequestSchema, TokenResponseSchema, ToolsetIdSchema, UnknownAgentEventSchema, UnknownMessageTypeError, byokBlobContentPath, byokBlobFinalizePath, byokBlobUrlPath, byokRecordPath, byokSkillPackFilePath, canTransition, checkResultDocument, createEnvelope, decodeEnvelope, encodeEnvelope, isKnownAgentEvent, isServerToDaemonType, parseMessage, partitionAgentEvents };
567
+ export { AgentEventOrUnknownSchema, AgentEventSchema, BYOK_ACTIVITY_PATH, BYOK_BLOBS_PATH, BYOK_BLOB_CONTENT_ROUTE, BYOK_BLOB_FINALIZE_ROUTE, BYOK_BLOB_URL_ROUTE, BYOK_BOARD_CLAIM_ROUTE, BYOK_BOARD_PATH, BYOK_BOARD_STATUS_ROUTE, BYOK_BOARD_STREAM_PATH, BYOK_BOARD_UNCLAIM_ROUTE, BYOK_CAPABILITIES_PATH, BYOK_CHALLENGE_PATH, BYOK_EVENTS_PATH, BYOK_MESSAGES_PATH, BYOK_PAIR_PATH, BYOK_PRESENCE_PATH, BYOK_RECORDS_PATH, BYOK_RECORD_ROUTE, BYOK_SKILL_PACKS_PATH, BYOK_SKILL_PACK_FILE_ROUTE, BYOK_TOKEN_PATH, BYOK_WS_PATH, BlobDownloadUrlResponseSchema, BlobRefSchema, CAPABILITY_FLAGS, CONFIGURED_TOOLSETS_MAX_ITEMS, CONTENT_HASH_RE, ChallengeRequestSchema, ChallengeResponseSchema, ConfiguredToolsetsSchema, ConnAckPayloadSchema, ConnHelloPayloadSchema, CreateBlobRequestSchema, CreateBlobResponseSchema, DAEMON_TO_SERVER_TYPES, DispatchSelectionSchema, EnvelopeParseError, EnvelopeSchema, EnvelopeValidationError, EventsPollQuerySchema, EventsPollResponseSchema, KNOWN_AGENT_EVENT_TYPES, MAX_MESSAGES_PER_BATCH, MESSAGE_PAYLOAD_SCHEMAS, MESSAGE_TYPES, MessagesSendRequestSchema, MessagesSendResponseSchema, PERMISSION_MODES, PROTOCOL_VERSION, PairRequestSchema, PairResponseSchema, PermissionPolicySchema, ProtocolError, RESULT_DOCUMENT_MAX_BYTES, RequiredToolsetsSchema, RuntimeCapabilitiesSchema, RuntimeIdSchema, RuntimeInfoSchema, SERVER_TO_DAEMON_TYPES, TASK_STATES, TASK_TRANSITIONS, TaskApprovalResolvedPayloadSchema, TaskApprovePayloadSchema, TaskArtifactPayloadSchema, TaskAwaitApprovalPayloadSchema, TaskCancelPayloadSchema, TaskCancelledPayloadSchema, TaskClaimPayloadSchema, TaskCompletePayloadSchema, TaskDeclinePayloadSchema, TaskFailPayloadSchema, TaskOfferPayloadSchema, TaskOfferWithToolsetsPayloadSchema, TaskProgressPayloadSchema, TaskRejectPayloadSchema, TaskStartedPayloadSchema, TaskSteerPayloadSchema, TokenRequestSchema, TokenResponseSchema, ToolsetIdSchema, UnknownAgentEventSchema, UnknownMessageTypeError, byokBlobContentPath, byokBlobFinalizePath, byokBlobUrlPath, byokRecordPath, byokSkillPackFilePath, canTransition, checkResultDocument, createEnvelope, decodeEnvelope, encodeEnvelope, isKnownAgentEvent, isServerToDaemonType, parseMessage, partitionAgentEvents };
560
568
  //# sourceMappingURL=index.js.map
561
569
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/version.ts","../src/blob.ts","../src/permission.ts","../src/agent-event.ts","../src/task-state.ts","../src/messages.ts","../src/envelope.ts","../src/errors.ts","../src/codec.ts","../src/http-api.ts"],"names":["z"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Wire protocol version. Bump on breaking (non-additive) changes to the envelope\n * or message shapes. Additive changes (new optional fields, new message types)\n * do not require a bump — servers negotiate the highest common version and\n * daemons/servers must ignore unknown fields and unknown message types.\n *\n * FROZEN v1 (end of M2 — see docs/protocol.md \"Freeze rule\"): the pi, claude,\n * and codex runtime adapters have all exercised the wire, and every M1/M2\n * protocol gap has been closed. `PROTOCOL_VERSION` stays `1` from here\n * forward; it does not bump for additive changes (new optional fields, new\n * message types, new `AgentEvent` variants, new capability flags) — only for\n * a breaking one (changing, removing, or retyping anything that already\n * exists).\n *\n * IMPORTANT: changing this constant, or changing/removing/retyping any\n * already-frozen schema in this package, requires a DELIBERATE update to the\n * committed golden fixtures in `src/__tests__/golden/` (`v1.frozen.json`,\n * `v1.envelopes.ndjson`) — see `src/__tests__/freeze-guard.test.ts`, which\n * fails loudly on exactly that kind of drift. A passing freeze-guard run\n * after such a change means either (a) the change was genuinely additive and\n * the golden was regenerated with justification, or (b) this constant was\n * bumped alongside a new golden generation for the new version — never a\n * silent edit to either file to make the test pass.\n */\nexport const PROTOCOL_VERSION = 1;\n\n/**\n * Capability flags exchanged during the connection handshake (`conn.hello` /\n * `conn.ack`). Additional flags may be introduced without a protocol version\n * bump; unrecognized flags must be ignored by both sides.\n *\n * `interactive-approval` is RESERVED as of this addition: it gates the\n * (currently unexercised) approval seam — a server must not route an\n * approval-requiring policy to a daemon that hasn't advertised this flag. No\n * bundled runtime adapter emits it yet; that's expected until interactive\n * approval is actually wired up in a later wave.\n *\n * `approval_resolved` (additive-minor): a SERVER-advertised flag meaning\n * \"I understand the `task.approval_resolved` message\" (`messages.ts`). This\n * is the N/N-1 answer for that new daemon -> server message: an old server's\n * `CAPABILITY_FLAGS`/`conn.ack.capabilities` never includes it, so a new\n * daemon talking to an old server never sends `task.approval_resolved` at\n * all (see `packages/client`'s `task-runner.ts`) and falls back to the\n * pre-existing implicit-resume inference\n * (`ConnectionHub.resumeIfImplicitlyApproved`, `packages/server/src/hub.ts`)\n * unconditionally, exactly as before this flag existed. Unlike\n * `interactive-approval`, this one IS exercised the moment both sides\n * support it — there is no reserved/dormant period for it.\n */\n/**\n * `approval-targeting` (M5, additive-minor): unlike `approval_resolved`\n * above, this flag is purely INFORMATIONAL/semantic, not a functional gate.\n * `task.await_approval`/`task.approve`/`task.reject` all carry their new\n * `approvalId` field UNCONDITIONALLY on both sides once each peer is\n * upgraded -- the wire is tolerant (a plain, non-`.strict()` `z.object()`\n * field, `messages.ts`), so no version/capability negotiation is needed just\n * to send it safely; an older peer that doesn't recognize the field simply\n * never reads it. Receivers decide whether to apply exact-match targeting\n * by FIELD PRESENCE on the specific message at hand (does this particular\n * `task.approve`/`task.reject`/`onApprovalResolved` payload carry an\n * `approvalId`, and does a stored one exist to compare it against?), never\n * by checking this flag -- see `ConnectionHub.approveTask`/`rejectTask`/\n * `onApprovalResolved` and `TaskRunner.handleApprove`/`handleReject`\n * (`packages/client`'s `task-runner.ts`). This flag exists only so each side\n * can advertise, and an embedder/operator can observe (`ConnectionHub.\n * getDeviceCapabilities`), whether the OTHER side is new enough to\n * participate in targeting at all -- the same N/N-1-safe shape as every\n * other flag here, just consumed for observability instead of gating.\n */\n/**\n * `result-document` (additive-minor): a SERVER-advertised flag meaning \"I\n * understand the optional `task.complete.document` field\" (`messages.ts`).\n * Functionally gating, like `approval_resolved` and unlike\n * `approval-targeting`.\n *\n * This is the N/N-1 answer for that new daemon -> server FIELD. An old\n * server's `CAPABILITY_FLAGS`/`conn.ack.capabilities` never includes it, and\n * its `TaskCompletePayloadSchema` is a tolerant (non-`.strict()`)\n * `z.object()`, so a `document` sent to it would be silently STRIPPED on\n * parse and vanish without a trace. That is exactly why emission is gated\n * here rather than sent unconditionally the way `approvalId` is: `document`\n * carries the task's primary structured RESULT, so losing it silently is\n * data loss, not a missed observability hint. A new daemon talking to an old\n * server therefore never sends `document` at all, and — if its configured\n * extractor did produce one — reports `task.fail` (retryable: false; the\n * same server will strip it on every retry too) instead of completing the\n * task with its main result quietly deleted (`packages/client`'s\n * `task-runner.ts`). A new server talking to an old daemon is unaffected:\n * the field is optional, and an old daemon simply never sets it.\n *\n * `dispatch-selection` (additive-minor) is a correctness gate for the\n * optional `task.offer.dispatchSelection` control field. An older v1 daemon\n * legally strips unknown optional fields, so a server must never send an\n * authoritative provider/model selection unless the target connection\n * advertises this flag. Absence means reject before task creation, not send\n * a legacy runtime-only offer that could reach a different provider.\n *\n * `toolset-selection` (additive-minor) means the daemon understands\n * `task.offer_with_toolsets` and can resolve its logical ids against local\n * MCP configuration. The distinct message type is also the N/N-1 safety\n * boundary for long-poll: an older daemon skips it as unknown and therefore\n * cannot accidentally execute the instruction without the required tools.\n */\nexport const CAPABILITY_FLAGS = [\n 'steer',\n 'blob-upload',\n 'interactive-approval',\n 'approval_resolved',\n 'approval-targeting',\n 'result-document',\n 'dispatch-selection',\n 'toolset-selection',\n] as const;\n\nexport type CapabilityFlag = (typeof CAPABILITY_FLAGS)[number];\n","import { z } from 'zod';\n\n/**\n * Canonical `contentHash` format (finding F9): `sha256:` followed by exactly\n * 64 lowercase hex characters (a SHA-256 digest). Pinned here — the single\n * source of truth both `BlobRefSchema` and `CreateBlobRequestSchema`\n * (`http-api.ts`) validate against — rather than left as a bare `z.string()`\n * that silently accepted any prefix (or none) and left the server to\n * reconcile the mismatch with an ad hoc normalization step. No compat shim:\n * the wire is pre-freeze (`v` stays `1`), so this is a straight tightening,\n * not a migration.\n */\nexport const CONTENT_HASH_RE = /^sha256:[0-9a-f]{64}$/;\n\n/**\n * Reference to a large payload that was pushed out-of-band (presigned PUT) or\n * is fetchable out-of-band (presigned GET), rather than inlined in an envelope.\n */\nexport const BlobRefSchema = z.object({\n blobId: z.string(),\n contentHash: z.string().regex(CONTENT_HASH_RE, 'contentHash must be \"sha256:<64 lowercase hex>\"'),\n size: z.number().int().nonnegative(),\n contentType: z.string(),\n url: z.string().optional(),\n});\n\nexport type BlobRef = z.infer<typeof BlobRefSchema>;\n","import { z } from 'zod';\n\nexport const PERMISSION_MODES = ['auto', 'confirm', 'readonly', 'plan'] as const;\n\nexport type PermissionMode = (typeof PERMISSION_MODES)[number];\n\n/**\n * Policy the server proposes for a task. The daemon/runtime adapter maps this\n * onto the concrete runtime's flags; anything that can't be expressed exactly\n * must fail closed (deny) rather than silently widen the grant.\n *\n * `.strict()`: this is control/security data, so per the freeze rule's\n * observability-vs-control asymmetry (docs/protocol.md \"Freeze rule\") an\n * unrecognized field must be REJECTED, not silently stripped-and-ignored the\n * way an ordinary payload's unknown field is (plain `z.object()`'s default\n * behavior). Without `.strict()`, a policy carrying a future constraint this\n * schema doesn't know about yet would parse successfully with that\n * constraint silently discarded — exactly the silent-widening failure mode\n * this type's own doc comment above warns against, since a stripped\n * constraint is indistinguishable from a constraint that was never sent.\n *\n * Consequence: adding a new field to this schema post-freeze is therefore a\n * BREAKING change requiring a `PROTOCOL_VERSION` bump — unlike the general\n * \"a new optional field on an existing payload is non-breaking\" rule the\n * freeze rule grants every other schema. That's intentional: a new\n * security/control constraint must force a conscious version bump so an\n * unupgraded peer can never silently ignore it, rather than being added the\n * same low-friction way a harmless observability field would be.\n */\nexport const PermissionPolicySchema = z\n .object({\n mode: z.enum(PERMISSION_MODES),\n allowTools: z.array(z.string()).optional(),\n denyTools: z.array(z.string()).optional(),\n workspaceRoot: z.string().optional(),\n network: z.boolean().optional(),\n })\n .strict();\n\nexport type PermissionPolicy = z.infer<typeof PermissionPolicySchema>;\n","import { z } from 'zod';\n\n/**\n * Normalized event shape that every runtime adapter (pi / claude / codex)\n * translates its native JSONL output into. This is the interior of a\n * `task.progress` payload's `events` array.\n */\nexport const AgentEventSchema = z.discriminatedUnion('type', [\n z.object({ type: z.literal('progress'), text: z.string() }),\n z.object({ type: z.literal('tool_use'), tool: z.string(), input: z.unknown().optional() }),\n z.object({ type: z.literal('tool_result'), tool: z.string(), output: z.unknown().optional() }),\n z.object({ type: z.literal('artifact'), name: z.string(), contentType: z.string() }),\n z.object({ type: z.literal('needs_approval'), summary: z.string() }),\n z.object({ type: z.literal('turn_end') }),\n z.object({ type: z.literal('error'), message: z.string() }),\n /**\n * Token usage reported by a runtime turn — maps from codex\n * `turn.completed.usage` and claude `result` usage (adapters wired up in a\n * later wave; this is just the variant + schema). All fields optional\n * because runtimes report different subsets.\n */\n z.object({\n type: z.literal('usage'),\n inputTokens: z.number().int().nonnegative().optional(),\n cachedInputTokens: z.number().int().nonnegative().optional(),\n outputTokens: z.number().int().nonnegative().optional(),\n reasoningTokens: z.number().int().nonnegative().optional(),\n totalTokens: z.number().int().nonnegative().optional(),\n }),\n]);\n\nexport type AgentEvent = z.infer<typeof AgentEventSchema>;\n\n/**\n * Known AgentEvent variant type discriminators — DERIVED directly from\n * {@link AgentEventSchema}'s own discriminated-union variants via\n * `z.toJSONSchema`, rather than hand-maintained as a second literal list.\n * This used to be a standalone array kept in sync with the schema above by\n * hand (dual authority); the freeze guard\n * (`__tests__/freeze-guard.test.ts`'s \"dual-authority cross-check\") already\n * asserted the two matched using this EXACT SAME `z.toJSONSchema` extraction\n * mechanism, which is why deriving it this way is safe: the guard already\n * proved this extraction produces the identical set the hand-written list\n * held. With the derivation below, the two can no longer drift apart at\n * all — there is only one authority now, {@link AgentEventSchema} itself.\n * The freeze guard test is kept anyway (now definitionally true rather than\n * a live check) as a regression net in case a future refactor reintroduces a\n * hand-written list.\n *\n * Exported (not module-private) so {@link isKnownAgentEvent} /\n * {@link partitionAgentEvents} (and the freeze guard) can check against the\n * exact same set without each reaching into zod's discriminated-union\n * internals directly. `z.toJSONSchema`'s output shape here (`.oneOf[].\n * properties.type.const`) is a public, documented zod v4 API — not\n * reaching into `._def`/internal fields — same as the freeze guard already\n * relies on.\n */\nexport const KNOWN_AGENT_EVENT_TYPES: readonly string[] = (\n z.toJSONSchema(AgentEventSchema) as unknown as { oneOf: Array<{ properties: { type: { const: string } } }> }\n).oneOf.map((branch) => branch.properties.type.const);\n\n/**\n * Pre-freeze compatibility widening (the freeze blocker this schema fixes):\n * an unknown-type event — one a future runtime/protocol minor version\n * introduces — parses as an opaque passthrough placeholder instead of\n * hard-failing the entire `task.progress` batch it arrived in. Without this,\n * `TaskProgressPayloadSchema.events: z.array(AgentEventSchema)` would throw\n * on the whole array the moment one event had an unrecognized `type`, which\n * made the wire's \"additive new variants are non-breaking\" promise false for\n * the installed base — and unfixable post-freeze.\n *\n * The `.refine` guard is load-bearing, not decorative: it excludes every\n * KNOWN type literal, so a *malformed* known variant (e.g. `progress`\n * missing `text`) still fails validation instead of silently matching this\n * fallback. Tolerance is only for unknown TYPES, never for malformed known\n * ones — see {@link AgentEventOrUnknownSchema}, which is what actually\n * combines this with {@link AgentEventSchema} for real use.\n *\n * Deliberately asymmetric with envelope-level control/security fields\n * (`instruction`, `policy` — see `messages.ts`/`permission.ts`), which stay\n * fail-closed on unknown shapes with no equivalent widening: this tolerance\n * applies only to observability data (agent progress events), never to\n * control/security surfaces. That asymmetry is the freeze rule.\n */\nexport const UnknownAgentEventSchema = z\n .object({ type: z.string() })\n .passthrough()\n .refine(\n (event) => !KNOWN_AGENT_EVENT_TYPES.includes(event.type),\n 'type refers to a known AgentEvent variant; malformed known variants must fail through AgentEventSchema, not fall back to unknown-tolerance',\n );\n\nexport type UnknownAgentEvent = z.infer<typeof UnknownAgentEventSchema>;\n\n/**\n * The actual element schema for `TaskProgressPayloadSchema.events`\n * (`messages.ts`): a known, fully-typed {@link AgentEvent} OR an opaque\n * unknown-type placeholder. `z.union` (not `discriminatedUnion`) is required\n * here because the fallback branch matches on \"not one of the known\n * literals\", which a discriminated union can't express directly.\n */\nexport const AgentEventOrUnknownSchema = z.union([AgentEventSchema, UnknownAgentEventSchema]);\n\nexport type AgentEventOrUnknown = z.infer<typeof AgentEventOrUnknownSchema>;\n\n/**\n * Type guard distinguishing a known, fully-typed {@link AgentEvent} from an\n * {@link UnknownAgentEvent} passthrough placeholder.\n */\nexport function isKnownAgentEvent(event: AgentEventOrUnknown): event is AgentEvent {\n return KNOWN_AGENT_EVENT_TYPES.includes(event.type);\n}\n\n/**\n * Split a `task.progress` events array into known (typed, actionable) and\n * unknown (opaque, safe-to-skip) events. Consumers should process `known`\n * and skip `unknown` rather than throwing on it — that's the point of the\n * pre-freeze tolerance above.\n */\nexport function partitionAgentEvents(events: readonly AgentEventOrUnknown[]): {\n known: AgentEvent[];\n unknown: UnknownAgentEvent[];\n} {\n const known: AgentEvent[] = [];\n const unknown: UnknownAgentEvent[] = [];\n for (const event of events) {\n if (isKnownAgentEvent(event)) {\n known.push(event);\n } else {\n unknown.push(event);\n }\n }\n return { known, unknown };\n}\n","export const TASK_STATES = [\n 'Offered',\n 'Claimed',\n 'Running',\n 'AwaitApproval',\n 'Complete',\n 'Failed',\n 'Cancelled',\n] as const;\n\nexport type TaskState = (typeof TASK_STATES)[number];\n\n/**\n * Legal state transitions for a task. `Complete` / `Failed` / `Cancelled` are\n * terminal (no outgoing edges). `Running` and `AwaitApproval` form a loop:\n * the daemon can request approval mid-run and resume once the server\n * approves (or fail/cancel out of the approval wait).\n *\n * `Offered -> Failed` (M1 gap #5, \"Declined vs. Failed\"): a daemon that\n * declines an offer pre-claim (`task.decline`) reports it through the\n * existing `Failed` state rather than a new `Declined` state. A decline and\n * a post-claim failure are the same outcome from the dispatcher's point of\n * view — this attempt produced no result, `reason`/`retryable` say why and\n * whether retrying elsewhere makes sense — so reusing `Failed` keeps the\n * state machine minimal instead of forking every terminal-state consumer\n * into \"Failed or Declined, handle both\". See docs/protocol.md for the full\n * writeup.\n */\nexport const TASK_TRANSITIONS: Readonly<Record<TaskState, readonly TaskState[]>> = {\n Offered: ['Claimed', 'Cancelled', 'Failed'],\n Claimed: ['Running', 'Failed', 'Cancelled'],\n Running: ['AwaitApproval', 'Complete', 'Failed', 'Cancelled'],\n AwaitApproval: ['Running', 'Failed', 'Cancelled'],\n Complete: [],\n Failed: [],\n Cancelled: [],\n};\n\n/** Whether `from -> to` is a legal transition per {@link TASK_TRANSITIONS}. */\nexport function canTransition(from: TaskState, to: TaskState): boolean {\n return TASK_TRANSITIONS[from].includes(to);\n}\n","import { z } from 'zod';\nimport { BlobRefSchema } from './blob';\nimport { PermissionPolicySchema } from './permission';\nimport { AgentEventOrUnknownSchema } from './agent-event';\n\n/** Max size of an inlined artifact payload, per the delivery-model spec (<=64KB). */\nconst MAX_INLINE_BYTES = 64 * 1024;\n\nfunction isWithinInlineByteLimit(value: string): boolean {\n return new TextEncoder().encode(value).length <= MAX_INLINE_BYTES;\n}\n\n// ---------------------------------------------------------------------------\n// conn.* — connection handshake\n// ---------------------------------------------------------------------------\n\nexport const RuntimeIdSchema = z.enum(['pi', 'claude', 'codex']);\nexport type RuntimeId = z.infer<typeof RuntimeIdSchema>;\n\n/**\n * Per-runtime feature flags reported in `conn.hello.runtimes[].capabilities`\n * (pre-freeze addition). Distinct from the connection-level `CAPABILITY_FLAGS`\n * (`version.ts`) / `conn.hello.capabilities` array: those are protocol-level\n * flags negotiated for the whole connection, while this is what one specific\n * detected runtime (pi/claude/codex) supports. The whole field is optional\n * end-to-end — older daemons omit `capabilities` entirely — and every field\n * inside it is itself optional, since detection can be partial.\n *\n * Per-tool allow/deny lists are deliberately NOT included here (noise).\n * `permissionModes` mirrors `PERMISSION_MODES` (`permission.ts`) but is kept\n * as a bare `string[]` rather than `z.enum(PERMISSION_MODES)`: this is a\n * runtime's self-reported observability data, not a control/security field,\n * so — per the freeze rule (tolerate unknown for observability, fail closed\n * for control/security; see `agent-event.ts`'s unknown-variant tolerance for\n * the same asymmetry applied to `task.progress` events) — it stays tolerant\n * of a mode string a newer runtime might report that this schema doesn't\n * enumerate yet, rather than rejecting the whole `conn.hello`.\n *\n * Unrecognized keys inside `capabilities` itself, by contrast, are silently\n * stripped (zod's default object behavior — same as every other payload\n * schema in this file) rather than passed through: this is a closed, typed\n * shape consumers can rely on, and a genuinely new capability flag gets added\n * here explicitly rather than round-tripped opaquely.\n */\nexport const RuntimeCapabilitiesSchema = z.object({\n steer: z.boolean().optional(),\n resume: z.boolean().optional(),\n approvalInteractive: z.boolean().optional(),\n /** Whether this runtime can project a locally configured MCP toolset into one task. */\n mcpToolsets: z.boolean().optional(),\n permissionModes: z.array(z.string()).optional(),\n});\nexport type RuntimeCapabilities = z.infer<typeof RuntimeCapabilitiesSchema>;\n\n/**\n * Runtime detection info reported in `conn.hello`. Supersedes the M0\n * `agents: unknown` field (M1 gap #4): typed, so the server no longer has to\n * best-effort-normalize an untyped blob.\n */\nexport const RuntimeInfoSchema = z.object({\n id: RuntimeIdSchema,\n version: z.string().optional(),\n authPresent: z.boolean().optional(),\n /** Optional: older daemons omit this entirely (pre-freeze addition). */\n capabilities: RuntimeCapabilitiesSchema.optional(),\n});\nexport type RuntimeInfo = z.infer<typeof RuntimeInfoSchema>;\n\n/** daemon -> server: opening handshake. */\nexport const ConnHelloPayloadSchema = z.object({\n protocolVersions: z.array(z.number().int()),\n capabilities: z.array(z.string()),\n deviceId: z.string(),\n productId: z.string(),\n /** Runtimes detected on this device (M1 gap #4; replaces `agents`). */\n runtimes: z.array(RuntimeInfoSchema).optional(),\n /**\n * Last `seq` this device has seen from the server (M1 redelivery cursor).\n * Omitted on a device's first-ever connection. The server replays any\n * server->daemon envelopes with `seq > cursor` it still holds — see\n * docs/protocol.md \"At-least-once delivery\".\n */\n cursor: z.number().int().optional(),\n});\nexport type ConnHelloPayload = z.infer<typeof ConnHelloPayloadSchema>;\n\n/** server -> daemon: handshake acknowledgement. */\nexport const ConnAckPayloadSchema = z.object({\n protocolVersion: z.number().int(),\n capabilities: z.array(z.string()),\n serverTime: z.iso.datetime({ offset: true }),\n});\nexport type ConnAckPayload = z.infer<typeof ConnAckPayloadSchema>;\n\n// ---------------------------------------------------------------------------\n// server -> daemon: task.*\n//\n// Every type in this section carries a *required* envelope `task_id` (M1 gap\n// #1) and a *required* envelope `seq` — a per-device monotonic counter the\n// daemon uses as a redelivery cursor (M1 gap; see `conn.hello.cursor` above\n// and docs/protocol.md \"At-least-once delivery\"). None of these payloads\n// duplicate `taskId` at the payload level (M1 gap #7): the envelope's\n// `task_id` is the single routing key.\n// ---------------------------------------------------------------------------\n\n/**\n * The out-of-band-reference form of `TaskOfferPayload.instruction` (the\n * alternative to an inlined string). `.strict()`: like `PermissionPolicySchema`\n * (`permission.ts`), this is control data — it's the task instruction itself,\n * the thing that authorizes what work gets done — so per the freeze rule's\n * observability-vs-control asymmetry (docs/protocol.md \"Freeze rule\") an\n * unrecognized field here must be REJECTED, not silently stripped the way an\n * ordinary payload's unknown field is. Without `.strict()`, a hypothetical\n * future field riding along next to `blobRef` (e.g. a routing/priority\n * override) would be silently discarded instead of failing the parse —\n * exactly the kind of silent reinterpretation the freeze rule forbids for\n * control/security payloads. Consequence: adding a field to this shape\n * post-freeze is a BREAKING change (version bump required), not the usual\n * non-breaking additive-optional-field case — same as `PermissionPolicySchema`.\n */\nconst InstructionBlobRefSchema = z.object({ blobRef: BlobRefSchema }).strict();\n\nconst DispatchTargetIdSchema = z\n .string()\n .min(1)\n .max(160)\n .regex(/^[^\\u0000\\r\\n]+$/u, 'dispatch target ids must not contain control line breaks');\n\n/**\n * The web-selected runtime/model target carried end to end with a task.\n *\n * This is an additive v1 field. The discriminated union makes lane ownership\n * fail closed at decode time: subscription credentials can only belong to the\n * vendor CLIs, while a BYOK provider can only be executed through Pi.\n */\nexport const DispatchSelectionSchema = z.discriminatedUnion('lane', [\n z\n .object({\n lane: z.literal('subscription'),\n runtimeId: z.enum(['claude', 'codex']),\n providerId: z.null(),\n modelId: DispatchTargetIdSchema,\n })\n .strict(),\n z\n .object({\n lane: z.literal('byok'),\n runtimeId: z.literal('pi'),\n providerId: DispatchTargetIdSchema,\n modelId: DispatchTargetIdSchema,\n })\n .strict(),\n]);\nexport type DispatchSelection = z.infer<typeof DispatchSelectionSchema>;\n\n/**\n * server -> daemon: offer a task for a device to claim.\n *\n * `taskId` used to be duplicated here; it is now carried only by the\n * envelope's `task_id` (M1 gap #7 — single source of truth for routing).\n */\nexport const TaskOfferPayloadSchema = z.object({\n instruction: z.union([z.string(), InstructionBlobRefSchema]),\n policy: PermissionPolicySchema,\n runtime: RuntimeIdSchema.optional(),\n dispatchSelection: DispatchSelectionSchema.optional(),\n sessionRef: z.string().optional(),\n workspaceHint: z.string().optional(),\n limits: z\n .object({\n maxDurationMs: z.number().int().positive().optional(),\n maxTokens: z.number().int().positive().optional(),\n })\n .optional(),\n});\nexport type TaskOfferPayload = z.infer<typeof TaskOfferPayloadSchema>;\n\n/**\n * Logical, host-owned MCP toolset identifier. A task may request this name,\n * but the executable/server definition behind it exists only in the daemon's\n * local configuration and never crosses the SaaS wire.\n */\nexport const ToolsetIdSchema = z\n .string()\n .min(1)\n .max(128)\n .regex(/^[a-z0-9]+(?:[._-][a-z0-9]+)*$/u, 'toolset ids must be lowercase logical identifiers');\nexport type ToolsetId = z.infer<typeof ToolsetIdSchema>;\n\n/** Every named toolset is required; duplicates are rejected instead of silently de-duplicated. */\nexport const RequiredToolsetsSchema = z\n .array(ToolsetIdSchema)\n .min(1)\n .max(16)\n .superRefine((ids, ctx) => {\n const seen = new Set<string>();\n for (const [index, id] of ids.entries()) {\n if (seen.has(id)) {\n ctx.addIssue({\n code: 'custom',\n path: [index],\n message: `duplicate required toolset: ${id}`,\n });\n }\n seen.add(id);\n }\n });\n\n/**\n * Additive v1 offer variant for tasks whose semantics require local MCP\n * tools. This is a distinct message type rather than an optional field on\n * `task.offer`: an older v1 daemon skips an unknown message type, whereas it\n * would legally strip an unknown optional control field and run the task\n * without its required tools. The whole payload is strict because every\n * field here affects execution authority.\n */\nexport const TaskOfferWithToolsetsPayloadSchema = TaskOfferPayloadSchema.extend({\n requiredToolsets: RequiredToolsetsSchema,\n}).strict();\nexport type TaskOfferWithToolsetsPayload = z.infer<typeof TaskOfferWithToolsetsPayloadSchema>;\n\n/**\n * server -> daemon: approve a pending `task.await_approval` request.\n *\n * Semantics (M1 gap #3): the server's own state is authoritative on its own\n * action — calling the server-side `approve()` API moves the task record\n * `AwaitApproval -> Running` immediately. This wire message is a best-effort\n * *notification* telling the daemon to resume the paused runtime session; the\n * daemon does not send a dedicated ack. Its outcome is observable through the\n * task's existing message stream (e.g. `task.progress` resuming, or\n * `task.fail`/`task.cancelled` if resuming turns out to be impossible) — no\n * new ack message type is introduced. See docs/protocol.md \"Approval flow\".\n *\n * `approvalId` (M5, additive-minor — docs/protocol.md §5.3): OPTIONAL target\n * identity for the SPECIFIC pending approval this decision resolves, rather\n * than \"whichever one is currently pending\" (the pre-M5 behavior, and still\n * what happens when this field is absent — a legacy server that never\n * learned an id, or one talking to a legacy daemon). When present, the\n * daemon compares it against its own currently-dispatched approval id\n * (`ActiveTask.pendingApprovalId`, `packages/client`'s `task-runner.ts`) and\n * treats a mismatch as a stale, audit-only no-op instead of resolving\n * whatever happens to be pending right now — see `TaskRunner.handleApprove`.\n */\nexport const TaskApprovePayloadSchema = z.object({\n approvalId: z.string().optional(),\n});\nexport type TaskApprovePayload = z.infer<typeof TaskApprovePayloadSchema>;\n\n/**\n * server -> daemon: reject a pending `task.await_approval` request.\n *\n * Same best-effort-notification semantics as `task.approve` (M1 gap #3): the\n * server moves its own record `AwaitApproval -> Failed` immediately; this\n * message just tells the daemon to stop, and the daemon reports the outcome\n * via its existing `task.fail` terminal message.\n *\n * `approvalId` (M5, additive-minor — docs/protocol.md §5.3): same optional\n * targeting semantics as `TaskApprovePayloadSchema.approvalId` above, applied\n * to the reject path (`TaskRunner.handleReject`).\n */\nexport const TaskRejectPayloadSchema = z.object({\n reason: z.string().optional(),\n approvalId: z.string().optional(),\n});\nexport type TaskRejectPayload = z.infer<typeof TaskRejectPayloadSchema>;\n\n/**\n * server -> daemon: cancel a task in any non-terminal state.\n *\n * Same best-effort-notification semantics (M1 gap #3): the server moves its\n * own record to `Cancelled` immediately on its own action and does not wait\n * for a daemon ack; this message just tells the daemon to stop local work.\n * The daemon reports the outcome via the explicit `task.cancelled` terminal\n * message (M1 gap #6) — not `task.fail`.\n */\nexport const TaskCancelPayloadSchema = z.object({\n reason: z.string().optional(),\n});\nexport type TaskCancelPayload = z.infer<typeof TaskCancelPayloadSchema>;\n\n/** server -> daemon: inject steering text into a running task. */\nexport const TaskSteerPayloadSchema = z.object({\n text: z.string(),\n});\nexport type TaskSteerPayload = z.infer<typeof TaskSteerPayloadSchema>;\n\n// ---------------------------------------------------------------------------\n// daemon -> server: task.*\n//\n// Every type in this section carries a *required* envelope `task_id` (M1 gap\n// #1 — these all route by task id). Envelope `seq` (the redelivery cursor)\n// stays optional in this direction: M1 only specifies at-least-once\n// server->daemon redelivery, not a daemon->server one (see\n// docs/protocol.md).\n// ---------------------------------------------------------------------------\n\n/**\n * daemon -> server: claim an offered task (idempotent CAS on the server\n * side). `taskId` used to be duplicated here; it is now carried only by the\n * envelope's `task_id` (M1 gap #7).\n *\n * Claiming no longer implies the task is `Running` (M1 gap #2) — see\n * `task.started`.\n *\n * `runtime` (M5, additive-minor — docs/protocol.md §3.1): the ACTUAL\n * adapter this device selected for the task, distinct from `task.offer`'s\n * own `runtime` (the merely REQUESTED one, `TaskOfferPayloadSchema.runtime`\n * above). When an offer names no runtime the daemon auto-selects (pi-first —\n * `TaskRunner.pickAdapter`, `packages/client`'s `task-runner.ts`), and before\n * this field existed the server had no way to learn which adapter actually\n * ran — `TaskSnapshot.runtime` (`packages/server`'s `types.ts`) only ever\n * recorded what was requested. Plain optional property on this already-\n * tolerant `z.object()`: an old server simply never reads it, so this needed\n * no version bump and no emission gating (same shape as `approvalId` on\n * `task.await_approval`/`task.approve`/`task.reject`, §5.3) — a new daemon\n * sends it unconditionally, regardless of whether the connected server is\n * new enough to store it.\n *\n * `capabilities` (S0/D-4, additive-minor — docs/protocol.md §2.4): the\n * TASK-level capability authority — what the claiming adapter reported about\n * itself at the moment it took this task, reusing `RuntimeCapabilitiesSchema`\n * (above) rather than introducing a second capability shape. Same source of\n * truth as `conn.hello.runtimes[].capabilities`, different scope: `conn.hello`\n * is CONNECTION-level discovery (\"what could this device run\"), which no\n * server-side control decision may read, because it is transport-shaped — a\n * long-poll-only daemon never sends `conn.hello` at all — and it describes a\n * device, not a task. This field is task-shaped: it shares a lifecycle with\n * the task↔runtime binding the claim itself establishes, so a control gate\n * (`steerTask()`, `packages/server`'s `hub.ts`) can key off it and stay\n * correct across reconnects, adapter-set changes, and transports. Plain\n * optional property on this already-tolerant `z.object()`, exactly like\n * `runtime` above: an old daemon simply omits it, and a consumer that gates on\n * it must fail closed on absence rather than assume a default.\n */\nexport const TaskClaimPayloadSchema = z.object({\n deviceId: z.string(),\n agentId: z.string().optional(),\n runtime: RuntimeIdSchema.optional(),\n capabilities: RuntimeCapabilitiesSchema.optional(),\n});\nexport type TaskClaimPayload = z.infer<typeof TaskClaimPayloadSchema>;\n\n/**\n * daemon -> server: explicit `Claimed -> Running` transition (M1 gap #2).\n * A `task.claim` no longer implies the task started running; the daemon\n * sends this once it has actually started the runtime session for the task.\n */\nexport const TaskStartedPayloadSchema = z.object({});\nexport type TaskStartedPayload = z.infer<typeof TaskStartedPayloadSchema>;\n\n/**\n * daemon -> server: decline an offer *before* claiming it (M1 gap #5) — e.g.\n * no compatible/available runtime, or the offered policy exceeds this\n * device's ceiling. Fail-closed rejections must use this instead of silently\n * dropping the offer.\n *\n * Decision (see docs/protocol.md \"Declined vs. Failed\" for the full\n * writeup): declining does *not* introduce a new `Declined` terminal state.\n * It maps onto the existing `Failed` state via a new `Offered -> Failed`\n * transition. `reason`/`retryable` intentionally mirror `TaskFailPayload`\n * exactly, because a pre-claim decline and a post-claim failure are the same\n * outcome from the dispatcher's point of view (this attempt produced no\n * result; here's whether retrying — e.g. offering to a different device —\n * makes sense), and keeping the state machine minimal avoids forking every\n * terminal-state consumer into \"Failed or Declined, handle both\".\n */\nexport const TaskDeclinePayloadSchema = z.object({\n reason: z.string(),\n retryable: z.boolean().optional(),\n});\nexport type TaskDeclinePayload = z.infer<typeof TaskDeclinePayloadSchema>;\n\n/**\n * daemon -> server: batch of normalized agent events.\n *\n * `events` elements are known-or-unknown (`AgentEventOrUnknownSchema` —\n * `agent-event.ts`), not bare `AgentEventSchema`: pre-freeze, an unrecognized\n * event `type` must not fail the whole batch, since a peer running a newer\n * minor version may have emitted an additive event variant this schema\n * doesn't know about yet. See `agent-event.ts` for the full rationale and\n * `partitionAgentEvents`/`isKnownAgentEvent` for how consumers should skip\n * unknowns instead of choking on them.\n */\nexport const TaskProgressPayloadSchema = z.object({\n seq: z.number().int(),\n events: z.array(AgentEventOrUnknownSchema),\n});\nexport type TaskProgressPayload = z.infer<typeof TaskProgressPayloadSchema>;\n\n/** daemon -> server: an artifact produced by the task, inline or by blob ref. */\nexport const TaskArtifactPayloadSchema = z.object({\n name: z.string(),\n contentType: z.string(),\n inline: z\n .string()\n .refine(isWithinInlineByteLimit, {\n message: 'inline artifact payload exceeds 64KB limit',\n })\n .optional(),\n blobRef: BlobRefSchema.optional(),\n});\nexport type TaskArtifactPayload = z.infer<typeof TaskArtifactPayloadSchema>;\n\n/**\n * daemon -> server: task is blocked on an out-of-band approval.\n *\n * `approvalId` (M5, additive-minor — docs/protocol.md §5.3): the daemon's own\n * locally-generated identity for THIS SPECIFIC pending approval\n * (`ApprovalRegistry`, `packages/client`'s `approvals.ts`) — included\n * unconditionally by an M5+ daemon, regardless of whether the connected\n * server has advertised the `approval-targeting` capability flag\n * (`version.ts`; see that flag's own doc comment for why no emission gating\n * is needed here — it's a tolerant `z.object()` field, so an older server\n * simply ignores it). Optional purely for wire tolerance with a pre-M5\n * daemon build that never set it at all: a server that never learns an id\n * for a task's current approval can't target a later `approve`/`reject`\n * decision and falls back to resolving \"whichever approval is currently\n * pending\" — the same behavior every server had before this field existed.\n */\nexport const TaskAwaitApprovalPayloadSchema = z.object({\n summary: z.string(),\n approvalId: z.string().optional(),\n});\nexport type TaskAwaitApprovalPayload = z.infer<typeof TaskAwaitApprovalPayloadSchema>;\n\n/**\n * Hard cap (1 MiB) on a single `task.complete.document` (see\n * {@link TaskCompletePayloadSchema}), measured as the UTF-8 byte length of\n * its canonical JSON encoding — NOT as a node/key count, since a document is\n * schema-neutral and its object shape is unbounded by design.\n *\n * The cap is a REJECT-AT-BOUNDARY limit on both sides: a daemon that\n * produces an over-cap document reports `task.fail` instead of sending it,\n * and a server rejects an over-cap document at schema validation. It is\n * never truncated — a truncated JSON document is not valid JSON, so\n * \"shrinking to fit\" can only hand the consumer garbage.\n *\n * 1 MiB is the conservative ceiling declared by the first real consumer\n * (`docs/researches/2026-08-12-salesko-consumption-evidence.md` §1/§2:\n * smallest real frame 8.4 KiB, typical 48-96 KiB, 512 KiB comfortable).\n * Producers should stay at or under ~512 KiB (docs/protocol.md); the extra\n * headroom exists because raising a protocol cap later is additive while\n * lowering one is breaking. A result too big for this channel belongs in\n * `artifactRefs` (the multi-file/binary/oversized channel), not here.\n */\nexport const RESULT_DOCUMENT_MAX_BYTES = 1_048_576;\n\n/**\n * Outcome of {@link checkResultDocument}.\n *\n * `bytes` is the measured canonical JSON UTF-8 byte length, present on both\n * the accept and the over-cap rejection so a caller can name the actual size\n * in a failure reason. `canonical` is the CANONICAL SNAPSHOT — the value a\n * sender must actually put on the wire; see {@link checkResultDocument}.\n */\nexport type ResultDocumentCheck =\n | { readonly ok: true; readonly bytes: number; readonly canonical: unknown }\n | { readonly ok: false; readonly reason: 'not-serializable' }\n | { readonly ok: false; readonly reason: 'over-cap'; readonly bytes: number }\n | { readonly ok: false; readonly reason: 'not-plain-json' };\n\n/**\n * Structural equality between a value and its own JSON round trip, written\n * out by hand (no node builtins — this package deliberately has none) over\n * exactly the three shapes pure JSON data can take.\n *\n * The asymmetry is the point: `a` is the CALLER's object, which may contain\n * anything JavaScript allows, while `b` is always the output of\n * `JSON.parse` — pure data. So every case where `a` is something JSON cannot\n * represent (a `Date`, a function, `undefined`, `NaN`, a symbol, a getter\n * that answers differently the second time, an object whose `toJSON` rewrote\n * it) shows up here as a shape or value mismatch against `b`, and is\n * rejected. `NaN !== NaN` falls out of strict equality for free, which is\n * exactly what we want: `NaN` serializes to `null`, so it is not\n * representable and must not pass.\n *\n * The one case structural comparison alone cannot see — an object whose data\n * lives somewhere other than its own enumerable string keys, e.g. a\n * populated `Map` — gets its own explicit rejection in the object branch\n * below.\n */\nfunction isSameJsonData(a: unknown, b: unknown): boolean {\n if (a === null || b === null) return a === b;\n\n const typeOfA = typeof a;\n if (typeOfA !== typeof b) return false;\n\n if (typeOfA !== 'object') {\n // string | number | boolean. Anything else (function, symbol, bigint,\n // undefined) can never equal a JSON.parse output, and `NaN === NaN` is\n // false, so this single comparison covers every primitive case.\n return a === b;\n }\n\n const aIsArray = Array.isArray(a);\n if (aIsArray !== Array.isArray(b)) return false;\n\n if (aIsArray) {\n const arrayA = a as unknown[];\n const arrayB = b as unknown[];\n if (arrayA.length !== arrayB.length) return false;\n for (let index = 0; index < arrayA.length; index += 1) {\n if (!isSameJsonData(arrayA[index], arrayB[index])) return false;\n }\n return true;\n }\n\n const objectA = a as Record<string, unknown>;\n const objectB = b as Record<string, unknown>;\n const keysA = Object.keys(objectA); // own enumerable string keys\n const keysB = Object.keys(objectB);\n\n // An object whose data does NOT live in its own enumerable string keys is\n // invisible to both `JSON.stringify` and the key comparison below — a\n // populated `Map`/`Set` serializes to `{}` and would otherwise compare\n // EQUAL to that `{}`, delivering an empty document as the task's truthful\n // structured result. That is the same silent-wrong-output class the whole\n // plain-data contract exists to stop, so a container-shaped value with no\n // own enumerable string keys and a non-plain prototype is rejected here.\n // Applied at every object node (this function recurses), so a nested\n // `Map`/`Set`/exotic instance dies exactly like a top-level one.\n //\n // The boundary this deliberately draws: a class instance WITH own\n // enumerable fields still passes, because its data really is in those\n // fields and survives the round trip intact. A class whose values come\n // from PROTOTYPE-level getters does not — those are invisible to JSON, so\n // such an instance is not plain JSON data and is outside this contract by\n // definition (docs/protocol.md §7.2). `Object.create(null)` is explicitly\n // fine: a null prototype is plain data, just without `Object.prototype`.\n if (keysA.length === 0) {\n const prototypeA: unknown = Object.getPrototypeOf(objectA);\n if (prototypeA !== Object.prototype && prototypeA !== null) return false;\n }\n\n if (keysA.length !== keysB.length) return false;\n for (const key of keysA) {\n if (!Object.prototype.hasOwnProperty.call(objectB, key)) return false;\n if (!isSameJsonData(objectA[key], objectB[key])) return false;\n }\n return true;\n}\n\n/**\n * THE single authority for \"is this a legal `task.complete.document`\", and\n * the one place its canonical form is produced. `TaskCompletePayloadSchema`'s\n * own refinement calls it, and the daemon-side pre-send gate\n * (`packages/client`'s `task-runner.ts`) imports and calls the exact same\n * function rather than re-deriving any part of it: a daemon that measured or\n * judged a document even slightly differently from the server that validates\n * it would either reject documents the wire would have accepted, or hand the\n * server a payload it is about to reject after the runtime session already\n * ended.\n *\n * **The contract is: a document must be PLAIN JSON DATA.** Not \"an object\n * that happens to survive `JSON.stringify`\" — that bar is far too low, and\n * two concrete attacks/mistakes live under it:\n *\n * 1. `JSON.stringify` succeeding does not mean the value was preserved. An\n * `undefined`-valued key, a `NaN`, a function-valued property, or a\n * `Date` all serialize \"successfully\" while silently becoming something\n * else (dropped, `null`, or a string). The result is a well-formed,\n * under-cap document that is not what the producer had — a confidently\n * wrong terminal result, the worst outcome this channel has.\n * 2. `toJSON(key)` receives the property key it is being serialized under,\n * so an object can legally answer one way at the root (`key === ''`,\n * where this function measures it) and a completely different way when\n * nested inside the envelope payload (`key === 'document'`, where the\n * codec actually serializes it). A root-only measurement is therefore\n * not a bound on what goes on the wire at all. The same hole exists for\n * any getter that answers differently on a second read.\n *\n * Both die together via the same mechanism. The steps:\n *\n * 1. `JSON.stringify` must succeed and not return `undefined`.\n * 2. Its UTF-8 byte length must be within {@link RESULT_DOCUMENT_MAX_BYTES}.\n * 3. `JSON.parse` that string — the CANONICAL SNAPSHOT. It is pure data:\n * no `toJSON`, no getters, no prototype, nothing left that can answer\n * differently a second time.\n * 4. The original must be structurally equal to the snapshot\n * ({@link isSameJsonData}). Any mismatch means the value was not plain\n * JSON data, and it is rejected rather than silently transformed.\n *\n * On success the snapshot is returned as `canonical`, and **every sender\n * must put THAT on the wire, never the original reference** — which is what\n * closes the contextual-`toJSON`/unstable-getter hole for good: pure data\n * serializes identically at the root and nested, so what was measured is\n * necessarily what is sent. The check is idempotent on pure data, so the\n * server re-running it on an already-parsed payload is a no-op that always\n * agrees.\n */\nexport function checkResultDocument(document: unknown): ResultDocumentCheck {\n let json: string | undefined;\n try {\n json = JSON.stringify(document);\n } catch {\n return { ok: false, reason: 'not-serializable' };\n }\n if (json === undefined) return { ok: false, reason: 'not-serializable' };\n\n const bytes = new TextEncoder().encode(json).length;\n if (bytes > RESULT_DOCUMENT_MAX_BYTES) return { ok: false, reason: 'over-cap', bytes };\n\n const canonical: unknown = JSON.parse(json);\n if (!isSameJsonData(document, canonical)) return { ok: false, reason: 'not-plain-json' };\n\n return { ok: true, bytes, canonical };\n}\n\n/**\n * daemon -> server: task finished successfully.\n *\n * `document` (additive-minor, docs/protocol.md \"Freeze rule\"): the OPTIONAL\n * structured terminal result of the task — one JSON value the product on the\n * other side consumes as the task's actual output, as opposed to `summary`\n * (human-readable prose) or `artifactRefs` (files). Deliberately\n * `z.unknown()`: this SDK never understands, validates, or transforms the\n * product's own document schema — that validation belongs to the consumer.\n * The only constraints the wire imposes are the ones\n * {@link checkResultDocument} enforces: it must be PLAIN JSON DATA (equal to\n * its own JSON round trip — see that function for why \"stringify succeeded\"\n * is not enough), and its canonical JSON UTF-8 encoding must be at most\n * {@link RESULT_DOCUMENT_MAX_BYTES}. An over-cap document is REJECTED here,\n * never truncated (see that constant's own doc comment). A sender puts the\n * check's `canonical` snapshot on the wire, never the original object.\n *\n * Unlike `approvalId` on `task.await_approval` above, emitting this field IS\n * gated on a capability flag (`result-document`, `version.ts`): a pre-\n * `result-document` server strips it silently as an unknown key (the\n * tolerant `z.object()` behavior §1 mandates), and silently losing the\n * task's primary structured result is not a tolerable degradation the way\n * losing an observability hint is. So a daemon sends `document` only to a\n * server that advertised the flag, and fails the task loudly otherwise —\n * see `packages/client`'s `task-runner.ts`.\n */\nexport const TaskCompletePayloadSchema = z.object({\n summary: z.string(),\n sessionRef: z.string(),\n artifactRefs: z.array(BlobRefSchema).optional(),\n document: z\n .unknown()\n .optional()\n .refine((value) => value === undefined || checkResultDocument(value).ok, {\n message: `task.complete.document must be plain JSON data (equal to its own JSON round trip) and at most ${RESULT_DOCUMENT_MAX_BYTES} bytes as canonical JSON (UTF-8)`,\n }),\n});\nexport type TaskCompletePayload = z.infer<typeof TaskCompletePayloadSchema>;\n\n/** daemon -> server: task failed. */\nexport const TaskFailPayloadSchema = z.object({\n reason: z.string(),\n retryable: z.boolean().optional(),\n});\nexport type TaskFailPayload = z.infer<typeof TaskFailPayloadSchema>;\n\n/**\n * daemon -> server: task ended in the `Cancelled` state (M1 gap #6) — either\n * in response to a server-sent `task.cancel`, or a cancellation the daemon\n * observed/decided locally (e.g. a local stop action) that the server didn't\n * initiate. This is the canonical way to report a `Cancelled` outcome; it\n * supersedes the M0 convention of `task.fail({ reason: 'cancelled' })`.\n *\n * This is deliberately its own message rather than folded into `task.fail`\n * (decision: prefer the explicit message — see docs/protocol.md) because\n * `Cancelled` is semantically distinct from `Failed`: one is an intentional\n * stop, the other an error. Overloading `task.fail` with a magic\n * `reason: 'cancelled'` string convention hid that distinction on the wire.\n *\n * Dual-purpose on receipt: if the server already moved its own record to\n * `Cancelled` (it initiated the cancel — M1 gap #3's \"server state is\n * authoritative\" rule), this is an idempotent no-op ack. If the server\n * hasn't yet (a locally-observed cancellation), this is the authoritative\n * trigger that moves `Claimed`/`Running`/`AwaitApproval -> Cancelled`.\n */\nexport const TaskCancelledPayloadSchema = z.object({\n reason: z.string().optional(),\n});\nexport type TaskCancelledPayload = z.infer<typeof TaskCancelledPayloadSchema>;\n\n/**\n * daemon -> server: a pending `task.await_approval` was resolved entirely\n * LOCALLY on the device — the local control-socket `approvals.resolve` RPC,\n * a fail-closed `requestApproval` timeout, or a fail-closed eviction/finish\n * rejection (see `packages/client`'s `task-runner.ts`/`approvals.ts`) —\n * *without* a wire `task.approve`/`task.reject` ever having been exchanged\n * for it. This is the additive-minor answer to a gap the M4 Phase 3 approval\n * work left open (see the \"Deferred additive candidate\" note this schema\n * resolves, `docs/protocol.md`): today the server only learns of a local\n * resolution IMPLICITLY, after the fact, once the daemon's next\n * `task.progress`/`task.artifact`/`task.complete` proves the task already\n * moved on (`ConnectionHub.resumeIfImplicitlyApproved`,\n * `packages/server/src/hub.ts`) — a window in which a SaaS-side\n * `TaskHandle.approve()`/`.reject()` can independently decide (and win) the\n * server's own authoritative record before that evidence ever arrives. This\n * message lets the daemon report the local resolution explicitly and\n * immediately, narrowing that window from \"until the next progress message\"\n * down to ordinary network latency; the implicit-inference path stays as-is,\n * unconditionally, as the compatibility fallback for an old server that\n * never advertises the `approval_resolved` capability flag (`version.ts`) or\n * an old daemon that predates this message entirely.\n *\n * Observability-class tolerance applies (not control/security — see the\n * freeze rule's asymmetry, `docs/protocol.md`): this is a daemon reporting\n * what it already did locally, not a payload that grants/denies anything on\n * its own — the receiving server's own state machine (`TASK_TRANSITIONS`,\n * `task-state.ts`) is still what decides whether the reported resolution is\n * legal to apply. Plain `z.object()` (not `.strict()`), same as every other\n * non-control payload in this file.\n *\n * `resolvedBy` is a single-value enum (`'local'`) rather than a bare string:\n * deliberately future-proof (a later wave could add e.g. `'operator-cli'` as\n * a DISTINCT value without a version bump — a new enum member is additive,\n * same as a new message type or capability flag), while still being a closed,\n * typed shape today rather than an open string a typo could silently widen.\n */\nexport const TaskApprovalResolvedPayloadSchema = z.object({\n approvalId: z.string(),\n decision: z.enum(['approve', 'reject']),\n resolvedBy: z.enum(['local']),\n at: z.iso.datetime({ offset: true }),\n});\nexport type TaskApprovalResolvedPayload = z.infer<typeof TaskApprovalResolvedPayloadSchema>;\n\n// ---------------------------------------------------------------------------\n// registry — single source of truth mapping message type -> payload schema\n// ---------------------------------------------------------------------------\n\nexport const MESSAGE_PAYLOAD_SCHEMAS = {\n 'conn.hello': ConnHelloPayloadSchema,\n 'conn.ack': ConnAckPayloadSchema,\n 'task.offer': TaskOfferPayloadSchema,\n 'task.offer_with_toolsets': TaskOfferWithToolsetsPayloadSchema,\n 'task.approve': TaskApprovePayloadSchema,\n 'task.reject': TaskRejectPayloadSchema,\n 'task.cancel': TaskCancelPayloadSchema,\n 'task.steer': TaskSteerPayloadSchema,\n 'task.claim': TaskClaimPayloadSchema,\n 'task.started': TaskStartedPayloadSchema,\n 'task.decline': TaskDeclinePayloadSchema,\n 'task.progress': TaskProgressPayloadSchema,\n 'task.artifact': TaskArtifactPayloadSchema,\n 'task.await_approval': TaskAwaitApprovalPayloadSchema,\n 'task.complete': TaskCompletePayloadSchema,\n 'task.fail': TaskFailPayloadSchema,\n 'task.cancelled': TaskCancelledPayloadSchema,\n 'task.approval_resolved': TaskApprovalResolvedPayloadSchema,\n} as const;\n\nexport type MessageType = keyof typeof MESSAGE_PAYLOAD_SCHEMAS;\n\nexport const MESSAGE_TYPES = Object.keys(MESSAGE_PAYLOAD_SCHEMAS) as MessageType[];\n\n/**\n * Message types the server sends to the daemon. Used by {@link EnvelopeSchema}\n * (`envelope.ts`) to decide which branches require envelope `seq` (M1\n * redelivery cursor).\n */\nexport const SERVER_TO_DAEMON_TYPES = [\n 'conn.ack',\n 'task.offer',\n 'task.offer_with_toolsets',\n 'task.approve',\n 'task.reject',\n 'task.cancel',\n 'task.steer',\n] as const satisfies readonly MessageType[];\n\n/**\n * Message types the daemon sends to the server — the flip side of\n * {@link SERVER_TO_DAEMON_TYPES}. `conn.hello` is deliberately excluded: it's\n * only ever valid as the first frame of a WS handshake (`ws-server.ts`), not\n * as ongoing inbound traffic through `ConnectionHub.handleInbound`.\n *\n * Used by `handleInbound` (`@byok-sdk/server`'s `hub.ts`) as the type-allow gate\n * for every inbound envelope, WS and `POST /byok/messages` alike (finding\n * P2): a `type` outside this set — a server -> daemon type arriving inbound,\n * or anything unrecognized — is rejected before it's dispatched to any\n * handler or counted `accepted` on the `/byok/messages` wire.\n */\nexport const DAEMON_TO_SERVER_TYPES = [\n 'task.claim',\n 'task.started',\n 'task.decline',\n 'task.progress',\n 'task.artifact',\n 'task.await_approval',\n 'task.complete',\n 'task.fail',\n 'task.cancelled',\n 'task.approval_resolved',\n] as const satisfies readonly MessageType[];\n","import { z } from 'zod';\nimport { MESSAGE_PAYLOAD_SCHEMAS, SERVER_TO_DAEMON_TYPES, type MessageType } from './messages';\n\nconst REQUIRED_TASK_ID = z.string().min(1);\nconst OPTIONAL_TASK_ID = REQUIRED_TASK_ID.optional();\nconst REQUIRED_SEQ = z.number().int();\nconst OPTIONAL_SEQ = REQUIRED_SEQ.optional();\n\n/**\n * Build the full envelope schema for one message type. Field order here\n * matches the spec's documented shape (`v, id, ts, type, task_id?,\n * session_ref?, seq?, payload`) so parsed output has predictable key order.\n *\n * `taskId`/`seq` are passed in as concrete schemas (not a boolean flag)\n * so each call site's return type is precisely the required-or-optional\n * shape for that branch — `z.infer<typeof EnvelopeSchema>` then reflects the\n * per-message-type requiredness rather than collapsing it to `string |\n * undefined` for every branch.\n */\nfunction envelopeShape<\n T extends MessageType,\n TaskId extends z.ZodTypeAny,\n Seq extends z.ZodTypeAny,\n>(type: T, taskId: TaskId, seq: Seq) {\n return z.object({\n v: z.number().int(),\n id: z.uuid(),\n ts: z.iso.datetime({ offset: true }),\n type: z.literal(type),\n task_id: taskId,\n session_ref: z.string().optional(),\n seq,\n payload: MESSAGE_PAYLOAD_SCHEMAS[type],\n });\n}\n\n/**\n * The wire envelope: common transport fields plus a `payload` whose shape is\n * determined by `type`. Unknown top-level fields are tolerated (stripped) for\n * forward-compat; unknown `type` values do not match any branch below and are\n * handled explicitly by {@link parseMessage} in `codec.ts`.\n *\n * Two cross-cutting requiredness rules, fixed at M1 (see docs/protocol.md\n * \"M0 -> M1 breaking changes\"):\n *\n * - `task_id` is REQUIRED for every `task.*` type (they all route by task id)\n * and stays optional for `conn.*` (M1 gap #1).\n * - `seq` is REQUIRED for every type the *server* sends to the daemon — a\n * per-device monotonic counter used as a redelivery cursor — and stays\n * optional for daemon -> server types (M1 redelivery cursor; see\n * `conn.hello.cursor` in `messages.ts`).\n */\nexport const EnvelopeSchema = z.discriminatedUnion('type', [\n // conn.* — task_id stays optional (no task routing). conn.hello is\n // daemon -> server (no cursor to satisfy on this leg); conn.ack is\n // server -> daemon and therefore carries the required redelivery `seq`.\n envelopeShape('conn.hello', OPTIONAL_TASK_ID, OPTIONAL_SEQ),\n envelopeShape('conn.ack', OPTIONAL_TASK_ID, REQUIRED_SEQ),\n\n // task.* server -> daemon: task_id required (routing key) + seq required\n // (per-device redelivery cursor).\n envelopeShape('task.offer', REQUIRED_TASK_ID, REQUIRED_SEQ),\n envelopeShape('task.offer_with_toolsets', REQUIRED_TASK_ID, REQUIRED_SEQ),\n envelopeShape('task.approve', REQUIRED_TASK_ID, REQUIRED_SEQ),\n envelopeShape('task.reject', REQUIRED_TASK_ID, REQUIRED_SEQ),\n envelopeShape('task.cancel', REQUIRED_TASK_ID, REQUIRED_SEQ),\n envelopeShape('task.steer', REQUIRED_TASK_ID, REQUIRED_SEQ),\n\n // task.* daemon -> server: task_id required (routing key); envelope-level\n // seq is not required in this direction in M1 (no daemon->server\n // redelivery cursor yet — see docs/protocol.md).\n envelopeShape('task.claim', REQUIRED_TASK_ID, OPTIONAL_SEQ),\n envelopeShape('task.started', REQUIRED_TASK_ID, OPTIONAL_SEQ),\n envelopeShape('task.decline', REQUIRED_TASK_ID, OPTIONAL_SEQ),\n envelopeShape('task.progress', REQUIRED_TASK_ID, OPTIONAL_SEQ),\n envelopeShape('task.artifact', REQUIRED_TASK_ID, OPTIONAL_SEQ),\n envelopeShape('task.await_approval', REQUIRED_TASK_ID, OPTIONAL_SEQ),\n envelopeShape('task.complete', REQUIRED_TASK_ID, OPTIONAL_SEQ),\n envelopeShape('task.fail', REQUIRED_TASK_ID, OPTIONAL_SEQ),\n envelopeShape('task.cancelled', REQUIRED_TASK_ID, OPTIONAL_SEQ),\n envelopeShape('task.approval_resolved', REQUIRED_TASK_ID, OPTIONAL_SEQ),\n]);\n\nexport type Envelope = z.infer<typeof EnvelopeSchema>;\n\n/** `true` for every message type the server sends to the daemon (envelope `seq` is required for these). */\nexport function isServerToDaemonType(type: MessageType): boolean {\n return (SERVER_TO_DAEMON_TYPES as readonly MessageType[]).includes(type);\n}\n","import type { ZodError } from 'zod';\n\n/** Base class for all protocol decode/validation errors. */\nexport class ProtocolError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = 'ProtocolError';\n }\n}\n\n/** The input was not valid JSON at all (only thrown by `decodeEnvelope`). */\nexport class EnvelopeParseError extends ProtocolError {\n constructor(message: string, cause?: unknown) {\n super(message, { cause });\n this.name = 'EnvelopeParseError';\n }\n}\n\n/**\n * The `type` field did not match any known message type. This is distinct\n * from {@link EnvelopeValidationError} on purpose: a daemon/server on an\n * older minor version should catch this specifically and skip the message\n * instead of treating it as a bug, since a newer peer may have introduced an\n * additive message type it doesn't understand yet.\n */\nexport class UnknownMessageTypeError extends ProtocolError {\n public readonly type: unknown;\n\n constructor(type: unknown) {\n super(`Unknown message type: ${String(type)}`);\n this.name = 'UnknownMessageTypeError';\n this.type = type;\n }\n}\n\n/** The `type` field was recognized but the envelope/payload failed schema validation. */\nexport class EnvelopeValidationError extends ProtocolError {\n public readonly issues: ZodError;\n\n constructor(message: string, issues: ZodError) {\n super(message, { cause: issues });\n this.name = 'EnvelopeValidationError';\n this.issues = issues;\n }\n}\n","import type { z } from 'zod';\nimport { EnvelopeSchema, type Envelope } from './envelope';\nimport { MESSAGE_TYPES, MESSAGE_PAYLOAD_SCHEMAS, type MessageType } from './messages';\nimport { PROTOCOL_VERSION } from './version';\nimport { EnvelopeParseError, EnvelopeValidationError, UnknownMessageTypeError } from './errors';\n\nconst MESSAGE_TYPE_SET = new Set<string>(MESSAGE_TYPES);\n\nfunction decodeText(input: string | Uint8Array): string {\n return typeof input === 'string' ? input : new TextDecoder('utf-8').decode(input);\n}\n\n/**\n * Validate an already-parsed JS value as an {@link Envelope}, narrowing\n * `payload` by `type`. Throws {@link UnknownMessageTypeError} when `type`\n * isn't a recognized message type (safe for the caller to skip/ignore), or\n * {@link EnvelopeValidationError} when a recognized type fails schema\n * validation.\n */\nexport function parseMessage(data: unknown): Envelope {\n const result = EnvelopeSchema.safeParse(data);\n if (result.success) {\n return result.data;\n }\n\n const typeValue =\n typeof data === 'object' && data !== null && !Array.isArray(data)\n ? (data as Record<string, unknown>).type\n : undefined;\n\n if (typeof typeValue !== 'string' || !MESSAGE_TYPE_SET.has(typeValue)) {\n throw new UnknownMessageTypeError(typeValue);\n }\n\n throw new EnvelopeValidationError(\n `Envelope failed validation for type \"${typeValue}\"`,\n result.error,\n );\n}\n\n/**\n * Decode a single NDJSON line into a validated {@link Envelope}. Accepts a\n * string or raw bytes (e.g. a WebSocket binary frame) — isomorphic, no\n * stream handling required of the caller.\n */\nexport function decodeEnvelope(line: string | Uint8Array): Envelope {\n const text = decodeText(line).replace(/[\\r\\n]+$/, '');\n let json: unknown;\n try {\n json = JSON.parse(text);\n } catch (cause) {\n throw new EnvelopeParseError('Envelope line is not valid JSON', cause);\n }\n return parseMessage(json);\n}\n\n/** Encode an {@link Envelope} as a single-line NDJSON string (trailing `\\n` included). */\nexport function encodeEnvelope(env: Envelope): string {\n return `${JSON.stringify(env)}\\n`;\n}\n\n// ---------------------------------------------------------------------------\n// createEnvelope — finding F1: `taskId`/`seq` used to be uniformly optional\n// regardless of `type`, so e.g. `createEnvelope('task.offer', payload)` (no\n// `taskId`, no `seq`) type-checked cleanly and only failed much later, at\n// runtime, wherever the resulting envelope happened to get decoded — often\n// on the *other* side of the wire, several hops from the actual mistake.\n//\n// `EnvelopeShapeOptions` is the per-type source of truth for `taskId`/`seq`\n// requiredness, deliberately parallel to `envelope.ts`'s own\n// `envelopeShape()` calls (which encode the identical rule at the schema\n// level — see docs/protocol.md §1.1/§1.2): every `task.*` type requires\n// `taskId`; every type the *server* sends to the daemon additionally\n// requires `seq`. `createEnvelope`'s `opts` parameter is conditionally\n// required/optional per `T` from this map (`RequiredKeys`/`CreateEnvelopeArgs`\n// below), so a call site missing a required field is a compile error, not a\n// runtime surprise. The constructed envelope is also validated against\n// {@link EnvelopeSchema} before being returned, throwing\n// {@link EnvelopeValidationError} on failure — a second, runtime-level net\n// for whatever the type system can't catch (e.g. a payload widened via `as`).\n// ---------------------------------------------------------------------------\n\ninterface EnvelopeShapeOptions {\n 'conn.hello': { taskId?: string; seq?: number };\n 'conn.ack': { taskId?: string; seq: number };\n 'task.offer': { taskId: string; seq: number };\n 'task.offer_with_toolsets': { taskId: string; seq: number };\n 'task.approve': { taskId: string; seq: number };\n 'task.reject': { taskId: string; seq: number };\n 'task.cancel': { taskId: string; seq: number };\n 'task.steer': { taskId: string; seq: number };\n 'task.claim': { taskId: string; seq?: number };\n 'task.started': { taskId: string; seq?: number };\n 'task.decline': { taskId: string; seq?: number };\n 'task.progress': { taskId: string; seq?: number };\n 'task.artifact': { taskId: string; seq?: number };\n 'task.await_approval': { taskId: string; seq?: number };\n 'task.complete': { taskId: string; seq?: number };\n 'task.fail': { taskId: string; seq?: number };\n 'task.cancelled': { taskId: string; seq?: number };\n 'task.approval_resolved': { taskId: string; seq?: number };\n}\n\ninterface EnvelopeCommonOptions {\n id?: string;\n ts?: string;\n v?: number;\n /** Always optional regardless of `type` (docs/protocol.md §1.3). */\n sessionRef?: string;\n}\n\n/** 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. */\nexport type CreateEnvelopeOptions<T extends MessageType = MessageType> = EnvelopeCommonOptions &\n EnvelopeShapeOptions[T];\n\n/** `never` unless every key of `T` is optional — i.e. whether `createEnvelope`'s `opts` argument can be omitted entirely for a given message type. */\ntype RequiredKeys<T> = { [K in keyof T]-?: object extends Pick<T, K> ? never : K }[keyof T];\n\n/** 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`. */\ntype CreateEnvelopeArgs<T extends MessageType> = RequiredKeys<EnvelopeShapeOptions[T]> extends never\n ? [opts?: CreateEnvelopeOptions<T>]\n : [opts: CreateEnvelopeOptions<T>];\n\ntype PayloadOf<T extends MessageType> = z.infer<(typeof MESSAGE_PAYLOAD_SCHEMAS)[T]>;\n\n/**\n * Build a well-formed {@link Envelope}, filling `v`/`id`/`ts` with defaults.\n * `opts` (`taskId`/`seq`) is required or optional depending on `type` — see\n * the module doc above — and the constructed envelope is validated against\n * {@link EnvelopeSchema} before being returned, throwing\n * {@link EnvelopeValidationError} if it doesn't satisfy the schema.\n */\nexport function createEnvelope<T extends MessageType>(\n type: T,\n payload: PayloadOf<T>,\n ...rest: CreateEnvelopeArgs<T>\n): Extract<Envelope, { type: T }> {\n // `rest[0]`'s precise per-T type doesn't survive being read back out\n // inside a generic function body (a well-known TS limitation, not a\n // soundness gap: every concrete instantiation of `T` at the call site was\n // already checked against `CreateEnvelopeArgs<T>` above) — `opts` is\n // treated as the loose, common shape here, and the schema validation below\n // is what actually guards this function's output either way.\n const opts = (rest[0] ?? {}) as CreateEnvelopeOptions;\n const envelope = {\n v: opts.v ?? PROTOCOL_VERSION,\n id: opts.id ?? crypto.randomUUID(),\n ts: opts.ts ?? new Date().toISOString(),\n type,\n ...(opts.taskId !== undefined ? { task_id: opts.taskId } : {}),\n ...(opts.sessionRef !== undefined ? { session_ref: opts.sessionRef } : {}),\n ...(opts.seq !== undefined ? { seq: opts.seq } : {}),\n payload,\n };\n\n const result = EnvelopeSchema.safeParse(envelope);\n if (!result.success) {\n throw new EnvelopeValidationError(`createEnvelope built an invalid envelope for type \"${type}\"`, result.error);\n }\n return result.data as Extract<Envelope, { type: T }>;\n}\n","import { z } from 'zod';\nimport { CONTENT_HASH_RE } from './blob';\nimport { EnvelopeSchema } from './envelope';\n\n/**\n * HTTP-side request/response shapes for the reference server's auth and blob\n * endpoints (M1 Part B). These are plain HTTP bodies, not wire envelopes —\n * kept in a separate module from `envelope.ts`/`messages.ts` because they\n * never travel over the WSS connection. Documented in full in\n * docs/protocol.md (\"Auth flows\", \"Blob flows\", \"Long-poll fallback\").\n *\n * The wire protocol version (`v:1`) is unaffected by any of this: pairing,\n * token renewal, and blob transfer are out-of-band HTTP calls that happen\n * before/alongside the WSS connection, not envelope types.\n */\n\n// ---------------------------------------------------------------------------\n// POST /byok/pair (v2) — one-time device pairing. An out-of-band pairing\n// code (minted by the SaaS's own auth/device-flow UI) plus a freshly\n// generated Ed25519 device keypair (private key never leaves the device)\n// register the device and mint its first access token.\n// ---------------------------------------------------------------------------\n\nexport const PairRequestSchema = z.object({\n pairingCode: z.string(),\n deviceName: z.string(),\n /** Ed25519 public key, base64url-encoded. Private key stays device-local (OS keychain or 0600 file). */\n devicePublicKey: z.string(),\n});\nexport type PairRequest = z.infer<typeof PairRequestSchema>;\n\nexport const PairResponseSchema = z.object({\n deviceId: z.string(),\n /** JWT, ~1h lifetime. */\n accessToken: z.string(),\n /** Opaque hint for when/how to renew (e.g. an ISO timestamp); not itself a credential. */\n refreshHint: z.string().optional(),\n});\nexport type PairResponse = z.infer<typeof PairResponseSchema>;\n\n// ---------------------------------------------------------------------------\n// POST /byok/challenge + POST /byok/token — token renewal without re-pairing.\n// Two-step challenge/response proves possession of the device private key\n// without ever transmitting it: the server hands out a one-time nonce, the\n// client signs it locally with the device key, and trades the signature for\n// a fresh access token.\n// ---------------------------------------------------------------------------\n\nexport const ChallengeRequestSchema = z.object({\n deviceId: z.string(),\n});\nexport type ChallengeRequest = z.infer<typeof ChallengeRequestSchema>;\n\nexport const ChallengeResponseSchema = z.object({\n /** One-time value the client must sign with its device private key. */\n nonce: z.string(),\n});\nexport type ChallengeResponse = z.infer<typeof ChallengeResponseSchema>;\n\nexport const TokenRequestSchema = z.object({\n deviceId: z.string(),\n nonce: z.string(),\n /** Ed25519 signature over `nonce`, base64url-encoded. */\n signature: z.string(),\n});\nexport type TokenRequest = z.infer<typeof TokenRequestSchema>;\n\nexport const TokenResponseSchema = z.object({\n accessToken: z.string(),\n expiresAt: z.iso.datetime({ offset: true }),\n});\nexport type TokenResponse = z.infer<typeof TokenResponseSchema>;\n\n/**\n * Revocation is server-side only (dashboard/API call on the SaaS's own\n * device registry) — there is no wire message for it. A revoked device's\n * next `/byok/challenge` or `/byok/token` call (or WSS connect) gets a 401;\n * the daemon's only recourse is to re-run `/byok/pair` from scratch.\n */\n\n// ---------------------------------------------------------------------------\n// Blob endpoints — presigned upload/download. Authed (bearer access token).\n// `BlobRef` (`blob.ts`) is unchanged; these are the HTTP calls that produce\n// the presigned URLs a `BlobRef` points at.\n// ---------------------------------------------------------------------------\n\n/** POST /byok/blobs request: declare a blob before uploading it. `contentHash` must be the canonical `sha256:<64 lowercase hex>` form (finding F9) — the server rejects anything else outright, no normalization. */\nexport const CreateBlobRequestSchema = z.object({\n size: z.number().int().nonnegative(),\n contentType: z.string(),\n contentHash: z.string().regex(CONTENT_HASH_RE, 'contentHash must be \"sha256:<64 lowercase hex>\"'),\n});\nexport type CreateBlobRequest = z.infer<typeof CreateBlobRequestSchema>;\n\n/** POST /byok/blobs response: presigned PUT target for the declared blob. */\nexport const CreateBlobResponseSchema = z.object({\n blobId: z.string(),\n uploadUrl: z.string(),\n});\nexport type CreateBlobResponse = z.infer<typeof CreateBlobResponseSchema>;\n\n/** GET /byok/blobs/:id/url response: presigned GET target for an existing blob. */\nexport const BlobDownloadUrlResponseSchema = z.object({\n downloadUrl: z.string(),\n});\nexport type BlobDownloadUrlResponse = z.infer<typeof BlobDownloadUrlResponseSchema>;\n\n// ---------------------------------------------------------------------------\n// GET /byok/events?cursor=N — long-poll fallback for environments where WSS\n// is unavailable. Authed; holds the request open ~50s waiting for new\n// events, same at-least-once/cursor semantics as the WSS redelivery path\n// (see docs/protocol.md \"At-least-once delivery\").\n// ---------------------------------------------------------------------------\n\nexport const EventsPollQuerySchema = z.object({\n /** Last `seq` this client has seen; omitted on a client's first-ever poll. Never negative — `seq` is a monotonically increasing counter starting at 1. */\n cursor: z.number().int().nonnegative().optional(),\n});\nexport type EventsPollQuery = z.infer<typeof EventsPollQuerySchema>;\n\nexport const EventsPollResponseSchema = z.object({\n events: z.array(EnvelopeSchema),\n cursor: z.number().int(),\n});\nexport type EventsPollResponse = z.infer<typeof EventsPollResponseSchema>;\n\n// ---------------------------------------------------------------------------\n// POST /byok/messages — finding F6: long-poll is now a full transport, not\n// receive-only. While a device is long-polling for S->D traffic (§8), it has\n// no live WS to carry its own D->S envelopes (task.claim, task.progress,\n// task.complete, etc.) — this endpoint is that path: a batch of envelopes,\n// authed the same way as every other bearer-authed route, routed through the\n// identical inbound handling a WS connection's messages get. See\n// docs/protocol.md §8.\n// ---------------------------------------------------------------------------\n\n/**\n * Batch size ceiling for a single `POST /byok/messages` call — generous for\n * normal redelivery-catchup bursts, but bounded so one request can't force\n * the server to process an unbounded batch. Exported (not just a local\n * const) so the client's own outbound drain (`ConnectionManager.drainOutbox`,\n * finding P1) can chunk against the exact same number instead of a\n * hard-coded, driftable copy of it.\n */\nexport const MAX_MESSAGES_PER_BATCH = 256;\n\nexport const MessagesSendRequestSchema = z.object({\n messages: z.array(EnvelopeSchema).max(MAX_MESSAGES_PER_BATCH),\n});\nexport type MessagesSendRequest = z.infer<typeof MessagesSendRequestSchema>;\n\n/**\n * `accepted` counts every envelope `ConnectionHub.handleInbound` returned\n * `'accepted'` *or* `'duplicate'` for (finding P2) — a dedup'd replay is a\n * wire-level success (§9's idempotency window), even though no handler ran\n * for it a second time. `rejected` (a type outside `DAEMON_TO_SERVER_TYPES`,\n * or an ownership mismatch — N2) is a separate, additive count: omitted\n * entirely when zero, so a batch with nothing rejected keeps the pre-P2\n * `{ accepted }` shape callers already depend on.\n */\nexport const MessagesSendResponseSchema = z.object({\n accepted: z.number().int().nonnegative(),\n rejected: z.number().int().nonnegative().optional(),\n});\nexport type MessagesSendResponse = z.infer<typeof MessagesSendResponseSchema>;\n\n// ---------------------------------------------------------------------------\n// Route paths — the single source of truth for the `/byok/*` HTTP surface.\n//\n// These literals were previously hand-copied across client, cloud, server, and\n// testkit; the doc comments above already named every one of them but exported\n// no constant. They ARE the wire contract (unlike the host-owned, opaque\n// capability vocabulary of ADR-010, which stays out of protocol), so they live\n// here and every package imports them — a route change is now one edit.\n//\n// Two shapes:\n// - Static paths (`BYOK_*_PATH`) — used identically by routers and clients.\n// - Parameterized routes: a router template (`BYOK_*_ROUTE`, with `:param`\n// placeholders) for mounting, plus a builder (`byok*Path(...)`) that fills\n// the params for a client request. Each builder reproduces its call site\n// byte-for-byte, including whether a segment is `encodeURIComponent`-encoded.\n// ---------------------------------------------------------------------------\n\n/** `GET /byok/ws` — WebSocket upgrade path. */\nexport const BYOK_WS_PATH = '/byok/ws';\n\n/** `POST /byok/pair` — one-time device pairing (§6). */\nexport const BYOK_PAIR_PATH = '/byok/pair';\n/** `POST /byok/challenge` — token-renewal challenge (§6.3). */\nexport const BYOK_CHALLENGE_PATH = '/byok/challenge';\n/** `POST /byok/token` — token-renewal exchange (§6.3). */\nexport const BYOK_TOKEN_PATH = '/byok/token';\n\n/** `GET /byok/capabilities` — ADR-010 declaration route. */\nexport const BYOK_CAPABILITIES_PATH = '/byok/capabilities';\n\n/** `GET /byok/events` — long-poll receive (§8). */\nexport const BYOK_EVENTS_PATH = '/byok/events';\n/** `POST /byok/messages` — long-poll batched send (§8.2). */\nexport const BYOK_MESSAGES_PATH = '/byok/messages';\n\n/** `PUT /byok/presence` — presence heartbeat. */\nexport const BYOK_PRESENCE_PATH = '/byok/presence';\n/** `POST /byok/activity` — activity-tail append. */\nexport const BYOK_ACTIVITY_PATH = '/byok/activity';\n\n/** `GET /byok/board` — coordination board list/poll. */\nexport const BYOK_BOARD_PATH = '/byok/board';\n/** `GET /byok/board/stream` — coordination board SSE. */\nexport const BYOK_BOARD_STREAM_PATH = '/byok/board/stream';\n/** Router template — `POST /byok/board/:id/claim`. */\nexport const BYOK_BOARD_CLAIM_ROUTE = '/byok/board/:id/claim';\n/** Router template — `POST /byok/board/:id/unclaim`. */\nexport const BYOK_BOARD_UNCLAIM_ROUTE = '/byok/board/:id/unclaim';\n/** Router template — `POST /byok/board/:id/status`. */\nexport const BYOK_BOARD_STATUS_ROUTE = '/byok/board/:id/status';\n\n/** `GET /byok/records` — truth manifest list (§12.3). */\nexport const BYOK_RECORDS_PATH = '/byok/records';\n/** Router template for a single truth record — `GET`/`PUT /byok/records/:kind/:key`. */\nexport const BYOK_RECORD_ROUTE = '/byok/records/:kind/:key';\n/** Client builder for a truth record path. Mirrors the `:kind/:key` template, each segment URL-encoded. */\nexport function byokRecordPath(kind: string, key: string): string {\n return `/byok/records/${encodeURIComponent(kind)}/${encodeURIComponent(key)}`;\n}\n\n/** `GET /byok/skill-packs` — skill-pack manifest catalogue. */\nexport const BYOK_SKILL_PACKS_PATH = '/byok/skill-packs';\n/** Router template for a skill-pack file — `GET /byok/skill-packs/:name/files/:path`. */\nexport const BYOK_SKILL_PACK_FILE_ROUTE = '/byok/skill-packs/:name/files/:path';\n/** Client builder for a skill-pack file path. Mirrors the `:name`/`:path` template, each segment URL-encoded. */\nexport function byokSkillPackFilePath(name: string, path: string): string {\n return `/byok/skill-packs/${encodeURIComponent(name)}/files/${encodeURIComponent(path)}`;\n}\n\n/** `POST /byok/blobs` — declare a blob upload (§7). */\nexport const BYOK_BLOBS_PATH = '/byok/blobs';\n/** Router template — `POST /byok/blobs/:id/finalize`. */\nexport const BYOK_BLOB_FINALIZE_ROUTE = '/byok/blobs/:id/finalize';\n/** Router template — `GET /byok/blobs/:id/url`. */\nexport const BYOK_BLOB_URL_ROUTE = '/byok/blobs/:id/url';\n/** Router template for the two presigned byte routes — `PUT`/`GET /byok/blobs/:id/content`. */\nexport const BYOK_BLOB_CONTENT_ROUTE = '/byok/blobs/:id/content';\n/** Client builder — `POST /byok/blobs/:id/finalize`, blob id URL-encoded (client-supplied). */\nexport function byokBlobFinalizePath(blobId: string): string {\n return `/byok/blobs/${encodeURIComponent(blobId)}/finalize`;\n}\n/** Client builder — `GET /byok/blobs/:id/url`, blob id URL-encoded (client-supplied). */\nexport function byokBlobUrlPath(blobId: string): string {\n return `/byok/blobs/${encodeURIComponent(blobId)}/url`;\n}\n/**\n * Path portion of a presigned `/byok/blobs/:id/content` URL. The blob id here\n * is a server-minted token (NOT URL-encoded, matching the reference stores that\n * mint these signed URLs); callers append the `?sig=&exp=` query themselves.\n */\nexport function byokBlobContentPath(blobId: string): string {\n return `/byok/blobs/${blobId}/content`;\n}\n"]}
1
+ {"version":3,"sources":["../src/version.ts","../src/blob.ts","../src/permission.ts","../src/agent-event.ts","../src/task-state.ts","../src/messages.ts","../src/envelope.ts","../src/errors.ts","../src/codec.ts","../src/http-api.ts"],"names":["z"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Wire protocol version. Bump on breaking (non-additive) changes to the envelope\n * or message shapes. Additive changes (new optional fields, new message types)\n * do not require a bump — servers negotiate the highest common version and\n * daemons/servers must ignore unknown fields and unknown message types.\n *\n * FROZEN v1 (end of M2 — see docs/protocol.md \"Freeze rule\"): the pi, claude,\n * and codex runtime adapters have all exercised the wire, and every M1/M2\n * protocol gap has been closed. `PROTOCOL_VERSION` stays `1` from here\n * forward; it does not bump for additive changes (new optional fields, new\n * message types, new `AgentEvent` variants, new capability flags) — only for\n * a breaking one (changing, removing, or retyping anything that already\n * exists).\n *\n * IMPORTANT: changing this constant, or changing/removing/retyping any\n * already-frozen schema in this package, requires a DELIBERATE update to the\n * committed golden fixtures in `src/__tests__/golden/` (`v1.frozen.json`,\n * `v1.envelopes.ndjson`) — see `src/__tests__/freeze-guard.test.ts`, which\n * fails loudly on exactly that kind of drift. A passing freeze-guard run\n * after such a change means either (a) the change was genuinely additive and\n * the golden was regenerated with justification, or (b) this constant was\n * bumped alongside a new golden generation for the new version — never a\n * silent edit to either file to make the test pass.\n */\nexport const PROTOCOL_VERSION = 1;\n\n/**\n * Capability flags exchanged during the connection handshake (`conn.hello` /\n * `conn.ack`). Additional flags may be introduced without a protocol version\n * bump; unrecognized flags must be ignored by both sides.\n *\n * `interactive-approval` is RESERVED as of this addition: it gates the\n * (currently unexercised) approval seam — a server must not route an\n * approval-requiring policy to a daemon that hasn't advertised this flag. No\n * bundled runtime adapter emits it yet; that's expected until interactive\n * approval is actually wired up in a later wave.\n *\n * `approval_resolved` (additive-minor): a SERVER-advertised flag meaning\n * \"I understand the `task.approval_resolved` message\" (`messages.ts`). This\n * is the N/N-1 answer for that new daemon -> server message: an old server's\n * `CAPABILITY_FLAGS`/`conn.ack.capabilities` never includes it, so a new\n * daemon talking to an old server never sends `task.approval_resolved` at\n * all (see `packages/client`'s `task-runner.ts`) and falls back to the\n * pre-existing implicit-resume inference\n * (`ConnectionHub.resumeIfImplicitlyApproved`, `packages/server/src/hub.ts`)\n * unconditionally, exactly as before this flag existed. Unlike\n * `interactive-approval`, this one IS exercised the moment both sides\n * support it — there is no reserved/dormant period for it.\n */\n/**\n * `approval-targeting` (M5, additive-minor): unlike `approval_resolved`\n * above, this flag is purely INFORMATIONAL/semantic, not a functional gate.\n * `task.await_approval`/`task.approve`/`task.reject` all carry their new\n * `approvalId` field UNCONDITIONALLY on both sides once each peer is\n * upgraded -- the wire is tolerant (a plain, non-`.strict()` `z.object()`\n * field, `messages.ts`), so no version/capability negotiation is needed just\n * to send it safely; an older peer that doesn't recognize the field simply\n * never reads it. Receivers decide whether to apply exact-match targeting\n * by FIELD PRESENCE on the specific message at hand (does this particular\n * `task.approve`/`task.reject`/`onApprovalResolved` payload carry an\n * `approvalId`, and does a stored one exist to compare it against?), never\n * by checking this flag -- see `ConnectionHub.approveTask`/`rejectTask`/\n * `onApprovalResolved` and `TaskRunner.handleApprove`/`handleReject`\n * (`packages/client`'s `task-runner.ts`). This flag exists only so each side\n * can advertise, and an embedder/operator can observe (`ConnectionHub.\n * getDeviceCapabilities`), whether the OTHER side is new enough to\n * participate in targeting at all -- the same N/N-1-safe shape as every\n * other flag here, just consumed for observability instead of gating.\n */\n/**\n * `result-document` (additive-minor): a SERVER-advertised flag meaning \"I\n * understand the optional `task.complete.document` field\" (`messages.ts`).\n * Functionally gating, like `approval_resolved` and unlike\n * `approval-targeting`.\n *\n * This is the N/N-1 answer for that new daemon -> server FIELD. An old\n * server's transport advertisement (`conn.ack.capabilities` on WS or\n * `EventsPollResponse.capabilities` on long-poll) never includes it, and\n * its `TaskCompletePayloadSchema` is a tolerant (non-`.strict()`)\n * `z.object()`, so a `document` sent to it would be silently STRIPPED on\n * parse and vanish without a trace. That is exactly why emission is gated\n * here rather than sent unconditionally the way `approvalId` is: `document`\n * carries the task's primary structured RESULT, so losing it silently is\n * data loss, not a missed observability hint. A new daemon talking to an old\n * server therefore never sends `document` at all, and — if its configured\n * extractor did produce one — reports `task.fail` (retryable: false; the\n * same server will strip it on every retry too) instead of completing the\n * task with its main result quietly deleted (`packages/client`'s\n * `task-runner.ts`). A new server talking to an old daemon is unaffected:\n * the field is optional, and an old daemon simply never sets it.\n *\n * `dispatch-selection` (additive-minor) is a correctness gate for the\n * optional `task.offer.dispatchSelection` control field. An older v1 daemon\n * legally strips unknown optional fields, so a server must never send an\n * authoritative provider/model selection unless the target connection\n * advertises this flag. Absence means reject before task creation, not send\n * a legacy runtime-only offer that could reach a different provider.\n *\n * `toolset-selection` (additive-minor) means the daemon understands\n * `task.offer_with_toolsets` and can resolve its logical ids against local\n * MCP configuration. The distinct message type is also the N/N-1 safety\n * boundary for long-poll: an older daemon skips it as unknown and therefore\n * cannot accidentally execute the instruction without the required tools.\n */\nexport const CAPABILITY_FLAGS = [\n 'steer',\n 'blob-upload',\n 'interactive-approval',\n 'approval_resolved',\n 'approval-targeting',\n 'result-document',\n 'dispatch-selection',\n 'toolset-selection',\n] as const;\n\nexport type CapabilityFlag = (typeof CAPABILITY_FLAGS)[number];\n","import { z } from 'zod';\n\n/**\n * Canonical `contentHash` format (finding F9): `sha256:` followed by exactly\n * 64 lowercase hex characters (a SHA-256 digest). Pinned here — the single\n * source of truth both `BlobRefSchema` and `CreateBlobRequestSchema`\n * (`http-api.ts`) validate against — rather than left as a bare `z.string()`\n * that silently accepted any prefix (or none) and left the server to\n * reconcile the mismatch with an ad hoc normalization step. No compat shim:\n * the wire is pre-freeze (`v` stays `1`), so this is a straight tightening,\n * not a migration.\n */\nexport const CONTENT_HASH_RE = /^sha256:[0-9a-f]{64}$/;\n\n/**\n * Reference to a large payload that was pushed out-of-band (presigned PUT) or\n * is fetchable out-of-band (presigned GET), rather than inlined in an envelope.\n */\nexport const BlobRefSchema = z.object({\n blobId: z.string(),\n contentHash: z.string().regex(CONTENT_HASH_RE, 'contentHash must be \"sha256:<64 lowercase hex>\"'),\n size: z.number().int().nonnegative(),\n contentType: z.string(),\n url: z.string().optional(),\n});\n\nexport type BlobRef = z.infer<typeof BlobRefSchema>;\n","import { z } from 'zod';\n\nexport const PERMISSION_MODES = ['auto', 'confirm', 'readonly', 'plan'] as const;\n\nexport type PermissionMode = (typeof PERMISSION_MODES)[number];\n\n/**\n * Policy the server proposes for a task. The daemon/runtime adapter maps this\n * onto the concrete runtime's flags; anything that can't be expressed exactly\n * must fail closed (deny) rather than silently widen the grant.\n *\n * `.strict()`: this is control/security data, so per the freeze rule's\n * observability-vs-control asymmetry (docs/protocol.md \"Freeze rule\") an\n * unrecognized field must be REJECTED, not silently stripped-and-ignored the\n * way an ordinary payload's unknown field is (plain `z.object()`'s default\n * behavior). Without `.strict()`, a policy carrying a future constraint this\n * schema doesn't know about yet would parse successfully with that\n * constraint silently discarded — exactly the silent-widening failure mode\n * this type's own doc comment above warns against, since a stripped\n * constraint is indistinguishable from a constraint that was never sent.\n *\n * Consequence: adding a new field to this schema post-freeze is therefore a\n * BREAKING change requiring a `PROTOCOL_VERSION` bump — unlike the general\n * \"a new optional field on an existing payload is non-breaking\" rule the\n * freeze rule grants every other schema. That's intentional: a new\n * security/control constraint must force a conscious version bump so an\n * unupgraded peer can never silently ignore it, rather than being added the\n * same low-friction way a harmless observability field would be.\n */\nexport const PermissionPolicySchema = z\n .object({\n mode: z.enum(PERMISSION_MODES),\n allowTools: z.array(z.string()).optional(),\n denyTools: z.array(z.string()).optional(),\n workspaceRoot: z.string().optional(),\n network: z.boolean().optional(),\n })\n .strict();\n\nexport type PermissionPolicy = z.infer<typeof PermissionPolicySchema>;\n","import { z } from 'zod';\n\n/**\n * Normalized event shape that every runtime adapter (pi / claude / codex)\n * translates its native JSONL output into. This is the interior of a\n * `task.progress` payload's `events` array.\n */\nexport const AgentEventSchema = z.discriminatedUnion('type', [\n z.object({ type: z.literal('progress'), text: z.string() }),\n z.object({ type: z.literal('tool_use'), tool: z.string(), input: z.unknown().optional() }),\n z.object({ type: z.literal('tool_result'), tool: z.string(), output: z.unknown().optional() }),\n z.object({ type: z.literal('artifact'), name: z.string(), contentType: z.string() }),\n z.object({ type: z.literal('needs_approval'), summary: z.string() }),\n z.object({ type: z.literal('turn_end') }),\n z.object({ type: z.literal('error'), message: z.string() }),\n /**\n * Token usage reported by a runtime turn — maps from codex\n * `turn.completed.usage` and claude `result` usage (adapters wired up in a\n * later wave; this is just the variant + schema). All fields optional\n * because runtimes report different subsets.\n */\n z.object({\n type: z.literal('usage'),\n inputTokens: z.number().int().nonnegative().optional(),\n cachedInputTokens: z.number().int().nonnegative().optional(),\n outputTokens: z.number().int().nonnegative().optional(),\n reasoningTokens: z.number().int().nonnegative().optional(),\n totalTokens: z.number().int().nonnegative().optional(),\n }),\n]);\n\nexport type AgentEvent = z.infer<typeof AgentEventSchema>;\n\n/**\n * Known AgentEvent variant type discriminators — DERIVED directly from\n * {@link AgentEventSchema}'s own discriminated-union variants via\n * `z.toJSONSchema`, rather than hand-maintained as a second literal list.\n * This used to be a standalone array kept in sync with the schema above by\n * hand (dual authority); the freeze guard\n * (`__tests__/freeze-guard.test.ts`'s \"dual-authority cross-check\") already\n * asserted the two matched using this EXACT SAME `z.toJSONSchema` extraction\n * mechanism, which is why deriving it this way is safe: the guard already\n * proved this extraction produces the identical set the hand-written list\n * held. With the derivation below, the two can no longer drift apart at\n * all — there is only one authority now, {@link AgentEventSchema} itself.\n * The freeze guard test is kept anyway (now definitionally true rather than\n * a live check) as a regression net in case a future refactor reintroduces a\n * hand-written list.\n *\n * Exported (not module-private) so {@link isKnownAgentEvent} /\n * {@link partitionAgentEvents} (and the freeze guard) can check against the\n * exact same set without each reaching into zod's discriminated-union\n * internals directly. `z.toJSONSchema`'s output shape here (`.oneOf[].\n * properties.type.const`) is a public, documented zod v4 API — not\n * reaching into `._def`/internal fields — same as the freeze guard already\n * relies on.\n */\nexport const KNOWN_AGENT_EVENT_TYPES: readonly string[] = (\n z.toJSONSchema(AgentEventSchema) as unknown as { oneOf: Array<{ properties: { type: { const: string } } }> }\n).oneOf.map((branch) => branch.properties.type.const);\n\n/**\n * Pre-freeze compatibility widening (the freeze blocker this schema fixes):\n * an unknown-type event — one a future runtime/protocol minor version\n * introduces — parses as an opaque passthrough placeholder instead of\n * hard-failing the entire `task.progress` batch it arrived in. Without this,\n * `TaskProgressPayloadSchema.events: z.array(AgentEventSchema)` would throw\n * on the whole array the moment one event had an unrecognized `type`, which\n * made the wire's \"additive new variants are non-breaking\" promise false for\n * the installed base — and unfixable post-freeze.\n *\n * The `.refine` guard is load-bearing, not decorative: it excludes every\n * KNOWN type literal, so a *malformed* known variant (e.g. `progress`\n * missing `text`) still fails validation instead of silently matching this\n * fallback. Tolerance is only for unknown TYPES, never for malformed known\n * ones — see {@link AgentEventOrUnknownSchema}, which is what actually\n * combines this with {@link AgentEventSchema} for real use.\n *\n * Deliberately asymmetric with envelope-level control/security fields\n * (`instruction`, `policy` — see `messages.ts`/`permission.ts`), which stay\n * fail-closed on unknown shapes with no equivalent widening: this tolerance\n * applies only to observability data (agent progress events), never to\n * control/security surfaces. That asymmetry is the freeze rule.\n */\nexport const UnknownAgentEventSchema = z\n .object({ type: z.string() })\n .passthrough()\n .refine(\n (event) => !KNOWN_AGENT_EVENT_TYPES.includes(event.type),\n 'type refers to a known AgentEvent variant; malformed known variants must fail through AgentEventSchema, not fall back to unknown-tolerance',\n );\n\nexport type UnknownAgentEvent = z.infer<typeof UnknownAgentEventSchema>;\n\n/**\n * The actual element schema for `TaskProgressPayloadSchema.events`\n * (`messages.ts`): a known, fully-typed {@link AgentEvent} OR an opaque\n * unknown-type placeholder. `z.union` (not `discriminatedUnion`) is required\n * here because the fallback branch matches on \"not one of the known\n * literals\", which a discriminated union can't express directly.\n */\nexport const AgentEventOrUnknownSchema = z.union([AgentEventSchema, UnknownAgentEventSchema]);\n\nexport type AgentEventOrUnknown = z.infer<typeof AgentEventOrUnknownSchema>;\n\n/**\n * Type guard distinguishing a known, fully-typed {@link AgentEvent} from an\n * {@link UnknownAgentEvent} passthrough placeholder.\n */\nexport function isKnownAgentEvent(event: AgentEventOrUnknown): event is AgentEvent {\n return KNOWN_AGENT_EVENT_TYPES.includes(event.type);\n}\n\n/**\n * Split a `task.progress` events array into known (typed, actionable) and\n * unknown (opaque, safe-to-skip) events. Consumers should process `known`\n * and skip `unknown` rather than throwing on it — that's the point of the\n * pre-freeze tolerance above.\n */\nexport function partitionAgentEvents(events: readonly AgentEventOrUnknown[]): {\n known: AgentEvent[];\n unknown: UnknownAgentEvent[];\n} {\n const known: AgentEvent[] = [];\n const unknown: UnknownAgentEvent[] = [];\n for (const event of events) {\n if (isKnownAgentEvent(event)) {\n known.push(event);\n } else {\n unknown.push(event);\n }\n }\n return { known, unknown };\n}\n","export const TASK_STATES = [\n 'Offered',\n 'Claimed',\n 'Running',\n 'AwaitApproval',\n 'Complete',\n 'Failed',\n 'Cancelled',\n] as const;\n\nexport type TaskState = (typeof TASK_STATES)[number];\n\n/**\n * Legal state transitions for a task. `Complete` / `Failed` / `Cancelled` are\n * terminal (no outgoing edges). `Running` and `AwaitApproval` form a loop:\n * the daemon can request approval mid-run and resume once the server\n * approves (or fail/cancel out of the approval wait).\n *\n * `Offered -> Failed` (M1 gap #5, \"Declined vs. Failed\"): a daemon that\n * declines an offer pre-claim (`task.decline`) reports it through the\n * existing `Failed` state rather than a new `Declined` state. A decline and\n * a post-claim failure are the same outcome from the dispatcher's point of\n * view — this attempt produced no result, `reason`/`retryable` say why and\n * whether retrying elsewhere makes sense — so reusing `Failed` keeps the\n * state machine minimal instead of forking every terminal-state consumer\n * into \"Failed or Declined, handle both\". See docs/protocol.md for the full\n * writeup.\n */\nexport const TASK_TRANSITIONS: Readonly<Record<TaskState, readonly TaskState[]>> = {\n Offered: ['Claimed', 'Cancelled', 'Failed'],\n Claimed: ['Running', 'Failed', 'Cancelled'],\n Running: ['AwaitApproval', 'Complete', 'Failed', 'Cancelled'],\n AwaitApproval: ['Running', 'Failed', 'Cancelled'],\n Complete: [],\n Failed: [],\n Cancelled: [],\n};\n\n/** Whether `from -> to` is a legal transition per {@link TASK_TRANSITIONS}. */\nexport function canTransition(from: TaskState, to: TaskState): boolean {\n return TASK_TRANSITIONS[from].includes(to);\n}\n","import { z } from 'zod';\nimport { BlobRefSchema } from './blob';\nimport { PermissionPolicySchema } from './permission';\nimport { AgentEventOrUnknownSchema } from './agent-event';\n\n/** Max size of an inlined artifact payload, per the delivery-model spec (<=64KB). */\nconst MAX_INLINE_BYTES = 64 * 1024;\n\nfunction isWithinInlineByteLimit(value: string): boolean {\n return new TextEncoder().encode(value).length <= MAX_INLINE_BYTES;\n}\n\n// ---------------------------------------------------------------------------\n// conn.* — connection handshake\n// ---------------------------------------------------------------------------\n\nexport const RuntimeIdSchema = z.enum(['pi', 'claude', 'codex']);\nexport type RuntimeId = z.infer<typeof RuntimeIdSchema>;\n\n/**\n * Per-runtime feature flags reported in `conn.hello.runtimes[].capabilities`\n * (pre-freeze addition). Distinct from the connection-level `CAPABILITY_FLAGS`\n * (`version.ts`) / `conn.hello.capabilities` array: those are protocol-level\n * flags negotiated for the whole connection, while this is what one specific\n * detected runtime (pi/claude/codex) supports. The whole field is optional\n * end-to-end — older daemons omit `capabilities` entirely — and every field\n * inside it is itself optional, since detection can be partial.\n *\n * Per-tool allow/deny lists are deliberately NOT included here (noise).\n * `permissionModes` mirrors `PERMISSION_MODES` (`permission.ts`) but is kept\n * as a bare `string[]` rather than `z.enum(PERMISSION_MODES)`: this is a\n * runtime's self-reported observability data, not a control/security field,\n * so — per the freeze rule (tolerate unknown for observability, fail closed\n * for control/security; see `agent-event.ts`'s unknown-variant tolerance for\n * the same asymmetry applied to `task.progress` events) — it stays tolerant\n * of a mode string a newer runtime might report that this schema doesn't\n * enumerate yet, rather than rejecting the whole `conn.hello`.\n *\n * Unrecognized keys inside `capabilities` itself, by contrast, are silently\n * stripped (zod's default object behavior — same as every other payload\n * schema in this file) rather than passed through: this is a closed, typed\n * shape consumers can rely on, and a genuinely new capability flag gets added\n * here explicitly rather than round-tripped opaquely.\n */\nexport const RuntimeCapabilitiesSchema = z.object({\n steer: z.boolean().optional(),\n resume: z.boolean().optional(),\n approvalInteractive: z.boolean().optional(),\n /** Whether this runtime can project a locally configured MCP toolset into one task. */\n mcpToolsets: z.boolean().optional(),\n permissionModes: z.array(z.string()).optional(),\n});\nexport type RuntimeCapabilities = z.infer<typeof RuntimeCapabilitiesSchema>;\n\n/**\n * Runtime detection info reported in `conn.hello`. Supersedes the M0\n * `agents: unknown` field (M1 gap #4): typed, so the server no longer has to\n * best-effort-normalize an untyped blob.\n */\nexport const RuntimeInfoSchema = z.object({\n id: RuntimeIdSchema,\n version: z.string().optional(),\n authPresent: z.boolean().optional(),\n /** Optional: older daemons omit this entirely (pre-freeze addition). */\n capabilities: RuntimeCapabilitiesSchema.optional(),\n});\nexport type RuntimeInfo = z.infer<typeof RuntimeInfoSchema>;\n\n/** Maximum logical toolsets one daemon may advertise as locally configured. */\nexport const CONFIGURED_TOOLSETS_MAX_ITEMS = 64;\n\n/**\n * Logical, host-owned MCP toolset identifier. A task may request this name,\n * but the executable/server definition behind it exists only in the daemon's\n * local configuration and never crosses the SaaS wire.\n */\nexport const ToolsetIdSchema = z\n .string()\n .min(1)\n .max(128)\n .regex(/^[a-z0-9]+(?:[._-][a-z0-9]+)*$/u, 'toolset ids must be lowercase logical identifiers');\nexport type ToolsetId = z.infer<typeof ToolsetIdSchema>;\n\nfunction rejectDuplicateToolsets(\n ids: readonly string[],\n ctx: z.RefinementCtx,\n label: 'configured' | 'required',\n): void {\n const seen = new Set<string>();\n for (const [index, id] of ids.entries()) {\n if (seen.has(id)) {\n ctx.addIssue({ code: 'custom', path: [index], message: `duplicate ${label} toolset: ${id}` });\n }\n seen.add(id);\n }\n}\n\n/**\n * Device-local inventory projected for discovery. IDs only: executable MCP\n * definitions, arguments, environment and credentials never enter this shape.\n * Empty means known-none; omission on the containing message means unknown.\n */\nexport const ConfiguredToolsetsSchema = z\n .array(ToolsetIdSchema)\n .max(CONFIGURED_TOOLSETS_MAX_ITEMS)\n .superRefine((ids, ctx) => rejectDuplicateToolsets(ids, ctx, 'configured'));\n\n/** daemon -> server: opening handshake. */\nexport const ConnHelloPayloadSchema = z.object({\n protocolVersions: z.array(z.number().int()),\n capabilities: z.array(z.string()),\n deviceId: z.string(),\n productId: z.string(),\n /** Runtimes detected on this device (M1 gap #4; replaces `agents`). */\n runtimes: z.array(RuntimeInfoSchema).optional(),\n /** Validated logical IDs configured on this device; definitions and secrets stay local. */\n configuredToolsets: ConfiguredToolsetsSchema.optional(),\n /**\n * Last `seq` this device has seen from the server (M1 redelivery cursor).\n * Omitted on a device's first-ever connection. The server replays any\n * server->daemon envelopes with `seq > cursor` it still holds — see\n * docs/protocol.md \"At-least-once delivery\".\n */\n cursor: z.number().int().optional(),\n});\nexport type ConnHelloPayload = z.infer<typeof ConnHelloPayloadSchema>;\n\n/** server -> daemon: handshake acknowledgement. */\nexport const ConnAckPayloadSchema = z.object({\n protocolVersion: z.number().int(),\n capabilities: z.array(z.string()),\n serverTime: z.iso.datetime({ offset: true }),\n});\nexport type ConnAckPayload = z.infer<typeof ConnAckPayloadSchema>;\n\n// ---------------------------------------------------------------------------\n// server -> daemon: task.*\n//\n// Every type in this section carries a *required* envelope `task_id` (M1 gap\n// #1) and a *required* envelope `seq` — a per-device monotonic counter the\n// daemon uses as a redelivery cursor (M1 gap; see `conn.hello.cursor` above\n// and docs/protocol.md \"At-least-once delivery\"). None of these payloads\n// duplicate `taskId` at the payload level (M1 gap #7): the envelope's\n// `task_id` is the single routing key.\n// ---------------------------------------------------------------------------\n\n/**\n * The out-of-band-reference form of `TaskOfferPayload.instruction` (the\n * alternative to an inlined string). `.strict()`: like `PermissionPolicySchema`\n * (`permission.ts`), this is control data — it's the task instruction itself,\n * the thing that authorizes what work gets done — so per the freeze rule's\n * observability-vs-control asymmetry (docs/protocol.md \"Freeze rule\") an\n * unrecognized field here must be REJECTED, not silently stripped the way an\n * ordinary payload's unknown field is. Without `.strict()`, a hypothetical\n * future field riding along next to `blobRef` (e.g. a routing/priority\n * override) would be silently discarded instead of failing the parse —\n * exactly the kind of silent reinterpretation the freeze rule forbids for\n * control/security payloads. Consequence: adding a field to this shape\n * post-freeze is a BREAKING change (version bump required), not the usual\n * non-breaking additive-optional-field case — same as `PermissionPolicySchema`.\n */\nconst InstructionBlobRefSchema = z.object({ blobRef: BlobRefSchema }).strict();\n\nconst DispatchTargetIdSchema = z\n .string()\n .min(1)\n .max(160)\n .regex(/^[^\\u0000\\r\\n]+$/u, 'dispatch target ids must not contain control line breaks');\n\n/**\n * The web-selected runtime/model target carried end to end with a task.\n *\n * This is an additive v1 field. The discriminated union makes lane ownership\n * fail closed at decode time: subscription credentials can only belong to the\n * vendor CLIs, while a BYOK provider can only be executed through Pi.\n */\nexport const DispatchSelectionSchema = z.discriminatedUnion('lane', [\n z\n .object({\n lane: z.literal('subscription'),\n runtimeId: z.enum(['claude', 'codex']),\n providerId: z.null(),\n modelId: DispatchTargetIdSchema,\n })\n .strict(),\n z\n .object({\n lane: z.literal('byok'),\n runtimeId: z.literal('pi'),\n providerId: DispatchTargetIdSchema,\n modelId: DispatchTargetIdSchema,\n })\n .strict(),\n]);\nexport type DispatchSelection = z.infer<typeof DispatchSelectionSchema>;\n\n/**\n * server -> daemon: offer a task for a device to claim.\n *\n * `taskId` used to be duplicated here; it is now carried only by the\n * envelope's `task_id` (M1 gap #7 — single source of truth for routing).\n */\nexport const TaskOfferPayloadSchema = z.object({\n instruction: z.union([z.string(), InstructionBlobRefSchema]),\n policy: PermissionPolicySchema,\n runtime: RuntimeIdSchema.optional(),\n dispatchSelection: DispatchSelectionSchema.optional(),\n sessionRef: z.string().optional(),\n workspaceHint: z.string().optional(),\n limits: z\n .object({\n maxDurationMs: z.number().int().positive().optional(),\n maxTokens: z.number().int().positive().optional(),\n })\n .optional(),\n});\nexport type TaskOfferPayload = z.infer<typeof TaskOfferPayloadSchema>;\n\n/** Every named toolset is required; duplicates are rejected instead of silently de-duplicated. */\nexport const RequiredToolsetsSchema = z\n .array(ToolsetIdSchema)\n .min(1)\n .max(16)\n .superRefine((ids, ctx) => rejectDuplicateToolsets(ids, ctx, 'required'));\n\n/**\n * Additive v1 offer variant for tasks whose semantics require local MCP\n * tools. This is a distinct message type rather than an optional field on\n * `task.offer`: an older v1 daemon skips an unknown message type, whereas it\n * would legally strip an unknown optional control field and run the task\n * without its required tools. The whole payload is strict because every\n * field here affects execution authority.\n */\nexport const TaskOfferWithToolsetsPayloadSchema = TaskOfferPayloadSchema.extend({\n requiredToolsets: RequiredToolsetsSchema,\n}).strict();\nexport type TaskOfferWithToolsetsPayload = z.infer<typeof TaskOfferWithToolsetsPayloadSchema>;\n\n/**\n * server -> daemon: approve a pending `task.await_approval` request.\n *\n * Semantics (M1 gap #3): the server's own state is authoritative on its own\n * action — calling the server-side `approve()` API moves the task record\n * `AwaitApproval -> Running` immediately. This wire message is a best-effort\n * *notification* telling the daemon to resume the paused runtime session; the\n * daemon does not send a dedicated ack. Its outcome is observable through the\n * task's existing message stream (e.g. `task.progress` resuming, or\n * `task.fail`/`task.cancelled` if resuming turns out to be impossible) — no\n * new ack message type is introduced. See docs/protocol.md \"Approval flow\".\n *\n * `approvalId` (M5, additive-minor — docs/protocol.md §5.3): OPTIONAL target\n * identity for the SPECIFIC pending approval this decision resolves, rather\n * than \"whichever one is currently pending\" (the pre-M5 behavior, and still\n * what happens when this field is absent — a legacy server that never\n * learned an id, or one talking to a legacy daemon). When present, the\n * daemon compares it against its own currently-dispatched approval id\n * (`ActiveTask.pendingApprovalId`, `packages/client`'s `task-runner.ts`) and\n * treats a mismatch as a stale, audit-only no-op instead of resolving\n * whatever happens to be pending right now — see `TaskRunner.handleApprove`.\n */\nexport const TaskApprovePayloadSchema = z.object({\n approvalId: z.string().optional(),\n});\nexport type TaskApprovePayload = z.infer<typeof TaskApprovePayloadSchema>;\n\n/**\n * server -> daemon: reject a pending `task.await_approval` request.\n *\n * Same best-effort-notification semantics as `task.approve` (M1 gap #3): the\n * server moves its own record `AwaitApproval -> Failed` immediately; this\n * message just tells the daemon to stop, and the daemon reports the outcome\n * via its existing `task.fail` terminal message.\n *\n * `approvalId` (M5, additive-minor — docs/protocol.md §5.3): same optional\n * targeting semantics as `TaskApprovePayloadSchema.approvalId` above, applied\n * to the reject path (`TaskRunner.handleReject`).\n */\nexport const TaskRejectPayloadSchema = z.object({\n reason: z.string().optional(),\n approvalId: z.string().optional(),\n});\nexport type TaskRejectPayload = z.infer<typeof TaskRejectPayloadSchema>;\n\n/**\n * server -> daemon: cancel a task in any non-terminal state.\n *\n * Same best-effort-notification semantics (M1 gap #3): the server moves its\n * own record to `Cancelled` immediately on its own action and does not wait\n * for a daemon ack; this message just tells the daemon to stop local work.\n * The daemon reports the outcome via the explicit `task.cancelled` terminal\n * message (M1 gap #6) — not `task.fail`.\n */\nexport const TaskCancelPayloadSchema = z.object({\n reason: z.string().optional(),\n});\nexport type TaskCancelPayload = z.infer<typeof TaskCancelPayloadSchema>;\n\n/** server -> daemon: inject steering text into a running task. */\nexport const TaskSteerPayloadSchema = z.object({\n text: z.string(),\n});\nexport type TaskSteerPayload = z.infer<typeof TaskSteerPayloadSchema>;\n\n// ---------------------------------------------------------------------------\n// daemon -> server: task.*\n//\n// Every type in this section carries a *required* envelope `task_id` (M1 gap\n// #1 — these all route by task id). Envelope `seq` (the redelivery cursor)\n// stays optional in this direction: M1 only specifies at-least-once\n// server->daemon redelivery, not a daemon->server one (see\n// docs/protocol.md).\n// ---------------------------------------------------------------------------\n\n/**\n * daemon -> server: claim an offered task (idempotent CAS on the server\n * side). `taskId` used to be duplicated here; it is now carried only by the\n * envelope's `task_id` (M1 gap #7).\n *\n * Claiming no longer implies the task is `Running` (M1 gap #2) — see\n * `task.started`.\n *\n * `runtime` (M5, additive-minor — docs/protocol.md §3.1): the ACTUAL\n * adapter this device selected for the task, distinct from `task.offer`'s\n * own `runtime` (the merely REQUESTED one, `TaskOfferPayloadSchema.runtime`\n * above). When an offer names no runtime the daemon auto-selects (pi-first —\n * `TaskRunner.pickAdapter`, `packages/client`'s `task-runner.ts`), and before\n * this field existed the server had no way to learn which adapter actually\n * ran — `TaskSnapshot.runtime` (`packages/server`'s `types.ts`) only ever\n * recorded what was requested. Plain optional property on this already-\n * tolerant `z.object()`: an old server simply never reads it, so this needed\n * no version bump and no emission gating (same shape as `approvalId` on\n * `task.await_approval`/`task.approve`/`task.reject`, §5.3) — a new daemon\n * sends it unconditionally, regardless of whether the connected server is\n * new enough to store it.\n *\n * `capabilities` (S0/D-4, additive-minor — docs/protocol.md §2.4): the\n * TASK-level capability authority — what the claiming adapter reported about\n * itself at the moment it took this task, reusing `RuntimeCapabilitiesSchema`\n * (above) rather than introducing a second capability shape. Same source of\n * truth as `conn.hello.runtimes[].capabilities`, different scope: `conn.hello`\n * is CONNECTION-level discovery (\"what could this device run\"), which no\n * server-side control decision may read, because it is transport-shaped — a\n * long-poll-only daemon never sends `conn.hello` at all — and it describes a\n * device, not a task. This field is task-shaped: it shares a lifecycle with\n * the task↔runtime binding the claim itself establishes, so a control gate\n * (`steerTask()`, `packages/server`'s `hub.ts`) can key off it and stay\n * correct across reconnects, adapter-set changes, and transports. Plain\n * optional property on this already-tolerant `z.object()`, exactly like\n * `runtime` above: an old daemon simply omits it, and a consumer that gates on\n * it must fail closed on absence rather than assume a default.\n */\nexport const TaskClaimPayloadSchema = z.object({\n deviceId: z.string(),\n agentId: z.string().optional(),\n runtime: RuntimeIdSchema.optional(),\n capabilities: RuntimeCapabilitiesSchema.optional(),\n});\nexport type TaskClaimPayload = z.infer<typeof TaskClaimPayloadSchema>;\n\n/**\n * daemon -> server: explicit `Claimed -> Running` transition (M1 gap #2).\n * A `task.claim` no longer implies the task started running; the daemon\n * sends this once it has actually started the runtime session for the task.\n */\nexport const TaskStartedPayloadSchema = z.object({});\nexport type TaskStartedPayload = z.infer<typeof TaskStartedPayloadSchema>;\n\n/**\n * daemon -> server: decline an offer *before* claiming it (M1 gap #5) — e.g.\n * no compatible/available runtime, or the offered policy exceeds this\n * device's ceiling. Fail-closed rejections must use this instead of silently\n * dropping the offer.\n *\n * Decision (see docs/protocol.md \"Declined vs. Failed\" for the full\n * writeup): declining does *not* introduce a new `Declined` terminal state.\n * It maps onto the existing `Failed` state via a new `Offered -> Failed`\n * transition. `reason`/`retryable` intentionally mirror `TaskFailPayload`\n * exactly, because a pre-claim decline and a post-claim failure are the same\n * outcome from the dispatcher's point of view (this attempt produced no\n * result; here's whether retrying — e.g. offering to a different device —\n * makes sense), and keeping the state machine minimal avoids forking every\n * terminal-state consumer into \"Failed or Declined, handle both\".\n */\nexport const TaskDeclinePayloadSchema = z.object({\n reason: z.string(),\n retryable: z.boolean().optional(),\n});\nexport type TaskDeclinePayload = z.infer<typeof TaskDeclinePayloadSchema>;\n\n/**\n * daemon -> server: batch of normalized agent events.\n *\n * `events` elements are known-or-unknown (`AgentEventOrUnknownSchema` —\n * `agent-event.ts`), not bare `AgentEventSchema`: pre-freeze, an unrecognized\n * event `type` must not fail the whole batch, since a peer running a newer\n * minor version may have emitted an additive event variant this schema\n * doesn't know about yet. See `agent-event.ts` for the full rationale and\n * `partitionAgentEvents`/`isKnownAgentEvent` for how consumers should skip\n * unknowns instead of choking on them.\n */\nexport const TaskProgressPayloadSchema = z.object({\n seq: z.number().int(),\n events: z.array(AgentEventOrUnknownSchema),\n});\nexport type TaskProgressPayload = z.infer<typeof TaskProgressPayloadSchema>;\n\n/** daemon -> server: an artifact produced by the task, inline or by blob ref. */\nexport const TaskArtifactPayloadSchema = z.object({\n name: z.string(),\n contentType: z.string(),\n inline: z\n .string()\n .refine(isWithinInlineByteLimit, {\n message: 'inline artifact payload exceeds 64KB limit',\n })\n .optional(),\n blobRef: BlobRefSchema.optional(),\n});\nexport type TaskArtifactPayload = z.infer<typeof TaskArtifactPayloadSchema>;\n\n/**\n * daemon -> server: task is blocked on an out-of-band approval.\n *\n * `approvalId` (M5, additive-minor — docs/protocol.md §5.3): the daemon's own\n * locally-generated identity for THIS SPECIFIC pending approval\n * (`ApprovalRegistry`, `packages/client`'s `approvals.ts`) — included\n * unconditionally by an M5+ daemon, regardless of whether the connected\n * server has advertised the `approval-targeting` capability flag\n * (`version.ts`; see that flag's own doc comment for why no emission gating\n * is needed here — it's a tolerant `z.object()` field, so an older server\n * simply ignores it). Optional purely for wire tolerance with a pre-M5\n * daemon build that never set it at all: a server that never learns an id\n * for a task's current approval can't target a later `approve`/`reject`\n * decision and falls back to resolving \"whichever approval is currently\n * pending\" — the same behavior every server had before this field existed.\n */\nexport const TaskAwaitApprovalPayloadSchema = z.object({\n summary: z.string(),\n approvalId: z.string().optional(),\n});\nexport type TaskAwaitApprovalPayload = z.infer<typeof TaskAwaitApprovalPayloadSchema>;\n\n/**\n * Hard cap (1 MiB) on a single `task.complete.document` (see\n * {@link TaskCompletePayloadSchema}), measured as the UTF-8 byte length of\n * its canonical JSON encoding — NOT as a node/key count, since a document is\n * schema-neutral and its object shape is unbounded by design.\n *\n * The cap is a REJECT-AT-BOUNDARY limit on both sides: a daemon that\n * produces an over-cap document reports `task.fail` instead of sending it,\n * and a server rejects an over-cap document at schema validation. It is\n * never truncated — a truncated JSON document is not valid JSON, so\n * \"shrinking to fit\" can only hand the consumer garbage.\n *\n * 1 MiB is the conservative ceiling declared by the first real consumer\n * (`docs/researches/2026-08-12-salesko-consumption-evidence.md` §1/§2:\n * smallest real frame 8.4 KiB, typical 48-96 KiB, 512 KiB comfortable).\n * Producers should stay at or under ~512 KiB (docs/protocol.md); the extra\n * headroom exists because raising a protocol cap later is additive while\n * lowering one is breaking. A result too big for this channel belongs in\n * `artifactRefs` (the multi-file/binary/oversized channel), not here.\n */\nexport const RESULT_DOCUMENT_MAX_BYTES = 1_048_576;\n\n/**\n * Outcome of {@link checkResultDocument}.\n *\n * `bytes` is the measured canonical JSON UTF-8 byte length, present on both\n * the accept and the over-cap rejection so a caller can name the actual size\n * in a failure reason. `canonical` is the CANONICAL SNAPSHOT — the value a\n * sender must actually put on the wire; see {@link checkResultDocument}.\n */\nexport type ResultDocumentCheck =\n | { readonly ok: true; readonly bytes: number; readonly canonical: unknown }\n | { readonly ok: false; readonly reason: 'not-serializable' }\n | { readonly ok: false; readonly reason: 'over-cap'; readonly bytes: number }\n | { readonly ok: false; readonly reason: 'not-plain-json' };\n\n/**\n * Structural equality between a value and its own JSON round trip, written\n * out by hand (no node builtins — this package deliberately has none) over\n * exactly the three shapes pure JSON data can take.\n *\n * The asymmetry is the point: `a` is the CALLER's object, which may contain\n * anything JavaScript allows, while `b` is always the output of\n * `JSON.parse` — pure data. So every case where `a` is something JSON cannot\n * represent (a `Date`, a function, `undefined`, `NaN`, a symbol, a getter\n * that answers differently the second time, an object whose `toJSON` rewrote\n * it) shows up here as a shape or value mismatch against `b`, and is\n * rejected. `NaN !== NaN` falls out of strict equality for free, which is\n * exactly what we want: `NaN` serializes to `null`, so it is not\n * representable and must not pass.\n *\n * The one case structural comparison alone cannot see — an object whose data\n * lives somewhere other than its own enumerable string keys, e.g. a\n * populated `Map` — gets its own explicit rejection in the object branch\n * below.\n */\nfunction isSameJsonData(a: unknown, b: unknown): boolean {\n if (a === null || b === null) return a === b;\n\n const typeOfA = typeof a;\n if (typeOfA !== typeof b) return false;\n\n if (typeOfA !== 'object') {\n // string | number | boolean. Anything else (function, symbol, bigint,\n // undefined) can never equal a JSON.parse output, and `NaN === NaN` is\n // false, so this single comparison covers every primitive case.\n return a === b;\n }\n\n const aIsArray = Array.isArray(a);\n if (aIsArray !== Array.isArray(b)) return false;\n\n if (aIsArray) {\n const arrayA = a as unknown[];\n const arrayB = b as unknown[];\n if (arrayA.length !== arrayB.length) return false;\n for (let index = 0; index < arrayA.length; index += 1) {\n if (!isSameJsonData(arrayA[index], arrayB[index])) return false;\n }\n return true;\n }\n\n const objectA = a as Record<string, unknown>;\n const objectB = b as Record<string, unknown>;\n const keysA = Object.keys(objectA); // own enumerable string keys\n const keysB = Object.keys(objectB);\n\n // An object whose data does NOT live in its own enumerable string keys is\n // invisible to both `JSON.stringify` and the key comparison below — a\n // populated `Map`/`Set` serializes to `{}` and would otherwise compare\n // EQUAL to that `{}`, delivering an empty document as the task's truthful\n // structured result. That is the same silent-wrong-output class the whole\n // plain-data contract exists to stop, so a container-shaped value with no\n // own enumerable string keys and a non-plain prototype is rejected here.\n // Applied at every object node (this function recurses), so a nested\n // `Map`/`Set`/exotic instance dies exactly like a top-level one.\n //\n // The boundary this deliberately draws: a class instance WITH own\n // enumerable fields still passes, because its data really is in those\n // fields and survives the round trip intact. A class whose values come\n // from PROTOTYPE-level getters does not — those are invisible to JSON, so\n // such an instance is not plain JSON data and is outside this contract by\n // definition (docs/protocol.md §7.2). `Object.create(null)` is explicitly\n // fine: a null prototype is plain data, just without `Object.prototype`.\n if (keysA.length === 0) {\n const prototypeA: unknown = Object.getPrototypeOf(objectA);\n if (prototypeA !== Object.prototype && prototypeA !== null) return false;\n }\n\n if (keysA.length !== keysB.length) return false;\n for (const key of keysA) {\n if (!Object.prototype.hasOwnProperty.call(objectB, key)) return false;\n if (!isSameJsonData(objectA[key], objectB[key])) return false;\n }\n return true;\n}\n\n/**\n * THE single authority for \"is this a legal `task.complete.document`\", and\n * the one place its canonical form is produced. `TaskCompletePayloadSchema`'s\n * own refinement calls it, and the daemon-side pre-send gate\n * (`packages/client`'s `task-runner.ts`) imports and calls the exact same\n * function rather than re-deriving any part of it: a daemon that measured or\n * judged a document even slightly differently from the server that validates\n * it would either reject documents the wire would have accepted, or hand the\n * server a payload it is about to reject after the runtime session already\n * ended.\n *\n * **The contract is: a document must be PLAIN JSON DATA.** Not \"an object\n * that happens to survive `JSON.stringify`\" — that bar is far too low, and\n * two concrete attacks/mistakes live under it:\n *\n * 1. `JSON.stringify` succeeding does not mean the value was preserved. An\n * `undefined`-valued key, a `NaN`, a function-valued property, or a\n * `Date` all serialize \"successfully\" while silently becoming something\n * else (dropped, `null`, or a string). The result is a well-formed,\n * under-cap document that is not what the producer had — a confidently\n * wrong terminal result, the worst outcome this channel has.\n * 2. `toJSON(key)` receives the property key it is being serialized under,\n * so an object can legally answer one way at the root (`key === ''`,\n * where this function measures it) and a completely different way when\n * nested inside the envelope payload (`key === 'document'`, where the\n * codec actually serializes it). A root-only measurement is therefore\n * not a bound on what goes on the wire at all. The same hole exists for\n * any getter that answers differently on a second read.\n *\n * Both die together via the same mechanism. The steps:\n *\n * 1. `JSON.stringify` must succeed and not return `undefined`.\n * 2. Its UTF-8 byte length must be within {@link RESULT_DOCUMENT_MAX_BYTES}.\n * 3. `JSON.parse` that string — the CANONICAL SNAPSHOT. It is pure data:\n * no `toJSON`, no getters, no prototype, nothing left that can answer\n * differently a second time.\n * 4. The original must be structurally equal to the snapshot\n * ({@link isSameJsonData}). Any mismatch means the value was not plain\n * JSON data, and it is rejected rather than silently transformed.\n *\n * On success the snapshot is returned as `canonical`, and **every sender\n * must put THAT on the wire, never the original reference** — which is what\n * closes the contextual-`toJSON`/unstable-getter hole for good: pure data\n * serializes identically at the root and nested, so what was measured is\n * necessarily what is sent. The check is idempotent on pure data, so the\n * server re-running it on an already-parsed payload is a no-op that always\n * agrees.\n */\nexport function checkResultDocument(document: unknown): ResultDocumentCheck {\n let json: string | undefined;\n try {\n json = JSON.stringify(document);\n } catch {\n return { ok: false, reason: 'not-serializable' };\n }\n if (json === undefined) return { ok: false, reason: 'not-serializable' };\n\n const bytes = new TextEncoder().encode(json).length;\n if (bytes > RESULT_DOCUMENT_MAX_BYTES) return { ok: false, reason: 'over-cap', bytes };\n\n const canonical: unknown = JSON.parse(json);\n if (!isSameJsonData(document, canonical)) return { ok: false, reason: 'not-plain-json' };\n\n return { ok: true, bytes, canonical };\n}\n\n/**\n * daemon -> server: task finished successfully.\n *\n * `document` (additive-minor, docs/protocol.md \"Freeze rule\"): the OPTIONAL\n * structured terminal result of the task — one JSON value the product on the\n * other side consumes as the task's actual output, as opposed to `summary`\n * (human-readable prose) or `artifactRefs` (files). Deliberately\n * `z.unknown()`: this SDK never understands, validates, or transforms the\n * product's own document schema — that validation belongs to the consumer.\n * The only constraints the wire imposes are the ones\n * {@link checkResultDocument} enforces: it must be PLAIN JSON DATA (equal to\n * its own JSON round trip — see that function for why \"stringify succeeded\"\n * is not enough), and its canonical JSON UTF-8 encoding must be at most\n * {@link RESULT_DOCUMENT_MAX_BYTES}. An over-cap document is REJECTED here,\n * never truncated (see that constant's own doc comment). A sender puts the\n * check's `canonical` snapshot on the wire, never the original object.\n *\n * Unlike `approvalId` on `task.await_approval` above, emitting this field IS\n * gated on a capability flag (`result-document`, `version.ts`): a pre-\n * `result-document` server strips it silently as an unknown key (the\n * tolerant `z.object()` behavior §1 mandates), and silently losing the\n * task's primary structured result is not a tolerable degradation the way\n * losing an observability hint is. So a daemon sends `document` only to a\n * server that advertised the flag, and fails the task loudly otherwise —\n * see `packages/client`'s `task-runner.ts`.\n */\nexport const TaskCompletePayloadSchema = z.object({\n summary: z.string(),\n sessionRef: z.string(),\n artifactRefs: z.array(BlobRefSchema).optional(),\n document: z\n .unknown()\n .optional()\n .refine((value) => value === undefined || checkResultDocument(value).ok, {\n message: `task.complete.document must be plain JSON data (equal to its own JSON round trip) and at most ${RESULT_DOCUMENT_MAX_BYTES} bytes as canonical JSON (UTF-8)`,\n }),\n});\nexport type TaskCompletePayload = z.infer<typeof TaskCompletePayloadSchema>;\n\n/** daemon -> server: task failed. */\nexport const TaskFailPayloadSchema = z.object({\n reason: z.string(),\n retryable: z.boolean().optional(),\n});\nexport type TaskFailPayload = z.infer<typeof TaskFailPayloadSchema>;\n\n/**\n * daemon -> server: task ended in the `Cancelled` state (M1 gap #6) — either\n * in response to a server-sent `task.cancel`, or a cancellation the daemon\n * observed/decided locally (e.g. a local stop action) that the server didn't\n * initiate. This is the canonical way to report a `Cancelled` outcome; it\n * supersedes the M0 convention of `task.fail({ reason: 'cancelled' })`.\n *\n * This is deliberately its own message rather than folded into `task.fail`\n * (decision: prefer the explicit message — see docs/protocol.md) because\n * `Cancelled` is semantically distinct from `Failed`: one is an intentional\n * stop, the other an error. Overloading `task.fail` with a magic\n * `reason: 'cancelled'` string convention hid that distinction on the wire.\n *\n * Dual-purpose on receipt: if the server already moved its own record to\n * `Cancelled` (it initiated the cancel — M1 gap #3's \"server state is\n * authoritative\" rule), this is an idempotent no-op ack. If the server\n * hasn't yet (a locally-observed cancellation), this is the authoritative\n * trigger that moves `Claimed`/`Running`/`AwaitApproval -> Cancelled`.\n */\nexport const TaskCancelledPayloadSchema = z.object({\n reason: z.string().optional(),\n});\nexport type TaskCancelledPayload = z.infer<typeof TaskCancelledPayloadSchema>;\n\n/**\n * daemon -> server: a pending `task.await_approval` was resolved entirely\n * LOCALLY on the device — the local control-socket `approvals.resolve` RPC,\n * a fail-closed `requestApproval` timeout, or a fail-closed eviction/finish\n * rejection (see `packages/client`'s `task-runner.ts`/`approvals.ts`) —\n * *without* a wire `task.approve`/`task.reject` ever having been exchanged\n * for it. This is the additive-minor answer to a gap the M4 Phase 3 approval\n * work left open (see the \"Deferred additive candidate\" note this schema\n * resolves, `docs/protocol.md`): today the server only learns of a local\n * resolution IMPLICITLY, after the fact, once the daemon's next\n * `task.progress`/`task.artifact`/`task.complete` proves the task already\n * moved on (`ConnectionHub.resumeIfImplicitlyApproved`,\n * `packages/server/src/hub.ts`) — a window in which a SaaS-side\n * `TaskHandle.approve()`/`.reject()` can independently decide (and win) the\n * server's own authoritative record before that evidence ever arrives. This\n * message lets the daemon report the local resolution explicitly and\n * immediately, narrowing that window from \"until the next progress message\"\n * down to ordinary network latency; the implicit-inference path stays as-is,\n * unconditionally, as the compatibility fallback for an old server that\n * never advertises the `approval_resolved` capability flag (`version.ts`) or\n * an old daemon that predates this message entirely.\n *\n * Observability-class tolerance applies (not control/security — see the\n * freeze rule's asymmetry, `docs/protocol.md`): this is a daemon reporting\n * what it already did locally, not a payload that grants/denies anything on\n * its own — the receiving server's own state machine (`TASK_TRANSITIONS`,\n * `task-state.ts`) is still what decides whether the reported resolution is\n * legal to apply. Plain `z.object()` (not `.strict()`), same as every other\n * non-control payload in this file.\n *\n * `resolvedBy` is a single-value enum (`'local'`) rather than a bare string:\n * deliberately future-proof (a later wave could add e.g. `'operator-cli'` as\n * a DISTINCT value without a version bump — a new enum member is additive,\n * same as a new message type or capability flag), while still being a closed,\n * typed shape today rather than an open string a typo could silently widen.\n */\nexport const TaskApprovalResolvedPayloadSchema = z.object({\n approvalId: z.string(),\n decision: z.enum(['approve', 'reject']),\n resolvedBy: z.enum(['local']),\n at: z.iso.datetime({ offset: true }),\n});\nexport type TaskApprovalResolvedPayload = z.infer<typeof TaskApprovalResolvedPayloadSchema>;\n\n// ---------------------------------------------------------------------------\n// registry — single source of truth mapping message type -> payload schema\n// ---------------------------------------------------------------------------\n\nexport const MESSAGE_PAYLOAD_SCHEMAS = {\n 'conn.hello': ConnHelloPayloadSchema,\n 'conn.ack': ConnAckPayloadSchema,\n 'task.offer': TaskOfferPayloadSchema,\n 'task.offer_with_toolsets': TaskOfferWithToolsetsPayloadSchema,\n 'task.approve': TaskApprovePayloadSchema,\n 'task.reject': TaskRejectPayloadSchema,\n 'task.cancel': TaskCancelPayloadSchema,\n 'task.steer': TaskSteerPayloadSchema,\n 'task.claim': TaskClaimPayloadSchema,\n 'task.started': TaskStartedPayloadSchema,\n 'task.decline': TaskDeclinePayloadSchema,\n 'task.progress': TaskProgressPayloadSchema,\n 'task.artifact': TaskArtifactPayloadSchema,\n 'task.await_approval': TaskAwaitApprovalPayloadSchema,\n 'task.complete': TaskCompletePayloadSchema,\n 'task.fail': TaskFailPayloadSchema,\n 'task.cancelled': TaskCancelledPayloadSchema,\n 'task.approval_resolved': TaskApprovalResolvedPayloadSchema,\n} as const;\n\nexport type MessageType = keyof typeof MESSAGE_PAYLOAD_SCHEMAS;\n\nexport const MESSAGE_TYPES = Object.keys(MESSAGE_PAYLOAD_SCHEMAS) as MessageType[];\n\n/**\n * Message types the server sends to the daemon. Used by {@link EnvelopeSchema}\n * (`envelope.ts`) to decide which branches require envelope `seq` (M1\n * redelivery cursor).\n */\nexport const SERVER_TO_DAEMON_TYPES = [\n 'conn.ack',\n 'task.offer',\n 'task.offer_with_toolsets',\n 'task.approve',\n 'task.reject',\n 'task.cancel',\n 'task.steer',\n] as const satisfies readonly MessageType[];\n\n/**\n * Message types the daemon sends to the server — the flip side of\n * {@link SERVER_TO_DAEMON_TYPES}. `conn.hello` is deliberately excluded: it's\n * only ever valid as the first frame of a WS handshake (`ws-server.ts`), not\n * as ongoing inbound traffic through `ConnectionHub.handleInbound`.\n *\n * Used by `handleInbound` (`@byok-sdk/server`'s `hub.ts`) as the type-allow gate\n * for every inbound envelope, WS and `POST /byok/messages` alike (finding\n * P2): a `type` outside this set — a server -> daemon type arriving inbound,\n * or anything unrecognized — is rejected before it's dispatched to any\n * handler or counted `accepted` on the `/byok/messages` wire.\n */\nexport const DAEMON_TO_SERVER_TYPES = [\n 'task.claim',\n 'task.started',\n 'task.decline',\n 'task.progress',\n 'task.artifact',\n 'task.await_approval',\n 'task.complete',\n 'task.fail',\n 'task.cancelled',\n 'task.approval_resolved',\n] as const satisfies readonly MessageType[];\n","import { z } from 'zod';\nimport { MESSAGE_PAYLOAD_SCHEMAS, SERVER_TO_DAEMON_TYPES, type MessageType } from './messages';\n\nconst REQUIRED_TASK_ID = z.string().min(1);\nconst OPTIONAL_TASK_ID = REQUIRED_TASK_ID.optional();\nconst REQUIRED_SEQ = z.number().int();\nconst OPTIONAL_SEQ = REQUIRED_SEQ.optional();\n\n/**\n * Build the full envelope schema for one message type. Field order here\n * matches the spec's documented shape (`v, id, ts, type, task_id?,\n * session_ref?, seq?, payload`) so parsed output has predictable key order.\n *\n * `taskId`/`seq` are passed in as concrete schemas (not a boolean flag)\n * so each call site's return type is precisely the required-or-optional\n * shape for that branch — `z.infer<typeof EnvelopeSchema>` then reflects the\n * per-message-type requiredness rather than collapsing it to `string |\n * undefined` for every branch.\n */\nfunction envelopeShape<\n T extends MessageType,\n TaskId extends z.ZodTypeAny,\n Seq extends z.ZodTypeAny,\n>(type: T, taskId: TaskId, seq: Seq) {\n return z.object({\n v: z.number().int(),\n id: z.uuid(),\n ts: z.iso.datetime({ offset: true }),\n type: z.literal(type),\n task_id: taskId,\n session_ref: z.string().optional(),\n seq,\n payload: MESSAGE_PAYLOAD_SCHEMAS[type],\n });\n}\n\n/**\n * The wire envelope: common transport fields plus a `payload` whose shape is\n * determined by `type`. Unknown top-level fields are tolerated (stripped) for\n * forward-compat; unknown `type` values do not match any branch below and are\n * handled explicitly by {@link parseMessage} in `codec.ts`.\n *\n * Two cross-cutting requiredness rules, fixed at M1 (see docs/protocol.md\n * \"M0 -> M1 breaking changes\"):\n *\n * - `task_id` is REQUIRED for every `task.*` type (they all route by task id)\n * and stays optional for `conn.*` (M1 gap #1).\n * - `seq` is REQUIRED for every type the *server* sends to the daemon — a\n * per-device monotonic counter used as a redelivery cursor — and stays\n * optional for daemon -> server types (M1 redelivery cursor; see\n * `conn.hello.cursor` in `messages.ts`).\n */\nexport const EnvelopeSchema = z.discriminatedUnion('type', [\n // conn.* — task_id stays optional (no task routing). conn.hello is\n // daemon -> server (no cursor to satisfy on this leg); conn.ack is\n // server -> daemon and therefore carries the required redelivery `seq`.\n envelopeShape('conn.hello', OPTIONAL_TASK_ID, OPTIONAL_SEQ),\n envelopeShape('conn.ack', OPTIONAL_TASK_ID, REQUIRED_SEQ),\n\n // task.* server -> daemon: task_id required (routing key) + seq required\n // (per-device redelivery cursor).\n envelopeShape('task.offer', REQUIRED_TASK_ID, REQUIRED_SEQ),\n envelopeShape('task.offer_with_toolsets', REQUIRED_TASK_ID, REQUIRED_SEQ),\n envelopeShape('task.approve', REQUIRED_TASK_ID, REQUIRED_SEQ),\n envelopeShape('task.reject', REQUIRED_TASK_ID, REQUIRED_SEQ),\n envelopeShape('task.cancel', REQUIRED_TASK_ID, REQUIRED_SEQ),\n envelopeShape('task.steer', REQUIRED_TASK_ID, REQUIRED_SEQ),\n\n // task.* daemon -> server: task_id required (routing key); envelope-level\n // seq is not required in this direction in M1 (no daemon->server\n // redelivery cursor yet — see docs/protocol.md).\n envelopeShape('task.claim', REQUIRED_TASK_ID, OPTIONAL_SEQ),\n envelopeShape('task.started', REQUIRED_TASK_ID, OPTIONAL_SEQ),\n envelopeShape('task.decline', REQUIRED_TASK_ID, OPTIONAL_SEQ),\n envelopeShape('task.progress', REQUIRED_TASK_ID, OPTIONAL_SEQ),\n envelopeShape('task.artifact', REQUIRED_TASK_ID, OPTIONAL_SEQ),\n envelopeShape('task.await_approval', REQUIRED_TASK_ID, OPTIONAL_SEQ),\n envelopeShape('task.complete', REQUIRED_TASK_ID, OPTIONAL_SEQ),\n envelopeShape('task.fail', REQUIRED_TASK_ID, OPTIONAL_SEQ),\n envelopeShape('task.cancelled', REQUIRED_TASK_ID, OPTIONAL_SEQ),\n envelopeShape('task.approval_resolved', REQUIRED_TASK_ID, OPTIONAL_SEQ),\n]);\n\nexport type Envelope = z.infer<typeof EnvelopeSchema>;\n\n/** `true` for every message type the server sends to the daemon (envelope `seq` is required for these). */\nexport function isServerToDaemonType(type: MessageType): boolean {\n return (SERVER_TO_DAEMON_TYPES as readonly MessageType[]).includes(type);\n}\n","import type { ZodError } from 'zod';\n\n/** Base class for all protocol decode/validation errors. */\nexport class ProtocolError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = 'ProtocolError';\n }\n}\n\n/** The input was not valid JSON at all (only thrown by `decodeEnvelope`). */\nexport class EnvelopeParseError extends ProtocolError {\n constructor(message: string, cause?: unknown) {\n super(message, { cause });\n this.name = 'EnvelopeParseError';\n }\n}\n\n/**\n * The `type` field did not match any known message type. This is distinct\n * from {@link EnvelopeValidationError} on purpose: a daemon/server on an\n * older minor version should catch this specifically and skip the message\n * instead of treating it as a bug, since a newer peer may have introduced an\n * additive message type it doesn't understand yet.\n */\nexport class UnknownMessageTypeError extends ProtocolError {\n public readonly type: unknown;\n\n constructor(type: unknown) {\n super(`Unknown message type: ${String(type)}`);\n this.name = 'UnknownMessageTypeError';\n this.type = type;\n }\n}\n\n/** The `type` field was recognized but the envelope/payload failed schema validation. */\nexport class EnvelopeValidationError extends ProtocolError {\n public readonly issues: ZodError;\n\n constructor(message: string, issues: ZodError) {\n super(message, { cause: issues });\n this.name = 'EnvelopeValidationError';\n this.issues = issues;\n }\n}\n","import type { z } from 'zod';\nimport { EnvelopeSchema, type Envelope } from './envelope';\nimport { MESSAGE_TYPES, MESSAGE_PAYLOAD_SCHEMAS, type MessageType } from './messages';\nimport { PROTOCOL_VERSION } from './version';\nimport { EnvelopeParseError, EnvelopeValidationError, UnknownMessageTypeError } from './errors';\n\nconst MESSAGE_TYPE_SET = new Set<string>(MESSAGE_TYPES);\n\nfunction decodeText(input: string | Uint8Array): string {\n return typeof input === 'string' ? input : new TextDecoder('utf-8').decode(input);\n}\n\n/**\n * Validate an already-parsed JS value as an {@link Envelope}, narrowing\n * `payload` by `type`. Throws {@link UnknownMessageTypeError} when `type`\n * isn't a recognized message type (safe for the caller to skip/ignore), or\n * {@link EnvelopeValidationError} when a recognized type fails schema\n * validation.\n */\nexport function parseMessage(data: unknown): Envelope {\n const result = EnvelopeSchema.safeParse(data);\n if (result.success) {\n return result.data;\n }\n\n const typeValue =\n typeof data === 'object' && data !== null && !Array.isArray(data)\n ? (data as Record<string, unknown>).type\n : undefined;\n\n if (typeof typeValue !== 'string' || !MESSAGE_TYPE_SET.has(typeValue)) {\n throw new UnknownMessageTypeError(typeValue);\n }\n\n throw new EnvelopeValidationError(\n `Envelope failed validation for type \"${typeValue}\"`,\n result.error,\n );\n}\n\n/**\n * Decode a single NDJSON line into a validated {@link Envelope}. Accepts a\n * string or raw bytes (e.g. a WebSocket binary frame) — isomorphic, no\n * stream handling required of the caller.\n */\nexport function decodeEnvelope(line: string | Uint8Array): Envelope {\n const text = decodeText(line).replace(/[\\r\\n]+$/, '');\n let json: unknown;\n try {\n json = JSON.parse(text);\n } catch (cause) {\n throw new EnvelopeParseError('Envelope line is not valid JSON', cause);\n }\n return parseMessage(json);\n}\n\n/** Encode an {@link Envelope} as a single-line NDJSON string (trailing `\\n` included). */\nexport function encodeEnvelope(env: Envelope): string {\n return `${JSON.stringify(env)}\\n`;\n}\n\n// ---------------------------------------------------------------------------\n// createEnvelope — finding F1: `taskId`/`seq` used to be uniformly optional\n// regardless of `type`, so e.g. `createEnvelope('task.offer', payload)` (no\n// `taskId`, no `seq`) type-checked cleanly and only failed much later, at\n// runtime, wherever the resulting envelope happened to get decoded — often\n// on the *other* side of the wire, several hops from the actual mistake.\n//\n// `EnvelopeShapeOptions` is the per-type source of truth for `taskId`/`seq`\n// requiredness, deliberately parallel to `envelope.ts`'s own\n// `envelopeShape()` calls (which encode the identical rule at the schema\n// level — see docs/protocol.md §1.1/§1.2): every `task.*` type requires\n// `taskId`; every type the *server* sends to the daemon additionally\n// requires `seq`. `createEnvelope`'s `opts` parameter is conditionally\n// required/optional per `T` from this map (`RequiredKeys`/`CreateEnvelopeArgs`\n// below), so a call site missing a required field is a compile error, not a\n// runtime surprise. The constructed envelope is also validated against\n// {@link EnvelopeSchema} before being returned, throwing\n// {@link EnvelopeValidationError} on failure — a second, runtime-level net\n// for whatever the type system can't catch (e.g. a payload widened via `as`).\n// ---------------------------------------------------------------------------\n\ninterface EnvelopeShapeOptions {\n 'conn.hello': { taskId?: string; seq?: number };\n 'conn.ack': { taskId?: string; seq: number };\n 'task.offer': { taskId: string; seq: number };\n 'task.offer_with_toolsets': { taskId: string; seq: number };\n 'task.approve': { taskId: string; seq: number };\n 'task.reject': { taskId: string; seq: number };\n 'task.cancel': { taskId: string; seq: number };\n 'task.steer': { taskId: string; seq: number };\n 'task.claim': { taskId: string; seq?: number };\n 'task.started': { taskId: string; seq?: number };\n 'task.decline': { taskId: string; seq?: number };\n 'task.progress': { taskId: string; seq?: number };\n 'task.artifact': { taskId: string; seq?: number };\n 'task.await_approval': { taskId: string; seq?: number };\n 'task.complete': { taskId: string; seq?: number };\n 'task.fail': { taskId: string; seq?: number };\n 'task.cancelled': { taskId: string; seq?: number };\n 'task.approval_resolved': { taskId: string; seq?: number };\n}\n\ninterface EnvelopeCommonOptions {\n id?: string;\n ts?: string;\n v?: number;\n /** Always optional regardless of `type` (docs/protocol.md §1.3). */\n sessionRef?: string;\n}\n\n/** 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. */\nexport type CreateEnvelopeOptions<T extends MessageType = MessageType> = EnvelopeCommonOptions &\n EnvelopeShapeOptions[T];\n\n/** `never` unless every key of `T` is optional — i.e. whether `createEnvelope`'s `opts` argument can be omitted entirely for a given message type. */\ntype RequiredKeys<T> = { [K in keyof T]-?: object extends Pick<T, K> ? never : K }[keyof T];\n\n/** 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`. */\ntype CreateEnvelopeArgs<T extends MessageType> = RequiredKeys<EnvelopeShapeOptions[T]> extends never\n ? [opts?: CreateEnvelopeOptions<T>]\n : [opts: CreateEnvelopeOptions<T>];\n\ntype PayloadOf<T extends MessageType> = z.infer<(typeof MESSAGE_PAYLOAD_SCHEMAS)[T]>;\n\n/**\n * Build a well-formed {@link Envelope}, filling `v`/`id`/`ts` with defaults.\n * `opts` (`taskId`/`seq`) is required or optional depending on `type` — see\n * the module doc above — and the constructed envelope is validated against\n * {@link EnvelopeSchema} before being returned, throwing\n * {@link EnvelopeValidationError} if it doesn't satisfy the schema.\n */\nexport function createEnvelope<T extends MessageType>(\n type: T,\n payload: PayloadOf<T>,\n ...rest: CreateEnvelopeArgs<T>\n): Extract<Envelope, { type: T }> {\n // `rest[0]`'s precise per-T type doesn't survive being read back out\n // inside a generic function body (a well-known TS limitation, not a\n // soundness gap: every concrete instantiation of `T` at the call site was\n // already checked against `CreateEnvelopeArgs<T>` above) — `opts` is\n // treated as the loose, common shape here, and the schema validation below\n // is what actually guards this function's output either way.\n const opts = (rest[0] ?? {}) as CreateEnvelopeOptions;\n const envelope = {\n v: opts.v ?? PROTOCOL_VERSION,\n id: opts.id ?? crypto.randomUUID(),\n ts: opts.ts ?? new Date().toISOString(),\n type,\n ...(opts.taskId !== undefined ? { task_id: opts.taskId } : {}),\n ...(opts.sessionRef !== undefined ? { session_ref: opts.sessionRef } : {}),\n ...(opts.seq !== undefined ? { seq: opts.seq } : {}),\n payload,\n };\n\n const result = EnvelopeSchema.safeParse(envelope);\n if (!result.success) {\n throw new EnvelopeValidationError(`createEnvelope built an invalid envelope for type \"${type}\"`, result.error);\n }\n return result.data as Extract<Envelope, { type: T }>;\n}\n","import { z } from 'zod';\nimport { CONTENT_HASH_RE } from './blob';\nimport { EnvelopeSchema } from './envelope';\n\n/**\n * HTTP-side request/response shapes for the reference server's auth and blob\n * endpoints (M1 Part B). These are plain HTTP bodies, not wire envelopes —\n * kept in a separate module from `envelope.ts`/`messages.ts` because they\n * never travel over the WSS connection. Documented in full in\n * docs/protocol.md (\"Auth flows\", \"Blob flows\", \"Long-poll fallback\").\n *\n * The wire protocol version (`v:1`) is unaffected by any of this: pairing,\n * token renewal, and blob transfer are out-of-band HTTP calls that happen\n * before/alongside the WSS connection, not envelope types.\n */\n\n// ---------------------------------------------------------------------------\n// POST /byok/pair (v2) — one-time device pairing. An out-of-band pairing\n// code (minted by the SaaS's own auth/device-flow UI) plus a freshly\n// generated Ed25519 device keypair (private key never leaves the device)\n// register the device and mint its first access token.\n// ---------------------------------------------------------------------------\n\nexport const PairRequestSchema = z.object({\n pairingCode: z.string(),\n deviceName: z.string(),\n /** Ed25519 public key, base64url-encoded. Private key stays device-local (OS keychain or 0600 file). */\n devicePublicKey: z.string(),\n});\nexport type PairRequest = z.infer<typeof PairRequestSchema>;\n\nexport const PairResponseSchema = z.object({\n deviceId: z.string(),\n /** JWT, ~1h lifetime. */\n accessToken: z.string(),\n /** Opaque hint for when/how to renew (e.g. an ISO timestamp); not itself a credential. */\n refreshHint: z.string().optional(),\n});\nexport type PairResponse = z.infer<typeof PairResponseSchema>;\n\n// ---------------------------------------------------------------------------\n// POST /byok/challenge + POST /byok/token — token renewal without re-pairing.\n// Two-step challenge/response proves possession of the device private key\n// without ever transmitting it: the server hands out a one-time nonce, the\n// client signs it locally with the device key, and trades the signature for\n// a fresh access token.\n// ---------------------------------------------------------------------------\n\nexport const ChallengeRequestSchema = z.object({\n deviceId: z.string(),\n});\nexport type ChallengeRequest = z.infer<typeof ChallengeRequestSchema>;\n\nexport const ChallengeResponseSchema = z.object({\n /** One-time value the client must sign with its device private key. */\n nonce: z.string(),\n});\nexport type ChallengeResponse = z.infer<typeof ChallengeResponseSchema>;\n\nexport const TokenRequestSchema = z.object({\n deviceId: z.string(),\n nonce: z.string(),\n /** Ed25519 signature over `nonce`, base64url-encoded. */\n signature: z.string(),\n});\nexport type TokenRequest = z.infer<typeof TokenRequestSchema>;\n\nexport const TokenResponseSchema = z.object({\n accessToken: z.string(),\n expiresAt: z.iso.datetime({ offset: true }),\n});\nexport type TokenResponse = z.infer<typeof TokenResponseSchema>;\n\n/**\n * Revocation is server-side only (dashboard/API call on the SaaS's own\n * device registry) — there is no wire message for it. A revoked device's\n * next `/byok/challenge` or `/byok/token` call (or WSS connect) gets a 401;\n * the daemon's only recourse is to re-run `/byok/pair` from scratch.\n */\n\n// ---------------------------------------------------------------------------\n// Blob endpoints — presigned upload/download. Authed (bearer access token).\n// `BlobRef` (`blob.ts`) is unchanged; these are the HTTP calls that produce\n// the presigned URLs a `BlobRef` points at.\n// ---------------------------------------------------------------------------\n\n/** POST /byok/blobs request: declare a blob before uploading it. `contentHash` must be the canonical `sha256:<64 lowercase hex>` form (finding F9) — the server rejects anything else outright, no normalization. */\nexport const CreateBlobRequestSchema = z.object({\n size: z.number().int().nonnegative(),\n contentType: z.string(),\n contentHash: z.string().regex(CONTENT_HASH_RE, 'contentHash must be \"sha256:<64 lowercase hex>\"'),\n});\nexport type CreateBlobRequest = z.infer<typeof CreateBlobRequestSchema>;\n\n/** POST /byok/blobs response: presigned PUT target for the declared blob. */\nexport const CreateBlobResponseSchema = z.object({\n blobId: z.string(),\n uploadUrl: z.string(),\n});\nexport type CreateBlobResponse = z.infer<typeof CreateBlobResponseSchema>;\n\n/** GET /byok/blobs/:id/url response: presigned GET target for an existing blob. */\nexport const BlobDownloadUrlResponseSchema = z.object({\n downloadUrl: z.string(),\n});\nexport type BlobDownloadUrlResponse = z.infer<typeof BlobDownloadUrlResponseSchema>;\n\n// ---------------------------------------------------------------------------\n// GET /byok/events?cursor=N — long-poll fallback for environments where WSS\n// is unavailable. Authed; holds the request open ~50s waiting for new\n// events, same at-least-once/cursor semantics as the WSS redelivery path\n// (see docs/protocol.md \"At-least-once delivery\").\n// ---------------------------------------------------------------------------\n\nexport const EventsPollQuerySchema = z.object({\n /** Last `seq` this client has seen; omitted on a client's first-ever poll. Never negative — `seq` is a monotonically increasing counter starting at 1. */\n cursor: z.number().int().nonnegative().optional(),\n});\nexport type EventsPollQuery = z.infer<typeof EventsPollQuerySchema>;\n\nexport const EventsPollResponseSchema = z.object({\n events: z.array(EnvelopeSchema),\n cursor: z.number().int(),\n /**\n * Server capabilities for THIS long-poll response. This is the HTTP\n * transport's equivalent of `conn.ack.capabilities`: a new daemon treats\n * absence as no advertised capabilities, so old responders remain\n * fail-closed for gated daemon -> server fields and messages.\n */\n capabilities: z.array(z.string()).optional(),\n});\nexport type EventsPollResponse = z.infer<typeof EventsPollResponseSchema>;\n\n// ---------------------------------------------------------------------------\n// POST /byok/messages — finding F6: long-poll is now a full transport, not\n// receive-only. While a device is long-polling for S->D traffic (§8), it has\n// no live WS to carry its own D->S envelopes (task.claim, task.progress,\n// task.complete, etc.) — this endpoint is that path: a batch of envelopes,\n// authed the same way as every other bearer-authed route, routed through the\n// identical inbound handling a WS connection's messages get. See\n// docs/protocol.md §8.\n// ---------------------------------------------------------------------------\n\n/**\n * Batch size ceiling for a single `POST /byok/messages` call — generous for\n * normal redelivery-catchup bursts, but bounded so one request can't force\n * the server to process an unbounded batch. Exported (not just a local\n * const) so the client's own outbound drain (`ConnectionManager.drainOutbox`,\n * finding P1) can chunk against the exact same number instead of a\n * hard-coded, driftable copy of it.\n */\nexport const MAX_MESSAGES_PER_BATCH = 256;\n\nexport const MessagesSendRequestSchema = z.object({\n messages: z.array(EnvelopeSchema).max(MAX_MESSAGES_PER_BATCH),\n});\nexport type MessagesSendRequest = z.infer<typeof MessagesSendRequestSchema>;\n\n/**\n * `accepted` counts every envelope `ConnectionHub.handleInbound` returned\n * `'accepted'` *or* `'duplicate'` for (finding P2) — a dedup'd replay is a\n * wire-level success (§9's idempotency window), even though no handler ran\n * for it a second time. `rejected` (a type outside `DAEMON_TO_SERVER_TYPES`,\n * or an ownership mismatch — N2) is a separate, additive count: omitted\n * entirely when zero, so a batch with nothing rejected keeps the pre-P2\n * `{ accepted }` shape callers already depend on.\n */\nexport const MessagesSendResponseSchema = z.object({\n accepted: z.number().int().nonnegative(),\n rejected: z.number().int().nonnegative().optional(),\n});\nexport type MessagesSendResponse = z.infer<typeof MessagesSendResponseSchema>;\n\n// ---------------------------------------------------------------------------\n// Route paths — the single source of truth for the `/byok/*` HTTP surface.\n//\n// These literals were previously hand-copied across client, cloud, server, and\n// testkit; the doc comments above already named every one of them but exported\n// no constant. They ARE the wire contract (unlike the host-owned, opaque\n// capability vocabulary of ADR-010, which stays out of protocol), so they live\n// here and every package imports them — a route change is now one edit.\n//\n// Two shapes:\n// - Static paths (`BYOK_*_PATH`) — used identically by routers and clients.\n// - Parameterized routes: a router template (`BYOK_*_ROUTE`, with `:param`\n// placeholders) for mounting, plus a builder (`byok*Path(...)`) that fills\n// the params for a client request. Each builder reproduces its call site\n// byte-for-byte, including whether a segment is `encodeURIComponent`-encoded.\n// ---------------------------------------------------------------------------\n\n/** `GET /byok/ws` — WebSocket upgrade path. */\nexport const BYOK_WS_PATH = '/byok/ws';\n\n/** `POST /byok/pair` — one-time device pairing (§6). */\nexport const BYOK_PAIR_PATH = '/byok/pair';\n/** `POST /byok/challenge` — token-renewal challenge (§6.3). */\nexport const BYOK_CHALLENGE_PATH = '/byok/challenge';\n/** `POST /byok/token` — token-renewal exchange (§6.3). */\nexport const BYOK_TOKEN_PATH = '/byok/token';\n\n/** `GET /byok/capabilities` — ADR-010 declaration route. */\nexport const BYOK_CAPABILITIES_PATH = '/byok/capabilities';\n\n/** `GET /byok/events` — long-poll receive (§8). */\nexport const BYOK_EVENTS_PATH = '/byok/events';\n/** `POST /byok/messages` — long-poll batched send (§8.2). */\nexport const BYOK_MESSAGES_PATH = '/byok/messages';\n\n/** `PUT /byok/presence` — presence heartbeat. */\nexport const BYOK_PRESENCE_PATH = '/byok/presence';\n/** `POST /byok/activity` — activity-tail append. */\nexport const BYOK_ACTIVITY_PATH = '/byok/activity';\n\n/** `GET /byok/board` — coordination board list/poll. */\nexport const BYOK_BOARD_PATH = '/byok/board';\n/** `GET /byok/board/stream` — coordination board SSE. */\nexport const BYOK_BOARD_STREAM_PATH = '/byok/board/stream';\n/** Router template — `POST /byok/board/:id/claim`. */\nexport const BYOK_BOARD_CLAIM_ROUTE = '/byok/board/:id/claim';\n/** Router template — `POST /byok/board/:id/unclaim`. */\nexport const BYOK_BOARD_UNCLAIM_ROUTE = '/byok/board/:id/unclaim';\n/** Router template — `POST /byok/board/:id/status`. */\nexport const BYOK_BOARD_STATUS_ROUTE = '/byok/board/:id/status';\n\n/** `GET /byok/records` — truth manifest list (§12.3). */\nexport const BYOK_RECORDS_PATH = '/byok/records';\n/** Router template for a single truth record — `GET`/`PUT /byok/records/:kind/:key`. */\nexport const BYOK_RECORD_ROUTE = '/byok/records/:kind/:key';\n/** Client builder for a truth record path. Mirrors the `:kind/:key` template, each segment URL-encoded. */\nexport function byokRecordPath(kind: string, key: string): string {\n return `/byok/records/${encodeURIComponent(kind)}/${encodeURIComponent(key)}`;\n}\n\n/** `GET /byok/skill-packs` — skill-pack manifest catalogue. */\nexport const BYOK_SKILL_PACKS_PATH = '/byok/skill-packs';\n/** Router template for a skill-pack file — `GET /byok/skill-packs/:name/files/:path`. */\nexport const BYOK_SKILL_PACK_FILE_ROUTE = '/byok/skill-packs/:name/files/:path';\n/** Client builder for a skill-pack file path. Mirrors the `:name`/`:path` template, each segment URL-encoded. */\nexport function byokSkillPackFilePath(name: string, path: string): string {\n return `/byok/skill-packs/${encodeURIComponent(name)}/files/${encodeURIComponent(path)}`;\n}\n\n/** `POST /byok/blobs` — declare a blob upload (§7). */\nexport const BYOK_BLOBS_PATH = '/byok/blobs';\n/** Router template — `POST /byok/blobs/:id/finalize`. */\nexport const BYOK_BLOB_FINALIZE_ROUTE = '/byok/blobs/:id/finalize';\n/** Router template — `GET /byok/blobs/:id/url`. */\nexport const BYOK_BLOB_URL_ROUTE = '/byok/blobs/:id/url';\n/** Router template for the two presigned byte routes — `PUT`/`GET /byok/blobs/:id/content`. */\nexport const BYOK_BLOB_CONTENT_ROUTE = '/byok/blobs/:id/content';\n/** Client builder — `POST /byok/blobs/:id/finalize`, blob id URL-encoded (client-supplied). */\nexport function byokBlobFinalizePath(blobId: string): string {\n return `/byok/blobs/${encodeURIComponent(blobId)}/finalize`;\n}\n/** Client builder — `GET /byok/blobs/:id/url`, blob id URL-encoded (client-supplied). */\nexport function byokBlobUrlPath(blobId: string): string {\n return `/byok/blobs/${encodeURIComponent(blobId)}/url`;\n}\n/**\n * Path portion of a presigned `/byok/blobs/:id/content` URL. The blob id here\n * is a server-minted token (NOT URL-encoded, matching the reference stores that\n * mint these signed URLs); callers append the `?sig=&exp=` query themselves.\n */\nexport function byokBlobContentPath(blobId: string): string {\n return `/byok/blobs/${blobId}/content`;\n}\n"]}
@@ -60,6 +60,21 @@ export declare const RuntimeInfoSchema: z.ZodObject<{
60
60
  }, z.core.$strip>>;
61
61
  }, z.core.$strip>;
62
62
  export type RuntimeInfo = z.infer<typeof RuntimeInfoSchema>;
63
+ /** Maximum logical toolsets one daemon may advertise as locally configured. */
64
+ export declare const CONFIGURED_TOOLSETS_MAX_ITEMS = 64;
65
+ /**
66
+ * Logical, host-owned MCP toolset identifier. A task may request this name,
67
+ * but the executable/server definition behind it exists only in the daemon's
68
+ * local configuration and never crosses the SaaS wire.
69
+ */
70
+ export declare const ToolsetIdSchema: z.ZodString;
71
+ export type ToolsetId = z.infer<typeof ToolsetIdSchema>;
72
+ /**
73
+ * Device-local inventory projected for discovery. IDs only: executable MCP
74
+ * definitions, arguments, environment and credentials never enter this shape.
75
+ * Empty means known-none; omission on the containing message means unknown.
76
+ */
77
+ export declare const ConfiguredToolsetsSchema: z.ZodArray<z.ZodString>;
63
78
  /** daemon -> server: opening handshake. */
64
79
  export declare const ConnHelloPayloadSchema: z.ZodObject<{
65
80
  protocolVersions: z.ZodArray<z.ZodNumber>;
@@ -82,6 +97,7 @@ export declare const ConnHelloPayloadSchema: z.ZodObject<{
82
97
  permissionModes: z.ZodOptional<z.ZodArray<z.ZodString>>;
83
98
  }, z.core.$strip>>;
84
99
  }, z.core.$strip>>>;
100
+ configuredToolsets: z.ZodOptional<z.ZodArray<z.ZodString>>;
85
101
  cursor: z.ZodOptional<z.ZodNumber>;
86
102
  }, z.core.$strip>;
87
103
  export type ConnHelloPayload = z.infer<typeof ConnHelloPayloadSchema>;
@@ -169,13 +185,6 @@ export declare const TaskOfferPayloadSchema: z.ZodObject<{
169
185
  }, z.core.$strip>>;
170
186
  }, z.core.$strip>;
171
187
  export type TaskOfferPayload = z.infer<typeof TaskOfferPayloadSchema>;
172
- /**
173
- * Logical, host-owned MCP toolset identifier. A task may request this name,
174
- * but the executable/server definition behind it exists only in the daemon's
175
- * local configuration and never crosses the SaaS wire.
176
- */
177
- export declare const ToolsetIdSchema: z.ZodString;
178
- export type ToolsetId = z.infer<typeof ToolsetIdSchema>;
179
188
  /** Every named toolset is required; duplicates are rejected instead of silently de-duplicated. */
180
189
  export declare const RequiredToolsetsSchema: z.ZodArray<z.ZodString>;
181
190
  /**
@@ -694,6 +703,7 @@ export declare const MESSAGE_PAYLOAD_SCHEMAS: {
694
703
  permissionModes: z.ZodOptional<z.ZodArray<z.ZodString>>;
695
704
  }, z.core.$strip>>;
696
705
  }, z.core.$strip>>>;
706
+ configuredToolsets: z.ZodOptional<z.ZodArray<z.ZodString>>;
697
707
  cursor: z.ZodOptional<z.ZodNumber>;
698
708
  }, z.core.$strip>;
699
709
  readonly 'conn.ack': z.ZodObject<{
package/dist/version.d.ts CHANGED
@@ -73,7 +73,8 @@ export declare const PROTOCOL_VERSION = 1;
73
73
  * `approval-targeting`.
74
74
  *
75
75
  * This is the N/N-1 answer for that new daemon -> server FIELD. An old
76
- * server's `CAPABILITY_FLAGS`/`conn.ack.capabilities` never includes it, and
76
+ * server's transport advertisement (`conn.ack.capabilities` on WS or
77
+ * `EventsPollResponse.capabilities` on long-poll) never includes it, and
77
78
  * its `TaskCompletePayloadSchema` is a tolerant (non-`.strict()`)
78
79
  * `z.object()`, so a `document` sent to it would be silently STRIPPED on
79
80
  * parse and vanish without a trace. That is exactly why emission is gated
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@byok-sdk/protocol",
3
- "version": "0.3.0",
3
+ "version": "0.4.1",
4
4
  "description": "BYOK SDK wire protocol: envelope schema, message types, and codec helpers",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -14,7 +14,7 @@
14
14
  },
15
15
  "homepage": "https://github.com/Ancienttwo/byok-sdk#readme",
16
16
  "engines": {
17
- "node": ">=22.19.0"
17
+ "node": ">=22.22.0"
18
18
  },
19
19
  "sideEffects": false,
20
20
  "main": "./dist/index.js",
@@ -35,9 +35,6 @@
35
35
  "publishConfig": {
36
36
  "access": "public"
37
37
  },
38
- "dependencies": {
39
- "zod": "^4.4.3"
40
- },
41
38
  "scripts": {
42
39
  "build": "tsup && tsc -p tsconfig.build.json",
43
40
  "dev": "tsup --watch",
@@ -45,5 +42,8 @@
45
42
  "test:watch": "vitest",
46
43
  "typecheck": "tsc --noEmit",
47
44
  "clean": "rm -rf dist"
45
+ },
46
+ "dependencies": {
47
+ "zod": "^4.4.3"
48
48
  }
49
- }
49
+ }