@schoolai/shipyard 3.14.0 → 3.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/dist/capability-detector-worker.js +5 -5
  2. package/dist/{chunk-OX3UY44R.js → chunk-5PBWS7BB.js} +31 -42
  3. package/dist/chunk-5PBWS7BB.js.map +1 -0
  4. package/dist/{chunk-RCEAMZVG.js → chunk-6SK6FBYC.js} +2 -2
  5. package/dist/{chunk-M3WBYTB3.js → chunk-AXHG3QQA.js} +16 -1
  6. package/dist/chunk-AXHG3QQA.js.map +1 -0
  7. package/dist/{chunk-T3OTZ66B.js → chunk-B7WZUKYX.js} +219 -86
  8. package/dist/chunk-B7WZUKYX.js.map +1 -0
  9. package/dist/{chunk-BUAEJNUG.js → chunk-HPDRJ4VZ.js} +3 -3
  10. package/dist/{chunk-BUAEJNUG.js.map → chunk-HPDRJ4VZ.js.map} +1 -1
  11. package/dist/{chunk-WGS7ZW6N.js → chunk-L5FQEZ6Y.js} +3 -3
  12. package/dist/{chunk-KUPHN3ZN.js → chunk-RHHRJR3L.js} +2 -2
  13. package/dist/{chunk-3KE2VDKA.js → chunk-VKY7UXTP.js} +25 -3
  14. package/dist/chunk-VKY7UXTP.js.map +1 -0
  15. package/dist/{chunk-IWBDVGD2.js → chunk-VTALUCDB.js} +2 -2
  16. package/dist/cursor-runner.js +3 -3
  17. package/dist/electron-utility.js +2 -2
  18. package/dist/index.js +4 -4
  19. package/dist/{login-HRR3T4SZ.js → login-EYGYOEXL.js} +3 -3
  20. package/dist/{mcp-servers-GUA5WOHO.js → mcp-servers-DSOX243C.js} +8 -2
  21. package/dist/{plan-backfill-QNJUWOYP.js → plan-backfill-SMJEKTMM.js} +4 -4
  22. package/dist/{serve-25I4ML7R.js → serve-DAP6V7SY.js} +339 -147
  23. package/dist/{serve-25I4ML7R.js.map → serve-DAP6V7SY.js.map} +1 -1
  24. package/dist/{start-O2DXLW23.js → start-AX2ZK3AY.js} +6 -6
  25. package/package.json +1 -1
  26. package/dist/chunk-3KE2VDKA.js.map +0 -1
  27. package/dist/chunk-M3WBYTB3.js.map +0 -1
  28. package/dist/chunk-OX3UY44R.js.map +0 -1
  29. package/dist/chunk-T3OTZ66B.js.map +0 -1
  30. /package/dist/{chunk-RCEAMZVG.js.map → chunk-6SK6FBYC.js.map} +0 -0
  31. /package/dist/{chunk-WGS7ZW6N.js.map → chunk-L5FQEZ6Y.js.map} +0 -0
  32. /package/dist/{chunk-KUPHN3ZN.js.map → chunk-RHHRJR3L.js.map} +0 -0
  33. /package/dist/{chunk-IWBDVGD2.js.map → chunk-VTALUCDB.js.map} +0 -0
  34. /package/dist/{login-HRR3T4SZ.js.map → login-EYGYOEXL.js.map} +0 -0
  35. /package/dist/{mcp-servers-GUA5WOHO.js.map → mcp-servers-DSOX243C.js.map} +0 -0
  36. /package/dist/{plan-backfill-QNJUWOYP.js.map → plan-backfill-SMJEKTMM.js.map} +0 -0
  37. /package/dist/{start-O2DXLW23.js.map → start-AX2ZK3AY.js.map} +0 -0
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  isImageMimeType,
4
4
  parseAssetUri
5
- } from "./chunk-IWBDVGD2.js";
5
+ } from "./chunk-VTALUCDB.js";
6
6
  import {
7
7
  assertNever
8
8
  } from "./chunk-X3MULCV5.js";
@@ -588,4 +588,4 @@ export {
588
588
  toRecord,
589
589
  buildCursorUserPrompt
590
590
  };
591
- //# sourceMappingURL=chunk-RCEAMZVG.js.map
591
+ //# sourceMappingURL=chunk-6SK6FBYC.js.map
@@ -949,6 +949,19 @@ function classifyClaudeCodeCompatibility(detected, tested = TESTED_CLAUDE_CODE_V
949
949
  return { status: "untested", detected, tested };
950
950
  }
951
951
 
952
+ // ../../packages/session/src/codex-compat.ts
953
+ var TESTED_CODEX_VERSION = "0.138.0";
954
+ var SEMVER_RE = /^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/;
955
+ function classifyCodexCompatibility(detected, tested = TESTED_CODEX_VERSION) {
956
+ if (!detected || !SEMVER_RE.test(detected)) return { status: "unknown" };
957
+ const detectedParts = detected.split(".");
958
+ const testedParts = tested.split(".");
959
+ if (detectedParts[0] === testedParts[0] && detectedParts[1] === testedParts[1]) {
960
+ return { status: "compatible" };
961
+ }
962
+ return { status: "untested", detected, tested };
963
+ }
964
+
952
965
  // ../../packages/session/src/collab-room-connection.ts
