@openclaw/gateway-protocol 2026.8.1-beta.2 → 2026.8.1-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -14,6 +14,7 @@ version and the additive schema surface. Dates are authoring dates (2026).
14
14
  - Slim worker and session-catalog payloads to the active wire contract.
15
15
  - Remove dead protocol surfaces and add since-vintage metadata to retained schemas and methods.
16
16
  - Add optional `step` on `SystemAgentChatResult` carrying the full awaited wizard step.
17
+ - Project plugin tab `placement` in `hello-ok` so active plugins can target native Control UI routes.
17
18
 
18
19
  ## Protocol v4 (current)
19
20
 
@@ -1,4 +1,4 @@
1
- import { i as normalizeOptionalProtocolString } from "./protocol-value-normalization-Bexl3yr1.mjs";
1
+ import { n as normalizeOptionalProtocolString } from "./protocol-value-normalization-DcAoxLUs.mjs";
2
2
  //#region src/client-info.ts
3
3
  /**
4
4
  * Shared gateway client identity contract.
@@ -17,6 +17,7 @@ declare const ConnectErrorDetailCodes: {
17
17
  readonly AUTH_TAILSCALE_PROXY_MISSING: "AUTH_TAILSCALE_PROXY_MISSING";
18
18
  readonly AUTH_TAILSCALE_WHOIS_FAILED: "AUTH_TAILSCALE_WHOIS_FAILED";
19
19
  readonly AUTH_TAILSCALE_IDENTITY_MISMATCH: "AUTH_TAILSCALE_IDENTITY_MISMATCH";
20
+ readonly AUTH_IDENTITY_HEADER_REQUIRED: "AUTH_IDENTITY_HEADER_REQUIRED";
20
21
  readonly CONTROL_UI_BUILD_MISMATCH: "CONTROL_UI_BUILD_MISMATCH";
21
22
  readonly CONTROL_UI_ORIGIN_NOT_ALLOWED: "CONTROL_UI_ORIGIN_NOT_ALLOWED";
22
23
  readonly PROTOCOL_MISMATCH: "PROTOCOL_MISMATCH";
@@ -115,6 +116,10 @@ declare function classifyGatewayConnectFailure(input: {
115
116
  kind: "pairing-required";
116
117
  userMessage: string;
117
118
  remediation: string;
119
+ } | {
120
+ kind: "identity-proxy";
121
+ userMessage: string;
122
+ remediation: string;
118
123
  } | {
119
124
  remediation?: string | undefined;
120
125
  kind: "device-identity-required" | "scope-mismatch" | "rate-limited" | "auth-rejected" | "gateway-rejected" | "unreachable";
@@ -1,4 +1,5 @@
1
- import { i as normalizeOptionalProtocolString } from "./protocol-value-normalization-Bexl3yr1.mjs";
1
+ import { n as isRecord } from "./record-coerce-BKHrZ46I.mjs";
2
+ import { n as normalizeOptionalProtocolString } from "./protocol-value-normalization-DcAoxLUs.mjs";
2
3
  //#region src/connect-error-details.ts
3
4
  /**
4
5
  * Shared gateway connect-error detail helpers.
@@ -29,6 +30,7 @@ const ConnectErrorDetailCodes = {
29
30
  AUTH_TAILSCALE_PROXY_MISSING: "AUTH_TAILSCALE_PROXY_MISSING",
30
31
  AUTH_TAILSCALE_WHOIS_FAILED: "AUTH_TAILSCALE_WHOIS_FAILED",
31
32
  AUTH_TAILSCALE_IDENTITY_MISMATCH: "AUTH_TAILSCALE_IDENTITY_MISMATCH",
33
+ AUTH_IDENTITY_HEADER_REQUIRED: "AUTH_IDENTITY_HEADER_REQUIRED",
32
34
  CONTROL_UI_BUILD_MISMATCH: "CONTROL_UI_BUILD_MISMATCH",
33
35
  CONTROL_UI_ORIGIN_NOT_ALLOWED: "CONTROL_UI_ORIGIN_NOT_ALLOWED",
34
36
  PROTOCOL_MISMATCH: "PROTOCOL_MISMATCH",
@@ -95,6 +97,7 @@ const CONNECT_PAIRING_REQUIRED_MESSAGE_BY_REASON = {
95
97
  };
96
98
  /** Maps internal auth failure reasons to public connect-error detail codes. */