953
966
  var CollabRoomConnection = class {
954
967
  state = "disconnected";
@@ -1556,6 +1569,8 @@ var PublishListResponseSchema = external_exports.object({
1556
1569
  export {
1557
1570
  TESTED_CLAUDE_CODE_VERSION,
1558
1571
  classifyClaudeCodeCompatibility,
1572
+ TESTED_CODEX_VERSION,
1573
+ classifyCodexCompatibility,
1559
1574
  HealthResponseSchema,
1560
1575
  TurnHealthResponseSchema,
1561
1576
  BRANCH_NAME_PATTERN,
@@ -1604,4 +1619,4 @@ export {
1604
1619
  PublishCreateResponseSchema,
1605
1620
  ROUTES
1606
1621
  };
1607
- //# sourceMappingURL=chunk-M3WBYTB3.js.map
1622
+ //# sourceMappingURL=chunk-AXHG3QQA.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../packages/session/src/command-schemas.ts","../../../packages/session/src/feedback-schemas.ts","../../../packages/session/src/skill-schemas.ts","../../../packages/session/src/health-schemas.ts","../../../packages/session/src/schemas.ts","../../../packages/session/src/protocol-version.ts","../../../packages/session/src/routes.ts","../../../packages/session/src/claude-code-compat.ts","../../../packages/session/src/codex-compat.ts","../../../packages/session/src/collab-room-connection.ts","../../../packages/session/src/personal-room-connection.ts","../../../packages/session/src/publish-constants.ts"],"sourcesContent":["import { z } from 'zod';\n\nexport const CommandCompatibleAgentSchema = z.enum(['claude-code', 'codex', 'cursor']);\nexport type CommandCompatibleAgent = z.infer<typeof CommandCompatibleAgentSchema>;\n\nexport const CommandInfoSchema = z.object({\n name: z.string(),\n description: z.string(),\n path: z.string().optional(),\n sourceAgent: z.string().optional(),\n compatibleAgents: z.array(CommandCompatibleAgentSchema).optional(),\n source: z.enum(['user', 'project']).optional(),\n});\nexport type CommandInfo = z.infer<typeof CommandInfoSchema>;\n","import { z } from 'zod';\n\nconst FEEDBACK_IMAGE_MIME_TYPES = ['image/png', 'image/jpeg', 'image/gif', 'image/webp'] as const;\n\n/**\n * Screenshot attached to in-app feedback. Base64 payload is uploaded to GitHub\n * issue assets by the session-server when creating the issue.\n */\nexport const FeedbackImageSchema = z.object({\n mimeType: z.enum(FEEDBACK_IMAGE_MIME_TYPES),\n fileName: z.string().min(1).max(256),\n /** Base64-encoded image bytes (no data: URL prefix). */\n data: z.string().min(1).max(6_000_000),\n});\n\nexport type FeedbackImage = z.infer<typeof FeedbackImageSchema>;\n","import { z } from 'zod';\n\n/**\n * Skill compat list — kept in lockstep with `AgentIdSchema` in\n * `@shipyard/loro-schema/agent-capability-profile`. Widening this here\n * (instead of importing the loro-schema enum) avoids a session →\n * loro-schema dependency edge while still admitting every registered\n * agent id the daemon can stamp on a stored skill block.\n */\nexport const SkillCompatibleAgentSchema = z.enum(['claude-code', 'codex', 'cursor']);\nexport type SkillCompatibleAgent = z.infer<typeof SkillCompatibleAgentSchema>;\n\nexport const SkillBodyResolutionSchema = z.enum(['native', 'pass-in', 'mirror', 'unreachable']);\nexport type SkillBodyResolution = z.infer<typeof SkillBodyResolutionSchema>;\n\nexport const SkillInfoSchema = z.object({\n name: z.string(),\n description: z.string(),\n path: z.string().optional(),\n sourceAgent: z.string().optional(),\n /**\n * Which agent runtimes can invoke this skill. Defaults to `[sourceAgent]`\n * on read for back-compat. Set by the daemon's compat resolver after\n * detection. Codex pass-in via `[$name](skill://path)` extends a Claude\n * skill's reach to include 'codex'; mirror=on makes both runtimes native.\n * See plan: skill-compat.ts.\n */\n compatibleAgents: z.array(SkillCompatibleAgentSchema).optional(),\n /**\n * How this skill reaches the active agent. `native` = agent's own resolver\n * indexed it; `pass-in` = injected via `skill://` path at submission time;\n * `mirror` = available through a Shipyard-managed symlink; `unreachable` =\n * cannot be invoked from the active agent. Default on read: `'native'`.\n */\n bodyResolution: SkillBodyResolutionSchema.optional(),\n /**\n * Size of the SKILL.md in bytes. Used for context-budget warnings in the\n * composer (per-skill display + per-session injection watermark). Excludes\n * referenced asset files.\n */\n bodyBytes: z.number().optional(),\n /**\n * Plugin id, when the skill is provided by a plugin install (under a plugin\n * cache path such as\n * `~/.claude/plugins/cache/<marketplace>/<plugin-id>/<version>/skills/...`\n * or\n * `~/.codex/plugins/cache/<marketplace>/<plugin-id>/<version>/skills/...`).\n * Used as the SDK invocation prefix (`plugin:name`) — the only namespace\n * form Claude's `Options.skills` and Codex's skill resolver recognize.\n * Unset for user-, project-, and system-scoped skills (the SDK accepts\n * bare names for those). Synthetic prefixes like `'user'`, `'project'`, the\n * marketplace name, or the agent name (e.g. `'claude-code'`) are NEVER\n * written here — they would be rejected at invocation time with \"Skill X is\n * not in this session's skills allowlist\".\n */\n namespace: z.string().optional(),\n /**\n * Set when the entry was loaded from the persistent Codex skills cache at\n * daemon boot (before the live `listSkills` RPC has replied). The composer\n * may render these with a \"stale\" treatment; once the RPC result lands the\n * flag is cleared. Never persisted in the cache file itself — the file\n * carries fresh skills and the loader stamps `stale: true` on read.\n */\n stale: z.boolean().optional(),\n /**\n * Content-hash dedup key for the SKILL.md body. Stamped by `readSkillsFromDir`\n * from the already-read file content using the same `hash:<sha256>` format as\n * `contentHashForBody` in `apps/daemon/src/services/skills/dedup.ts`. Present\n * on all path-bearing detected skills; absent on pathless fallbacks and\n * library skills. Used by `buildSkillCandidates` so the production detection\n * path produces real content-hash keys instead of falling back to `name:`.\n * Aligns with the optional `contentHash` field on `SkillInfoWireSchema`.\n */\n contentHash: z.string().optional(),\n});\nexport type SkillInfo = z.infer<typeof SkillInfoSchema>;\n","import { z } from 'zod';\n\n/**\n * GET /health response schema.\n *\n * Returns service health status and environment info.\n */\nexport const HealthResponseSchema = z.object({\n status: z.literal('ok'),\n service: z.string().min(1),\n environment: z.enum(['development', 'production']),\n turn: z\n .object({\n configured: z.boolean(),\n status: z.enum(['configured', 'unconfigured']),\n })\n .optional(),\n});\n\nexport type HealthResponse = z.infer<typeof HealthResponseSchema>;\n\n/**\n * TURN canary response. This route is intentionally separate from `/health`\n * because it calls the TURN provider and should be used by smoke checks, not\n * high-frequency liveness probes.\n */\nexport const TurnHealthResponseSchema = z.object({\n status: z.enum(['ok', 'degraded']),\n service: z.string().min(1),\n environment: z.enum(['development', 'production']),\n turn: z.object({\n configured: z.boolean(),\n status: z.enum(['ready', 'unconfigured', 'degraded']),\n ttlSeconds: z.number().int().positive(),\n urlCount: z.number().int().nonnegative(),\n stunUrlCount: z.number().int().nonnegative(),\n turnUrlCount: z.number().int().nonnegative(),\n fetchDurationMs: z.number().int().nonnegative(),\n }),\n});\n\nexport type TurnHealthResponse = z.infer<typeof TurnHealthResponseSchema>;\n","/**\n * Zod schemas for all signaling server request/response bodies and WebSocket messages.\n *\n * This module provides:\n * - Validation schemas for all HTTP endpoints\n * - WebSocket message schemas for personal and collab rooms\n * - Inferred TypeScript types for type-safe API usage\n * - Error response schemas for consistent error handling\n *\n * This is the single source of truth for all API schemas.\n * The client directory is designed to be self-contained and hoistable\n * to packages/shared without depending on server code.\n *\n * @module client/schemas\n */\n\nimport { z } from 'zod';\nimport { CommandInfoSchema } from './command-schemas';\nimport { FeedbackImageSchema } from './feedback-schemas.js';\nimport { SkillInfoSchema } from './skill-schemas';\n\nexport * from './command-schemas';\nexport { type FeedbackImage, FeedbackImageSchema } from './feedback-schemas.js';\nexport * from './health-schemas';\nexport * from './skill-schemas';\n\n/**\n * Pattern for valid git branch names.\n * Shared between daemon validation and web form validation.\n */\nexport const BRANCH_NAME_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9/_.-]*$/;\n\n/**\n * Pattern for browser/agent machineId query params on personal-room and\n * collab-room WebSocket upgrades. Used by the server to validate before\n * composing participantIds. Clients below MIN_PROTOCOL_VERSION that omit\n * it will be rejected elsewhere, but the server falls back gracefully.\n */\nexport const MACHINE_ID_PATTERN = /^[\\w-]+$/;\nexport const MACHINE_ID_MAX_LENGTH = 128;\n\n/**\n * Standard error response schema used across all endpoints.\n */\nexport const ErrorResponseSchema = z.object({\n error: z.string(),\n message: z.string(),\n});\n\nexport type ErrorResponse = z.infer<typeof ErrorResponseSchema>;\n\n/**\n * Validation error response with field-level details.\n */\nexport const ValidationErrorResponseSchema = ErrorResponseSchema.extend({\n details: z\n .array(\n z.object({\n path: z.array(z.union([z.string(), z.number()])),\n message: z.string(),\n code: z.string().optional(),\n })\n )\n .optional(),\n});\n\nexport type ValidationErrorResponse = z.infer<typeof ValidationErrorResponseSchema>;\n\n/**\n * Not found error response with available endpoints.\n */\nexport const NotFoundResponseSchema = ErrorResponseSchema.extend({\n endpoints: z.array(z.string()),\n});\n\nexport type NotFoundResponse = z.infer<typeof NotFoundResponseSchema>;\n\n/**\n * POST /auth/github/callback request body schema.\n *\n * Used to exchange a GitHub OAuth code for a Shipyard JWT.\n */\nexport const AuthGitHubCallbackRequestSchema = z.object({\n /** GitHub OAuth authorization code */\n code: z.string().min(1, 'code is required'),\n /** OAuth redirect URI that was used in the authorize request */\n redirect_uri: z.string().url('redirect_uri must be a valid URL'),\n});\n\nexport type AuthGitHubCallbackRequest = z.infer<typeof AuthGitHubCallbackRequestSchema>;\n\n/**\n * User info returned from successful OAuth.\n */\nexport const OAuthUserSchema = z.object({\n /** Shipyard user ID (\"usr_abc123\") */\n id: z.string(),\n displayName: z.string(),\n login: z.string().optional(),\n /** GitHub avatar URL */\n avatarUrl: z.string().nullable(),\n /** Linked providers */\n providers: z.array(z.string()),\n});\n\nexport type OAuthUser = z.infer<typeof OAuthUserSchema>;\n\n/**\n * POST /auth/github/callback response schema.\n *\n * Returns a Shipyard JWT and user info on successful OAuth.\n */\nexport const AuthGitHubCallbackResponseSchema = z.object({\n /** Shipyard JWT for authentication */\n token: z.string(),\n /** User info from GitHub */\n user: OAuthUserSchema,\n /** GitHub access token for API calls (~8h expiry for GitHub App tokens) */\n githubAccessToken: z.string().optional(),\n /** GitHub refresh token for obtaining new access tokens (~6 month expiry) */\n githubRefreshToken: z.string().optional(),\n /** Present and true if request came from a mobile device */\n is_mobile: z.boolean().optional(),\n});\n\nexport type AuthGitHubCallbackResponse = z.infer<typeof AuthGitHubCallbackResponseSchema>;\n\n/**\n * POST /auth/github/refresh request body schema.\n *\n * Exchange a GitHub refresh token for a new access token.\n */\nexport const AuthGitHubRefreshRequestSchema = z.object({\n /** GitHub refresh token from the original OAuth flow */\n refreshToken: z.string().min(1, 'refreshToken is required'),\n});\n\nexport type AuthGitHubRefreshRequest = z.infer<typeof AuthGitHubRefreshRequestSchema>;\n\n/**\n * POST /auth/github/refresh response schema.\n */\nexport const AuthGitHubRefreshResponseSchema = z.object({\n /** New GitHub access token */\n githubAccessToken: z.string(),\n /** New GitHub refresh token (GitHub rotates these on each use) */\n githubRefreshToken: z.string(),\n});\n\nexport type AuthGitHubRefreshResponse = z.infer<typeof AuthGitHubRefreshResponseSchema>;\n\n/**\n * GET /auth/verify response schema.\n *\n * Validates a JWT against the database and returns user info or failure reason.\n */\nexport const AuthVerifyResponseSchema = z.discriminatedUnion('valid', [\n z.object({ valid: z.literal(true), user: OAuthUserSchema }),\n z.object({\n valid: z.literal(false),\n reason: z.enum(['invalid_token', 'user_not_found']),\n }),\n]);\n\nexport type AuthVerifyResponse = z.infer<typeof AuthVerifyResponseSchema>;\n\n/**\n * All valid participant roles in a collab room.\n * Owner is the task creator; collaborator roles come from the invite link.\n */\nexport const ParticipantRoleSchema = z.enum([\n 'owner',\n 'collaborator-full',\n 'collaborator-review',\n 'viewer',\n]);\nexport type ParticipantRole = z.infer<typeof ParticipantRoleSchema>;\n\n/**\n * Collaborator-only roles (subset of ParticipantRole, excludes 'owner').\n * Used for invite link creation where the owner role is never assignable.\n */\nexport const CollaboratorRoleSchema = ParticipantRoleSchema.extract([\n 'collaborator-full',\n 'collaborator-review',\n 'viewer',\n]);\nexport type CollaboratorRole = z.infer<typeof CollaboratorRoleSchema>;\n\n/**\n * POST /collab/create request body schema.\n *\n * Used to create a new collaboration room with a pre-signed URL.\n */\nexport const CollabCreateRequestSchema = z.object({\n /** ID of the task to collaborate on */\n taskId: z.string().min(1, 'taskId is required'),\n /** How long the collaboration link should be valid (1-10080 minutes / 7 days max, default 60) */\n expiresInMinutes: z.number().min(1).max(10080).default(60),\n /** Permission level for the invitee (defaults to 'collaborator-full') */\n role: CollaboratorRoleSchema.optional(),\n});\n\nexport type CollabCreateRequest = z.infer<typeof CollabCreateRequestSchema>;\n\n/**\n * POST /collab/create response schema.\n *\n * Returns the pre-signed WebSocket URL for joining the collaboration room.\n */\nexport const CollabCreateResponseSchema = z.object({\n /** Pre-signed WebSocket URL for joining the room */\n url: z.string().url(),\n /** Unique room identifier */\n roomId: z.string(),\n /** Unix timestamp when the collaboration link expires */\n expiresAt: z.number(),\n});\n\nexport type CollabCreateResponse = z.infer<typeof CollabCreateResponseSchema>;\n\n/**\n * GET /personal/:userId error responses.\n *\n * WebSocket messages are defined below.\n *\n * The actual WebSocket is handled by the Durable Object.\n * These are HTTP error responses before upgrade.\n */\nexport const WsPersonalErrorSchema = z.discriminatedUnion('error', [\n ErrorResponseSchema.extend({\n error: z.literal('upgrade_required'),\n }),\n ErrorResponseSchema.extend({\n error: z.literal('missing_token'),\n }),\n ErrorResponseSchema.extend({\n error: z.literal('invalid_token'),\n }),\n ErrorResponseSchema.extend({\n error: z.literal('forbidden'),\n }),\n]);\n\nexport type WsPersonalError = z.infer<typeof WsPersonalErrorSchema>;\n\n/**\n * GET /collab/:roomId error responses.\n *\n * WebSocket messages are defined below.\n *\n * The actual WebSocket is handled by the Durable Object.\n * These are HTTP error responses before upgrade.\n */\nexport const WsCollabErrorSchema = z.discriminatedUnion('error', [\n ErrorResponseSchema.extend({\n error: z.literal('upgrade_required'),\n }),\n ErrorResponseSchema.extend({\n error: z.literal('missing_token'),\n }),\n ErrorResponseSchema.extend({\n error: z.literal('invalid_token'),\n }),\n ErrorResponseSchema.extend({\n error: z.literal('forbidden'),\n }),\n ErrorResponseSchema.extend({\n error: z.literal('expired'),\n }),\n]);\n\nexport type WsCollabError = z.infer<typeof WsCollabErrorSchema>;\n\n/**\n * Shipyard JWT claims schema.\n *\n * This is the payload embedded in the JWT returned by /auth/github/callback.\n */\nexport const ShipyardJWTClaimsSchema = z.object({\n /** Shipyard user ID: \"usr_abc123\" */\n sub: z.string(),\n /** Display name from primary provider */\n displayName: z.string(),\n /** Linked OAuth providers */\n providers: z.array(z.string()),\n iat: z.number(),\n exp: z.number(),\n /** Optional: Scope for agent tokens (e.g., 'task:abc123') */\n scope: z.string().optional(),\n /** Optional: Machine ID for daemon tokens */\n machineId: z.string().optional(),\n});\n\nexport type ShipyardJWTClaims = z.infer<typeof ShipyardJWTClaimsSchema>;\n\n/**\n * Pre-signed URL payload schema.\n *\n * Embedded in the token query parameter for /collab/:roomId.\n */\nexport const PresignedUrlPayloadSchema = z.object({\n /** Room ID this token is valid for */\n roomId: z.string(),\n /** Task ID being collaborated on */\n taskId: z.string(),\n /** User ID of the person who created the invite */\n inviterId: z.string(),\n /** Expiration timestamp (Unix ms) */\n exp: z.number(),\n /** Permission level for the invitee (defaults to 'collaborator-full') */\n role: CollaboratorRoleSchema.optional(),\n /** Generation counter for link revocation — tokens with a generation older than the room's current generation are rejected */\n generation: z.number().optional(),\n});\n\nexport type PresignedUrlPayload = z.infer<typeof PresignedUrlPayloadSchema>;\n\n/**\n * Reasoning capability schema for models that support configurable reasoning effort.\n */\n/*\n * Keep in sync with ReasoningEffortSchema in packages/loro-schema/src/shared-enums.ts.\n * Inlined here because packages/session has no dependency on @shipyard/loro-schema.\n * Drift watcher: effort-probe-exhaustive.test.ts fires if the CLI gains a tier.\n */\nexport const ReasoningCapabilitySchema = z\n .object({\n efforts: z.array(z.enum(['none', 'low', 'medium', 'high', 'xhigh', 'ultracode', 'max'])).min(1),\n defaultEffort: z.enum(['none', 'low', 'medium', 'high', 'xhigh', 'ultracode', 'max']),\n })\n .refine((data) => data.efforts.includes(data.defaultEffort), {\n message: 'defaultEffort must be one of the supported efforts',\n });\n\nexport type ReasoningCapability = z.infer<typeof ReasoningCapabilitySchema>;\n\n/**\n * Model info schema for machine capabilities.\n */\nexport const ModelInfoSchema = z.object({\n id: z.string(),\n name: z.string(),\n provider: z.string(),\n description: z.string().optional(),\n reasoning: ReasoningCapabilitySchema.optional(),\n supportsFastMode: z.boolean().optional(),\n /** Optional model-level caveat (e.g. data-retention) shown in the picker. */\n note: z.string().optional(),\n});\n\nexport type ModelInfo = z.infer<typeof ModelInfoSchema>;\n\n/**\n * Git repo info schema for machine capabilities.\n */\nexport const GitRepoInfoSchema = z.object({\n path: z.string(),\n name: z.string(),\n branch: z.string(),\n remote: z.string().optional(),\n});\n\nexport type GitRepoInfo = z.infer<typeof GitRepoInfoSchema>;\n\n/**\n * Permission mode schema for machine capabilities.\n */\nexport const PermissionModeSchema = z.enum(['default', 'accept-edits', 'plan', 'bypass', 'auto']);\n\nexport type PermissionMode = z.infer<typeof PermissionModeSchema>;\n\n/**\n * Anthropic auth status — detected from env vars, Claude Code OAuth, or cloud providers.\n */\nconst AnthropicAuthMethodEnum = z.enum([\n 'claude-ai',\n 'console',\n 'api-key',\n 'sso',\n 'gateway',\n 'bedrock',\n 'vertex',\n 'foundry',\n 'none',\n]);\n\nexport const AnthropicAuthStatusSchema = z.object({\n status: z.enum(['authenticated', 'unauthenticated', 'unknown']),\n method: AnthropicAuthMethodEnum,\n email: z.string().optional(),\n orgName: z.string().optional(),\n subscriptionType: z.string().nullable().optional(),\n apiProvider: z.string().optional(),\n /**\n * Account identifier surfaced for collision detection on the web side\n * (Step 19). Populated from the email claim or subject claim of the\n * id_token. Optional/nullable so older daemons that omit the field\n * still parse cleanly.\n */\n accountId: z.string().optional(),\n authMismatch: z\n .object({\n selected: AnthropicAuthMethodEnum,\n envMethod: AnthropicAuthMethodEnum,\n envVar: z.string(),\n })\n .optional(),\n});\n\nexport type AnthropicAuthStatus = z.infer<typeof AnthropicAuthStatusSchema>;\n\n/**\n * Codex auth status (PROTOCOL_VERSION 83). Mirrors `AnthropicAuthStatus`\n * shape but uses the Codex auth-method vocabulary. Carries an\n * `apiKeyHint` (`sk-***xxxxx` style) for the api-key path so the\n * settings card can render a meaningful \"logged in\" line without\n * exposing the full secret. Email + accountId come from the\n * `tokens.id_token` JWT claims for the chatgpt path.\n */\n/**\n * Cursor auth status — mirrors `CursorAuthStatusSchema` in\n * `packages/loro-schema/src/protocols/channel-protocol.ts`. Both\n * packages keep their own copy so neither depends on the other's\n * Zod surface for wire validation.\n */\nexport const CursorAuthStatusSchema = z.object({\n status: z.enum(['authenticated', 'unauthenticated', 'unknown']),\n method: z.enum(['api-key', 'none']).optional(),\n email: z.string().optional(),\n accountId: z.string().optional(),\n apiKeyHint: z.string().optional(),\n /** Human-readable API key name from `Cursor.me()`. Optional — absent until the network call succeeds. */\n apiKeyName: z.string().optional(),\n});\nexport type CursorAuthStatus = z.infer<typeof CursorAuthStatusSchema>;\n\nexport const CodexAuthStatusSchema = z.object({\n status: z.enum(['authenticated', 'unauthenticated', 'unknown']),\n method: z.enum(['chatgpt', 'api-key', 'bedrock', 'none']).optional(),\n email: z.string().optional(),\n accountId: z.string().optional(),\n apiKeyHint: z.string().optional(),\n});\nexport type CodexAuthStatus = z.infer<typeof CodexAuthStatusSchema>;\n\nexport const MCPServerAuthStatusSchema = z.enum(['authenticated', 'unauthenticated', 'unknown']);\nexport type MCPServerAuthStatus = z.infer<typeof MCPServerAuthStatusSchema>;\n\nexport const MCPServerSourceSchema = z.enum([\n 'project',\n 'user',\n 'local',\n 'mcp-json',\n 'plugin',\n 'claudeai',\n]);\nexport type MCPServerSource = z.infer<typeof MCPServerSourceSchema>;\n\nconst MCP_SERVER_TYPES = ['stdio', 'http', 'sse'] as const;\nexport const MCPServerTypeSchema = z.enum(MCP_SERVER_TYPES);\nexport type MCPServerType = z.infer<typeof MCPServerTypeSchema>;\n\nexport const MCPOAuthConfigSchema = z.object({\n clientId: z.string().optional(),\n /**\n * `.positive()` rejects `0` at the boundary so a malformed\n * `.mcp.json` with `oauth.callbackPort: 0` doesn't propagate\n * through to DCR and cement `http://localhost:0/callback` in the\n * OAuth state. Downstream defensive guards still treat undefined\n * the same as \"missing\".\n */\n callbackPort: z.number().int().positive().optional(),\n});\n\nexport const MCPServerInfoSchema = z.object({\n name: z.string(),\n type: MCPServerTypeSchema.default('stdio'),\n runtimeId: z.string().min(1).optional(),\n command: z.string().optional(),\n args: z.array(z.string()).optional(),\n env: z.record(z.string(), z.string()).optional(),\n url: z.string().optional(),\n headers: z.record(z.string(), z.string()).optional(),\n oauth: MCPOAuthConfigSchema.optional(),\n enabled: z.boolean(),\n source: MCPServerSourceSchema,\n sourceLabel: z.string().optional(),\n sourcePath: z.string().optional(),\n authStatus: MCPServerAuthStatusSchema,\n /**\n * Anthropic gateway server id (`mcpsrv_…`) from `GET /v1/mcp_servers`. Present\n * only for subscription connectors that route through the gateway; the proxy\n * uses it to build `mcp-proxy.anthropic.com/v1/mcp/{id}`. Additive (PROTOCOL 130).\n */\n id: z.string().optional(),\n /**\n * Provenance: the full set of sources this canonical server was discovered\n * from. Dedup collapses same-URL servers to ONE entry (priority wins) but\n * preserves the set here so the picker can show \"Account + Local\" instead of\n * silently dropping a source. Additive (PROTOCOL 130).\n */\n sources: z.array(MCPServerSourceSchema).optional(),\n});\nexport type MCPServerInfo = z.infer<typeof MCPServerInfoSchema>;\n\n/**\n * Machine capabilities schema — advertised by daemons at registration time.\n */\nexport const MarketplacePluginInfoSchema = z.object({\n name: z.string(),\n description: z.string(),\n author: z.string(),\n marketplace: z.string(),\n installCount: z.number().optional(),\n installed: z.boolean(),\n enabled: z.boolean(),\n version: z.string().optional(),\n isExternal: z.boolean(),\n});\nexport type MarketplacePluginInfo = z.infer<typeof MarketplacePluginInfoSchema>;\n\nexport const RuntimeIdSchema = z.string().min(1);\nexport type RuntimeId = z.infer<typeof RuntimeIdSchema>;\n\nexport const ClaudeCodeAgentSchema = z.object({\n id: z.literal('claude-code'),\n providerId: z.literal('anthropic').default('anthropic'),\n name: z.literal('Claude Code'),\n version: z.string().optional(),\n});\n\nexport const CodexAgentSchema = z.object({\n id: z.literal('codex'),\n providerId: z.literal('openai').default('openai'),\n name: z.literal('Codex'),\n version: z.string().optional(),\n});\n\nexport const CursorAgentSchema = z.object({\n id: z.literal('cursor'),\n providerId: z.literal('anysphere').default('anysphere'),\n name: z.literal('Cursor'),\n version: z.string().optional(),\n});\n\nexport const InstalledAgentSchema = z.object({\n id: RuntimeIdSchema,\n providerId: z.string().min(1).default('unknown').optional(),\n name: z.string().min(1),\n version: z.string().optional(),\n});\nexport type InstalledAgent = z.infer<typeof InstalledAgentSchema>;\nexport const InstalledRuntimeSchema = InstalledAgentSchema;\nexport type InstalledRuntime = InstalledAgent;\n\nexport const RuntimeAuthStatusSchema = z.object({\n runtimeId: RuntimeIdSchema,\n providerId: z.string().min(1),\n status: z.enum(['authenticated', 'unauthenticated', 'unknown']),\n method: z.string().optional(),\n email: z.string().optional(),\n /**\n * Stable account identifier from the provider. Optional — only set\n * when the auth payload includes it (codex chatgpt path, cursor\n * api-key path once `/v1/me` is wired).\n */\n accountId: z.string().optional(),\n /**\n * Masked hint for the api-key auth method, e.g. `crsr_abc***xyz12` or\n * `sk-abc***xyz12`. Lets the settings card show \"Signed in as ...\"\n * without exposing the full secret.\n */\n apiKeyHint: z.string().optional(),\n /**\n * Human-readable API key name from `Cursor.me()`. Present only for\n * Cursor's `api-key` method when the identity fetch has succeeded.\n */\n apiKeyName: z.string().optional(),\n orgName: z.string().optional(),\n subscriptionType: z.string().nullable().optional(),\n apiProvider: z.string().optional(),\n authMismatch: z\n .object({\n selected: z.string().min(1),\n envMethod: z.string().min(1),\n envVar: z.string(),\n })\n .optional(),\n});\nexport type RuntimeAuthStatus = z.infer<typeof RuntimeAuthStatusSchema>;\n\n/**\n * Quick-pick directory the daemon detected on the user's machine (e.g.\n * `~/Documents`, `~/Desktop`, `~/repos`). Renders as a chip in the env\n * picker so users can spawn an agent from a non-repo dir without typing\n * a path. Detected once at boot by `findCommonDirs` and rebroadcast on\n * every capability refresh.\n */\nexport const CommonDirSchema = z.object({\n path: z.string(),\n label: z.string(),\n});\nexport type CommonDir = z.infer<typeof CommonDirSchema>;\n\nexport const MachineCapabilitiesSchema = z.object({\n models: z.array(ModelInfoSchema),\n environments: z.array(GitRepoInfoSchema),\n permissionModes: z.array(PermissionModeSchema),\n homeDir: z.string(),\n /**\n * Pre-detected common directories (e.g. ~/Documents). Optional with a\n * default so pre-v79 daemons that don't send the field still parse —\n * the browser falls back to a Home-only picker. See PROTOCOL_VERSION 79.\n */\n commonDirs: z.array(CommonDirSchema).default([]),\n anthropicAuth: AnthropicAuthStatusSchema.optional(),\n /**\n * Codex auth status (PROTOCOL_VERSION 83). Optional + `.nullable()`\n * because pre-v83 daemons don't emit the field; older browsers reading\n * a v83 daemon's snapshot ignore the unknown key.\n */\n codexAuth: CodexAuthStatusSchema.nullable().optional(),\n /**\n * Cursor auth status (PROTOCOL_VERSION 102). Same nullable+optional\n * shape as `codexAuth` so pre-cursor daemons / older browsers stay\n * compatible. The `cursor-agent` profile's `authDetector` populates\n * this on every `refreshFull` / cursor login.\n */\n cursorAuth: CursorAuthStatusSchema.nullable().optional(),\n runtimeAuth: z.record(z.string(), RuntimeAuthStatusSchema).default({}).optional(),\n mcpServers: z.array(MCPServerInfoSchema),\n skills: z.array(SkillInfoSchema),\n commands: z.array(CommandInfoSchema).default([]).optional(),\n marketplacePlugins: z.array(MarketplacePluginInfoSchema),\n autoModeEnabled: z.boolean().optional(),\n installedAgents: z.array(InstalledAgentSchema),\n installedRuntimes: z.array(InstalledRuntimeSchema).default([]).optional(),\n /**\n * False until `runCapabilityDetection` resolves (success OR failure).\n * Browser banner predicates that treat empty arrays as authoritative-empty\n * MUST gate on this — otherwise they flicker on every reconnect because\n * the stub is sent first with all arrays empty. Defaults to false via Zod\n * for backwards compatibility.\n */\n detectionComplete: z.boolean().default(false),\n /**\n * True when the daemon detects Graphite on the machine (gt CLI present OR\n * ~/.config/graphite/user_config contains an authToken). Additive field —\n * defaults to false on older daemons. No PROTOCOL_VERSION bump required.\n */\n usesGraphite: z.boolean().default(false),\n /**\n * Dependency installation status for the current workspace cwd (additive,\n * protocol-safe). Recomputed on cwd change via buildCwdScopedPatch.\n * Absent when no lockfile is found or detection hasn't run yet.\n */\n depsStatus: z\n .object({\n lockfileKind: z.enum(['pnpm', 'npm', 'yarn', 'bun']).nullable(),\n nodeModulesMissing: z.boolean(),\n })\n .optional(),\n /**\n * True when the daemon started without a scoped workspace (e.g. cwd === $HOME\n * or no project marker). Additive optional — defaults to false/absent.\n */\n unscopedWorkspace: z.boolean().optional(),\n});\n\nexport type MachineCapabilities = z.infer<typeof MachineCapabilitiesSchema>;\n\n/**\n * Register agent message schema for personal room WebSocket.\n * Capabilities are no longer sent via signaling -- they flow through\n * Loro ephemeral on the room document instead.\n */\n/**\n * Local-direct advertisement — when the daemon is running a LocalDirectServer\n * on a loopback port, it advertises that port + connection token so a browser\n * on the same machine can bypass WebRTC/signaling entirely. Additive, optional.\n *\n * Token is carried here because the browser cannot read the daemon's on-disk\n * advertisement file. Signaling is JWT-authenticated, so a token over this\n * channel is safe for a single-user dev box. For multi-user hosts, audit the\n * threat model before shipping phase 2 (default-on).\n */\nexport const LocalDirectAdvertisementSchema = z.object({\n port: z.number().int().min(1).max(65535),\n token: z.string().min(16),\n});\n\nexport type LocalDirectAdvertisement = z.infer<typeof LocalDirectAdvertisementSchema>;\n\nexport const RegisterAgentSchema = z.object({\n type: z.literal('register-agent'),\n agentId: z.string(),\n machineId: z.string(),\n machineName: z.string(),\n agentType: z.string(),\n protocolVersion: z.number().optional(),\n npmVersion: z.string().optional(),\n localDirect: LocalDirectAdvertisementSchema.optional(),\n});\n\n/**\n * Unregister agent message schema for personal room WebSocket.\n */\nexport const UnregisterAgentSchema = z.object({\n type: z.literal('unregister-agent'),\n agentId: z.string(),\n});\n\n/**\n * Agent status update message schema for personal room WebSocket.\n */\nexport const AgentStatusSchema = z.object({\n type: z.literal('agent-status'),\n agentId: z.string(),\n status: z.enum(['idle', 'running', 'error']),\n activeTaskId: z.string().optional(),\n});\n\n/**\n * WebRTC offer message schema for personal room WebSocket.\n *\n * When sent by a client: `targetMachineId` is the intended recipient.\n * When relayed by the server: `fromMachineId` is added to identify the sender.\n *\n * `generationId` is a UUID minted by the offerer when it constructs the\n * RTCPeerConnection. It flows through the answer and every subsequent ICE\n * candidate for that PC so both sides can discard messages belonging to a\n * prior generation after a teardown-and-replace cycle. Without it, stale\n * candidates in flight across the signaling relay silently contaminate the\n * successor PC's ICE state.\n */\nexport const WebRTCOfferSchema = z.object({\n type: z.literal('webrtc-offer'),\n targetMachineId: z.string(),\n fromMachineId: z.string().optional(),\n generationId: z.string(),\n offer: z.unknown(),\n requestId: z.string().optional(),\n});\n\n/**\n * WebRTC answer message schema for personal room WebSocket.\n *\n * When sent by a client: `targetMachineId` is the intended recipient.\n * When relayed by the server: `fromMachineId` is added to identify the sender.\n *\n * `generationId` echoes the offerer's generationId so the offerer can\n * discard answers that don't correspond to its current PC.\n */\nexport const WebRTCAnswerSchema = z.object({\n type: z.literal('webrtc-answer'),\n targetMachineId: z.string(),\n fromMachineId: z.string().optional(),\n generationId: z.string(),\n answer: z.unknown(),\n requestId: z.string().optional(),\n});\n\n/**\n * WebRTC ICE candidate message schema for personal room WebSocket.\n *\n * When sent by a client: `targetMachineId` is the intended recipient.\n * When relayed by the server: `fromMachineId` is added to identify the sender.\n *\n * `generationId` identifies the PC that emitted this candidate. Receivers\n * drop candidates whose generationId doesn't match the PC currently tracked\n * for the sender.\n */\nexport const WebRTCIceSchema = z.object({\n type: z.literal('webrtc-ice'),\n targetMachineId: z.string(),\n fromMachineId: z.string().optional(),\n generationId: z.string(),\n candidate: z.unknown(),\n});\n\n/**\n * ICE server entry schema (matches RTCIceServer).\n */\nexport const IceServerSchema = z.object({\n urls: z.union([z.string(), z.array(z.string())]),\n username: z.string().optional(),\n credential: z.string().optional(),\n});\n\nexport type IceServer = z.infer<typeof IceServerSchema>;\n\n/**\n * Default ICE servers used when the server has not provided TURN credentials.\n * Centralized here so all consumers (browser, daemon, session-server) share\n * one source of truth for the fallback STUN URL.\n */\nexport const DEFAULT_ICE_SERVERS: IceServer[] = [{ urls: 'stun:stun.cloudflare.com:3478' }];\n\n/**\n * Server→client message delivering ICE server configuration (STUN + TURN).\n * Sent after authentication so clients have TURN credentials before any\n * peer connections are created.\n */\nexport const IceServersSchema = z.object({\n type: z.literal('ice-servers'),\n iceServers: z.array(IceServerSchema),\n});\n\n/**\n * Error message schema for WebSocket connections.\n */\nexport const ErrorMessageSchema = z.object({\n type: z.literal('error'),\n code: z.string(),\n message: z.string(),\n requestId: z.string().optional(),\n});\n\n/**\n * User settings schema — stored in CRDT, synced across daemons.\n */\nexport const UserSettingsSchema = z.object({\n worktreeSetupScript: z.string().nullable(),\n});\n\nexport type UserSettings = z.infer<typeof UserSettingsSchema>;\n\n/**\n * Browser requests daemon self-update via signaling relay.\n * Follows the WebRTC relay pattern: browser sends with targetMachineId,\n * server relays to daemon with fromMachineId added.\n */\nexport const RequestDaemonUpdateSchema = z.object({\n type: z.literal('request-daemon-update'),\n targetMachineId: z.string(),\n fromMachineId: z.string().optional(),\n installCommand: z.string(),\n});\n\nexport type RequestDaemonUpdate = z.infer<typeof RequestDaemonUpdateSchema>;\n\n/**\n * Daemon reports self-update progress via signaling relay.\n * Sent by daemon, server broadcasts to all browser connections.\n *\n * Status lifecycle — daemon-as-stager model:\n * downloading → downloaded → handoff → (daemon exits) → installing →\n * verifying → restarting → (new daemon boots) → succeeded|rolled-back|failed\n * The succeeded transition is reported by the NEW daemon on first authenticated\n * browser connection (see readAndClearUpdateStatus).\n */\nexport const DaemonUpdateStatusSchema = z.object({\n type: z.literal('daemon-update-status'),\n fromMachineId: z.string().optional(),\n status: z.enum([\n 'downloading',\n 'downloaded',\n 'handoff',\n 'installing',\n 'verifying',\n 'restarting',\n 'succeeded',\n 'rolled-back',\n 'failed',\n ]),\n error: z.string().optional(),\n targetVersion: z.string().optional(),\n previousVersion: z.string().optional(),\n});\n\nexport type DaemonUpdateStatus = z.infer<typeof DaemonUpdateStatusSchema>;\n\n/**\n * Union of all client-to-server messages for personal room WebSocket.\n */\nexport const PersonalRoomClientMessageSchema = z.discriminatedUnion('type', [\n RegisterAgentSchema,\n UnregisterAgentSchema,\n AgentStatusSchema,\n WebRTCOfferSchema,\n WebRTCAnswerSchema,\n WebRTCIceSchema,\n ErrorMessageSchema,\n RequestDaemonUpdateSchema,\n DaemonUpdateStatusSchema,\n]);\n\nexport type PersonalRoomClientMessage = z.infer<typeof PersonalRoomClientMessageSchema>;\n\n/**\n * Authentication success message schema for personal room WebSocket.\n */\nexport const AuthenticatedSchema = z.object({\n type: z.literal('authenticated'),\n userId: z.string(),\n username: z.string(),\n});\n\n/**\n * Agent info schema for personal room WebSocket.\n * Capabilities are no longer included -- they flow through Loro ephemeral.\n */\nexport const AgentInfoSchema = z.object({\n agentId: z.string(),\n machineId: z.string(),\n machineName: z.string(),\n agentType: z.string(),\n status: z.enum(['idle', 'running', 'error']),\n activeTaskId: z.string().optional(),\n protocolVersion: z.number().optional(),\n npmVersion: z.string().optional(),\n localDirect: LocalDirectAdvertisementSchema.optional(),\n});\n\nexport type AgentInfo = z.infer<typeof AgentInfoSchema>;\n\n/**\n * Agents list message schema for personal room WebSocket.\n */\nexport const AgentsListSchema = z.object({\n type: z.literal('agents-list'),\n agents: z.array(AgentInfoSchema),\n hasDaemonRegistered: z.boolean().optional(),\n});\n\n/**\n * Agent joined notification message schema for personal room WebSocket.\n */\nexport const AgentJoinedSchema = z.object({\n type: z.literal('agent-joined'),\n agent: AgentInfoSchema,\n});\n\n/**\n * Agent left notification message schema for personal room WebSocket.\n *\n * `connectionId` is minted by the PersonalRoom DO at WebSocket accept and\n * mirrored back on `agent-left` so daemons that route this message can\n * discriminate stale teardown events from a prior session (Invariant #2a).\n * Without it, a delayed `agent-left` arriving after the same agent has\n * re-registered would tear down the freshly-established peer.\n */\nexport const AgentLeftSchema = z.object({\n type: z.literal('agent-left'),\n agentId: z.string(),\n connectionId: z.string(),\n});\n\n/**\n * Browser left notification message schema for personal room WebSocket.\n *\n * Mirrors `agent-left` for browser sessions. The DO emits this on\n * `webSocketClose` for browser connections so the daemon can tear down\n * its WebRTC peer immediately rather than waiting for ICE timeout (~30s).\n *\n * `connectionId` is the DO-minted per-WebSocket discriminator used to\n * suppress stale leaves when the same machineId has reconnected with a\n * newer session (Invariant #2a).\n */\nexport const BrowserLeftSchema = z.object({\n type: z.literal('browser-left'),\n machineId: z.string(),\n connectionId: z.string(),\n});\n\n/**\n * Agent status changed notification message schema for personal room WebSocket.\n */\nexport const AgentStatusChangedSchema = z.object({\n type: z.literal('agent-status-changed'),\n agentId: z.string(),\n status: z.enum(['idle', 'running', 'error']),\n activeTaskId: z.string().optional(),\n /**\n * Mirror the daemon's local-direct advert on status updates so browsers\n * joining mid-session (or recovering from a signaling reconnect) always\n * see the authoritative port/token. The browser's status-merge path\n * preserves prior-frame fields via spread; making this explicit avoids\n * relying on that incidental behavior.\n */\n localDirect: LocalDirectAdvertisementSchema.optional(),\n});\n\n/**\n * Webhook event schema for events ingested from external services (e.g. Linear)\n * and forwarded to connected daemons via the PersonalRoom WebSocket.\n */\nexport const WebhookEventSchema = z.object({\n type: z.literal('webhook-event'),\n source: z.string(),\n event: z.string(),\n payload: z.record(z.string(), z.unknown()),\n timestamp: z.number(),\n});\n\nexport type WebhookEvent = z.infer<typeof WebhookEventSchema>;\n\n/**\n * Union of all server-to-client messages for personal room WebSocket.\n */\nexport const PersonalRoomServerMessageSchema = z.discriminatedUnion('type', [\n AuthenticatedSchema,\n AgentsListSchema,\n AgentJoinedSchema,\n AgentLeftSchema,\n BrowserLeftSchema,\n AgentStatusChangedSchema,\n ErrorMessageSchema,\n WebRTCOfferSchema,\n WebRTCAnswerSchema,\n WebRTCIceSchema,\n IceServersSchema,\n WebhookEventSchema,\n RequestDaemonUpdateSchema,\n DaemonUpdateStatusSchema,\n]);\n\nexport type PersonalRoomServerMessage = z.infer<typeof PersonalRoomServerMessageSchema>;\n\n/**\n * WebRTC offer message schema for collab room WebSocket.\n *\n * `generationId` is minted per-PeerConnection by the offerer so both sides\n * can discriminate messages from a prior PC generation after teardown and\n * replacement. Mirrors the personal-room pattern; without it, stale ICE or\n * answers from a pre-reconnect PC silently contaminate the successor PC's\n * state (see PR #2244 and the 2026-04-22 collab-refresh deadlock incident).\n */\nexport const CollabWebRTCOfferSchema = z.object({\n type: z.literal('webrtc-offer'),\n targetUserId: z.string(),\n fromUserId: z.string().optional(),\n generationId: z.string(),\n offer: z.unknown(),\n});\n\n/**\n * WebRTC answer message schema for collab room WebSocket.\n *\n * Echoes the offerer's `generationId` so the offerer can drop answers that\n * don't correspond to its current PC.\n */\nexport const CollabWebRTCAnswerSchema = z.object({\n type: z.literal('webrtc-answer'),\n targetUserId: z.string(),\n fromUserId: z.string().optional(),\n generationId: z.string(),\n answer: z.unknown(),\n});\n\n/**\n * WebRTC ICE candidate message schema for collab room WebSocket.\n *\n * `generationId` identifies the PC that emitted this candidate. Receivers\n * drop candidates whose generationId doesn't match the PC currently tracked\n * for the sender.\n */\nexport const CollabWebRTCIceSchema = z.object({\n type: z.literal('webrtc-ice'),\n targetUserId: z.string(),\n fromUserId: z.string().optional(),\n generationId: z.string(),\n candidate: z.unknown(),\n});\n\n/**\n * Union of all client-to-server messages for collab room WebSocket.\n */\nexport const CollabRoomClientMessageSchema = z.discriminatedUnion('type', [\n CollabWebRTCOfferSchema,\n CollabWebRTCAnswerSchema,\n CollabWebRTCIceSchema,\n]);\n\nexport type CollabRoomClientMessage = z.infer<typeof CollabRoomClientMessageSchema>;\n\n/**\n * Participant info schema for collab room WebSocket.\n *\n * `connectionId` is minted by the CollabRoom DO at each WebSocket accept\n * (per-connection, opaque to clients). Clients track it per-userId so they\n * can discard stale `participant-left` events from a prior session that\n * arrive after the same user has rejoined with a newer connection — the\n * root cause of the 2026-04-22 collab-refresh deadlock. Consumers MUST\n * record the latest connectionId they've seen for each userId and match\n * against incoming leave events before tearing down peer state.\n */\nexport const ParticipantSchema = z.object({\n userId: z.string(),\n username: z.string(),\n avatarUrl: z.string().nullable().optional(),\n role: ParticipantRoleSchema,\n connectionId: z.string(),\n /**\n * True when this participant is the collab room's WebRTC hub — the sole\n * peer that other browsers open a PeerConnection to. Always derived by\n * the CollabRoom DO from `role === 'owner'` at message emission time,\n * never stored independently. Browsers MUST find the hub via this flag\n * (not by inspecting role) so the rule lives in one place server-side.\n * See AGENTS.md Invariant #15.\n */\n isHub: z.boolean(),\n});\n\nexport type Participant = z.infer<typeof ParticipantSchema>;\n\n/**\n * Authentication success message schema for collab room WebSocket.\n */\nexport const CollabAuthenticatedSchema = z.object({\n type: z.literal('authenticated'),\n userId: z.string(),\n username: z.string(),\n taskId: z.string(),\n});\n\n/**\n * Participants list message schema for collab room WebSocket.\n */\nexport const ParticipantsListSchema = z.object({\n type: z.literal('participants-list'),\n participants: z.array(ParticipantSchema),\n});\n\n/**\n * Participant joined notification message schema for collab room WebSocket.\n */\nexport const ParticipantJoinedSchema = z.object({\n type: z.literal('participant-joined'),\n participant: ParticipantSchema,\n});\n\n/**\n * Participant left notification message schema for collab room WebSocket.\n *\n * `connectionId` identifies WHICH connection left. When a user refreshes,\n * the DO's webSocketClose may fire after the refreshed tab has already\n * joined with a new connection — consumers drop a leave whose connectionId\n * doesn't match the latest one tracked for that userId. This is the\n * stale-disconnect discriminator that prevents ghost-teardown of the\n * freshly-established peer.\n */\nexport const ParticipantLeftSchema = z.object({\n type: z.literal('participant-left'),\n userId: z.string(),\n connectionId: z.string(),\n});\n\n/**\n * Union of all server-to-client messages for collab room WebSocket.\n */\nexport const CollabRoomServerMessageSchema = z.discriminatedUnion('type', [\n CollabAuthenticatedSchema,\n ParticipantsListSchema,\n ParticipantJoinedSchema,\n ParticipantLeftSchema,\n ErrorMessageSchema,\n CollabWebRTCOfferSchema,\n CollabWebRTCAnswerSchema,\n CollabWebRTCIceSchema,\n IceServersSchema,\n]);\n\nexport type CollabRoomServerMessage = z.infer<typeof CollabRoomServerMessageSchema>;\n\n/**\n * GitHub issue type for feedback categorization.\n */\nexport const GitHubIssueTypeSchema = z.enum(['bug', 'feature', 'question', 'praise']);\nexport type GitHubIssueType = z.infer<typeof GitHubIssueTypeSchema>;\n\n/**\n * POST /feedback/issue request body schema.\n */\nexport const GitHubIssueCreateRequestSchema = z.object({\n type: GitHubIssueTypeSchema,\n title: z.string().min(1, 'title is required').max(256, 'title must be 256 chars or fewer'),\n description: z.string().max(10000).optional(),\n logs: z.string().max(50000).optional(),\n images: z.array(FeedbackImageSchema).max(3).optional(),\n context: z\n .object({\n url: z.string().optional(),\n taskId: z.string().optional(),\n userAgent: z.string().optional(),\n timestamp: z.string().optional(),\n })\n .optional(),\n});\n\nexport type GitHubIssueCreateRequest = z.infer<typeof GitHubIssueCreateRequestSchema>;\n\n/**\n * POST /feedback/issue response schema.\n */\nexport const GitHubIssueCreateResponseSchema = z.object({\n issueUrl: z.string(),\n issueNumber: z.number(),\n});\n\nexport type GitHubIssueCreateResponse = z.infer<typeof GitHubIssueCreateResponseSchema>;\n\n/**\n * POST /auth/linear/callback request body schema.\n *\n * Used to exchange a Linear OAuth code for Linear access tokens.\n * Requires an authenticated Shipyard JWT.\n */\nexport const AuthLinearCallbackRequestSchema = z.object({\n /** Linear OAuth authorization code */\n code: z.string().min(1, 'code is required'),\n /** OAuth redirect URI that was used in the authorize request */\n redirect_uri: z.string().url('redirect_uri must be a valid URL'),\n});\n\nexport type AuthLinearCallbackRequest = z.infer<typeof AuthLinearCallbackRequestSchema>;\n\n/**\n * POST /auth/linear/callback response schema.\n *\n * Returns Linear OAuth tokens and user info on successful OAuth.\n */\nexport const AuthLinearCallbackResponseSchema = z.object({\n /** Linear access token */\n linearAccessToken: z.string(),\n /** OAuth refresh token, when Linear returns one (requires offline_access scope) */\n refreshToken: z.string().optional(),\n /** Seconds until the access token expires (from Linear's token response) */\n expiresIn: z.number().optional(),\n /** Linear user info */\n linearUser: z.object({\n id: z.string(),\n name: z.string().nullable(),\n email: z.string().nullable(),\n avatarUrl: z.string().nullable(),\n }),\n});\n\nexport type AuthLinearCallbackResponse = z.infer<typeof AuthLinearCallbackResponseSchema>;\n\n/** POST /auth/device/start — no request body needed */\nexport const DeviceStartResponseSchema = z.object({\n deviceCode: z.string(),\n userCode: z.string(),\n verificationUri: z.string().url(),\n expiresIn: z.number(),\n interval: z.number(),\n});\nexport type DeviceStartResponse = z.infer<typeof DeviceStartResponseSchema>;\n\n/** POST /auth/device/poll request */\nexport const DevicePollRequestSchema = z.object({\n deviceCode: z.string().min(1, 'deviceCode is required'),\n});\nexport type DevicePollRequest = z.infer<typeof DevicePollRequestSchema>;\n\n/** POST /auth/device/poll response (success) */\nexport const DevicePollResponseSchema = z.object({\n token: z.string(),\n user: OAuthUserSchema,\n});\nexport type DevicePollResponse = z.infer<typeof DevicePollResponseSchema>;\n\n/** POST /auth/device/poll response (pending/errors) */\nexport const DevicePollPendingSchema = z.object({\n error: z.enum(['authorization_pending', 'slow_down', 'expired_token']),\n});\nexport type DevicePollPending = z.infer<typeof DevicePollPendingSchema>;\n\n/** POST /auth/device/authorize — browser authorizes a device using its own JWT */\nexport const DeviceAuthorizeRequestSchema = z.object({\n userCode: z.string().min(1, 'userCode is required'),\n});\nexport type DeviceAuthorizeRequest = z.infer<typeof DeviceAuthorizeRequestSchema>;\n\nexport const DeviceAuthorizeResponseSchema = z.object({\n authorized: z.boolean(),\n});\nexport type DeviceAuthorizeResponse = z.infer<typeof DeviceAuthorizeResponseSchema>;\n\n/** POST /auth/device/exchange-code — daemon exchanges userCode for deviceCode */\nexport const DeviceExchangeCodeRequestSchema = z.object({\n userCode: z.string().min(1, 'userCode is required'),\n});\nexport type DeviceExchangeCodeRequest = z.infer<typeof DeviceExchangeCodeRequestSchema>;\n\nexport const DeviceExchangeCodeResponseSchema = z.object({\n deviceCode: z.string(),\n});\nexport type DeviceExchangeCodeResponse = z.infer<typeof DeviceExchangeCodeResponseSchema>;\n\n/** POST /auth/browser-code — daemon requests a one-time code for browser auth */\nexport const BrowserCodeResponseSchema = z.object({\n code: z.string(),\n expiresIn: z.number(),\n});\nexport type BrowserCodeResponse = z.infer<typeof BrowserCodeResponseSchema>;\n\n/** POST /auth/browser-code/exchange — browser exchanges code for JWT */\nexport const BrowserCodeExchangeRequestSchema = z.object({\n code: z.string().min(1, 'code is required'),\n});\nexport type BrowserCodeExchangeRequest = z.infer<typeof BrowserCodeExchangeRequestSchema>;\n\nexport const BrowserCodeExchangeResponseSchema = z.object({\n token: z.string(),\n user: OAuthUserSchema,\n});\nexport type BrowserCodeExchangeResponse = z.infer<typeof BrowserCodeExchangeResponseSchema>;\n\n/** GET /vault/key response */\nexport const VaultKeyGetResponseSchema = z.object({\n encryptedKey: z.string(),\n createdAt: z.number(),\n});\nexport type VaultKeyGetResponse = z.infer<typeof VaultKeyGetResponseSchema>;\n\n/** PUT /vault/key request */\nexport const VaultKeyPutRequestSchema = z.object({\n encryptedKey: z.string().min(1, 'encryptedKey is required').max(4096),\n});\nexport type VaultKeyPutRequest = z.infer<typeof VaultKeyPutRequestSchema>;\n\n/** PUT /vault/key response (created) */\nexport const VaultKeyPutResponseSchema = z.object({\n created: z.boolean(),\n});\nexport type VaultKeyPutResponse = z.infer<typeof VaultKeyPutResponseSchema>;\n\n/** Metrics ingestion */\n\nexport const MetricsEventSchema = z.object({\n eventType: z.string().min(1).max(100),\n taskId: z.string().max(200).optional(),\n payload: z.record(z.unknown()).default({}),\n clientTimestamp: z.number().int().positive(),\n});\nexport type MetricsEvent = z.infer<typeof MetricsEventSchema>;\n\nexport const MetricsIngestRequestSchema = z.object({\n events: z.array(MetricsEventSchema).min(1).max(100),\n});\nexport type MetricsIngestRequest = z.infer<typeof MetricsIngestRequestSchema>;\n\nexport const MetricsIngestResponseSchema = z.object({\n accepted: z.number(),\n});\nexport type MetricsIngestResponse = z.infer<typeof MetricsIngestResponseSchema>;\n","/**\n * Monotonic protocol version for daemon↔browser compatibility.\n *\n * Bump ONLY on breaking changes to the communication protocol between\n * the daemon and browser (new/removed message types, changed ephemeral\n * schemas, etc.). Feature releases that don't break the protocol do\n * NOT bump this number.\n *\n * The browser hardcodes a minimum acceptable version. When a daemon\n * connects with a lower version, the browser shows a blocking update\n * modal.\n */\n/** Bump 80 → 81 for ReviewSection groups/rules schema migration (v9 → v10). */\n/** Bump 81 → 82 for Codex browser UI + serve-factory permission wiring (codex c10). */\n/** Bump 82 → 83 for Codex onboarding completion (codex c12-c15). */\n/** Bump 83 → 84 for button-driven codex install (codex c16). */\n/** Bump 84 → 85 for model picker section grouping + daemon-side model meta. */\n/** Bump 85 → 86 for spawn router agent-unavailable rejection. */\n/** Bump 86 → 87 for per-agent brand color on AgentIconRef. */\n/** Bump 87 → 88 for sub-task spawn parity on agent_not_authenticated + create_task.model. */\n/** Bump 88 → 89 for runtime-seam capability payloads + thread-local settings control flow. */\n/** Bump 89 → 90 for project color persistence control message. */\n/** Bump 90 → 91 for skill_invocation ContentBlock variant (user-authored skill chips). */\n/** Bump 91 → 92 for legacy permission_mode_restart_required control message (removed from active emission in v96). */\n/** Bump 92 → 93 for auth_refresh_failed control message (codex parity followup C5). */\n/** Bump 93 → 94 for providerExtras discriminated sub-bag on TurnResult/PersistedTurnStats (Pattern D). Additive — MIN_PROTOCOL_VERSION stays at 91. */\n/**\n * Bump 94 → 95 for slim `annotation_snapshot` + lazy-fetch\n * `request_annotation_detail` / `annotation_detail_snapshot` pair. The\n * four heavy DOM-detail fields (elementHtml, cssClasses, computedStyles,\n * pageBackground) on PreviewElementAnnotation and CanvasSubElementAnnotation\n * are now optional on the wire. Hard MIN bump: a pre-v95 daemon would\n * include the unbounded strings inline and trip SCTP-frame overflow on\n * large annotation sets (same root-cause as the v85 task_index_snapshot\n * regression).\n */\n/**\n * Bump 95 → 96 for Codex commandExecution + fileChange approval kinds.\n * `PermissionRequestCodexPayloadSchema` gains `kind`, `command`,\n * `grantRoot`, and structured `requestedPaths` (the parsed\n * `RequestPermissionProfile` shape). Pre-v96 senders default to\n * `kind: 'permissions'` and omit the new fields; new browser falls back\n * to legacy behaviour. Purely additive — MIN stays at 92.\n *\n * Bump 96 → 97 for the MCP mid-thread reload hint + Codex thread restart on\n * MCP change. Two new message types:\n * - `mcp_mid_thread_reload_hint` (daemon → browser): emitted when the\n * coordinator tries to push an MCP set that diverges from Codex's\n * spawn-time staged set on a profile with `canMidSessionMcpReload ===\n * false`. Surfaces as a \"Restart Codex to apply MCP changes\" banner\n * with diff chips (added/removed server names).\n * - `request_codex_thread_restart` (browser → daemon): user clicked the\n * banner's Restart CTA. Daemon routes through the existing\n * `#runRestart` machinery (`reason: 'mcp-reload'`) which archives the\n * active thread and starts a new one with the current MCP set,\n * replaying the last user content. Same archive+replay shape as\n * sandbox-extension and permission-mode-change restarts.\n * Purely additive — pre-v97 clients ignore both unknown variants. MIN\n * stays at 92.\n */\n/**\n * Bump 97 → 98 for additive browser_throttled_detected + browser_throttled_cleared\n * variants (browser → daemon). Pure observability; MIN_PROTOCOL_VERSION unchanged.\n */\n/**\n * Bump 98 → 99 for lazy-load skills cutover. Skills move out of the inline\n * `capabilities.skills` payload onto a dedicated channel pair:\n * - `request_skills` (browser → daemon): one-shot request, fired on\n * control-channel open\n * - `skills_snapshot` (daemon → browser): response + proactive broadcast\n * on capability-watcher skill events\n * Daemon stops populating `capabilities.skills` (kept as an optional field\n * in the schema for graceful downgrade — pre-v99 browsers see `undefined`\n * and fall back to an empty skill list). Closes the\n * O(worktrees × skills_per_repo) bloat that exceeded the 5 MiB daemon-control\n * channel cap on power users (root cause of #composer-bricked, 2026-05-22:\n * 6.16 MB skills payload / 10,661 entries from 700 worktree-instances).\n * MIN stays at 92 — additive on the wire, browser falls back to lazy-load\n * store; old browsers reading from v99 daemon see empty skills (degraded,\n * not corrupted) until they upgrade.\n */\n/**\n * 100: Rate-limit gate & sticky-banner fixes.\n * - `task_error_cleared { taskId }` (daemon → browser): broadcast on\n * `turn_complete` so a sticky usage-limit banner clears once the agent\n * produces a successful turn.\n * - `subprocess_died` adapter event gains optional `syntheticReason` so\n * spawn-rejection stub deaths are distinguishable from real subprocess\n * deaths in daemon logs.\n * - `RateLimitInfoSchema` gains optional `firstRejectedAt` for the\n * bounded synthetic 15s pre-flight window.\n * Additive across the wire — MIN stays at 92.\n *\n * 101: Shipyard-owned auto-compaction control.\n * - `cancel_compact_session` (browser → daemon) cancels the pending\n * 3-second auto-compaction window\n * - `compaction_status: pending|cancelled` lets the browser render and\n * clear the above-composer cancel toast\n * Additive across the wire — MIN stays at 92.\n *\n * 102: Codex `item/tool/requestUserInput` handler. Extends\n * `PermissionRequestCodexPayloadSchema.kind` with `'input'` and adds an\n * `inputQuestions` array carrying the Codex questions in normalized form\n * (id + header + question + options). The corresponding\n * `permission_response_codex` schema gains an `inputAnswers` map keyed by\n * the preserved question id. Also wires `item/tool/call` (dynamicToolCall)\n * to a no-op decline so Codex doesn't see `-32601 No client handler` for\n * dynamic-tool invocations (Shipyard does not register dynamic tools). The\n * permission flow reuses the existing browser surface — the QuestionComposer\n * — so no new component is shipped. Purely additive on the wire — MIN stays\n * at 92, pre-v102 daemons never emit `kind: 'input'` and old browsers\n * already reject unknown kinds via Zod (no degradation, just no Codex input\n * flow until upgrade).\n *\n * 103: Codex permission_request_codex hardening — `PermissionRequestCodexPayloadSchema`\n * refactored from a flat `z.object` with optional kind-tagged fields\n * to a `z.discriminatedUnion('kind', ...)` so per-kind required\n * fields are enforced at parse time. Concretely: `kind: 'input'`\n * now REQUIRES `inputQuestions: z.array(...).min(1)`; `kind:\n * 'permissions'` REJECTS having `inputQuestions` set; same mirror\n * for `command` / `grantRoot`. Also drops the `.default('permissions')`\n * — the wire now requires an explicit `kind`. The\n * `permission_response_codex.inputAnswers` schema is tightened the\n * same way: each inner array `.min(1)` plus a non-empty outer\n * record `.refine`, blocking the empty-answer silent-approval\n * bug class. Also reshapes the browser-side answer routing to\n * carry the full ordered `codexInputQuestions: Array<{id,\n * question}>` on the PermissionRequest so duplicate question\n * text never silently overwrites prior id mappings.\n * Hard MIN bump — pre-v102 daemons emit no `kind`, the v103\n * browser's strict discriminated-union parse rejects them. There\n * is no graceful-downgrade path because the schema is now closed.\n * Also: a v103 browser sends the position-ordered\n * `codexInputQuestions` map shape implicitly via reuse of the\n * existing wire field, but the daemon-side\n * `handleCodexUserInputResponse` now hard-rejects approved\n * responses with missing/empty `inputAnswers`, so a pre-v103\n * browser that mis-routes an empty answer set sees a clear error\n * instead of silently advancing Codex with no input.\n *\n * 104: File attachment content block — adds `FileAttachmentBlockSchema`\n * (`type: 'file_attachment'`, `assetId`, `filename`, `mediaType`,\n * `sizeBytes`, optional `preview`) to `ContentBlockSchema`. The block is\n * produced by the browser when a user attaches a non-image file via the\n * plus-button or drag-and-drop; the daemon resolves `assetId` to feed the\n * content to the SDK. Additive — MIN stays at 103: old daemons never emit\n * `file_attachment` blocks and new browsers forward them; old browsers\n * receiving `file_attachment` from a new daemon will fail to parse the\n * block (discriminated union is closed), but that scenario doesn't arise\n * because daemons don't produce file_attachment blocks autonomously.\n * No MIN bump required.\n *\n * 105: Native folder-picker dialog RPC — adds `open_folder_dialog`\n * (browser→daemon) and `folder_dialog_result` (daemon→browser). The\n * browser sends `open_folder_dialog` with a `requestId`; the daemon\n * relays to Electron main via parentPort IPC (same pattern as\n * `safe-storage:*`) and replies with `folder_dialog_result`. On\n * non-Electron daemons the result is always cancelled. Additive —\n * MIN stays at 103: old daemons never emit `folder_dialog_result`\n * (old browser just never resolves the pending request). No MIN bump.\n *\n * 106: Add Cursor agent profile (AGENT_IDS extension, CursorIcon,\n * cursor-hook-allow / cursor-hook-deny-writes NativeApprovalPolicy variants,\n * permission_request_cursor / permission_response_cursor message types,\n * cursor_login_request / cursor_login_status / cursor_logout_request /\n * cursor_logout_status control messages, cursorAuth capability field,\n * rateLimitShape 'cursor', AGENT_SYSTEM_CURSOR participant constant,\n * CursorAuthMethodSchema). runtimeAuth['cursor'] map is the canonical\n * auth status route. MIN stays at 103 (set by v103) — cursor changes\n * are additive; older browsers receiving agentId:'cursor' messages are\n * gated by the daemon on negotiatedProtocolVersion >= 106.\n *\n * 107: Cursor MCP bridge — Cursor agents now receive workspace MCP servers\n * (Linear, Figma, plugin stdio servers, user `.mcp.json` entries) at spawn\n * time via the coordinator's local-HTTP loopback swap (same trust model as\n * Codex). Proxy-managed servers are swapped for `{type:'http', url,\n * headers:{Authorization:\"Bearer <token>\"}}` entries using the\n * `#applyCursorProxyHttpSwap` path in McpCoordinator. Mid-thread changes\n * emit `mcp_mid_thread_reload_needed` and require a Cursor agent restart\n * (no live reload — Cursor SDK 1.0.13 has no `agent.setMcpServers` API).\n * Additive — MIN stays at 103: Cursor MCP is a new capability on top of\n * the existing Cursor profile; older browsers receiving a Cursor task\n * with MCP servers see the same behavior as before this change.\n *\n * 108: Cursor permission bridge — `permission_response_cursor` gains optional\n * `updatedInput` (mutated tool args mirrored from Cursor SDK's hook\n * `updated_input`) and `persist: 'session' | 'once'` (session-scoped\n * sticky-allow per (taskId, toolName)). Both fields are additive; pre-v108\n * browsers send neither and the daemon treats absence as `persist: 'once'`\n * with no edit. Pre-v108 daemons strip unknown fields via Zod's default\n * `strip` behavior, falling through to single-use no-edit semantics. MIN\n * stays at 103. Pairs with the new daemon-side Cursor hook socket server +\n * shim binary that round-trips per-tool permission decisions through the\n * existing `permission_request_cursor` UI.\n *\n * 109: File-attachment block becomes load-bearing — composer drag-drop and\n * the plus-button file picker now classify uploaded files via\n * `classifyAttachment()` and emit `file_attachment` blocks (previously the\n * block schema existed but the composer only emitted bare `resource_link`\n * references to file paths). `FileAttachmentBlockSchema.assetId` becomes\n * optional so inline-text files (≤256 KB) can carry their content in\n * `preview` alone, while larger files (256 KB – 25 MB) upload via the\n * asset-transfer channel and reference the asset by `assetId`. Files\n * >25 MB are rejected at the browser composer with an inline aria-live\n * banner. Additive — pre-v109 browsers emit only `resource_link` blocks;\n * pre-v109 daemons that emitted `file_attachment` blocks always set\n * `assetId`, so the optional widening is backwards-compatible. MIN stays\n * at 103.\n *\n * 110: Per-task MCP isolation — `composerSettings.mcpServerSnapshot?` added\n * to `DaemonSettingsSchema` and `ComposerSettingsSchema` (TaskRecord). New\n * tasks freeze the workspace's known-server set at creation; the daemon's\n * resolver pushes ONLY snapshot members so workspace-added servers cannot\n * leak into running tasks (subscription-auth bulk sync, .mcp.json edits).\n * Pre-v110 daemons / browsers omit the field — daemon falls back to legacy\n * workspace-union resolution, so this is fully backwards-compatible.\n * MIN stays at 103.\n */\n/**\n * Bump 110 → 111 for AgentCapabilityProfile.compaction field +\n * UserSettingsRecord.compactionByRuntime + ComposerSettings.compactionPrefs\n */\n/**\n * Bump 111 → 112 for `compactionCapabilities` on the `profile_snapshot`\n * wire shape (`AgentCapabilityProfileSummary`). The daemon's\n * `summarizeAgentProfile` now projects each profile's\n * `compaction.capabilities` (data-only subset — event matchers stay\n * daemon-side) onto the summary so the browser drives compaction-control\n * UI gating from the daemon's single source of truth instead of a\n * browser-side hardcoded per-runtime capability matrix (now deleted).\n * Additive on the wire — pre-v112 daemons omit the field and the v112\n * browser falls back conservatively (treats the runtime as managing its\n * own context window, i.e. no compaction controls) rather than\n * re-introducing the static table. MIN_PROTOCOL_VERSION stays at 103.\n */\n/**\n * Bump 112 → 113 for `DaemonSettingsSchema.compactionPrefs`: the per-task\n * compaction override is now a first-class field on the update_task_settings\n * wire message, completing the browser → daemon round-trip for the override\n * sheet. Additive/optional — pre-v113 daemons ignore the field; MIN stays\n * at 103.\n */\n/**\n * Bump 113 → 114 for the shared Codex `app-server` engine migration: ONE\n * shared `codex app-server` process per daemon (single `~/.codex/auth.json`\n * token owner — fixes the 401-storm), each task on its own thread. Changes\n * are daemon-internal (codex transport: per-thread server-request dispatch,\n * per-thread sinks, bearer moved from `bearer_token_env_var` to\n * `http_headers.Authorization`, and an added optional `threadId` on the codex\n * MCP-elicitation server-request schema). The daemon↔browser wire shape is\n * unchanged; this bump satisfies the file-path CI guard for edits under\n * `packages/session/src/` + `apps/daemon/src/`. MIN stays at 103.\n */\n/**\n * Bump 114 → 115 for `handoff_status` message variant for cross-runtime\n * import outcomes. The daemon emits this when a per-runtime import path\n * (Claude sessionStore prep, Codex thread/inject_items, Cursor SQLite blob\n * injection) succeeds OR fails — and on the upstream stash-persistence\n * step that runs before per-runtime import. Browser handles `result:\n * 'failed'` by surfacing a danger banner so the user knows the destination\n * runtime started with an empty context (bridge-failed state per the\n * cross-runtime handoff state machine). Additive on the wire — pre-v115\n * daemons never emit it, pre-v115 browsers drop the unknown variant via\n * the discriminated-union parser. MIN_PROTOCOL_VERSION stays at 103.\n */\n/**\n * Bump 115 → 116 for the compaction reliability overhaul (Wave 1 — daemon +\n * protocol core). Three additive wire changes:\n * - `dismiss_compaction_failure { taskId }` (browser → daemon): clears a\n * stuck `failed` compaction banner (Retry/dismiss CTA) by driving the\n * compaction SM `failed → idle`. The SM's only prior exit\n * (`user_dismiss_failure`) had zero dispatch sites, so a failed\n * compaction wedged the banner until a daemon restart.\n * - `startedAt?: number` on `compaction_status` (daemon → browser):\n * epoch ms the compaction entered `compacting`, populated on the\n * `started` status so the browser can render a ticking\n * \"Compacting · Ns elapsed\" counter.\n * - `mode?: 'direct' | 'compact-then-bridge' | 'cursor-source-lossy'` on\n * `handoff_status` (daemon → browser): the handoff path the daemon\n * actually chose, so the browser banner reflects the real decision\n * (Wave 2b wires the emit).\n * - `status?: 'completed' | 'failed'` on the `compaction_boundary`\n * ContentBlock (JSONL + CRDT): the boundary card now records the real\n * outcome, written on the SM's completion transition rather than\n * optimistically before the compact runs.\n * All additive — pre-v116 daemons never emit the new fields/message and\n * pre-v116 browsers drop the unknown `dismiss_compaction_failure` request /\n * ignore the new optional fields. Absent `compaction_boundary.status` reads\n * as `'completed'` for back-compat (every pre-existing boundary block was a\n * successful compaction). MIN_PROTOCOL_VERSION stays at 103.\n */\n/**\n * Bump 116 → 117 (cursor-slow-to-start) for the `run_stall_timeout`\n * `TaskErrorSubKind` emitted when the Cursor stall watchdog fires. Additive\n * enum value on the task-error wire — a new daemon only emits it after the\n * 90s watchdog trips on a wedged Cursor run, and it pairs with a new browser\n * (web + daemon promote together). MIN_PROTOCOL_VERSION stays at 103: a\n * genuine stall already surfaces as a generic `sdk_error` to older browsers\n * that don't recognize the subkind, so no recovery UI is lost.\n *\n * Bump 117 → 118 for the modelHint redesign.\n * - `set_task_capabilities` wire message: `model` field renamed to\n * `modelHint` on `TaskCapabilitiesSchema` (and `StructuredTaskCapabilities`\n * / `StructuredTask`). The field is now free-form natural-language\n * guidance — advisory only, NEVER auto-applied as settings.model.\n * The user's chosen model always wins.\n * - TASK_STORE_VERSION 30 → 31: on-disk `model` keys renamed to `modelHint`\n * across capabilitiesOverrides, userTasks, and structuredTasks.\n * Breaking wire shape: pre-v118 daemons send `model` on set_task_capabilities;\n * a v118 browser's Zod parse strips the unknown field so the hint is silently\n * lost (not corrupted). Pre-v118 browsers send `model` which a v118 daemon\n * strips. Both directions degrade gracefully to \"no hint\" rather than corrupt\n * state — MIN_PROTOCOL_VERSION stays at 103. Existing data is migrated\n * forward by migrateTaskStore's v31 block (value preserved as hint text).\n *\n * Bump 118 → 119 for CI panel removal.\n * - `PRStatePayloadSchema`: removed `deployments` (EnvironmentDeployment[])\n * and `workflowRuns` (WorkflowRun[]) fields. The CI/delivery-status tab\n * is being repurposed as a Discover CTA; these fields are no longer\n * produced by the daemon or consumed by the browser. `requiredChecks`\n * (string[]) is retained for the PR checks panel.\n * - A pre-v119 daemon still emits these fields; a v119 browser ignores\n * them (Zod strips unknown fields on parse). A v119 daemon emits neither\n * field; a pre-v119 browser sees undefined where it expected arrays —\n * the CI tab it was rendering is now gone so no visible degradation.\n * MIN_PROTOCOL_VERSION stays at 103.\n *\n * Bump 119 → 120 for cross-model rewind capability broadcast.\n * - `AgentCapabilityProfileSummary` (profile_snapshot control message) gains\n * `rewindSupports: RewindTargetKind[]` — the data-only projection of the\n * daemon-side `profile.rewind.supports` Set. The browser gates the\n * conversation rewind/checkpoint divider on this (any non-`fresh` kind)\n * instead of the lossy Claude-only `canRewindByMessage` boolean.\n * Additive + optional: pre-v120 daemons omit the field and the browser falls\n * back to `canRewindByMessage`; pre-v120 browsers ignore the unknown field.\n * Both directions degrade gracefully — MIN_PROTOCOL_VERSION stays at 103.\n *\n * Bump 120 → 121 for the `plan_detection_failed` fanout variant (#4075/#3488).\n * - Decoupling plan-file detection from ExitPlanMode means an ExitPlanMode\n * observed with no plan file now emits an explicit `plan_detection_failed`\n * (reason `no_plan_file` | `not_in_plan_mode`) instead of a silent drop.\n * Additive: pre-v121 browsers hit the exhaustive-switch `default` no-op log and\n * ignore the unknown type; the daemon never relies on a reply. Transient\n * notice — not persisted/replayed on reconnect. MIN_PROTOCOL_VERSION stays at 103.\n *\n * Bump 121 → 122 for customizable spinner verbs.\n * - UserSettings gains `spinnerVerbs: string[]` + `hasSeededSpinnerVerbs:\n * boolean` (carried on user_settings_snapshot / user_settings_updated).\n * - New browser → daemon control message `reimport_spinner_verbs`.\n * Additive: pre-v122 daemons omit the fields and the browser falls back to the\n * embedded DEFAULT_SPINNER_VERBS; pre-v122 browsers ignore the unknown field\n * and never send the new message. MIN_PROTOCOL_VERSION stays at 103.\n *\n * Bump 122 → 123 for file-IO CRUD request variants (IDE-surface U0).\n * - Browser → daemon file-IO gains `mkdir`, `delete_file`, `rename_file`,\n * and `move_file`; daemon → browser gains `file_operation_success`.\n * - Browser-sent closed-union additions are not a graceful downgrade:\n * pre-v123 daemons reject the new variants without a response, leaving\n * the editor waiting for timeout. Bump MIN_PROTOCOL_VERSION to 123.\n *\n * Bump 123 → 124 for the diff/editor review surface git-history reads (IDE-surface U4).\n * - `BrowserToFileIOMessage` gains `git_blame` and `git_log_file` requests;\n * `DaemonToFileIOMessage` gains `git_blame_result` (per-line attribution\n * hunks) and `git_log_file_result` (per-file commit timeline).\n * Purely additive new message variants on the file-io data channel. A pre-v124\n * daemon never receives the new requests (the browser only sends them when the\n * blame gutter / history panel is opened, both new UI); a pre-v124 browser\n * never asks, so the daemon never emits the new results. MIN stays at 123.\n *\n * Bump 124 → 125 for the additive `mcp_state_snapshot` daemon→browser message:\n * one daemon-authoritative resolved MCP view per scope (workspace-default when\n * `taskId` absent, per-task resolved view when present) that the browser\n * mirrors directly, replacing its 4-source triangulation. Additive — the\n * legacy `mcp_server_status` / `task_session_mcps` pushes still fire and the\n * browser falls back to triangulation when no snapshot has arrived; pre-v125\n * browsers drop the unknown variant. MIN stays at 123 (unchanged — additive).\n *\n * Bump 125 → 126 for the additive cross-task inbox resync on reconnect:\n * browser→daemon `request_inbox_snapshot` and daemon→browser `inbox_snapshot`\n * (current unresolved-notification set across all tasks). Restores stale\n * inbox rows that went un-cleared because per-task `notification_*` broadcasts\n * are dropped while disconnected and only the active task is re-fetched on\n * reconnect (Architectural Invariant #2). Additive — a pre-v126 daemon never\n * receives the request (the browser only sends it on personal dc-open) and a\n * pre-v126 browser never asks, so neither emits the new variant. MIN stays at\n * 123 (unchanged — additive).\n *\n * Bump 126 → 127 for the 2-axis MCP read-model split: each `mcp_state_snapshot`\n * row gains `visible` (pure per-scope visibility = this scope's user intent,\n * distinct from `connectionStatus` which is global connection/auth health) and\n * `terminalReason: 'unsupported'` (DCR-incompatible servers, rendered calmly).\n * `enabled` is retained as the legacy AND (transport-up && visible) so existing\n * consumers keep working; the new picker reads `connectionStatus` for the dot\n * and `visible` for the toggle. Additive — `visible` is OPTIONAL on the wire so a\n * v127 browser still accepts snapshots from a v123-126 daemon (which omit it) and\n * falls back to `enabled`; pre-v127 browsers drop the unknown fields. MIN stays 123.\n *\n * Bump 127 → 128 for per-task resource subscriptions (#4304): the `turn_stats`\n * control message (`PersistedTurnStatsSchema`) gains optional `resourceItems`\n * (per-resource token estimate for the composer Context panel's \"Live\n * resources\" category). Additive — old browsers drop the unknown field, old\n * daemons omit it (category simply doesn't render). The mute toggle itself\n * rides the existing `update_task_settings` message (no new wire variant).\n * MIN stays 123.\n *\n * Bump 128 → 129 (2026-06-05, harness-centralization Unit F) for the\n * conversation-mode wire removal. BREAKING in both directions:\n * - Removed the `promote_task` (browser → daemon) and `promote_nudge`\n * (daemon → browser) wire-message variants — the promote-a-conversation-\n * to-a-task flow is gone. A pre-v129 browser still sends `promote_task`,\n * which a v129 daemon's closed control-message union no longer handles\n * (silent drop); a pre-v129 browser receiving the now-absent\n * `promote_nudge` simply never shows the nudge.\n * - Dropped `'conversation'` from the `create_task` `mode` enum\n * (`taskModes`). The enum is a closed Zod `z.enum`, so a pre-v129 daemon\n * that emits `mode: 'conversation'` fails a v129 browser's parse, and a\n * pre-v129 browser sending `mode: 'conversation'` on `create_task` fails\n * a v129 daemon's parse. (On-disk legacy TaskRecords are migrated\n * forward — `mode: 'conversation'` → `'task'` — at the store boundary;\n * this break is about the live wire enum, not persisted data.)\n * No graceful downgrade exists for either change, so MIN moves in lockstep\n * to 129.\n *\n * Bump 129 → 130 (2026-06-09, MCP connector holistic fix) — ADDITIVE only.\n * `mcp_state_snapshot` rows gain optional `errorType` (classified backend/\n * gateway error code), `remediation` ({message, url?}), and `sources` (dedup\n * provenance set). `MCPServerInfo` gains optional `id` (gateway `mcpsrv_…`)\n * and `sources`. All fields are `.optional()`; pre-v130 browsers drop the\n * unknown fields and a v130 browser tolerates their absence from a v129\n * daemon, so MIN stays at 129 (`protocol-safe`).\n *\n * Bump 130 → 131 for the additive per-task skills wire surface (Skills Holistic\n * Fix, track 1a): daemon→browser `task_session_skills` (the skills analog of\n * `task_session_mcps` — one resolved active-set snapshot per task, no connection\n * FSM fields) + `skill_toggle_ack` (workspace-scope reply, owner-only, mirrors\n * `mcp_toggle_ack`); browser→daemon `request_skill_toggle` (mirrors\n * `request_mcp_toggle`, with the same per-task / workspace scope split); and\n * optional curation fields on `SkillInfoWire` (`sources` / `usageCount` /\n * `contentHash` / `enabled`). Additive — every new message is a fresh variant a\n * pre-v131 peer drops, and the `SkillInfoWire` fields are optional so a v131\n * browser still parses a v99-130 daemon's `skills_snapshot`. MIN stays 129.\n */\n/**\n * Bump 131 → 132 for `request_create_skill` (browser → daemon) and\n * `skill_create_ack` (daemon → browser): diskless library skill authoring.\n * Owner-only — `skill_create_ack` is suppressed for collab peers like\n * `skill_toggle_ack`. Additive; MIN_PROTOCOL_VERSION stays at 129 —\n * pre-v132 daemons simply don't know the message and won't service the\n * request, leaving the create/import buttons in a pending state.\n *\n * Also on v132 (additive within the same unreleased bump, no further version\n * change): the conflict compare+choose surface — `request_resolve_skill_conflict`\n * + `skill_conflict_ack` (choose a winning version for a same-name conflict;\n * reuses the per-entry `enabled` flag) and `request_skill_body` + `skill_body`\n * (lazy-fetch a single SKILL.md body for the side-by-side compare view). All\n * owner-only and suppressed for collab peers like `skill_create_ack`.\n */\n/**\n * Bump 132 → 133 for `context_full` TaskErrorKind in `task_error` control messages.\n * Adding the enum variant to TaskErrorKindSchema is NOT protocol-safe: the browser\n * parses `task_error` through DaemonToBrowserControlMessageSchema.safeParse(); an\n * unknown `errorKind` makes the parse fail and the message is silently dropped —\n * worse than the pre-PR behaviour where the user saw a generic sdk_error banner.\n * MIN_PROTOCOL_VERSION stays at 129 — old daemons never emit `context_full`.\n */\n/**\n * Bump 133 → 134 for Skill Loadout v2 PR1 (daemon foundations). Adds\n * `origin` (curation source) to `SkillInfoWireSchema` so the manage modal\n * partitions INACTIVE skills (which only ride `skills_snapshot`) off the\n * daemon's real origin instead of guessing from the absolute on-disk path —\n * `task_session_skills` rows already carried `origin` for active skills.\n * Additive optional field reusing the shared `SkillOriginWireSchema` (same\n * enum the `task_session_skills` row already used). MIN stays at 129:\n * pre-v134 daemons omit `origin` and pre-v134 browsers drop the unknown field.\n *\n * Bump 134 → 135 for Skill Loadout v2 PR2 (native-parity default flip + U3\n * cross-project pool). Adds `sourceProject` (the slug of the project a skill was\n * borrowed-or-borrowable from) to BOTH `SkillInfoWireSchema` (`skills_snapshot`)\n * and the per-task `task_session_skills` row. The web derives \"this is a pool\n * skill\" from `sourceProject !== undefined`, so the shared `SkillOriginWireSchema`\n * is NOT widened — a borrowed skill rides its true `origin:'shipyard'` (it\n * resolves + delivers as a diskless library skill). Additive optional fields;\n * MIN stays at 129: pre-v135 daemons omit `sourceProject` and pre-v135 browsers\n * drop the unknown field.\n *\n * Bump 135 → 136 for builtin-theme re-sync (managed builtins). Adds\n * `deletedBuiltinIds` (Zod `.default([])`) to `ThemeConfigSchema` so the daemon\n * can tombstone a deliberately-deleted builtin and skip resurrecting it on the\n * boot upsert. Additive optional-with-default field that is daemon-authoritative\n * (the browser omits it and the daemon's `setThemeConfig` preserves the on-disk\n * value). MIN stays at 129: pre-v136 daemons never emit it and pre-v136 browsers\n * drop the unknown field; existing `theme-config.json` parses unchanged.\n *\n * Bump 136 → 137 for canvas viz-content re-delivery (Family-2 fix). Adds the\n * browser→daemon `request_viz_content_batch { taskId }` control message\n * (mirrors `request_annotation_snapshot`); the daemon responds with the\n * existing `viz_content_batch`. Fired on control-channel open + task-switch so\n * a reloaded browser (sessionStorage viz cache cleared) re-pulls the content\n * the canvas CRDT element references by slug instead of rendering \"Empty HTML\n * content\". Paired with the PRIMARY fix that now stores the viz HTML inline in\n * the canvas CRDT element `data.content`, so `html-element.tsx`'s\n * `storeEntry?.content ?? extractContent(data)` fallback resolves for ALL\n * version pairs once the CRDT syncs. Additive new message — MIN stays at 129: a\n * pre-137 daemon `safeParse`-drops the unknown request and the browser relies on\n * the inline-content fallback (no regression); a pre-137 browser never sends it.\n *\n * Bump 137 → 138 for side-thread usage stats. Adds optional `threadId` to\n * `turn_stats` so the browser can key composer usage by task/thread. Additive\n * optional field — MIN stays at 129: pre-v138 browsers drop the unknown field\n * and continue reading main-task stats; pre-v138 daemons simply omit it.\n *\n * Bump 138 → 139 for daemon→browser control-channel compression.\n * - Browsers at v139+ advertise their protocol version on the first\n * `request_capabilities` frame (additive optional `protocolVersion` field).\n * - The daemon reads this and gates per-peer deflate compression: frames ≥\n * 1 KB sent to a v139+ browser are deflated + base64-encoded with a\n * 1-char marker prefix (U+001E) that self-describes the frame format.\n * Old browsers (no `protocolVersion` in `request_capabilities`) receive\n * uncompressed frames unchanged — no graceful-downgrade hazard.\n * - `CONTROL_COMPRESSION_MIN_VERSION = 139` is the exported gate constant.\n * Additive: MIN stays at 129. Old browsers keep connecting and receiving plain\n * JSON. New browsers can decode both plain and compressed frames (the marker\n * is checked before JSON.parse; plain frames pass through `maybeInflateControlFrame`\n * unchanged via the marker-absent branch).\n */\nexport const PROTOCOL_VERSION = 139;\n\n/**\n * Minimum protocol version a browser must advertise (via the optional\n * `protocolVersion` field on `request_capabilities`) for the daemon to enable\n * per-peer deflate compression on the outbound daemon-control channel.\n *\n * The gate is intentionally additive: a browser below this version (or one\n * that omits the field entirely) receives uncompressed plain-JSON frames.\n */\nexport const CONTROL_COMPRESSION_MIN_VERSION = 139;\n\n/**\n * Minimum protocol version the browser requires from daemons.\n * Daemons reporting a version below this (or no version at all)\n * trigger the \"update your daemon\" modal.\n *\n * Version history:\n * 32: Canvas publish stability — `projectRoot` / `packageName` on DetectedPort;\n * `canvasElementId` / `projectRoot` on request_publish previewPort target;\n * new `published_artifacts_state` broadcast.\n * 33: Task index pagination — `partial` / `nextCursor` / `nextCursorId` /\n * `totalCount` on `task_index_snapshot` and matching `activeOnly` / `limit` /\n * `sinceLastActivityAt` / `sinceLastId` on `request_task_index`. Bounds the\n * initial startup payload for users with large task stores and lets the\n * browser fetch older pages on demand.\n * 34: Sidebar redesign — task coloring / favorites / projects. New message\n * types: set_task_color, toggle_favorite_task, set_task_project,\n * create_project, rename_project, delete_project, reorder_projects\n * (browser→daemon); projects_snapshot, project_updated, project_deleted\n * (daemon→browser). Adds `projectId` to TaskRecord and `taskColors` /\n * `favoriteTasks` / `collapsedProjects` to UserSettingsRecord.\n * Additive: MIN_PROTOCOL_VERSION stays at 33 — pre-v34 daemons still\n * function, just without the new sidebar features.\n * 35: Browser-initiated cwd persistence — `set_task_cwd` control message\n * (browser → daemon) that writes `TaskRecord.cwd` and invokes the\n * existing `onCwdChanged` hook so worktree auto-switch selections\n * survive page refresh. Hard bumps MIN to 35: pre-v35 daemons don't\n * handle `set_task_cwd`, so the browser's optimistic auto-switch\n * would write UI state but not persist, silently re-introducing the\n * refresh-reset bug.\n * 36: Preview-pointer refactor — canvas `preview` elements become\n * pointer-only in the CRDT (`{ ownerUserId }`). Port / url /\n * projectRoot / proxyPort / initialPath move to a daemon-local\n * `PreviewStateStore` broadcast via a new `preview_elements_state`\n * control message. Hard bumps MIN to 36: pre-v36 daemons still write\n * port/projectRoot into the CRDT, so a v36 browser reading\n * `preview_elements_state` would show \"preview pending\" forever\n * against an older daemon. Graceful downgrade isn't possible.\n * 37: No wire-protocol change. Forces the update-modal for v3.3.0 daemons\n * so users pick up the v3.3.1 hotfix for the node-pty `spawn-helper`\n * permissions bug — node-pty 1.1.0 ships the PTY-spawn intermediate\n * with mode 0644 (no execute bit), so every terminal spawn on\n * macOS/Linux fails with an opaque \"posix_spawnp failed.\" v3.3.1 adds\n * a postinstall chmod to the daemon's package-npm.json. Bumping MIN\n * forces the prompt because graceful downgrade leaves a featured\n * surface (terminal panel) broken with no user-visible clue why.\n * 38: Linear plugin filter/sort/scope persistence — adds `linearViewPreferences`,\n * `linearScope`, `linearSelectedTeamId` to UserSettingsRecord. Additive:\n * MIN_PROTOCOL_VERSION stays at 37 — pre-v38 daemons just ignore the new\n * fields and the browser falls back to local defaults.\n * 39: Worktree state-machine refactor + start_from_linear protocol.\n * Replaces the worktree_progress/worktree_created/worktree_error\n * message triple with a single worktree_phase_changed snapshot\n * carrying full WorktreeState (per Invariant #2). Adds\n * start_from_linear (browser→daemon) and task_starting_from_linear\n * (daemon→browser) for the Linear plugin one-click ergonomics flow.\n * TaskRecord adds startupPhase, awaitingSetup, startedFromLinear\n * with Zod defaults — no imperative migration. MIN bumped because\n * pre-v39 daemons emit removed message types and post-v39 browsers\n * don't know how to handle them (and vice versa).\n * 40: Daemon-owns-templates refactor — new browser→daemon `apply_template_request`\n * message. Removes browser-side template mirroring into TodoEnrichmentStore.\n * Hard bumps MIN to 40: pre-v40 daemons don't handle `apply_template_request`,\n * so applying a template to an existing task would silently no-op.\n * 41: Notification system foundation — adds task-scoped notification\n * control-channel messages (`notification_snapshot`, `notification_added`,\n * `notification_updated`, `notification_resolved` daemon→browser plus\n * `notification_ack` browser→daemon) and a `notifications` field on\n * `TaskRecord`. Hard MIN bump: pre-v41 daemons can't classify the new\n * D→B messages through `collab-outbound-filter`'s exhaustive switch\n * and won't honour browser acks, so the bell badge / sidebar Needs\n * attention bucket would silently desync from daemon state.\n * 42: Unified comment experience — DiffHunkAnnotation extended with\n * provider/external/baseRef/baseLabel/syncStatus, new annotation_updated\n * control message for reanchor broadcasts, PRComment extended with\n * commit_id/threadId/isResolved/isOutdated for richer GitHub mapping,\n * new unresolve_thread PRAction. Pre-v42 daemons can't emit\n * annotation_updated, so a v42 browser waiting for reanchor broadcasts\n * on commit would never receive them. Hard bumps MIN: graceful\n * downgrade leaves the auto-promote-on-commit feature silently broken.\n * 43: On-demand per-task hydration — adds `request_task_state` (browser→daemon).\n * Daemon responds with the existing per-task messages\n * (`published_artifacts_state`, `preview_elements_state`, `annotation_snapshot`,\n * `thread_list`, `permission_request`/`permission_resolved`, `plan_detected`,\n * `turn_stats`) only for the requested taskId. Replaces the daemon's\n * at-attach iterate-all-tasks fan-out (the thundering herd at 552\n * tasks: ~1,656 control sends per attach, self-feeding via SCTP\n * backpressure → channel close → reconnect loop). Pre-v43 daemons\n * don't handle `request_task_state`, so a v43 browser would never\n * hydrate published artifacts / preview elements / pending permissions\n * and the canvas + permissions UI would render empty. Hard MIN bump\n * — graceful downgrade isn't possible.\n * 44: Favorites zombie fix — removes the deprecated browser→daemon\n * `toggle_pin_task` control message and the `pinned` field from\n * TaskRecord (TASK_STORE_VERSION 14→15). Favorites now live exclusively\n * in `UserSettingsRecord.favoriteTasks`. Hard MIN bump: pre-v44\n * browsers still call `togglePinTask` on pin/unpin, which a v44 daemon\n * would reject as an unknown control message — silently breaking the\n * star toggle. The unified favorites surface is on every browser at\n * v34+, so the upgrade modal is the right UX.\n * 45: MCP architecture rebuild — adds browser→daemon `request_mcp_toggle`\n * (coordinator-driven runtime toggle, replaces overloading\n * `update_settings.disabledMcpServers` for the same purpose), renames\n * `mcp_server_status` enum value `pending` → `connecting` (the new\n * per-server FSM in `apps/daemon/src/services/mcp/server-fsm.ts` uses\n * `connecting` and the protocol now mirrors that vocabulary), and\n * routes lifecycle through the new `McpCoordinator` (single source of\n * truth, owns transports, persistence-before-ack, single broadcast\n * per transition). Hard MIN bump for two reasons: (1) pre-v45 daemons\n * emit `pending` which v45 browsers no longer accept, and (2) pre-v45\n * browsers send toggle intent via `update_settings.disabledMcpServers`\n * which v45 daemons no longer act on at the toggle level (Phase 3\n * collapses the path through the coordinator). Graceful downgrade\n * isn't possible on either side without re-introducing the bugs the\n * rebuild closes.\n * 46: ReasoningEffort enum change — removed `'auto'` (meta-value that silently\n * omitted `--effort`), added `'none'` (explicit thinking-off, translated to\n * `applyFlagSettings({alwaysThinkingEnabled:false})`). The\n * `ReasoningCapabilitySchema` in `packages/session` uses `z.enum()`, so a\n * v45 daemon advertising `'auto'` fails Zod parse on a v46 browser and\n * vice versa. Hard bumps MIN to 46.\n * 47: Per-item model+effort carrier — adds `set_task_capabilities`\n * (browser→daemon) for per-structured-task-item capability overrides.\n * Schema additions: `model` / `reasoningEffort` on `StructuredTask` +\n * `TemplateItem`; `capabilitiesOverrides` on `TaskOverlay`. Additive on\n * the wire — pre-v47 daemons don't handle `set_task_capabilities` (the\n * browser badge edit affordance no-ops) but everything else still\n * functions. MIN_PROTOCOL_VERSION stays at 46: a graceful downgrade\n * leaves only the new badge-edit UI inert, no silent corruption.\n * 48: Generic batch-state signal — adds `resource_batch_state`\n * (daemon→browser) and `flush_resource_batch` (browser→daemon).\n * `resource_batch_state` lets the browser display an authoritative\n * countdown for any batched resource (comments today, canvas, branch,\n * etc.) without polling. `flush_resource_batch` lets the browser\n * trigger an immediate flush for a specific URI. Additive: pre-v48\n * daemons don't emit `resource_batch_state` and don't handle\n * `flush_resource_batch`; all other surfaces still function.\n * MIN_PROTOCOL_VERSION stays at 46: graceful downgrade leaves the\n * countdown inert without corrupting any data.\n * 49: Agent-driven preview build config — adds `installCmd`, `buildCmd`,\n * `outDir` to `PreviewElementState` so `present({source:'port',\n * projectRoot, installCmd?, buildCmd?, outDir?})` can teach the\n * publish daemon how to build monorepo subdirectory spikes whose\n * framework defaults aren't reachable from `projectRoot` alone (e.g.\n * pnpm workspace deps installed at the workspace root). Pre-v49\n * daemons simply ignore the new fields and fall through to\n * framework-detect — the existing publish flow stays correct.\n * MIN_PROTOCOL_VERSION stays at 46: pure additive, no silent\n * corruption on graceful downgrade.\n * 50: Plugin sandbox capability expansion — adds `plugin_push`\n * (daemon→browser) for per-plugin live update channel, plus new\n * file-based MCP server registration, `events`/`provideResources`\n * declaration, `spawnAgent` capability, and a new `plugin://`\n * resource sub-resolver. Additive on the wire: pre-v50 browsers\n * drop unknown `plugin_push` (plugins simply don't receive live\n * updates), and pre-v50 daemons never emit it. Other plugin\n * capabilities work via the existing harness MCP path which is\n * unchanged. MIN_PROTOCOL_VERSION stays at 46.\n * 51: Collab WebRTC hub-and-spoke topology cutover — `ParticipantSchema.isHub`\n * (and the mirror on `collab_room_state.room.participants`) becomes\n * required. CollabRoom DO refuses non-owner-to-non-owner WebRTC relays\n * (`webrtc_relay_refused` error). Browsers open exactly one PC — to the\n * hub — and never initiate; daemon always initiates. Hard MIN bump:\n * pre-v51 browsers attempt mesh PC opens (lexicographic tiebreak) which\n * a v51 DO refuses, leaving the room with the daemon-PC connected but\n * all viewer-to-viewer attempts erroring; pre-v51 daemons emit\n * Participant payloads without `isHub`, which a v51 browser's Zod parse\n * rejects. Graceful downgrade isn't possible. See AGENTS.md Invariant #15.\n * 52: Connection-stability lifecycle discriminators (Cluster A7) —\n * adds `BrowserLeftSchema { type: 'browser-left', machineId,\n * connectionId }` to the personal-room server→client union and\n * requires `connectionId: string` on the existing `AgentLeftSchema`.\n * The DO emits `browser-left` on `webSocketClose` for browser\n * sessions so the daemon can tear down its WebRTC peer immediately\n * (eliminating the silent ICE-disconnect window that drove the\n * painful p95 reconnect tail). Hard MIN bump: pre-v52 daemons\n * sent `agent-left` without `connectionId`, so a v52 browser's\n * Zod parse rejects them outright; pre-v52 browsers ignore the\n * newly-required field. Graceful downgrade isn't possible because\n * the schema change is breaking on both sides.\n * 53: task_index_snapshot / task_state_update slim payload — daemon strips\n * three heavy fields (lastTurnStats, lastTokenCount,\n * lastPlanDetection) at emit time and the browser fetches them on\n * demand via new `request_task_detail` (browser → daemon) and\n * `task_detail_snapshot` (daemon → browser). structuredTasks +\n * taskOverlay + taskOverlays stay in the slim entry — the sidebar\n * and the active task's tile-slot read them live and stripping them\n * would force an extra round-trip on every overlay change. Cuts a\n * 50-task page from ~1.6 MB to ~400 KB for a user with 385 tasks,\n * eliminating the SCTP-overflow + ALO resend storm tracked at #2552.\n * Also: outer daemon-control GuardedChannel switches\n * `policy: 'queue'` → `'await-drain'` and exposes bufferedAmount;\n * GuardedChannel adds an `oversize` drop reason for single messages\n * > maxQueuedBytes (the existing `MAX_DELIVER=8` ALO ladder bounds\n * retries — `oversize` drops fast at the channel layer instead of\n * chewing SCTP buffer on each attempt). `task_detail_snapshot`\n * carries no version tag and uses property-presence merge: a key\n * absent from `detail` is left untouched on the browser side. This\n * means the snapshot can ADD or UPDATE heavy fields but cannot\n * CLEAR them — daemon-side clears (clearSession resets to\n * DEFAULT_TASK_OVERLAY rather than `undefined`, so this only\n * practically affects `lastTurnStats` / `lastPlanDetection` which\n * are owned by live message channels). Hard MIN bump: pre-v53\n * daemons emit fat records (no behavioral change for browser\n * parse), but pre-v53 browsers don't request detail and would\n * render with missing heavy fields after a v53-daemon snapshot.\n * 54: daemon-control at-least-once streamId derivation made symmetric.\n * Pre-v54 the browser's CCM hand-rolled `streamId = control-${sourceId}`\n * with sourceId = literal 'personal', while the daemon's\n * wireControlChannel hand-rolled `streamId = control-${controlPeerId}`\n * with controlPeerId = the DAEMON's own machineId. The values never\n * matched, so every browser→daemon control envelope was rejected by\n * `aloShell.receiveParsed` as passthrough, fell through to the\n * legacy parser, and was silently dropped with\n * \"Unknown or invalid control message type: unknown\" (literal\n * `unknown` because ALO envelopes use `kind` not `type`). Production\n * symptom: green WebRTC chip, \"No models\", \"Connecting to daemon...\"\n * on Settings → Agent — every browser↔daemon control message lost.\n * Fix: shared `controlChannelStreamId(sourceId)` helper used by both\n * sides, with a required `peerSourceId: 'personal' | \\`collab:\\${roomId}:\\${browserMachineId}\\``\n * threaded through `wireControlChannel`. Collab `sourceId` becomes\n * per-peer (was per-room) so each viewer's daemon-side ALO outbox\n * gets a distinct JSONL file (a room-scoped value caused 2+ viewers\n * to share an outbox, with peer A's acks truncating peer B's\n * pending messages). Hard MIN bump: pre-v54 browsers send\n * `streamId='control-personal'` (or `'control-collab:${roomId}'`)\n * which a v54 daemon's shell does not recognize → reject at\n * handshake instead of silently spinning a broken connection.\n * 55: Archive task — adds `archive_task` / `unarchive_task` (browser →\n * daemon) control messages and an `archivedAt: number | null` field\n * on `TaskRecord`. Pure-additive on the wire; archived tasks are\n * hidden from active surfaces and surfaced under a single \"Archived\"\n * sidebar section. Hard MIN bump because pre-v55 daemons reject the\n * new control message types and the browser would silently no-op\n * the archive context-menu action / ⌘E hotkey against an older\n * daemon, leaving users to wonder why nothing happened.\n * 56: Task-index version ping — adds `request_task_index_version`\n * (browser → daemon) and `task_index_version` (daemon → browser)\n * carrying `{version, totalCount}` (~32 bytes). Replaces the prior\n * 30s heartbeat that fired a full `request_task_index limit:50`\n * (~400 KB at slim wire shape × 50 entries) every tick regardless\n * of need. Browser fires the ping every 10s during silence; on\n * version mismatch it falls through to the existing\n * `request_task_index` path. Pure-additive: pre-v56 daemons\n * receiving the new message type fall through to the legacy\n * unknown-message-type path (silent drop). The browser handles\n * this by keeping the legacy heartbeat in place when talking to a\n * pre-v56 daemon — version is checked at handshake and the\n * browser picks the right strategy. MIN_PROTOCOL_VERSION stays\n * at 55: graceful downgrade is intentional so a pre-v56 daemon\n * that ships before the browser update still works at the prior\n * traffic level.\n * 57: Parallel threads in a task — `ThreadMetadata` gains `kind: 'main' |\n * 'parallel' | 'fork'` (defaults to 'fork' for legacy persisted\n * threads). `Message` gains optional `crossThreadOrigin` stamped by\n * the daemon when an agent calls `post_to_thread`. New control\n * message extension: `create_thread` extended with `kind`/`spawnMode`.\n * New MCP tool `post_to_thread` exposed via the harness server.\n * Hard MIN bump because the new `kind` field is a Zod-required\n * discriminant on the wire and pre-v57 daemons would reject the\n * create_thread extension.\n * 58: Truly slim wire shape — `TASK_INDEX_HEAVY_FIELDS` extended to strip\n * `notifications`, `structuredTasks`, `taskOverlay`, `taskOverlays`,\n * `attributedCommitShas`, `composerSettings` from `task_index_snapshot`\n * / `task_state_update` (in addition to the v53 strip set:\n * `lastTurnStats` / `lastTokenCount` / `lastPlanDetection`). New\n * derived fields `inProgressCount` / `unreadNotificationCount` on the\n * slim entry surface the counts the sidebar needs without the full\n * maps. Heavy fields delivered on demand for the active task via the\n * existing `task_detail_snapshot` (extended to include the 6\n * newly-stripped fields). User-measured impact: 50-task page goes\n * from ~1.0 MB to ~50 KB — 20× reduction on the dominant hot path.\n * Hard MIN bump per the v53 precedent: a v58 daemon emits slim\n * records that pre-v58 browsers would render with empty\n * `inProgressCount` / `unreadNotificationCount` (sidebar shows no\n * in-progress arc, no unread badge); a pre-v58 daemon emits fat\n * records but a v58 browser doesn't request detail (no protocol\n * change in pre-v58 daemon's handler), so heavy-field consumers see\n * missing data. Both directions break the sidebar — bump MIN.\n * 59: Additive ConnectorCap kind — `crow-zero-or-one` added to the\n * `ConnectorCap` discriminated union (loro-schema's\n * `connector-util-schema.ts`) and rendered in apps/web's\n * `connector-caps.tsx`. Pure additive — older browsers never\n * receive this cap unless a v59 client wrote a connector using it\n * (Zod would then reject that one connector with a parse error\n * on the older browser). Forward-compat additive: MIN stays at\n * 58 since the daemon protocol itself is unchanged; only the\n * data schema gained a variant.\n * 60: `request_capabilities.force` — optional boolean on the existing\n * `request_capabilities` browser→daemon control message. When set,\n * the daemon re-runs `detectCapabilities` (claude/codex install\n * probes, anthropic auth, MCP servers) before pushing the snapshot\n * instead of replaying the cached one. Used by the new \"Refresh\n * capabilities\" UI affordance and by the wedge-killing watcher path\n * (file watcher debounces multiple rapid changes into a single\n * force-detect call). Pure additive — pre-v60 daemons ignore the\n * field, pre-v60 browsers omit it. MIN stays at 58.\n * 61: Observability bundle — additive, MIN stays at 58.\n * browser → daemon: optional `renderTotals` and `idbWritesByStore`\n * on `browser_metrics_sample.metrics`, plus optional `windowEpoch`\n * on the same envelope. daemon → browser: optional `clockSkewMs`\n * and `windowEpoch` on `health_snapshot`, plus optional\n * `currentLabel` / `prevLabel` / `prevDurationMs` on\n * `event_loop_stall` (log-only, no wire). All fields nullable;\n * pre-v61 daemons/browsers omit them and the dashboard renders\n * null without breaking. The `windowEpoch` field on both\n * envelopes is a 10s-bucketed daemon-clock value used as a\n * correlation key to join browser-side render attribution to\n * daemon-side stall attribution within the same window.\n * 62: WebRTC outbox-epoch handshake — adds `daemon_outbox_epoch`\n * (daemon→browser) sent as the FIRST frame on every daemon-control\n * data channel open, outside the ALO envelope, so the browser\n * at-least-once layer can detect cursor-space resets the same way\n * the local-direct helloAck already does. Hard MIN bump: pre-v62\n * browsers have no handler for `daemon_outbox_epoch` and would\n * fall through to the unknown-message-type drop path (silent loss\n * of epoch info); post-v62 browsers require the epoch for correct\n * ALO cursor validation and cannot safely infer it from absence.\n * Graceful downgrade would silently re-introduce the duplicate\n * message delivery bug the epoch is designed to prevent.\n * 63: Reviews tab foundation — adds 'review' to the taskModes enum\n * (task-schemas.ts) and reviewSections + reviewDefaultRepos to\n * UserSettingsRecord (user-settings-schemas.ts). 'review' is a\n * new variant in a Zod z.enum() — pre-v63 daemons fail to parse\n * any TaskRecord with mode='review' (unknown enum value), and\n * pre-v63 browsers fail to render any task_index_snapshot that\n * contains a review task. Hard lockstep upgrade required.\n * MIN_PROTOCOL_VERSION bumped to 63. Additive UserSettingsRecord\n * fields (reviewSections, reviewDefaultRepos) use Zod .default([]).\n * 64: Per-tab ALO transport identity — daemon-control data channel label\n * changes from `daemon-control` (constant) to\n * `daemon-control:${sourceId}` where `sourceId` is per-tab in personal\n * mode (`personal:${tabId}` with tabId = `crypto.randomUUID()` minted\n * at JS module-load time, NOT sessionStorage) and per-tab in collab\n * mode (`collab:${roomId}:${browserMachineId}:${tabId}`). The daemon\n * parses the suffix from the data channel label and uses it as the\n * at-least-once peerSourceId, so each browser tab writes to its own\n * JSONL outbox + receive-cursor file under\n * `~/.shipyard/data/at-least-once-outbox/`. Pre-fix: three browser\n * tabs all used the literal sourceId `personal` and stomped a single\n * shared cursor file, breaking the daemon→browser snapshot stream\n * for capabilities / task index / settings — onboarding wedged on\n * \"Install Claude Code\" because the snapshot never reached any tab.\n * Hard MIN bump: pre-v64 daemons receiving a `daemon-control:${sourceId}`\n * label fall through to the `unknown` route in `routeDataChannel`\n * and never wire up the control channel; pre-v64 browsers send the\n * bare label which a v64 daemon's wireControlChannel still accepts\n * (defense-in-depth fallback to `PERSONAL_PEER_SOURCE_ID`), but the\n * multi-tab race remains. Co-deploy is required to actually fix the\n * bug. Also bundles two adjacent fixes: `getDaemonVersion()` now\n * reads the daemon's package.json via a path-walk from the running\n * module (replaces `process.env.npm_package_version` which returned\n * the literal string `\"unknown\"` for global installs and silently\n * defeated the v62 version-rotate-on-upgrade guard); and a\n * boot-time orphan-pruner deletes `*.jsonl` and `*.receive-cursor.json`\n * files older than 7 days from the outbox directory so per-tab\n * files don't accumulate forever as users open and close tabs. See\n * AGENTS.md Invariant #16 for the architectural rule this fix\n * codifies.\n * 65: Canvas element-type expansion — adds `text`, `embed`, `flow-step`,\n * `live-app` to `CANVAS_ELEMENT_TYPES` (and consequently to the\n * `CanvasElementDataShape` enum + `elementUtilSchemas` registry).\n * Each gets a permissive Zod schema in\n * `packages/loro-schema/src/canvas/elements/{type}-util-schema.ts`\n * with `passthrough().nullish()` to preserve unknown legacy fields.\n * `live-app` is distinct from `preview` — both render an external\n * URL but `preview` is daemon-managed (mutable port/url out of CRDT)\n * while `live-app` carries a user-supplied URL flat in CRDT data\n * and uses a broader sandbox for trusted-origin embeds.\n * Pure additive — pre-v65 daemons never write these types and pre-v65\n * browsers never parse them. MIN stays at 64: graceful downgrade leaves\n * the new types only renderable on v65+ peers, but no silent corruption.\n * 66: `session_error` added to `TaskErrorSubKindSchema` closed enum\n * (packages/loro-schema/src/protocols/channel-protocol.ts).\n * Older browsers parsing a `task_error` message with an unknown\n * `subKind` value will reject the entire message via Zod's closed\n * enum — the error notification is silently dropped. Hard MIN bump\n * to 66: pre-v66 browsers must be upgraded before they will\n * correctly display session-error notifications from the daemon.\n * 67: Per-task MCP isolation — `request_mcp_toggle` gains an optional\n * `taskId: string`. When present the daemon writes the toggle to\n * that task's `composerSettings.disabledMcpServers` (and asks the\n * MCP coordinator to re-push only the affected task's resolved\n * set — the per-server FSM is NOT transitioned). When absent the\n * daemon falls back to the workspace-default path (existing\n * behavior). Originally landed in v3.5.2 with MIN at 66 (silent\n * degrade on pre-v67 daemons). Hotfixed in v3.5.3 to bump MIN to\n * 67 so per-task MCP scoping is guaranteed end-to-end — pre-v67\n * daemons would silently apply per-task toggles workspace-wide,\n * which is a worse user experience than the blocking upgrade\n * modal. Pairs with a TASK_STORE_VERSION bump 20→21 that adds\n * `disabledMcpServers` on `ComposerSettingsSchema` so per-task\n * toggles persist through daemon restart.\n * 68: User settings: `keepMachineAwake: boolean` renamed to\n * `stayAwake: 'off' | 'during-tasks' | 'always'`. The user-settings\n * JSON store gets a v7→v8 migration that maps the old boolean to\n * the semantically equivalent enum value (false→off, true→during-tasks).\n * The new `always` mode is for \"I'm leaving the room, never sleep\"\n * use cases — caffeinate runs continuously regardless of task state.\n * Wire shape changes (the `user_settings_updated` snapshot field is\n * renamed) so MIN bumps in lockstep — pre-v68 browsers would parse\n * the snapshot and silently lose the keep-awake preference.\n * 69: Claude Code session import — adds browser → daemon\n * `list_claude_code_sessions` (with query/origin/timeRange/cursor\n * pagination) and `import_claude_code_session`, plus the matching\n * daemon → browser `claude_code_sessions_found` and\n * `claude_code_session_imported`. Drives the new sidebar\n * \"Import existing Claude Code session\" modal. Server-side search\n * (ripgrep over session JSONLs) and per-tab pagination keep the\n * wire payload bounded for users with thousands of on-disk\n * sessions. Pure additive: pre-v69 daemons never receive the new\n * request types and the v69+ browser surfaces an error in the\n * modal if it sees a downgraded daemon. MIN_PROTOCOL_VERSION\n * stays at 68 — graceful downgrade leaves the import modal inert\n * without corrupting any data. Pairs with TASK_STORE_VERSION\n * 21→22 (adds importedSessionId / importedSessionSourcePath on\n * TaskRecord). Suppressed for collab peers via\n * collab-outbound-filter per Invariant #7 (filesystem access\n * stays at the owner's daemon).\n * 70: Settings_ack split (#2728 / #3009) — `settings_ack` is replaced by\n * two distinct message types so the routing-classification module\n * (and its meta-test ratchet) can statically distinguish task-scoped\n * fanout from workspace-scoped reply: `task_settings_changed`\n * (broadcast, taskId required) and `mcp_toggle_ack` (unicast to the\n * originator, optional correlationId). Hard MIN bump because pre-v70\n * browsers expect `settings_ack` and a v70 daemon's discriminated\n * union no longer carries it; pre-v70 daemons still emit\n * `settings_ack` which a v70 browser's Zod parse rejects. Graceful\n * downgrade isn't possible.\n * 71: Global search panel — `git_grep` request gains optional\n * `wholeWord`, `includeGlobs`, `excludeGlobs`, `cwds` fields\n * (cross-task fan-out + VS Code-parity filters). `git_grep_result`\n * matches gain optional `cwd` tagging the source worktree when\n * multi-cwd was used. Pure additive: pre-v71 daemons strip\n * unknown fields and behave like single-cwd literal grep\n * (graceful degrade — cross-task and filter UI silently scope\n * to the channel's active cwd). MIN_PROTOCOL_VERSION stays\n * at 70 — no silent corruption on downgrade.\n * 72: Command-palette task search — adds browser → daemon\n * `request_task_search` and daemon → browser `task_search_result`\n * so the palette can find tasks beyond the ~50-entry slim snapshot\n * window AND surface review-mode tasks (the paginated `task_index_snapshot`\n * path filters them out). The new request carries a `requestId` so\n * concurrent searches don't race; the response echoes it for\n * correlation. Pure additive: pre-v72 daemons silently drop the\n * request and the v72+ browser falls back to local fuzzy-match over\n * its existing taskIndex (the legacy behaviour). MIN_PROTOCOL_VERSION\n * stays at 70 — graceful downgrade keeps search functional, just\n * limited to the slim snapshot the older daemon emits. Suppressed\n * for collab peers via collab-outbound-filter (Invariant #7) — task\n * search is a personal-mode operation; collab peers see at most\n * their assigned task. Closes #2885.\n * 73: Review-tasks bootstrap snapshot (#2885) — adds browser → daemon\n * `request_review_tasks` and daemon → browser `review_tasks_snapshot`.\n * Complements the v72 server-side `request_task_search` path:\n * v73 prefetches the full set of review-mode tasks at connect time\n * into a separate browser-side `useReviewTasksStore`, so the\n * command-palette can surface review tasks instantly without\n * waiting on the v72 debounce + RTT. Necessary because review\n * tasks are filtered out of the paginated `task_index_snapshot`\n * per #2896 (they have their own sidebar surface) — without the\n * v73 snapshot the palette renders zero review-task candidates\n * until the v72 server search debounce resolves. Pure additive:\n * pre-v73 daemons never receive the new request type and the\n * v73+ browser degrades gracefully to v72-only search.\n * MIN_PROTOCOL_VERSION stays at 70. Suppressed for collab peers\n * via collab-outbound-filter per Invariant #7 — review tasks are\n * owner-local (orchestrator polls owner's gh credentials).\n * 77: MCP terminal-reconnect classification (#2918, May 11 storm). The\n * daemon-side reconnect path now classifies outcomes via the pure\n * `classifyReconnectError` / `classifyPreflight` functions and emits\n * a new daemon → browser `mcp_server_needs_user_action {serverName,\n * reason: 'needs-auth'|'not-found'|'timeout'|'disabled', detail?}`\n * hint at most once per (server, reason) transition. Browser-side UI\n * hookup ships in a follow-up; pre-v77 clients ignore the unknown\n * variant (their discriminatedUnion parser drops it). The existing\n * `mcp_server_status` snapshot is unchanged — that remains the\n * full per-server FSM mirror; this message is the actionable signal\n * layered on top. Pure additive: MIN_PROTOCOL_VERSION stays at 70.\n * Collab peers receive null (Invariant #7) — the message targets the\n * owner whose credentials/config are involved.\n * 76: Proof-of-work deliverable file-IO proxy — adds asset-transfer\n * channel message `deliverable-file-request {taskId, deliverableId}`\n * (browser → daemon). The daemon resolves the deliverable's stored\n * filePath, validates size + that it's a regular file, then replies\n * with the existing `download-head` / chunked / `download-done`\n * sequence (mimeType resolved from the stored value or extension).\n * Replaces the \"pending viewer (follow-up PR)\" placeholder rendered\n * for filePath-backed deliverables — screenshots/markdown/logs now\n * render inline. Pure additive: pre-v76 daemons drop the unknown\n * message type, the browser falls back to the same placeholder a\n * v75 browser shows. MIN_PROTOCOL_VERSION stays at 70 — no silent\n * corruption on downgrade, and forcing every user to upgrade for an\n * optional viewer would violate Invariant #18.\n * 75: Proof-of-work deliverables — adds daemon → browser\n * `deliverable_registered` (single push on registration) and\n * `deliverables_snapshot` (full task-scoped list on hydration).\n * Both messages carry a `taskId` and a `DeliverableRecord` payload\n * and are classified as per-task fanout in `routing-classification.ts`.\n * Purely additive: both new message types are always-on (the\n * SHIPYARD_PROOF_OF_WORK env flag has been removed; the feature\n * ships unconditionally). Pre-v75 daemons never emit them and\n * pre-v75 browsers safely drop unknown types.\n * MIN_PROTOCOL_VERSION stays at 70 — bumping MIN for a purely\n * additive surface forces every user to upgrade for zero benefit\n * (see Invariant #18, incident 2869).\n * 74: Sidebar project-folder server-side fetch (#3047) — extends\n * `request_task_search` with an optional `projectId` filter so the\n * sidebar's ProjectsSection can hydrate each folder from the full\n * daemon store, not the slim 50-task `task_index_snapshot` page.\n * The browser fires one `request_task_search { query: '',\n * projectId, limit: 100 }` per rendered ProjectFolder on first\n * expand and merges the response into the personal taskIndex\n * partition. Structural completion of #3023 (which fixed the same\n * 50-cap visibility gap for the command palette via the v72\n * server-side search path). Pure additive: pre-v74 daemons strip\n * the unknown `projectId` field and return the empty-query\n * recency-sorted top-N — the browser still receives results that\n * `buckets.byProject` correctly routes (just with extra\n * unrelated tasks in the partition until the next snapshot).\n * MIN_PROTOCOL_VERSION stays at 70 — no silent corruption on\n * downgrade. Collab peers already had `task_search_result`\n * suppressed via collab-outbound-filter (Invariant #7); no\n * change needed.\n * 78: Deliverable mutation — adds daemon → browser `deliverable_updated`\n * (single push, full updated record) and `deliverable_deleted`\n * (`{ taskId, deliverableId }`) plus an `updatedAt: number | null`\n * field on `DeliverableRecord` stamped by the daemon on each\n * `update_deliverable` call. New MCP tools `update_deliverable`\n * and `delete_deliverable` round out the deliverable lifecycle.\n * Both new control messages are task-scoped fanout and pass\n * through `collab-outbound-filter` alongside `deliverable_registered`\n * / `deliverables_snapshot`.\n * Hard MIN bump: a v77 browser receiving `deliverable_updated` would\n * drop the unknown type and render stale content (the chip wouldn't\n * pulse, an open modal wouldn't refetch, and a deleted deliverable\n * would remain visible until a full snapshot rehydration). The point\n * of these tools is *visible* feedback to reviewers — graceful\n * downgrade defeats the feature.\n * 79: Quick-pick common directories (#3160) — adds `commonDirs: Array<{\n * path, label }>` to the capabilities payload. Detected on daemon\n * boot (~/Documents, ~/Desktop, ~/Downloads, ~/Projects, ~/repos\n * when each exists). Pre-v79 daemons omit the field; the schema\n * defaults to `[]` so the browser parses cleanly and the env picker\n * falls back to a Home-only quick-pick. MIN_PROTOCOL_VERSION stays\n * at 78 — purely additive (Invariant #18: don't force upgrades for\n * optional features).\n * 80: Publish deliverables (#3228) — adds `{ kind: 'deliverableId',\n * deliverableId }` variant to `request_publish.target`. Pure additive:\n * MIN_PROTOCOL_VERSION stays at 78. Pre-v80 daemons reject the new\n * variant at the discriminated-union parse; pre-v80 browsers don't\n * know to send it.\n * 81: ReviewSection groups/rules schema migration (v9 → v10). The\n * `ReviewSection.conditions` flat-AND array is replaced by a\n * `groups[].rules[].conditions[]` tree (groups OR / rules AND /\n * conditions OR with per-rule `isNegated`). Also adds the\n * `hasSeededReviewDefaults` flag so the canonical Graphite-style\n * default sections seed exactly once on first launch. Pre-v81\n * daemons emit ReviewSection records with `conditions[]` which a\n * v81 browser's strict Zod parse rejects; pre-v81 browsers emit\n * sections without `groups[]` which a v81 daemon rejects. Hard MIN\n * bump — graceful downgrade isn't possible.\n * 82: Codex browser UI (codex c10) — adds the `profile_snapshot`\n * daemon→browser control message carrying the JSON-serializable\n * subset of every registered `AgentCapabilityProfile` (declarative\n * fields only — closures stay daemon-side). The browser consumes\n * it into `profile-store` and uses it to drive the agent-aware\n * model picker (Option A: agent-section grouping), agent-aware\n * message avatars, per-profile settings cards, and the rate-limit\n * modal's provider variant. Pure additive: pre-v82 daemons never\n * emit the message, pre-v82 browsers drop the unknown variant.\n * MIN stays at 78 (Invariant #18).\n * 83: Real browser-driven Codex login (codex c12) — replaces the\n * copy-paste shell-command UI with the same wizard shape Claude\n * uses. Adds:\n * - `codex_login_request` (browser → daemon, `{ method, apiKey? }`)\n * - `codex_login_status` (daemon → browser, `{ requestId, method,\n * status, loginUrl?, error? }`)\n * - `codex_logout_request` (browser → daemon)\n * - `codex_logout_status` (daemon → browser)\n * - `codexAuth` field on `CapabilitiesPayload` (mirrors\n * `anthropicAuth` shape but Codex-tagged; carries email parsed\n * from `~/.codex/auth.json` id_token claims when present)\n * Pure additive: pre-v83 daemons never emit the new messages and\n * drop the new requests; pre-v83 browsers drop the new statuses.\n * MIN stays at 78 (Invariant #18 — no silent corruption on\n * graceful downgrade; users get the c12 copy-paste fallback\n * against an older daemon).\n * 84: Button-driven Codex install — widens `install_agent` and\n * `agent_install_status` `agentId` from `z.literal('claude-code')` to\n * `z.enum(['claude-code', 'codex'])` so the Settings → Agent Codex\n * onboarding card can drive the same daemon-side install spawn flow\n * Claude uses (with codex spawning `npm install -g @openai/codex@<v>`\n * instead of the curl pipe). MIN bumped to 79 in lockstep because\n * pre-v84 daemons reject the new `agentId` enum value at parse and\n * pre-v84 browsers would never render the codex install spinner /\n * transition states for a v84 daemon emitting status with\n * `agentId: 'codex'`.\n * 85: Model-picker meta on `ModelDescriptor` — adds optional `tagline`,\n * `speedTier` (`'fastest'|'fast'|'standard'|'slow'|'slowest'`),\n * `costInputUsd`, `costOutputUsd` so each profile ships its own\n * per-model display metadata over `profile_snapshot`. Pre-meta\n * daemons still parse cleanly (fields are optional); the browser's\n * `model-comparison-panel` falls back to a per-field FALLBACK_META\n * safety net so the panel never blanks. MIN stays at 79 — pure\n * additive (Invariant #18).\n * 86: Spawn-router rejection (codex Phase B) — adds two daemon → browser\n * control messages emitted by `spawnSubprocessFn` instead of silently\n * falling back to Claude when the selected model's agent profile\n * cannot spawn:\n * - `agent_not_installed { taskId, agentId, modelId }` fires when\n * the registered profile's `spawn.binaryResolver()` returns\n * null OR the model references an unknown provider. The browser\n * shows \"Codex isn't installed on this machine — pick a\n * different model or install Codex first.\"\n * - `agent_disabled_by_flag { taskId, agentId, modelId }` is\n * reserved for future agents that ship behind a feature flag.\n * Today no daemon path emits it (Codex went GA and bundled), but\n * the wire type stays so we don't have to bump the protocol when\n * a future agent re-introduces a per-agent flag gate.\n * Pure additive: pre-v86 daemons never emit these (they silently\n * fall back to Claude, which surfaces as the \"Claude says it\n * doesn't know that model\" symptom). Pre-v86 browsers drop the\n * unknown variants and the symptom remains identical to pre-v86\n * daemon behaviour. MIN stays at 79 — graceful downgrade is the\n * pre-existing buggy behaviour (Invariant #18).\n * 87: Per-agent brand color — adds optional `color?: string` to\n * `AgentIconRef` on `AgentCapabilityProfileSummary.iconRef`. The\n * browser tints the message avatar background and the attribution\n * name with the profile's color instead of the global `--accent`,\n * so Codex top-level turns render in blue and Claude turns keep\n * the existing rust/copper tone. Pure additive: pre-v87 daemons\n * omit the field and the browser falls back to the legacy\n * `text-accent` / `bg-accent/10` classes. MIN stays at 79\n * (Invariant #18).\n * 88: Subagent spawn parity — extends the v86 spawn-router rejection\n * set with a third variant for the not-authenticated case, plus\n * routes every sub-task spawn (Shipyard `create_task` MCP tool,\n * pre-warm adoption) through the same `decideSpawnRoute` chokepoint\n * so a parent agent picking a Codex model for a sub-task never\n * silently lands on a Claude pre-warm.\n * - `agent_not_authenticated { taskId, agentId, modelId }` fires\n * when the registered profile's binary exists but the user is\n * not signed in. Distinct UX from `agent_not_installed` (Sign\n * in vs Install). The browser shows the same per-task banner\n * with a \"Sign in to <agent>\" CTA instead of the install hint.\n * - The `create_task` MCP tool gains an optional `model` field so\n * a parent Claude agent can spawn a Codex sub-task (and vice\n * versa); the daemon validates the model against the registered\n * profile catalog and propagates it through `initialComposerSettings`.\n * - Pre-warm adoption now respects the target agent: a Codex-modeled\n * task no longer claims the Claude pre-warm subprocess (which would\n * silently run the wrong agent).\n * Pure additive: pre-v88 daemons never emit the new variant and don't\n * know about the new MCP field; pre-v88 browsers drop unknown variants.\n * MIN stays at 79 (Invariant #18).\n * 89: Runtime seam generalization for Codex parity — adds the\n * `update_thread_settings { taskId, threadId, settings }`\n * browser→daemon control message so side-thread composer changes persist\n * per thread instead of mutating the parent task defaults, and extends\n * capability/install payloads with runtime-keyed fields:\n * - `runtimeAuth` map on the daemon→browser capabilities snapshot\n * - `installedRuntimes` alongside legacy `installedAgents`\n * - generic string `agentId` on install/status payloads so future\n * runtimes are not hard-coded at the wire layer\n * Hard MIN bump: a v89 browser sends `update_thread_settings`\n * unconditionally from the thread panel, which a pre-v89 daemon rejects\n * as an unknown control message. That leaves live thread settings\n * silently wrong (cross-thread leakage / reset-on-refresh), so graceful\n * downgrade is not acceptable.\n * 90: Project color persistence — adds `set_project_color` browser→daemon\n * control message and `projectColors` in UserSettingsRecord. Hard MIN\n * bump because a v90 browser would otherwise optimistically show project\n * colors while a pre-v90 daemon silently fails to persist them.\n * 91: skill_invocation ContentBlock variant — adds a new discriminant value\n * (`'skill_invocation'`) to the `ContentBlockSchema` discriminated union\n * in loro-schema. The composer now emits `{ type: 'skill_invocation',\n * name, description?, path?, sourceAgent? }` blocks alongside the\n * existing `text` block whenever the textarea contains a recognized\n * skill sigil (`/qa` or `$qa`). Hard MIN bump: Zod's discriminated\n * union closed-set check rejects unknown discriminant values, so a\n * pre-v91 browser parsing a v91-daemon Message that contains a\n * skill_invocation block would drop the ENTIRE message line (per\n * jsonl-conversation-store.ts behavior), losing the text content too.\n * Graceful downgrade is impossible.\n * 92: slim annotation_snapshot heavy fields (elementHtml, cssClasses,\n * computedStyles, pageBackground) are now optional on the wire and\n * lazy-fetched via request_annotation_detail. A pre-v92 browser\n * parsing a v95-daemon snapshot would see the heavy fields absent\n * and render with broken hover previews — recoverable but visually\n * degraded. Bump MIN to v92 to surface the \"update browser\" modal\n * instead of shipping the silently-degraded path.\n * 102: Codex `item/tool/requestUserInput` handler lands the `'input'`\n * discriminant on `PermissionRequestCodexPayloadSchema.kind`. Per\n * Invariant #18 web prod and daemon `@latest` promote together,\n * so a v102 daemon emitting `kind: 'input'` MUST be paired with a\n * v102 browser. A pre-v102 browser's Zod discriminated-union parse\n * rejects unknown `kind` values and silently drops the entire\n * `permission_request_codex` message — the Codex daemon-side promise\n * then hangs until the user aborts the task. Bump MIN to 102 to\n * surface the upgrade modal rather than silently wedging the\n * Codex input flow.\n * 103: Codex `permission_request_codex` payload schema closed via\n * `z.discriminatedUnion('kind', ...)` with per-kind required fields\n * (`kind: 'input'` requires `inputQuestions: .min(1)`,\n * `kind: 'permissions'` rejects `inputQuestions`, etc.) and the\n * `.default('permissions')` removed — the wire now requires an\n * explicit `kind` discriminant. Mirror tightening on\n * `permission_response_codex.inputAnswers` (`.min(1)` per inner\n * array + non-empty outer record `.refine`) closes the\n * empty-answer silent-approval bug class. Bump MIN to 103\n * because pre-v102 daemons emit no `kind` field at all and the\n * v103 browser's closed-schema parse rejects those payloads with\n * no graceful fallback — surfacing the upgrade modal is the only\n * way to avoid silently losing the Codex permission flow.\n * 129: Conversation-mode wire removal (harness-centralization Unit F).\n * The `promote_task` / `promote_nudge` message variants were removed\n * and `'conversation'` was dropped from the closed `create_task` `mode`\n * enum. Both directions are breaking with no graceful downgrade (closed\n * control-message union + closed Zod enum), so MIN moves in lockstep\n * with PROTOCOL_VERSION to 129. See the PROTOCOL_VERSION 128 → 129 note.\n * 130: MCP connector holistic fix — additive optional fields on\n * `mcp_state_snapshot` rows (`errorType`, `remediation`, `sources`) and\n * `MCPServerInfo` (`id`, `sources`). MIN stays 129; pre-v130 browsers\n * drop the unknown fields. See the PROTOCOL_VERSION 129 → 130 note.\n */\nexport const MIN_PROTOCOL_VERSION = 129;\n\nexport type CompatResult =\n | { compatible: true }\n | { compatible: false; reason: 'protocol' | 'prerelease-mismatch'; installCommand: string };\n\nfunction isPrerelease(version: string): boolean {\n return /-(nightly|rc)\\./.test(version);\n}\n\nexport function getInstallCommand(version?: string, channel?: 'latest' | 'next'): string {\n if (channel) {\n return `npm install -g @schoolai/shipyard@${channel}`;\n }\n const tag = version && isPrerelease(version) ? '@next' : '@latest';\n return `npm install -g @schoolai/shipyard${tag}`;\n}\n\nexport function classifyDaemonCompatibility(\n daemon: { protocolVersion?: number; npmVersion?: string },\n browser: {\n minProtocolVersion: number;\n expectedDaemonVersion?: string;\n channel?: 'latest' | 'next';\n }\n): CompatResult {\n const installCommand = getInstallCommand(\n browser.expectedDaemonVersion ?? daemon.npmVersion,\n browser.channel\n );\n\n if (\n daemon.npmVersion &&\n browser.expectedDaemonVersion &&\n isPrerelease(daemon.npmVersion) &&\n isPrerelease(browser.expectedDaemonVersion) &&\n daemon.npmVersion !== browser.expectedDaemonVersion\n ) {\n return {\n compatible: false,\n reason: 'prerelease-mismatch',\n installCommand,\n };\n }\n\n if ((daemon.protocolVersion ?? 0) < browser.minProtocolVersion) {\n return {\n compatible: false,\n reason: 'protocol',\n installCommand,\n };\n }\n\n return { compatible: true };\n}\n","/**\n * Shared route constants for the signaling server.\n *\n * Used by both server (route registration) and client (request URLs).\n * Single source of truth prevents drift between implementations.\n *\n * @module client/routes\n */\nexport const ROUTES = {\n HEALTH: '/health',\n TURN_HEALTH: '/health/turn',\n AUTH_GITHUB_CALLBACK: '/auth/github/callback',\n AUTH_GITHUB_REFRESH: '/auth/github/refresh',\n AUTH_DEVICE_START: '/auth/device/start',\n AUTH_DEVICE_VERIFY: '/auth/device/verify',\n AUTH_DEVICE_POLL: '/auth/device/poll',\n AUTH_DEVICE_AUTHORIZE: '/auth/device/authorize',\n AUTH_DEVICE_EXCHANGE_CODE: '/auth/device/exchange-code',\n AUTH_BROWSER_CODE: '/auth/browser-code',\n AUTH_BROWSER_CODE_EXCHANGE: '/auth/browser-code/exchange',\n AUTH_VERIFY: '/auth/verify',\n COLLAB_CREATE: '/collab/create',\n COLLAB_REVOKE: '/collab/:roomId',\n GITHUB_ISSUE_CREATE: '/feedback/issue',\n VAULT_KEY: '/vault/key',\n AUTH_LINEAR: '/auth/linear',\n AUTH_LINEAR_CALLBACK: '/auth/linear/callback',\n WEBHOOK_INGEST: '/webhooks/:userId/:source',\n WS_PERSONAL: '/personal/:userId',\n WS_COLLAB: '/collab/:roomId',\n METRICS_INGEST: '/ingest',\n METRICS_QUERY_EVENTS: '/query/events',\n PUBLISH_UPLOAD: '/publish',\n PUBLISH_ITEM: '/publish/:id',\n PUBLISH_MINE: '/publish/mine',\n ELECTRON_INSTALL_STABLE: '/install.sh',\n ELECTRON_INSTALL_NEXT: '/install-next.sh',\n ELECTRON_INSTALL_NIGHTLY: '/install-nightly.sh',\n ELECTRON_DOWNLOAD: '/downloads/electron/:channel/:file',\n} as const;\n\n/**\n * Human-readable endpoint descriptions for documentation and error messages.\n */\nexport const ROUTE_DESCRIPTIONS = [\n `GET ${ROUTES.HEALTH}`,\n `GET ${ROUTES.TURN_HEALTH}`,\n `POST ${ROUTES.AUTH_GITHUB_CALLBACK}`,\n `POST ${ROUTES.AUTH_GITHUB_REFRESH}`,\n `POST ${ROUTES.AUTH_DEVICE_START}`,\n `GET ${ROUTES.AUTH_DEVICE_VERIFY}`,\n `POST ${ROUTES.AUTH_DEVICE_POLL}`,\n `POST ${ROUTES.AUTH_DEVICE_AUTHORIZE}`,\n `POST ${ROUTES.AUTH_DEVICE_EXCHANGE_CODE}`,\n `POST ${ROUTES.AUTH_BROWSER_CODE}`,\n `POST ${ROUTES.AUTH_BROWSER_CODE_EXCHANGE}`,\n `GET ${ROUTES.AUTH_VERIFY}`,\n `POST ${ROUTES.COLLAB_CREATE}`,\n `DELETE ${ROUTES.COLLAB_REVOKE}`,\n `POST ${ROUTES.GITHUB_ISSUE_CREATE}`,\n `GET ${ROUTES.VAULT_KEY}`,\n `PUT ${ROUTES.VAULT_KEY}`,\n `GET ${ROUTES.AUTH_LINEAR}`,\n `GET ${ROUTES.AUTH_LINEAR_CALLBACK}`,\n `POST ${ROUTES.WEBHOOK_INGEST}`,\n `WS ${ROUTES.WS_PERSONAL}`,\n `WS ${ROUTES.WS_COLLAB}`,\n `POST ${ROUTES.METRICS_INGEST}`,\n `GET ${ROUTES.METRICS_QUERY_EVENTS}`,\n `POST ${ROUTES.PUBLISH_UPLOAD}`,\n `PUT ${ROUTES.PUBLISH_ITEM}`,\n `DELETE ${ROUTES.PUBLISH_ITEM}`,\n `GET ${ROUTES.PUBLISH_MINE}`,\n `GET ${ROUTES.ELECTRON_INSTALL_STABLE}`,\n `GET ${ROUTES.ELECTRON_INSTALL_NEXT}`,\n `GET ${ROUTES.ELECTRON_INSTALL_NIGHTLY}`,\n `GET ${ROUTES.ELECTRON_DOWNLOAD}`,\n] as const;\n","/**\n * Claude Code version compatibility for the Shipyard daemon.\n *\n * The SDK ships a per-platform native `claude` binary via optional\n * dependencies (`@anthropic-ai/claude-agent-sdk-${platform}-${arch}`).\n * The user's global `claude` install is NOT what Shipyard spawns.\n * Pinning the SDK version effectively pins Claude Code. This module\n * provides observability: when the init message reports a runtime\n * version, we compare it against what we tested.\n */\n\n/** The Claude Code version bundled with the currently pinned SDK (0.3.170). */\nexport const TESTED_CLAUDE_CODE_VERSION = '2.1.170';\n\nexport type ClaudeCodeCompatResult =\n | { status: 'compatible' }\n | { status: 'untested'; detected: string; tested: string }\n | { status: 'unknown' };\n\n/**\n * Compare a detected Claude Code runtime version against the tested version.\n *\n * - `compatible`: major.minor matches the tested version\n * - `untested`: version detected but major.minor differs\n * - `unknown`: version could not be determined\n */\nexport function classifyClaudeCodeCompatibility(\n detected: string | undefined,\n tested: string = TESTED_CLAUDE_CODE_VERSION\n): ClaudeCodeCompatResult {\n if (!detected) return { status: 'unknown' };\n\n const detectedParts = detected.split('.');\n const testedParts = tested.split('.');\n\n if (detectedParts[0] === testedParts[0] && detectedParts[1] === testedParts[1]) {\n return { status: 'compatible' };\n }\n\n return { status: 'untested', detected, tested };\n}\n","/**\n * Codex version compatibility for the Shipyard daemon.\n *\n * `@openai/codex` is an exact-pinned daemon dependency with per-platform\n * native binaries. Shipyard resolves the bundled CLI wrapper with\n * `require.resolve('@openai/codex/bin/codex.js')`; the user's global `codex`\n * install is NOT what normal agent spawns use. This module mirrors\n * `claude-code-compat.ts` so spawn diagnostics can distinguish a bundled\n * runtime version mismatch from opaque process failures.\n */\n\n/** The Codex npm package version bundled with the currently pinned daemon. */\nexport const TESTED_CODEX_VERSION = '0.138.0';\n\nexport type CodexCompatResult =\n | { status: 'compatible' }\n | { status: 'untested'; detected: string; tested: string }\n | { status: 'unknown' };\n\nconst SEMVER_RE = /^\\d+\\.\\d+\\.\\d+(?:[-+][0-9A-Za-z.-]+)?$/;\n\n/**\n * Compare a detected Codex runtime package version against the tested version.\n *\n * - `compatible`: major.minor matches the tested version\n * - `untested`: version detected but major.minor differs\n * - `unknown`: version could not be determined or parsed\n */\nexport function classifyCodexCompatibility(\n detected: string | undefined,\n tested: string = TESTED_CODEX_VERSION\n): CodexCompatResult {\n if (!detected || !SEMVER_RE.test(detected)) return { status: 'unknown' };\n\n const detectedParts = detected.split('.');\n const testedParts = tested.split('.');\n\n if (detectedParts[0] === testedParts[0] && detectedParts[1] === testedParts[1]) {\n return { status: 'compatible' };\n }\n\n return { status: 'untested', detected, tested };\n}\n","import type { ConnectionState } from './personal-room-connection';\nimport type { CollabRoomClientMessage, CollabRoomServerMessage } from './schemas';\nimport { CollabRoomServerMessageSchema } from './schemas';\n\ninterface MinimalWebSocket {\n onopen: ((ev: unknown) => void) | null;\n onclose: ((ev: unknown) => void) | null;\n onerror: ((ev: unknown) => void) | null;\n onmessage: ((ev: { data: string }) => void) | null;\n send(data: string): void;\n close(): void;\n}\n\nexport interface CollabRoomConnectionConfig {\n url: string;\n WebSocketImpl?: new (url: string) => MinimalWebSocket;\n maxRetries?: number;\n initialDelayMs?: number;\n maxDelayMs?: number;\n backoffMultiplier?: number;\n /** Called when the server closes the connection with code 1000 (\"Room expired\"). Do not reconnect. */\n onRoomExpired?: () => void;\n}\n\nexport class CollabRoomConnection {\n private state: ConnectionState = 'disconnected';\n private ws: MinimalWebSocket | null = null;\n private readonly messageHandlers = new Set<(msg: CollabRoomServerMessage) => void>();\n private readonly stateChangeHandlers = new Set<(state: ConnectionState) => void>();\n private readonly config: CollabRoomConnectionConfig;\n private retryCount = 0;\n private retryTimer: ReturnType<typeof setTimeout> | null = null;\n private pingTimer: ReturnType<typeof setTimeout> | null = null;\n private intentionalDisconnect = false;\n\n constructor(config: CollabRoomConnectionConfig) {\n this.config = config;\n }\n\n getState(): ConnectionState {\n return this.state;\n }\n\n connect(): void {\n this.intentionalDisconnect = false;\n\n if (this.retryTimer !== null) {\n clearTimeout(this.retryTimer);\n this.retryTimer = null;\n }\n\n if (this.ws) {\n this.stopPingTimer();\n this.ws.onopen = null;\n this.ws.onclose = null;\n this.ws.onerror = null;\n this.ws.onmessage = null;\n this.ws.close();\n this.ws = null;\n }\n\n const Impl = this.config.WebSocketImpl ?? (WebSocket as never);\n try {\n this.ws = new Impl(this.config.url);\n } catch {\n this.setState('error');\n this.reconnectWithBackoff();\n return;\n }\n this.setState('connecting');\n\n this.ws.onopen = () => {\n this.setState('connected');\n this.startPingTimer();\n };\n\n this.ws.onclose = (ev: unknown) => {\n this.stopPingTimer();\n if (this.state !== 'error' && this.state !== 'reconnecting') {\n this.setState('disconnected');\n }\n const code = extractCloseCode(ev);\n /** NOTE: 1000 = \"Room expired\" — the Durable Object TTL has elapsed. Stop reconnecting and notify the caller. */\n if (code === 1000) {\n this.config.onRoomExpired?.();\n return;\n }\n /** NOTE: 4001 = \"Replaced by new connection\" — a newer WebSocket for the same userId was accepted by the server. Do not reconnect; the replacement is already active. */\n if (!this.intentionalDisconnect && code !== 4001) {\n this.reconnectWithBackoff();\n }\n };\n\n this.ws.onerror = () => {\n this.stopPingTimer();\n this.setState('error');\n if (!this.intentionalDisconnect) {\n this.reconnectWithBackoff();\n }\n };\n\n this.ws.onmessage = (event: { data: string }) => {\n this.startPingTimer();\n\n if (event.data === 'pong') return;\n\n let raw: unknown;\n try {\n raw = JSON.parse(event.data);\n } catch {\n return;\n }\n\n const result = CollabRoomServerMessageSchema.safeParse(raw);\n if (!result.success) {\n // biome-ignore lint/suspicious/noConsole: intentional debug visibility for silent Zod drops that hid a critical share-link bug\n console.warn(\n '[CollabRoom] Dropped unrecognized server message:',\n JSON.stringify(raw),\n result.error.issues\n );\n return;\n }\n\n for (const handler of [...this.messageHandlers]) {\n handler(result.data);\n }\n };\n }\n\n send(msg: CollabRoomClientMessage): void {\n try {\n this.ws?.send(JSON.stringify(msg));\n } catch {\n /** WebSocket may be in CLOSING/CLOSED state */\n }\n }\n\n onMessage(handler: (msg: CollabRoomServerMessage) => void): () => void {\n this.messageHandlers.add(handler);\n return () => {\n this.messageHandlers.delete(handler);\n };\n }\n\n onStateChange(handler: (state: ConnectionState) => void): () => void {\n this.stateChangeHandlers.add(handler);\n return () => {\n this.stateChangeHandlers.delete(handler);\n };\n }\n\n reconnect(): void {\n this.intentionalDisconnect = false;\n if (this.retryTimer !== null) {\n clearTimeout(this.retryTimer);\n this.retryTimer = null;\n }\n this.retryCount = 0;\n this.connect();\n }\n\n disconnect(): void {\n this.intentionalDisconnect = true;\n this.stopPingTimer();\n\n if (this.retryTimer !== null) {\n clearTimeout(this.retryTimer);\n this.retryTimer = null;\n }\n\n this.retryCount = 0;\n\n if (this.ws) {\n this.ws.onopen = null;\n this.ws.onclose = null;\n this.ws.onerror = null;\n this.ws.onmessage = null;\n this.ws.close();\n this.ws = null;\n }\n this.setState('disconnected');\n }\n\n private startPingTimer(): void {\n this.stopPingTimer();\n this.schedulePing();\n }\n\n private stopPingTimer(): void {\n if (this.pingTimer !== null) {\n clearTimeout(this.pingTimer);\n this.pingTimer = null;\n }\n }\n\n private schedulePing(): void {\n const jitter = Math.floor(Math.random() * 25_000);\n const delay = 30_000 + jitter;\n this.pingTimer = setTimeout(() => {\n this.pingTimer = null;\n try {\n this.ws?.send('ping');\n } catch {\n /** WebSocket may be in CLOSING/CLOSED state */\n }\n this.schedulePing();\n }, delay);\n }\n\n private reconnectWithBackoff(): void {\n if (this.retryTimer !== null) return;\n\n const maxRetries = this.config.maxRetries;\n if (maxRetries === undefined || maxRetries === 0) {\n return;\n }\n\n if (maxRetries !== -1 && this.retryCount >= maxRetries) {\n return;\n }\n\n this.setState('reconnecting');\n\n if (this.intentionalDisconnect) return;\n\n const initialDelay = this.config.initialDelayMs ?? 1000;\n const maxDelay = this.config.maxDelayMs ?? 30000;\n const multiplier = this.config.backoffMultiplier ?? 2;\n const delay = Math.min(initialDelay * multiplier ** this.retryCount, maxDelay);\n\n this.retryCount++;\n\n this.retryTimer = setTimeout(() => {\n this.retryTimer = null;\n this.connect();\n }, delay);\n }\n\n private setState(newState: ConnectionState): void {\n this.state = newState;\n if (newState === 'connected') {\n this.retryCount = 0;\n }\n for (const handler of [...this.stateChangeHandlers]) {\n handler(newState);\n }\n }\n}\n\nfunction extractCloseCode(ev: unknown): number | undefined {\n if (typeof ev !== 'object' || ev === null || !('code' in ev)) return undefined;\n const code: unknown = ev.code;\n return typeof code === 'number' ? code : undefined;\n}\n","import type { PersonalRoomClientMessage, PersonalRoomServerMessage } from './schemas';\nimport { PersonalRoomServerMessageSchema } from './schemas';\n\nexport type ConnectionState =\n | 'connecting'\n | 'connected'\n | 'disconnected'\n | 'reconnecting'\n | 'error';\n\ninterface MinimalWebSocket {\n onopen: ((ev: unknown) => void) | null;\n onclose: ((ev: unknown) => void) | null;\n onerror: ((ev: unknown) => void) | null;\n onmessage: ((ev: { data: string }) => void) | null;\n send(data: string): void;\n close(): void;\n}\n\nexport interface PersonalRoomConnectionConfig {\n url: string;\n WebSocketImpl?: new (url: string) => MinimalWebSocket;\n /**\n * -1 = retry forever (geometric ramp then slow-retry every `slowRetryDelayMs`);\n * 0 / undefined = no auto-reconnect (legacy backwards-compat default);\n * N>0 = stop after N attempts (legacy bounded retry, used by older callers).\n *\n * Cluster A8 introduces the slow-retry-forever path (`-1`) — no terminal\n * `failed` state. The bounded variant is preserved so existing tests and\n * callers don't break.\n */\n maxRetries?: number;\n /**\n * Geometric backoff schedule used for the first attempts. After the\n * schedule is exhausted, retries fire every `slowRetryDelayMs` indefinitely\n * (no terminal `failed` state — Cluster A8). Tests may inject a custom\n * schedule for determinism.\n *\n * Defaults to `[1000, 2000, 4000, 8000, 15000, 30000]` per the spec.\n */\n backoffSchedule?: readonly number[];\n /**\n * Slow-retry delay used after the geometric schedule is exhausted.\n * Defaults to 300_000 ms (5 minutes) — cap chosen to keep daemons well under\n * Cloudflare's anti-abuse trip threshold while still recovering within an\n * acceptable window after transient signaling outages.\n */\n slowRetryDelayMs?: number;\n /**\n * Proactive heartbeat configuration. Defaults: ping every 30s, expect pong\n * within 60s. Half-open WS detection (Cluster A4): if no pong arrives in\n * `pongTimeoutMs` after ping send, force-close the WS to trigger reconnect.\n */\n pingIntervalMs?: number;\n pongTimeoutMs?: number;\n /**\n * Liveness-probe timeout used by {@link PersonalRoomConnection.probe}. When a\n * comeback (tab focus / network online) finds the WS believed-`connected`, we\n * send a ping and force-close if no inbound traffic arrives within this short\n * window — distinct from the lazy 60s `pongTimeoutMs`. Defaults to 3000ms so a\n * half-open WS is caught in ~3s instead of ~90s, without tearing down a WS\n * that's actually healthy.\n */\n probeTimeoutMs?: number;\n /** @deprecated Legacy fields preserved for backwards-compat with older tests. */\n initialDelayMs?: number;\n maxDelayMs?: number;\n backoffMultiplier?: number;\n}\n\nconst DEFAULT_BACKOFF_SCHEDULE: readonly number[] = [1000, 2000, 4000, 8000, 15000, 30000];\nconst DEFAULT_SLOW_RETRY_DELAY_MS = 300_000;\nconst DEFAULT_PING_INTERVAL_MS = 30_000;\nconst DEFAULT_PONG_TIMEOUT_MS = 60_000;\nconst DEFAULT_PROBE_TIMEOUT_MS = 3_000;\n\nexport class PersonalRoomConnection {\n private state: ConnectionState = 'disconnected';\n private ws: MinimalWebSocket | null = null;\n private readonly messageHandlers = new Set<(msg: PersonalRoomServerMessage) => void>();\n private readonly stateChangeHandlers = new Set<(state: ConnectionState) => void>();\n private readonly rttSampleHandlers = new Set<(rttMs: number) => void>();\n private readonly config: PersonalRoomConnectionConfig;\n private retryCount = 0;\n private retryTimer: ReturnType<typeof setTimeout> | null = null;\n /**\n * Timestamp (ms) of the most recent outbound 'ping'. Cleared on the first\n * pong-shaped reply that arrives afterwards. RTT = now - lastPingSentAt.\n * Approximate when more than one ping is in flight at once (pong always\n * matches the latest send), but the proactive interval (30s) is two orders\n * of magnitude slower than typical RTTs so the approximation is fine.\n */\n private lastPingSentAt: number | null = null;\n /**\n * Cluster A3: proactive interval timer. Fires every `pingIntervalMs`\n * regardless of whether messages have been received. Today's reactive\n * timer (reset on every `onmessage`) hides half-open WS death until\n * kernel TCP RST (~30s on residential ISP).\n */\n private pingTimer: ReturnType<typeof setInterval> | null = null;\n /**\n * Cluster A4: pong watchdog. Set when a ping is sent; cleared on any\n * incoming message. If it fires before clear, we force-close the WS.\n */\n private pongWatchdog: ReturnType<typeof setTimeout> | null = null;\n /**\n * Short-lived watchdog armed by {@link probe}. Independent of `pongWatchdog`\n * so a comeback liveness check can't be masked by an already-pending lazy\n * pong deadline. Cleared on any inbound traffic; force-closes the WS on\n * expiry to trigger the reconnect path.\n */\n private probeWatchdog: ReturnType<typeof setTimeout> | null = null;\n private intentionalDisconnect = false;\n private readonly backoffSchedule: readonly number[];\n private readonly slowRetryDelayMs: number;\n private readonly pingIntervalMs: number;\n private readonly pongTimeoutMs: number;\n private readonly probeTimeoutMs: number;\n\n constructor(config: PersonalRoomConnectionConfig) {\n this.config = config;\n this.backoffSchedule = config.backoffSchedule ?? DEFAULT_BACKOFF_SCHEDULE;\n this.slowRetryDelayMs = config.slowRetryDelayMs ?? DEFAULT_SLOW_RETRY_DELAY_MS;\n this.pingIntervalMs = config.pingIntervalMs ?? DEFAULT_PING_INTERVAL_MS;\n this.pongTimeoutMs = config.pongTimeoutMs ?? DEFAULT_PONG_TIMEOUT_MS;\n this.probeTimeoutMs = config.probeTimeoutMs ?? DEFAULT_PROBE_TIMEOUT_MS;\n }\n\n getState(): ConnectionState {\n return this.state;\n }\n\n connect(): void {\n this.intentionalDisconnect = false;\n\n if (this.retryTimer !== null) {\n clearTimeout(this.retryTimer);\n this.retryTimer = null;\n }\n\n if (this.ws) {\n this.stopPingTimer();\n this.ws.onopen = null;\n this.ws.onclose = null;\n this.ws.onerror = null;\n this.ws.onmessage = null;\n this.ws.close();\n this.ws = null;\n }\n\n const Impl = this.config.WebSocketImpl ?? (WebSocket as never);\n try {\n this.ws = new Impl(this.config.url);\n } catch {\n this.setState('error');\n this.reconnectWithBackoff();\n return;\n }\n this.setState('connecting');\n\n this.ws.onopen = () => {\n this.setState('connected');\n this.startPingTimer();\n };\n\n this.ws.onclose = () => {\n this.stopPingTimer();\n if (this.state !== 'error' && this.state !== 'reconnecting') {\n this.setState('disconnected');\n }\n if (!this.intentionalDisconnect) {\n this.reconnectWithBackoff();\n }\n };\n\n this.ws.onerror = () => {\n this.stopPingTimer();\n this.setState('error');\n if (!this.intentionalDisconnect) {\n this.reconnectWithBackoff();\n }\n };\n\n this.ws.onmessage = (event: { data: string }) => {\n /**\n * Cluster A4: any incoming traffic (including server pongs) clears the\n * pong watchdog. The proactive ping interval keeps firing — we do NOT\n * reset it here (Cluster A3 — decouple from message receipt).\n */\n this.clearPongWatchdog();\n this.clearProbeWatchdog();\n\n if (event.data === 'pong') {\n this.handlePong();\n return;\n }\n\n let raw: unknown;\n try {\n raw = JSON.parse(event.data);\n } catch {\n return;\n }\n\n const result = PersonalRoomServerMessageSchema.safeParse(raw);\n if (!result.success) {\n if (this.state === 'connected') {\n // biome-ignore lint/suspicious/noConsole: intentional debug visibility for silent Zod drops that hid a critical share-link bug\n console.warn(\n '[PersonalRoom] Dropped unrecognized server message:',\n JSON.stringify(raw),\n result.error.issues\n );\n }\n return;\n }\n\n for (const handler of [...this.messageHandlers]) {\n handler(result.data);\n }\n };\n }\n\n send(msg: PersonalRoomClientMessage): void {\n try {\n this.ws?.send(JSON.stringify(msg));\n } catch {\n /** WebSocket may be in CLOSING/CLOSED state */\n }\n }\n\n onMessage(handler: (msg: PersonalRoomServerMessage) => void): () => void {\n this.messageHandlers.add(handler);\n return () => {\n this.messageHandlers.delete(handler);\n };\n }\n\n onStateChange(handler: (state: ConnectionState) => void): () => void {\n this.stateChangeHandlers.add(handler);\n return () => {\n this.stateChangeHandlers.delete(handler);\n };\n }\n\n /**\n * Register a listener for ping/pong RTT samples. Fires once per pong\n * received from the server with the round-trip duration in milliseconds.\n * Intended for observability: lets callers distinguish a high-latency\n * connection from a healthy responsive one.\n */\n onRttSample(handler: (rttMs: number) => void): () => void {\n this.rttSampleHandlers.add(handler);\n return () => {\n this.rttSampleHandlers.delete(handler);\n };\n }\n\n reconnect(): void {\n this.intentionalDisconnect = false;\n if (this.retryTimer !== null) {\n clearTimeout(this.retryTimer);\n this.retryTimer = null;\n }\n this.retryCount = 0;\n this.connect();\n }\n\n /**\n * Liveness check for a connection believed to be `connected`. Used on a\n * comeback (tab focus / network online) where the WS may be half-open\n * (reports OPEN but the peer is gone — common on mobile and Wi-Fi handoff).\n *\n * Sends a ping and arms a short watchdog ({@link probeTimeoutMs}, default\n * 3s). Any inbound traffic clears it (the DO auto-responds to `ping` with\n * `pong`). On expiry we force-close the WS so the `onclose` path reconnects.\n * This catches a dead WS in ~3s instead of waiting on the lazy 30s ping +\n * 60s pong watchdog (~90s), and — unlike a blind reconnect — leaves a\n * genuinely healthy WS untouched so the daemon's WebRTC peer isn't torn\n * down on every tab focus.\n *\n * If the connection is not currently `connected`, there is nothing to\n * probe — fall through to a normal reconnect.\n */\n probe(): void {\n if (this.state !== 'connected' || !this.ws) {\n this.reconnect();\n return;\n }\n this.intentionalDisconnect = false;\n try {\n this.ws.send('ping');\n this.lastPingSentAt = Date.now();\n } catch {\n /** WS slipped into CLOSING/CLOSED between the state check and send. */\n this.reconnect();\n return;\n }\n this.armProbeWatchdog();\n }\n\n disconnect(): void {\n this.intentionalDisconnect = true;\n this.stopPingTimer();\n\n if (this.retryTimer !== null) {\n clearTimeout(this.retryTimer);\n this.retryTimer = null;\n }\n\n this.retryCount = 0;\n\n if (this.ws) {\n this.ws.onopen = null;\n this.ws.onclose = null;\n this.ws.onerror = null;\n this.ws.onmessage = null;\n this.ws.close();\n this.ws = null;\n }\n this.setState('disconnected');\n }\n\n /**\n * Compute and emit the RTT for the most recent outbound 'ping' upon pong\n * receipt. Extracted from `onmessage` so the message-dispatch handler stays\n * within the cognitive-complexity cap.\n */\n private handlePong(): void {\n if (this.lastPingSentAt === null) return;\n const rttMs = Date.now() - this.lastPingSentAt;\n this.lastPingSentAt = null;\n for (const handler of [...this.rttSampleHandlers]) {\n handler(rttMs);\n }\n }\n\n /**\n * Cluster A3: start the proactive ping interval. Fires every `pingIntervalMs`\n * regardless of message receipt — half-open WS death is detected by the\n * absence of an inbound pong, not by the absence of inbound messages.\n */\n private startPingTimer(): void {\n this.stopPingTimer();\n this.pingTimer = setInterval(() => {\n try {\n this.ws?.send('ping');\n this.lastPingSentAt = Date.now();\n } catch {\n /** WebSocket may be in CLOSING/CLOSED state */\n }\n this.armPongWatchdog();\n }, this.pingIntervalMs);\n }\n\n private stopPingTimer(): void {\n if (this.pingTimer !== null) {\n clearInterval(this.pingTimer);\n this.pingTimer = null;\n }\n this.clearPongWatchdog();\n this.clearProbeWatchdog();\n /** Reset RTT bookkeeping so a stale send time doesn't carry across reconnects. */\n this.lastPingSentAt = null;\n }\n\n /**\n * Cluster A4: arm a one-shot watchdog that fires if no inbound traffic\n * arrives within `pongTimeoutMs`. On expiry we force-close the WS so the\n * onclose path runs the backoff and reconnect.\n *\n * If a watchdog is already pending (a prior ping hasn't been answered\n * yet), leave it alone — the deadline applies to the OLDEST unanswered\n * ping, not the most recent. Resetting on every ping would mask a\n * persistent half-open WS forever.\n */\n private armPongWatchdog(): void {\n if (this.pongWatchdog !== null) return;\n this.pongWatchdog = setTimeout(() => {\n this.pongWatchdog = null;\n try {\n this.ws?.close();\n } catch {\n /** WebSocket may already be in CLOSING/CLOSED state */\n }\n }, this.pongTimeoutMs);\n }\n\n private clearPongWatchdog(): void {\n if (this.pongWatchdog !== null) {\n clearTimeout(this.pongWatchdog);\n this.pongWatchdog = null;\n }\n }\n\n /**\n * Arm the short comeback-probe watchdog. Replaces any pending probe (the\n * latest probe's deadline is the one that matters). On expiry, force-close\n * the WS so `onclose` runs the reconnect path.\n */\n private armProbeWatchdog(): void {\n this.clearProbeWatchdog();\n this.probeWatchdog = setTimeout(() => {\n this.probeWatchdog = null;\n try {\n this.ws?.close();\n } catch {\n /** WebSocket may already be in CLOSING/CLOSED state */\n }\n }, this.probeTimeoutMs);\n }\n\n private clearProbeWatchdog(): void {\n if (this.probeWatchdog !== null) {\n clearTimeout(this.probeWatchdog);\n this.probeWatchdog = null;\n }\n }\n\n /**\n * Cluster A8: geometric backoff schedule replacing today's bimodal jump\n * (1s -> 30s). Schedule defaults to [1, 2, 4, 8, 15, 30]s. After the\n * schedule is exhausted we slow-retry every `slowRetryDelayMs` forever —\n * no terminal `failed` state. Bounded `maxRetries` is preserved for\n * legacy callers (still uses the geometric schedule for the bounded run,\n * but stops once `retryCount >= maxRetries`).\n *\n * Legacy `initialDelayMs` / `maxDelayMs` / `backoffMultiplier` configs\n * fall back to the original `min(initial * mult^count, max)` formula so\n * existing tests and call sites keep their semantics.\n */\n private reconnectWithBackoff(): void {\n if (this.retryTimer !== null) return;\n\n const maxRetries = this.config.maxRetries;\n if (maxRetries === undefined || maxRetries === 0) {\n return;\n }\n\n if (maxRetries !== -1 && this.retryCount >= maxRetries) {\n return;\n }\n\n this.setState('reconnecting');\n\n if (this.intentionalDisconnect) return;\n\n const delay = this.computeBackoffDelay(this.retryCount);\n\n this.retryCount++;\n\n this.retryTimer = setTimeout(() => {\n this.retryTimer = null;\n this.connect();\n }, delay);\n }\n\n private computeBackoffDelay(attempt: number): number {\n /**\n * Legacy path: callers that pass `initialDelayMs`/`backoffMultiplier`/`maxDelayMs`\n * keep the old `min(initial * mult^attempt, max)` semantics. New callers\n * use the geometric schedule + slow-retry-forever tail.\n */\n if (\n this.config.initialDelayMs !== undefined ||\n this.config.maxDelayMs !== undefined ||\n this.config.backoffMultiplier !== undefined\n ) {\n const initialDelay = this.config.initialDelayMs ?? 1000;\n const maxDelay = this.config.maxDelayMs ?? 30_000;\n const multiplier = this.config.backoffMultiplier ?? 2;\n return Math.min(initialDelay * multiplier ** attempt, maxDelay);\n }\n\n if (attempt < this.backoffSchedule.length) {\n return this.backoffSchedule[attempt] ?? this.slowRetryDelayMs;\n }\n return this.slowRetryDelayMs;\n }\n\n private setState(newState: ConnectionState): void {\n this.state = newState;\n if (newState === 'connected') {\n this.retryCount = 0;\n }\n for (const handler of [...this.stateChangeHandlers]) {\n handler(newState);\n }\n }\n}\n","/**\n * Shared constants and schemas for the Shipyard publish feature.\n *\n * Lives in `@shipyard/session` (not `@shipyard/loro-schema`) because it must\n * be consumable from Cloudflare Workers (session-server, publish-worker) where\n * loro-crdt's WASM barrel cannot load in vitest-pool-workers. The session\n * package is Worker-safe by construction.\n *\n * `@shipyard/loro-schema` re-exports these for backwards compatibility with\n * daemon/browser imports that expect the schema to own the shape.\n *\n * @module publish-constants\n */\n\nimport { z } from 'zod';\n\n/**\n * The set of supported publish kinds. Mirrors the D1 CHECK constraint on\n * published_previews.kind.\n */\nexport const PUBLISHED_PREVIEW_KINDS = [\n 'html',\n 'mermaid',\n 'svg',\n 'image',\n 'static-site',\n 'dom-snapshot',\n] as const;\n\nexport type PublishedPreviewKind = (typeof PUBLISHED_PREVIEW_KINDS)[number];\n\nexport const PublishedPreviewKindSchema = z.enum(PUBLISHED_PREVIEW_KINDS);\n\n/**\n * TTL choices exposed to users. Mapped to ms via {@link ttlToMilliseconds}.\n */\nexport const PUBLISH_TTL_CHOICES = ['24h', '7d', '30d'] as const;\nexport type PublishTtl = (typeof PUBLISH_TTL_CHOICES)[number];\n\nexport const PublishTtlSchema = z.enum(PUBLISH_TTL_CHOICES).default('24h');\n\n/**\n * Convert a TTL choice to milliseconds.\n */\nexport function ttlToMilliseconds(ttl: PublishTtl): number {\n switch (ttl) {\n case '24h':\n return 24 * 60 * 60 * 1000;\n case '7d':\n return 7 * 24 * 60 * 60 * 1000;\n case '30d':\n return 30 * 24 * 60 * 60 * 1000;\n }\n}\n\n/** Max bytes for a single-file publish (html, mermaid, svg, image, dom-snapshot). */\nexport const MAX_SINGLE_FILE_BYTES = 50 * 1024 * 1024;\n/** Max aggregate bytes across a multi-file build (static-site). */\nexport const MAX_BUILD_TOTAL_BYTES = 200 * 1024 * 1024;\n/** Max bytes for any single file within a build. */\nexport const MAX_BUILD_PER_FILE_BYTES = 10 * 1024 * 1024;\n/** Max number of files in a build. */\nexport const MAX_BUILD_FILE_COUNT = 2000;\n\n/** Per-user publish rate limit (publishes per hour). */\nexport const PUBLISH_RATE_LIMIT_PER_HOUR = 20;\n/** Per-user total active-publish storage quota (bytes). */\nexport const PUBLISH_QUOTA_TOTAL_BYTES = 500 * 1024 * 1024;\n\n/**\n * POST /publish request fields.\n *\n * Note: the endpoint accepts multipart/form-data, not JSON. This schema\n * describes the string fields only; files are transported as `files[]`\n * entries and validated separately against size/count limits.\n */\nexport const PublishCreateRequestSchema = z.object({\n kind: PublishedPreviewKindSchema,\n ttl: PublishTtlSchema,\n entryPath: z.string().min(1).max(512),\n title: z.string().max(200).optional(),\n /** \"1\" to enable SPA fallback (index.html served for unknown paths). */\n spaFallback: z.enum(['0', '1']).default('0'),\n});\nexport type PublishCreateRequest = z.infer<typeof PublishCreateRequestSchema>;\n\n/**\n * POST /publish response body.\n *\n * Discriminated on `ok` so consumers cannot silently read `undefined` from\n * `id`/`url`/`expiresAt` on failure (previously all three were required on a\n * single flat object, which TypeScript happily parsed but which didn't survive\n * the HTTP error paths — e.g. 4xx bodies with `{ error, message }`). The\n * discriminant forces callers to narrow before reading the success fields.\n *\n * Session-server routes emit `ok: true` alongside the data on 2xx and\n * `ok: false` alongside `error` on 4xx — see routes/publish.ts.\n */\nexport const PublishCreateResponseSchema = z.discriminatedUnion('ok', [\n z.object({\n ok: z.literal(true),\n id: z.string().min(1),\n url: z.string().url(),\n expiresAt: z.number().int(),\n }),\n z.object({\n ok: z.literal(false),\n error: z.string(),\n }),\n]);\nexport type PublishCreateResponse = z.infer<typeof PublishCreateResponseSchema>;\n\n/**\n * DELETE /publish/:id response body.\n */\nexport const PublishDeleteResponseSchema = z.object({\n ok: z.literal(true),\n});\nexport type PublishDeleteResponse = z.infer<typeof PublishDeleteResponseSchema>;\n\n/**\n * Row shape for GET /publish/mine.\n */\nexport const PublishListItemSchema = z.object({\n id: z.string().min(1),\n url: z.string().url(),\n kind: z.string(),\n title: z.string().nullable(),\n createdAt: z.number().int(),\n expiresAt: z.number().int(),\n sizeBytes: z.number().int().nonnegative(),\n});\nexport type PublishListItem = z.infer<typeof PublishListItemSchema>;\n\n/**\n * GET /publish/mine response body.\n */\nexport const PublishListResponseSchema = z.object({\n items: z.array(PublishListItemSchema),\n});\nexport type PublishListResponse = z.infer<typeof PublishListResponseSchema>;\n"],"mappings":"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","names":[]}