97
99
  function resolveAuthConnectErrorDetailCode(reason) {
100
+ if (reason?.startsWith("trusted_proxy_missing_header_")) return ConnectErrorDetailCodes.AUTH_IDENTITY_HEADER_REQUIRED;
98
101
  switch (reason) {
99
102
  case "token_missing": return ConnectErrorDetailCodes.AUTH_TOKEN_MISSING;
100
103
  case "token_mismatch": return ConnectErrorDetailCodes.AUTH_TOKEN_MISMATCH;
@@ -128,13 +131,14 @@ function resolveDeviceAuthConnectErrorDetailCode(reason) {
128
131
  }
129
132
  /** Reads a non-empty detail code from an untrusted error details payload. */
130
133
  function readConnectErrorDetailCode(details) {
131
- if (!details || typeof details !== "object" || Array.isArray(details)) return null;
134
+ if (!isRecord(details)) return null;
132
135
  const code = details.code;
133
136
  return typeof code === "string" && code.trim().length > 0 ? code.trim() : null;
134
137
  }
135
138
  /** Read the exact target artifact from an untrusted reload-required rejection. */
136
139
  function readControlUiBuildMismatchId(details) {
137
- if (readConnectErrorDetailCode(details) !== ConnectErrorDetailCodes.CONTROL_UI_BUILD_MISMATCH) return null;
140
+ const code = readConnectErrorDetailCode(details);
141
+ if (code !== ConnectErrorDetailCodes.PROTOCOL_MISMATCH && code !== ConnectErrorDetailCodes.CONTROL_UI_BUILD_MISMATCH) return null;
138
142
  const raw = details;
139
143
  const gatewayBuildId = normalizeOptionalProtocolString(raw.gatewayBuildId);
140
144
  if (!gatewayBuildId || gatewayBuildId.length > 96 || raw.reloadRequired !== true) return null;
@@ -142,7 +146,7 @@ function readControlUiBuildMismatchId(details) {
142
146
  }
143
147
  /** Extracts normalized retry advice from untrusted connect-error details. */
144
148
  function readConnectErrorRecoveryAdvice(details) {
145
- if (!details || typeof details !== "object" || Array.isArray(details)) return {};
149
+ if (!isRecord(details)) return {};
146
150
  const raw = details;
147
151
  const canRetryWithDeviceToken = typeof raw.canRetryWithDeviceToken === "boolean" ? raw.canRetryWithDeviceToken : void 0;
148
152
  const normalizedNextStep = normalizeOptionalProtocolString(raw.recommendedNextStep) ?? "";
@@ -226,7 +230,7 @@ function buildPairingConnectCloseReason(params) {
226
230
  /** Reads and backfills pairing-required details from an untrusted details object. */
227
231
  function readPairingConnectErrorDetails(details) {
228
232
  if (readConnectErrorDetailCode(details) !== ConnectErrorDetailCodes.PAIRING_REQUIRED) return null;
229
- if (!details || typeof details !== "object" || Array.isArray(details)) return null;
233
+ if (!isRecord(details)) return null;
230
234
  const raw = details;
231
235
  const reason = normalizePairingConnectReason(raw.reason);
232
236
  const requestId = normalizePairingConnectRequestId(raw.requestId);
@@ -275,7 +279,29 @@ const DEVICE_TOKEN_REMEDIATION = "Rotate the paired-device token with `openclaw
275
279
  const SHARED_TOKEN_REMEDIATION = "Verify `gateway.remote.token` matches `gateway.auth.token`. If a paired-device token is stale, rotate it with `openclaw devices rotate --device <deviceId> --role operator`, then reconnect.";
276
280
  const SCOPE_MISMATCH_REMEDIATION = "Review approved scopes with `openclaw devices list`; if an upgrade is pending, preview it with `openclaw devices approve --latest`, approve the printed request, then reconnect.";
277
281
  const RATE_LIMITED_REMEDIATION = "Wait for the temporary authentication lockout to expire, then retry.";
282
+ const IDENTITY_PROXY_REMEDIATION = "An identity-aware proxy rejected the WebSocket upgrade. Configure gateway.remote.edgeAuth for the configured Gateway origin, then reconnect. See https://docs.openclaw.ai/gateway/remote#gateway-behind-an-identity-aware-proxy.";
283
+ const CLOUDFLARE_ACCESS_REMEDIATION = "Cloudflare Access detected: configure its token header or service-token headers in gateway.remote.edgeAuth.";
284
+ const IDENTITY_PROXY_HTTP_STATUSES = /* @__PURE__ */ new Set([
285
+ 301,
286
+ 302,
287
+ 303,
288
+ 307,
289
+ 308,
290
+ 401,
291
+ 403
292
+ ]);
278
293
  const GATEWAY_CLOSED_MESSAGE_PATTERN = /\bgateway closed \(\d+\):/i;
294
+ function readIdentityProxyRejection(details) {
295
+ if (!isRecord(details)) return null;
296
+ if (details.reason !== "websocket-upgrade-rejected" || typeof details.httpStatus !== "number" || !IDENTITY_PROXY_HTTP_STATUSES.has(details.httpStatus)) return null;
297
+ const location = normalizeOptionalProtocolString(details.location);
298
+ if (!location) return { cloudflareAccess: false };
299
+ try {
300
+ return { cloudflareAccess: new URL(location).hostname.toLowerCase().replace(/\.+$/u, "").endsWith(".cloudflareaccess.com") };
301
+ } catch {
302
+ return { cloudflareAccess: false };
303
+ }
304
+ }
279
305
  /** Classifies Gateway connect failures from structured details, with one legacy text fallback. */
280
306
  function classifyGatewayConnectFailure(input) {
281
307
  const code = readConnectErrorDetailCode(input.details);
@@ -290,6 +316,12 @@ function classifyGatewayConnectFailure(input) {
290
316
  userMessage: code === ConnectErrorDetailCodes.PAIRING_REQUIRED ? formatConnectPairingRequiredMessage(input.details) : userMessage ?? "device pairing required",
291
317
  remediation: PAIRING_APPROVAL_REMEDIATION
292
318
  };
319
+ const identityProxy = readIdentityProxyRejection(input.details);
320
+ if (identityProxy) return {
321
+ kind: "identity-proxy",
322
+ userMessage: userMessage ?? "identity-aware proxy rejected websocket upgrade",
323
+ remediation: identityProxy.cloudflareAccess ? `${IDENTITY_PROXY_REMEDIATION}\n${CLOUDFLARE_ACCESS_REMEDIATION}` : IDENTITY_PROXY_REMEDIATION
324
+ };
293
325
  const deviceIdentityRequired = code === ConnectErrorDetailCodes.DEVICE_IDENTITY_REQUIRED || code === ConnectErrorDetailCodes.CONTROL_UI_DEVICE_IDENTITY_REQUIRED || normalized.includes("device identity required");
294
326
  const scopeMismatch = code === ConnectErrorDetailCodes.AUTH_SCOPE_MISMATCH || normalized.includes("scope mismatch");
295
327
  const rateLimited = code === ConnectErrorDetailCodes.AUTH_RATE_LIMITED || !code && normalized.includes("too many failed authentication attempts");
@@ -1,4 +1,4 @@
1
- import { a as EventFrame, c as GatewayFrame, f as RequestFrame, m as ResponseFrame, r as ErrorShape, t as ConnectParams, u as HelloOk } from "./frames-B9De7i2H.mjs";
1
+ import { a as EventFrame, c as GatewayFrame, f as RequestFrame, m as ResponseFrame, r as ErrorShape, t as ConnectParams, u as HelloOk } from "./frames-BKvowx_k.mjs";
2
2
 
3
3
  //#region src/frame-guards.d.ts
4
4
  declare function isGatewayEventFrame(value: unknown): value is EventFrame;
@@ -1,20 +1,21 @@
1
- import { n as isNonEmptyProtocolString, r as isProtocolRecord } from "./protocol-value-normalization-Bexl3yr1.mjs";
1
+ import { n as isRecord } from "./record-coerce-BKHrZ46I.mjs";
2
+ import { t as isNonEmptyProtocolString } from "./protocol-value-normalization-DcAoxLUs.mjs";
2
3
  //#region src/frame-guards.ts
3
4
  function isNonNegativeInteger(value) {
4
5
  return typeof value === "number" && Number.isInteger(value) && value >= 0;
5
6
  }
6
7
  function isGatewayErrorShape(value) {
7
- if (!isProtocolRecord(value)) return false;
8
+ if (!isRecord(value)) return false;
8
9
  if (!isNonEmptyProtocolString(value.code) || !isNonEmptyProtocolString(value.message)) return false;
9
10
  if (value.retryable !== void 0 && typeof value.retryable !== "boolean") return false;
10
11
  return value.retryAfterMs === void 0 || isNonNegativeInteger(value.retryAfterMs);
11
12
  }
12
13
  function isGatewayEventFrame(value) {
13
- if (!isProtocolRecord(value) || value.type !== "event" || !isNonEmptyProtocolString(value.event)) return false;
14
+ if (!isRecord(value) || value.type !== "event" || !isNonEmptyProtocolString(value.event)) return false;
14
15
  return value.seq === void 0 || isNonNegativeInteger(value.seq);
15
16
  }
16
17
  function isGatewayResponseFrame(value) {
17
- if (!isProtocolRecord(value) || value.type !== "res" || !isNonEmptyProtocolString(value.id) || typeof value.ok !== "boolean") return false;
18
+ if (!isRecord(value) || value.type !== "res" || !isNonEmptyProtocolString(value.id) || typeof value.ok !== "boolean") return false;
18
19
  return value.error === void 0 || isGatewayErrorShape(value.error);
19
20
  }
20
21
  //#endregion
@@ -4,6 +4,9 @@ import { Static, Type } from "typebox";
4
4
  declare const GATEWAY_SERVER_CAPS: {
5
5
  readonly BOARD_WIDGET_PUT_CANVAS_DOC: "board-widget-put-canvas-doc";
6
6
  readonly CHAT_SEND_ROUTING_CONTRACT: "chat-send-routing-contract";
7
+ readonly GATEWAY_RESTART_TARGET_SAFE: "gateway-restart-target-safe-v1";
8
+ readonly NODE_WORKER_BUNDLE_RETENTION: "node-worker-bundle-retention-v1";
9
+ readonly NODE_WORKER_BUNDLE_STATUS: "node-worker-bundle-status-v1";
7
10
  readonly SYSTEM_AGENT_WIZARD_CANCEL: "openclaw-chat-wizard-cancel";
8
11
  readonly SYSTEM_AGENT_SETUP_MODEL_REF: "openclaw-setup-model-ref";
9
12
  readonly TASK_SUGGESTIONS_ACCEPT_MODES: "taskSuggestions.acceptModes";
@@ -40,11 +43,12 @@ declare const ConnectParamsSchema: Type.TObject<{
40
43
  }>;
41
44
  caps: Type.TOptional<Type.TArray<Type.TString>>;
42
45
  commands: Type.TOptional<Type.TArray<Type.TString>>; /** Additive Computer Use declaration; the owning core contract validates its bounded shape. */
43
- computerUse: Type.TOptional<Type.TUnknown>; /** Additive node-local worker build identity; presence advertises session hosting. */
46
+ computerUse: Type.TOptional<Type.TUnknown>; /** @deprecated Accepted for the shipped v1 node-host envelope; current hosts use runner inventory. */
44
47
  workerRuns: Type.TOptional<Type.TObject<{
45
48
  bundleHash: Type.TString;
46
49
  openclawVersion: Type.TString;
47
50
  protocolFeatures: Type.TArray<Type.TString>;
51
+ bundlePrewarm: Type.TOptional<Type.TInteger>;
48
52
  }>>;
49
53
  permissions: Type.TOptional<Type.TRecord<"^.*$", Type.TBoolean>>;
50
54
  pathEnv: Type.TOptional<Type.TString>;
@@ -238,6 +242,7 @@ declare const HelloOkSchema: Type.TObject<{
238
242
  stateDir: Type.TOptional<Type.TString>;
239
243
  sessionDefaults: Type.TOptional<Type.TObject<{
240
244
  defaultAgentId: Type.TString;
245
+ modelConfigured: Type.TOptional<Type.TBoolean>;
241
246
  ownership: Type.TOptional<Type.TUnion<[Type.TLiteral<"sole">, Type.TLiteral<"legacy">, Type.TLiteral<"explicit">]>>;
242
247
  selectionRequired: Type.TOptional<Type.TBoolean>;
243
248
  mainKey: Type.TString;
@@ -322,6 +327,7 @@ declare const HelloOkSchema: Type.TObject<{
322
327
  description: Type.TOptional<Type.TString>;
323
328
  icon: Type.TOptional<Type.TString>;
324
329
  path: Type.TOptional<Type.TString>;
330
+ placement: Type.TOptional<Type.TString>;
325
331
  requiresGatewayAuth: Type.TOptional<Type.TBoolean>;
326
332
  group: Type.TOptional<Type.TUnion<[Type.TLiteral<"control">, Type.TLiteral<"agent">]>>;
327
333
  order: Type.TOptional<Type.TNumber>;
@@ -332,9 +338,6 @@ declare const HelloOkSchema: Type.TObject<{
332
338
  label: Type.TString;
333
339
  }>>>;
334
340
  pluginSurfaceUrls: Type.TOptional<Type.TRecord<"^.*$", Type.TString>>;
335
- deviceAuthMigration: Type.TOptional<Type.TObject<{
336
- pending: Type.TLiteral<true>;
337
- }>>;
338
341
  auth: Type.TObject<{
339
342
  deviceToken: Type.TOptional<Type.TString>;
340
343
  recoveryMigrationAllowed: Type.TOptional<Type.TLiteral<true>>;
@@ -13,14 +13,22 @@ declare const ErrorCodes: {
13
13
  type ErrorCode = (typeof ErrorCodes)[keyof typeof ErrorCodes];
14
14
  /** Stable discriminants for structured method-level failures. */
15
15
  declare const GatewayErrorDetailCodes: {
16
+ readonly CRON_JOB_NOT_FOUND: "CRON_JOB_NOT_FOUND";
16
17
  readonly MISSING_SCOPE: "MISSING_SCOPE";
17
18
  readonly MCP_APP_VIEW_EXPIRED: "MCP_APP_VIEW_EXPIRED";
19
+ readonly OUTBOUND_DELIVERY_QUEUED: "OUTBOUND_DELIVERY_QUEUED";
18
20
  readonly USER_PREFS_LIMIT_EXCEEDED: "USER_PREFS_LIMIT_EXCEEDED";
19
21
  readonly SESSION_COMPANION_BUSY: "SESSION_COMPANION_BUSY";
22
+ readonly SKILL_PROPOSAL_REVISION_CHANGED: "SKILL_PROPOSAL_REVISION_CHANGED";
20
23
  readonly PROJECT_CLONE_FAILED: "PROJECT_CLONE_FAILED";
21
24
  readonly UNKNOWN_AGENT_ID: "UNKNOWN_AGENT_ID";
22
25
  readonly WIZARD_NOT_FOUND: "WIZARD_NOT_FOUND";
23
26
  };
27
+ /** Missing cron automation identified by its exact store key. */
28
+ type CronJobNotFoundErrorDetails = {
29
+ code: typeof GatewayErrorDetailCodes.CRON_JOB_NOT_FOUND;
30
+ jobId: string;
31
+ };
24
32
  /** Missing operator-scope details shared by WebSocket and HTTP responses. */
25
33
  type MissingScopeErrorDetails = {
26
34
  code: typeof GatewayErrorDetailCodes.MISSING_SCOPE;
@@ -30,6 +38,9 @@ type MissingScopeErrorDetails = {
30
38
  type McpAppViewExpiredErrorDetails = {
31
39
  code: typeof GatewayErrorDetailCodes.MCP_APP_VIEW_EXPIRED;
32
40
  };
41
+ type OutboundDeliveryQueuedErrorDetails = {
42
+ code: typeof GatewayErrorDetailCodes.OUTBOUND_DELIVERY_QUEUED;
43
+ };
33
44
  /** Per-profile preference quota details returned by users.prefs.set. */
34
45
  type UserPrefsLimitExceededErrorDetails = {
35
46
  code: typeof GatewayErrorDetailCodes.USER_PREFS_LIMIT_EXCEEDED;
@@ -50,8 +61,23 @@ type ProjectCloneErrorDetails = {
50
61
  code: typeof GatewayErrorDetailCodes.PROJECT_CLONE_FAILED;
51
62
  cause: ProjectCloneFailureCause;
52
63
  };
64
+ /** Optimistic-concurrency mismatch for an operator-reviewed Skill Workshop draft. */
65
+ type SkillProposalRevisionChangedErrorDetails = {
66
+ code: typeof GatewayErrorDetailCodes.SKILL_PROPOSAL_REVISION_CHANGED;
67
+ expectedRevisionHash: string;
68
+ currentRevisionHash: string;
69
+ };
53
70
  /** Structured details emitted by method-level failures. */
54
- type GatewayErrorDetails = MissingScopeErrorDetails | McpAppViewExpiredErrorDetails | UserPrefsLimitExceededErrorDetails | ProjectCloneErrorDetails | UnknownAgentIdErrorDetails | WizardNotFoundErrorDetails;
71
+ type GatewayErrorDetails = CronJobNotFoundErrorDetails | MissingScopeErrorDetails | McpAppViewExpiredErrorDetails | OutboundDeliveryQueuedErrorDetails | UserPrefsLimitExceededErrorDetails | SkillProposalRevisionChangedErrorDetails | ProjectCloneErrorDetails | UnknownAgentIdErrorDetails | WizardNotFoundErrorDetails;
72
+ /** Reads a typed cron lookup miss without parsing operator-facing prose. */
73
+ declare function readCronJobNotFoundError(error: unknown): CronJobNotFoundErrorDetails | null;
74
+ /** Builds the canonical stale-draft details shared by Skill Workshop RPCs. */
75
+ declare function buildSkillProposalRevisionChangedErrorDetails(params: {
76
+ expectedRevisionHash: string;
77
+ currentRevisionHash: string;
78
+ }): SkillProposalRevisionChangedErrorDetails;
79
+ /** Reads a stale Skill Workshop decision without parsing operator-facing prose. */
80
+ declare function readSkillProposalRevisionChangedError(error: unknown): SkillProposalRevisionChangedErrorDetails | null;
55
81
  /** Reads validated missing-scope details from an untrusted protocol payload. */
56
82
  declare function readMissingScopeErrorDetails(details: unknown): MissingScopeErrorDetails | null;
57
83
  declare function isMcpAppViewExpiredError(error: unknown): boolean;
@@ -61,4 +87,4 @@ declare function isMcpAppViewExpiredError(error: unknown): boolean;
61
87
  */
62
88
  declare function readMissingScopeError(error: unknown): MissingScopeErrorDetails | null;
63
89
  //#endregion
64
- export { McpAppViewExpiredErrorDetails as a, ProjectCloneFailureCause as c, WizardNotFoundErrorDetails as d, isMcpAppViewExpiredError as f, GatewayErrorDetails as i, UnknownAgentIdErrorDetails as l, readMissingScopeErrorDetails as m, ErrorCodes as n, MissingScopeErrorDetails as o, readMissingScopeError as p, GatewayErrorDetailCodes as r, ProjectCloneErrorDetails as s, ErrorCode as t, UserPrefsLimitExceededErrorDetails as u };
90
+ export { readCronJobNotFoundError as _, GatewayErrorDetails as a, readSkillProposalRevisionChangedError as b, OutboundDeliveryQueuedErrorDetails as c, SkillProposalRevisionChangedErrorDetails as d, UnknownAgentIdErrorDetails as f, isMcpAppViewExpiredError as g, buildSkillProposalRevisionChangedErrorDetails as h, GatewayErrorDetailCodes as i, ProjectCloneErrorDetails as l, WizardNotFoundErrorDetails as m, ErrorCode as n, McpAppViewExpiredErrorDetails as o, UserPrefsLimitExceededErrorDetails as p, ErrorCodes as r, MissingScopeErrorDetails as s, CronJobNotFoundErrorDetails as t, ProjectCloneFailureCause as u, readMissingScopeError as v, readMissingScopeErrorDetails as y };
@@ -1,2 +1,2 @@
1
- import { a as McpAppViewExpiredErrorDetails, c as ProjectCloneFailureCause, d as WizardNotFoundErrorDetails, f as isMcpAppViewExpiredError, i as GatewayErrorDetails, l as UnknownAgentIdErrorDetails, m as readMissingScopeErrorDetails, n as ErrorCodes, o as MissingScopeErrorDetails, p as readMissingScopeError, r as GatewayErrorDetailCodes, s as ProjectCloneErrorDetails, t as ErrorCode, u as UserPrefsLimitExceededErrorDetails } from "./gateway-error-details-BXoMHq1i.mjs";
2
- export { ErrorCode, ErrorCodes, GatewayErrorDetailCodes, GatewayErrorDetails, McpAppViewExpiredErrorDetails, MissingScopeErrorDetails, ProjectCloneErrorDetails, ProjectCloneFailureCause, UnknownAgentIdErrorDetails, UserPrefsLimitExceededErrorDetails, WizardNotFoundErrorDetails, isMcpAppViewExpiredError, readMissingScopeError, readMissingScopeErrorDetails };
1
+ import { _ as readCronJobNotFoundError, a as GatewayErrorDetails, b as readSkillProposalRevisionChangedError, c as OutboundDeliveryQueuedErrorDetails, d as SkillProposalRevisionChangedErrorDetails, f as UnknownAgentIdErrorDetails, g as isMcpAppViewExpiredError, h as buildSkillProposalRevisionChangedErrorDetails, i as GatewayErrorDetailCodes, l as ProjectCloneErrorDetails, m as WizardNotFoundErrorDetails, n as ErrorCode, o as McpAppViewExpiredErrorDetails, p as UserPrefsLimitExceededErrorDetails, r as ErrorCodes, s as MissingScopeErrorDetails, t as CronJobNotFoundErrorDetails, u as ProjectCloneFailureCause, v as readMissingScopeError, y as readMissingScopeErrorDetails } from "./gateway-error-details-DR-CyzeW.mjs";
2
+ export { CronJobNotFoundErrorDetails, ErrorCode, ErrorCodes, GatewayErrorDetailCodes, GatewayErrorDetails, McpAppViewExpiredErrorDetails, MissingScopeErrorDetails, OutboundDeliveryQueuedErrorDetails, ProjectCloneErrorDetails, ProjectCloneFailureCause, SkillProposalRevisionChangedErrorDetails, UnknownAgentIdErrorDetails, UserPrefsLimitExceededErrorDetails, WizardNotFoundErrorDetails, buildSkillProposalRevisionChangedErrorDetails, isMcpAppViewExpiredError, readCronJobNotFoundError, readMissingScopeError, readMissingScopeErrorDetails, readSkillProposalRevisionChangedError };
@@ -1,4 +1,4 @@
1
- import { t as asProtocolRecord } from "./protocol-value-normalization-Bexl3yr1.mjs";
1
+ import { t as asNullableRecord } from "./record-coerce-BKHrZ46I.mjs";
2
2
  //#region src/gateway-error-details.ts
3
3
  /** Gateway JSON-RPC style error codes shared by clients and server handlers. */
4
4
  const ErrorCodes = {
@@ -19,18 +19,52 @@ const ErrorCodes = {
19
19
  };
20
20
  /** Stable discriminants for structured method-level failures. */
21
21
  const GatewayErrorDetailCodes = {
22
+ CRON_JOB_NOT_FOUND: "CRON_JOB_NOT_FOUND",
22
23
  MISSING_SCOPE: "MISSING_SCOPE",
23
24
  MCP_APP_VIEW_EXPIRED: "MCP_APP_VIEW_EXPIRED",
25
+ OUTBOUND_DELIVERY_QUEUED: "OUTBOUND_DELIVERY_QUEUED",
24
26
  USER_PREFS_LIMIT_EXCEEDED: "USER_PREFS_LIMIT_EXCEEDED",
25
27
  SESSION_COMPANION_BUSY: "SESSION_COMPANION_BUSY",
28
+ SKILL_PROPOSAL_REVISION_CHANGED: "SKILL_PROPOSAL_REVISION_CHANGED",
26
29
  PROJECT_CLONE_FAILED: "PROJECT_CLONE_FAILED",
27
30
  UNKNOWN_AGENT_ID: "UNKNOWN_AGENT_ID",
28
31
  WIZARD_NOT_FOUND: "WIZARD_NOT_FOUND"
29
32
  };
30
33
  const LEGACY_MISSING_SCOPE_PATTERN = /\bmissing scope:\s*([a-z0-9._-]+)/i;
34
+ const SHA256_PATTERN = /^[a-fA-F0-9]{64}$/;
35
+ /** Reads a typed cron lookup miss without parsing operator-facing prose. */
36
+ function readCronJobNotFoundError(error) {
37
+ const details = asNullableRecord(asNullableRecord(error)?.details);
38
+ if (details?.code !== GatewayErrorDetailCodes.CRON_JOB_NOT_FOUND) return null;
39
+ const jobId = typeof details.jobId === "string" ? details.jobId.trim() : "";
40
+ return jobId ? {
41
+ code: GatewayErrorDetailCodes.CRON_JOB_NOT_FOUND,
42
+ jobId
43
+ } : null;
44
+ }
45
+ /** Builds the canonical stale-draft details shared by Skill Workshop RPCs. */
46
+ function buildSkillProposalRevisionChangedErrorDetails(params) {
47
+ return {
48
+ code: GatewayErrorDetailCodes.SKILL_PROPOSAL_REVISION_CHANGED,
49
+ expectedRevisionHash: params.expectedRevisionHash,
50
+ currentRevisionHash: params.currentRevisionHash
51
+ };
52
+ }
53
+ /** Reads a stale Skill Workshop decision without parsing operator-facing prose. */
54
+ function readSkillProposalRevisionChangedError(error) {
55
+ const details = asNullableRecord(asNullableRecord(error)?.details);
56
+ if (details?.code !== GatewayErrorDetailCodes.SKILL_PROPOSAL_REVISION_CHANGED) return null;
57
+ const expectedRevisionHash = typeof details.expectedRevisionHash === "string" ? details.expectedRevisionHash : "";
58
+ const currentRevisionHash = typeof details.currentRevisionHash === "string" ? details.currentRevisionHash : "";
59
+ if (!SHA256_PATTERN.test(expectedRevisionHash) || !SHA256_PATTERN.test(currentRevisionHash)) return null;
60
+ return buildSkillProposalRevisionChangedErrorDetails({
61
+ expectedRevisionHash,
62
+ currentRevisionHash
63
+ });
64
+ }
31
65
  /** Reads validated missing-scope details from an untrusted protocol payload. */
32
66
  function readMissingScopeErrorDetails(details) {
33
- const record = asProtocolRecord(details);
67
+ const record = asNullableRecord(details);
34
68
  if (record?.code !== GatewayErrorDetailCodes.MISSING_SCOPE) return null;
35
69
  const missingScope = typeof record.missingScope === "string" ? record.missingScope.trim() : "";
36
70
  const requiredScopes = Array.isArray(record.requiredScopes) ? record.requiredScopes.map((scope) => typeof scope === "string" ? scope.trim() : "") : [];
@@ -42,14 +76,14 @@ function readMissingScopeErrorDetails(details) {
42
76
  };
43
77
  }
44
78
  function isMcpAppViewExpiredError(error) {
45
- return asProtocolRecord(asProtocolRecord(error)?.details)?.code === GatewayErrorDetailCodes.MCP_APP_VIEW_EXPIRED;
79
+ return asNullableRecord(asNullableRecord(error)?.details)?.code === GatewayErrorDetailCodes.MCP_APP_VIEW_EXPIRED;
46
80
  }
47
81
  /**
48
82
  * Reads a method-level missing-scope failure, preferring structured details.
49
83
  * The message fallback keeps clients compatible with gateways predating structured details.
50
84
  */
51
85
  function readMissingScopeError(error) {
52
- const record = asProtocolRecord(error);
86
+ const record = asNullableRecord(error);
53
87
  if (!record) return null;
54
88
  const structured = readMissingScopeErrorDetails(record.details);
55
89
  if (structured) return structured;
@@ -64,4 +98,4 @@ function readMissingScopeError(error) {
64
98
  } : null;
65
99
  }
66
100
  //#endregion
67
- export { ErrorCodes, GatewayErrorDetailCodes, isMcpAppViewExpiredError, readMissingScopeError, readMissingScopeErrorDetails };
101
+ export { ErrorCodes, GatewayErrorDetailCodes, buildSkillProposalRevisionChangedErrorDetails, isMcpAppViewExpiredError, readCronJobNotFoundError, readMissingScopeError, readMissingScopeErrorDetails, readSkillProposalRevisionChangedError };