@agimon-ai/doompi-core 0.0.1-alpha.77 → 0.0.1-alpha.79

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 (129) hide show
  1. package/dist/headless-hub.cjs +48 -3
  2. package/dist/headless-hub.cjs.map +1 -1
  3. package/dist/headless-hub.d.cts +5 -1
  4. package/dist/headless-hub.d.cts.map +1 -1
  5. package/dist/headless-hub.d.mts +5 -1
  6. package/dist/headless-hub.d.mts.map +1 -1
  7. package/dist/headless-hub.mjs +48 -3
  8. package/dist/headless-hub.mjs.map +1 -1
  9. package/dist/headless-server.cjs +17 -2
  10. package/dist/headless-server.cjs.map +1 -1
  11. package/dist/headless-server.d.cts +5 -0
  12. package/dist/headless-server.d.cts.map +1 -1
  13. package/dist/headless-server.d.mts +5 -0
  14. package/dist/headless-server.d.mts.map +1 -1
  15. package/dist/headless-server.mjs +17 -2
  16. package/dist/headless-server.mjs.map +1 -1
  17. package/dist/headless.d.cts +2 -2
  18. package/dist/headless.d.mts +2 -2
  19. package/dist/hub-channel.d.cts +2 -2
  20. package/dist/hub-channel.d.mts +2 -2
  21. package/dist/mcp-facet.d.cts +2 -2
  22. package/dist/mcp-facet.d.mts +2 -2
  23. package/dist/runtime-cordis-host.cjs.map +1 -1
  24. package/dist/runtime-cordis-host.d.cts +1 -1
  25. package/dist/runtime-cordis-host.d.cts.map +1 -1
  26. package/dist/runtime-cordis-host.d.mts +1 -1
  27. package/dist/runtime-cordis-host.d.mts.map +1 -1
  28. package/dist/runtime-cordis-host.mjs.map +1 -1
  29. package/dist/runtime-pi-extension.cjs +6 -2
  30. package/dist/runtime-pi-extension.cjs.map +1 -1
  31. package/dist/runtime-pi-extension.d.cts.map +1 -1
  32. package/dist/runtime-pi-extension.d.mts.map +1 -1
  33. package/dist/runtime-pi-extension.mjs +6 -2
  34. package/dist/runtime-pi-extension.mjs.map +1 -1
  35. package/dist/schemas/builtinApiContracts.cjs +1 -1
  36. package/dist/schemas/builtinApiContracts.cjs.map +1 -1
  37. package/dist/schemas/builtinApiContracts.mjs +1 -1
  38. package/dist/schemas/builtinApiContracts.mjs.map +1 -1
  39. package/dist/schemas/headless.cjs.map +1 -1
  40. package/dist/schemas/headless.d.cts +15 -0
  41. package/dist/schemas/headless.d.cts.map +1 -1
  42. package/dist/schemas/headless.d.mts +15 -0
  43. package/dist/schemas/headless.d.mts.map +1 -1
  44. package/dist/schemas/headless.mjs.map +1 -1
  45. package/dist/schemas/httpApiContracts.cjs +6 -0
  46. package/dist/schemas/httpApiContracts.cjs.map +1 -1
  47. package/dist/schemas/httpApiContracts.mjs +6 -0
  48. package/dist/schemas/httpApiContracts.mjs.map +1 -1
  49. package/dist/schemas/hubChannel.d.cts +21 -0
  50. package/dist/schemas/hubChannel.d.cts.map +1 -1
  51. package/dist/schemas/hubChannel.d.mts +21 -0
  52. package/dist/schemas/hubChannel.d.mts.map +1 -1
  53. package/dist/schemas/mcpFacet.cjs.map +1 -1
  54. package/dist/schemas/mcpFacet.d.cts +27 -0
  55. package/dist/schemas/mcpFacet.d.cts.map +1 -1
  56. package/dist/schemas/mcpFacet.d.mts +27 -0
  57. package/dist/schemas/mcpFacet.d.mts.map +1 -1
  58. package/dist/schemas/mcpFacet.mjs.map +1 -1
  59. package/dist/server/headlessProtocol.cjs +7 -1
  60. package/dist/server/headlessProtocol.cjs.map +1 -1
  61. package/dist/server/headlessProtocol.mjs +7 -1
  62. package/dist/server/headlessProtocol.mjs.map +1 -1
  63. package/dist/server/sessionMcpHandler.cjs +113 -21
  64. package/dist/server/sessionMcpHandler.cjs.map +1 -1
  65. package/dist/server/sessionMcpHandler.d.cts +6 -1
  66. package/dist/server/sessionMcpHandler.d.cts.map +1 -1
  67. package/dist/server/sessionMcpHandler.d.mts +6 -1
  68. package/dist/server/sessionMcpHandler.d.mts.map +1 -1
  69. package/dist/server/sessionMcpHandler.mjs +113 -21
  70. package/dist/server/sessionMcpHandler.mjs.map +1 -1
  71. package/dist/server/sessionMcpRoutes.cjs +224 -15
  72. package/dist/server/sessionMcpRoutes.cjs.map +1 -1
  73. package/dist/server/sessionMcpRoutes.mjs +213 -7
  74. package/dist/server/sessionMcpRoutes.mjs.map +1 -1
  75. package/dist/services/historyCreationFileSystem/index.cjs +3 -2
  76. package/dist/services/historyCreationFileSystem/index.cjs.map +1 -1
  77. package/dist/services/historyCreationFileSystem/index.mjs +3 -2
  78. package/dist/services/historyCreationFileSystem/index.mjs.map +1 -1
  79. package/dist/services/openSessionRegistry/index.cjs +13 -6
  80. package/dist/services/openSessionRegistry/index.cjs.map +1 -1
  81. package/dist/services/openSessionRegistry/index.d.cts +2 -1
  82. package/dist/services/openSessionRegistry/index.d.cts.map +1 -1
  83. package/dist/services/openSessionRegistry/index.d.mts +2 -1
  84. package/dist/services/openSessionRegistry/index.d.mts.map +1 -1
  85. package/dist/services/openSessionRegistry/index.mjs +13 -6
  86. package/dist/services/openSessionRegistry/index.mjs.map +1 -1
  87. package/dist/services/piExtensionHost/index.cjs +20 -8
  88. package/dist/services/piExtensionHost/index.cjs.map +1 -1
  89. package/dist/services/piExtensionHost/index.mjs +20 -8
  90. package/dist/services/piExtensionHost/index.mjs.map +1 -1
  91. package/dist/services/pluginLifecycle/index.cjs +1 -1
  92. package/dist/services/pluginLifecycle/index.cjs.map +1 -1
  93. package/dist/services/pluginLifecycle/index.mjs +1 -1
  94. package/dist/services/pluginLifecycle/index.mjs.map +1 -1
  95. package/dist/services/sessionMcpAuthorization/index.cjs +56 -0
  96. package/dist/services/sessionMcpAuthorization/index.cjs.map +1 -1
  97. package/dist/services/sessionMcpAuthorization/index.d.cts +12 -0
  98. package/dist/services/sessionMcpAuthorization/index.d.cts.map +1 -1
  99. package/dist/services/sessionMcpAuthorization/index.d.mts +12 -0
  100. package/dist/services/sessionMcpAuthorization/index.d.mts.map +1 -1
  101. package/dist/services/sessionMcpAuthorization/index.mjs +56 -1
  102. package/dist/services/sessionMcpAuthorization/index.mjs.map +1 -1
  103. package/dist/services/sessionMcpConversations/index.cjs +170 -0
  104. package/dist/services/sessionMcpConversations/index.cjs.map +1 -0
  105. package/dist/services/sessionMcpConversations/index.mjs +165 -0
  106. package/dist/services/sessionMcpConversations/index.mjs.map +1 -0
  107. package/dist/services/sessionMcpRegistrationStore/index.cjs +85 -0
  108. package/dist/services/sessionMcpRegistrationStore/index.cjs.map +1 -0
  109. package/dist/services/sessionMcpRegistrationStore/index.mjs +82 -0
  110. package/dist/services/sessionMcpRegistrationStore/index.mjs.map +1 -0
  111. package/dist/systems/main/adapters/headlessSessionHost.cjs +82 -12
  112. package/dist/systems/main/adapters/headlessSessionHost.cjs.map +1 -1
  113. package/dist/systems/main/adapters/headlessSessionHost.d.cts.map +1 -1
  114. package/dist/systems/main/adapters/headlessSessionHost.d.mts.map +1 -1
  115. package/dist/systems/main/adapters/headlessSessionHost.mjs +81 -12
  116. package/dist/systems/main/adapters/headlessSessionHost.mjs.map +1 -1
  117. package/dist/testing/pi/testHost.cjs +6 -0
  118. package/dist/testing/pi/testHost.cjs.map +1 -1
  119. package/dist/testing/pi/testHost.mjs +6 -0
  120. package/dist/testing/pi/testHost.mjs.map +1 -1
  121. package/dist/types/server/sessionToolSurface.d.cts +5 -2
  122. package/dist/types/server/sessionToolSurface.d.cts.map +1 -1
  123. package/dist/types/server/sessionToolSurface.d.mts +5 -2
  124. package/dist/types/server/sessionToolSurface.d.mts.map +1 -1
  125. package/dist/web/types/webPlugin.d.cts +8 -2
  126. package/dist/web/types/webPlugin.d.cts.map +1 -1
  127. package/dist/web/types/webPlugin.d.mts +8 -2
  128. package/dist/web/types/webPlugin.d.mts.map +1 -1
  129. package/package.json +14 -14
@@ -22,6 +22,11 @@ function scopeOf(session) {
22
22
  /** Owns direct, same-process session and channel state for presentation adapters. */
23
23
  function createHeadlessHub(options) {
24
24
  const sessions = /* @__PURE__ */ new Map();
25
+ const pendingSetups = /* @__PURE__ */ new Map();
26
+ const present = (session) => pendingSetups.has(session.id) ? {
27
+ ...session,
28
+ pendingSetups: pendingSetups.get(session.id)
29
+ } : session;
25
30
  const pluginRegistry = require_pluginProtocol.createDoomPluginRegistry();
26
31
  const channels = /* @__PURE__ */ new Map();
27
32
  const listeners = /* @__PURE__ */ new Set();
@@ -169,7 +174,11 @@ function createHeadlessHub(options) {
169
174
  };
170
175
  const emit = (event) => {
171
176
  if (closed) return;
172
- for (const listener of listeners) listener(event);
177
+ const projected = event.kind === "upsert" ? {
178
+ ...event,
179
+ session: present(event.session)
180
+ } : event;
181
+ for (const listener of listeners) listener(projected);
173
182
  };
174
183
  const sessionScopes = () => [...sessions.values()].map(scopeOf);
175
184
  const publish = (frameType, sessionId, payload, connectionId) => {
@@ -198,12 +207,35 @@ function createHeadlessHub(options) {
198
207
  headers
199
208
  };
200
209
  };
210
+ const reservationsFor = (mount) => {
211
+ const reservations = options.sessionReservations;
212
+ if (!reservations) return void 0;
213
+ const check = (parentId) => {
214
+ const parent = sessions.get(parentId);
215
+ if (!parent || !belongs(mount, parent)) throw new Error("Parent session is outside this mount.");
216
+ };
217
+ return {
218
+ read: (id, parentId) => {
219
+ check(parentId);
220
+ return reservations.read(id, parentId);
221
+ },
222
+ prepare: (id, parentId, cwd) => {
223
+ check(parentId);
224
+ return reservations.prepare(id, parentId, cwd);
225
+ },
226
+ complete: (id, parentId) => {
227
+ check(parentId);
228
+ return reservations.complete(id, parentId);
229
+ }
230
+ };
231
+ };
201
232
  const channelHost = (frameType, mount) => ({
202
233
  sessions: () => sessionScopes().filter((scope) => {
203
234
  const session = sessions.get(scope.sessionId);
204
235
  return session !== void 0 && serves(mount, frameType, session);
205
236
  }),
206
237
  sessionService: {
238
+ reservations: reservationsFor(mount),
207
239
  create: async (request) => {
208
240
  if (mount.scope !== "global") {
209
241
  const parent = request.parentSessionId === void 0 ? void 0 : sessions.get(request.parentSessionId);
@@ -426,6 +458,7 @@ function createHeadlessHub(options) {
426
458
  },
427
459
  close: closeSession,
428
460
  isLive: (sessionId) => !closed && sessions.has(sessionId),
461
+ reservations: options.sessionReservations,
429
462
  canCommunicate,
430
463
  bindCommunication
431
464
  };
@@ -501,8 +534,20 @@ function createHeadlessHub(options) {
501
534
  return {
502
535
  sessionService,
503
536
  directEvents,
504
- snapshot: () => [...sessions.values()],
505
- session: (sessionId) => sessions.get(sessionId),
537
+ snapshot: () => [...sessions.values()].map(present),
538
+ session: (sessionId) => {
539
+ const session = sessions.get(sessionId);
540
+ return session === void 0 ? void 0 : present(session);
541
+ },
542
+ setPendingSessionSetups(sessionId, pending) {
543
+ if (JSON.stringify(pendingSetups.get(sessionId) ?? []) === JSON.stringify(pending)) return;
544
+ pendingSetups.set(sessionId, Object.freeze([...pending]));
545
+ const session = sessions.get(sessionId);
546
+ if (session) emit({
547
+ kind: "upsert",
548
+ session
549
+ });
550
+ },
506
551
  runtime: (sessionId) => sessions.get(sessionId)?.host.runtime,
507
552
  onEvent(listener) {
508
553
  listeners.add(listener);
@@ -1 +1 @@
1
- {"version":3,"file":"headless-hub.cjs","names":["createDoomPluginRegistry","createDoomServerHost","installServerFacets"],"sources":["../src/server/headlessHub.ts"],"sourcesContent":["import type {\n DoomDirectEventBus,\n DoomHubChannel,\n DoomHubChannelConnection,\n DoomHubChannelHost,\n DoomHubSessionApiRequest,\n DoomHubSessionCreateRequest,\n DoomHubSessionScope,\n DoomHubSessionService,\n DoomSessionCommunicationEndpoint,\n} from '../exports/hubChannel';\nimport type { DoomWebComposition } from '../exports/packageApi';\nimport type { DoomApiContext, DoomApiMount } from '../exports/packageApi';\nimport {\n createDoomPluginRegistry,\n type DoomPluginCaller,\n type DoomPluginDirection,\n type DoomPluginRegistry,\n} from '../exports/pluginProtocol';\nimport {\n createDoomServerHost,\n installServerFacets,\n type DoomServerFacet,\n type DoomServerHost,\n type InstalledServerFacets,\n type LoadedServerFacet,\n} from '../exports/serverFacet';\nimport type { HeadlessSessionHost, HeadlessSessionHostOptions } from '../systems/main/types/headlessSessionHost';\nimport type { HeadlessSessionManager } from '../systems/main/types/headlessSessionManager';\n\nconst AUTHORIZATION_HEADER = 'authorization';\nconst CHANNEL_PRIORITY = { global: 0, workspace: 1, session: 2 } as const;\n\nexport interface HeadlessHubSession {\n readonly id: string;\n readonly workspaceId?: string;\n webComposition?: DoomWebComposition;\n readonly name: string;\n readonly cwd: string;\n readonly createdAt: string;\n readonly parentSessionId?: string;\n readonly sessionProvenance?: string;\n readonly updatedAt?: string;\n readonly phase?: 'idle' | 'turn' | 'compaction' | 'retry';\n readonly phaseSince?: string;\n readonly pendingMessageCount?: number;\n readonly everPrompted?: boolean;\n readonly awaitingInput?: boolean;\n readonly lastSettledAt?: string;\n /** Environment admitted for this session, when supplied by the host. */\n readonly environment?: Readonly<Record<string, string | undefined>>;\n readonly host: HeadlessSessionHost;\n}\n\nexport interface HeadlessWorkspace {\n readonly id: string;\n readonly root: string;\n readonly available?: boolean;\n}\n\nexport type HeadlessHubEvent =\n | { kind: 'upsert'; session: HeadlessHubSession }\n | { kind: 'removed'; sessionId: string }\n | { kind: 'workspace_upsert'; workspace: HeadlessWorkspace }\n | { kind: 'workspace_removed'; workspaceId: string }\n | { kind: 'channel'; frameType: string; sessionId: string; payload: unknown; connectionId?: string };\n\nexport interface HeadlessHubOptions {\n manager: HeadlessSessionManager;\n /** Resolves the hub credential, because the server reads its token file after the hub exists. */\n hubToken?: () => string | undefined;\n /** Creates a session through the canonical cockpit lifecycle for hub channels. */\n createSession?: (request: DoomHubSessionCreateRequest) => Promise<DoomHubSessionScope>;\n onNotice?: (message: string) => void;\n requestSessionApi?: (scope: DoomHubSessionScope, request: DoomHubSessionApiRequest) => Promise<Response>;\n admitWorkspace?: (root: string) => Promise<HeadlessWorkspace>;\n onWorkspaceRemoved?: (workspaceId: string) => void;\n}\n\nexport interface HeadlessHub {\n /** Canonical direct lifecycle shared by hub channels, APIs, and sessions. */\n readonly sessionService: DoomHubSessionService;\n /** Lifecycle-owned event path shared by session facets and hub channels. */\n readonly directEvents: DoomDirectEventBus;\n snapshot(): readonly HeadlessHubSession[];\n session(sessionId: string): HeadlessHubSession | undefined;\n runtime(sessionId: string): HeadlessSessionHost['runtime'] | undefined;\n onEvent(listener: (event: HeadlessHubEvent) => void): () => void;\n register(session: HeadlessHubSession): void;\n create(options: HeadlessSessionHostOptions): Promise<HeadlessHubSession>;\n closeSession(sessionId: string): Promise<void>;\n /** Publishes removal of a persisted session that has no live host. */\n notifySessionRemoved(sessionId: string): void;\n /** Dispatches an authenticated package API request inside the owning process. */\n requestSessionApi(scope: DoomHubSessionScope, request: DoomHubSessionApiRequest): Promise<Response>;\n /** Mounts the selected hub facets once, before the headless server accepts clients. */\n mountFacets(facets: readonly (DoomServerFacet | LoadedServerFacet)[], context?: DoomApiContext): Promise<void>;\n workspaces(): readonly HeadlessWorkspace[];\n registerWorkspace(workspace: HeadlessWorkspace): void;\n admitWorkspace(root: string): Promise<HeadlessWorkspace>;\n removeWorkspace(workspaceId: string): Promise<void>;\n requestApi(mount: DoomApiMount, basePath: string, request: Request): Promise<Response>;\n /** The dispatch table hub facets mount into, shared with session hosts this hub serves. */\n readonly pluginRegistry: DoomPluginRegistry;\n invokePlugin(call: unknown, direction: DoomPluginDirection, caller?: DoomPluginCaller): Promise<unknown>;\n registerChannel(channel: DoomHubChannel, mount?: DoomApiMount): () => void;\n channelTypes(): readonly string[];\n channelFrames(sessionId: string): Array<{ type: string; sessionId: string; payload: unknown }>;\n /** Resolves a channel-owned child journal for the browser thread projection. */\n threadJournal(sessionId: string, threadId: string): string | undefined;\n receiveChannel(sessionId: string, frameType: string, payload: unknown, connectionId: string): void;\n disconnectChannels(connectionId: string): void;\n close(): Promise<void>;\n}\n\ninterface StartedChannel {\n readonly channel: DoomHubChannel;\n readonly source: ReturnType<DoomHubChannel['start']>;\n readonly mount: DoomApiMount;\n}\n\nfunction scopeOf(session: HeadlessHubSession): DoomHubSessionScope {\n return {\n sessionId: session.id,\n ...(session.workspaceId === undefined ? {} : { workspaceId: session.workspaceId }),\n cwd: session.cwd,\n ...(session.environment === undefined ? {} : { environment: session.environment }),\n };\n}\n\n/** Owns direct, same-process session and channel state for presentation adapters. */\nexport function createHeadlessHub(options: HeadlessHubOptions): HeadlessHub {\n const sessions = new Map<string, HeadlessHubSession>();\n const pluginRegistry = createDoomPluginRegistry();\n const channels = new Map<string, StartedChannel>();\n const listeners = new Set<(event: HeadlessHubEvent) => void>();\n const subscriptions = new Set<() => void>();\n const sessionCleanups = new Map<string, () => void>();\n const presentationCleanups = new Map<string, () => void>();\n const sessionShutdowns = new Map<string, { session: HeadlessHubSession; promise: Promise<void>; pending: boolean }>();\n const directEventListeners = new Map<string, Set<(payload: unknown) => void>>();\n const directEventLatest = new Map<string, unknown>();\n const maxDirectEventLatest = 4096;\n const communicationEndpoints = new Map<\n string,\n {\n listeners: Map<string, Set<(sourceSessionId: string, payload: unknown) => void>>;\n readyListeners: Set<(peerSessionId: string) => void>;\n ready: boolean;\n endpoint: DoomSessionCommunicationEndpoint;\n }\n >();\n let closed = false;\n const mounts = new Map<string, { host: DoomServerHost; installed: InstalledServerFacets }>();\n const workspaces = new Map<string, HeadlessWorkspace>();\n let sessionService: DoomHubSessionService;\n\n const mountKey = (mount: DoomApiMount): string =>\n mount.scope === 'global'\n ? 'global'\n : mount.scope === 'workspace'\n ? `workspace:${mount.workspaceId}`\n : `session:${mount.sessionId}`;\n const belongs = (mount: DoomApiMount, session: HeadlessHubSession): boolean =>\n mount.scope === 'global' ||\n (mount.scope === 'workspace' ? session.workspaceId === mount.workspaceId : session.id === mount.sessionId);\n const canClose = (mount: DoomApiMount, session: HeadlessHubSession): boolean =>\n belongs(mount, session) || (mount.scope === 'session' && session.parentSessionId === mount.sessionId);\n const canCommunicate = (sourceId: string, targetId: string): boolean => {\n if (closed || sourceId === targetId) return false;\n const source = sessions.get(sourceId);\n const target = sessions.get(targetId);\n return (\n source !== undefined &&\n target !== undefined &&\n (source.parentSessionId === target.id || target.parentSessionId === source.id)\n );\n };\n const selectedChannels = (session: HeadlessHubSession): StartedChannel[] => {\n const selected = new Map<string, StartedChannel>();\n for (const started of channels.values()) {\n if (!belongs(started.mount, session)) continue;\n const previous = selected.get(started.channel.frameType);\n if (!previous || CHANNEL_PRIORITY[started.mount.scope] > CHANNEL_PRIORITY[previous.mount.scope])\n selected.set(started.channel.frameType, started);\n }\n return [...selected.values()];\n };\n const selectedChannel = (session: HeadlessHubSession, frameType: string): StartedChannel | undefined =>\n selectedChannels(session).find((started) => started.channel.frameType === frameType);\n /**\n * One channel serves a session: the narrowest mount that reaches it. A package that\n * registers the same channel globally and per workspace would otherwise run two\n * sources against one session, and only the narrower one can publish or receive.\n *\n * Compared by mount scope rather than identity so a channel being started already\n * knows which sessions it is taking over.\n */\n const serves = (mount: DoomApiMount, frameType: string, session: HeadlessHubSession): boolean => {\n if (!belongs(mount, session)) return false;\n const current = selectedChannel(session, frameType);\n return current === undefined || CHANNEL_PRIORITY[mount.scope] >= CHANNEL_PRIORITY[current.mount.scope];\n };\n\n const directEventKey = (frameType: string, sessionId: string): string => `${frameType}\\0${sessionId}`;\n const directEvents: DoomDirectEventBus = {\n publish(frameType, sessionId, payload) {\n if (closed) return;\n const key = directEventKey(frameType, sessionId);\n directEventLatest.set(key, payload);\n while (directEventLatest.size > maxDirectEventLatest) {\n const oldest = directEventLatest.keys().next().value;\n if (oldest === undefined) break;\n directEventLatest.delete(oldest);\n }\n const listeners = directEventListeners.get(key);\n if (listeners === undefined) return;\n for (const listener of listeners) listener(payload);\n },\n subscribe(frameType, sessionId, listener, options) {\n if (closed) return () => undefined;\n const key = directEventKey(frameType, sessionId);\n const listeners = directEventListeners.get(key) ?? new Set<(payload: unknown) => void>();\n listeners.add(listener);\n directEventListeners.set(key, listeners);\n if (options?.replayLatest && directEventLatest.has(key)) listener(directEventLatest.get(key));\n let released = false;\n return () => {\n if (released) return;\n released = true;\n listeners.delete(listener);\n if (listeners.size === 0) directEventListeners.delete(key);\n };\n },\n clearSession(sessionId) {\n const suffix = `\\0${sessionId}`;\n for (const key of directEventLatest.keys()) if (key.endsWith(suffix)) directEventLatest.delete(key);\n },\n close() {\n directEventListeners.clear();\n directEventLatest.clear();\n },\n };\n\n const announceCommunicationReady = (sessionId: string): void => {\n const current = communicationEndpoints.get(sessionId);\n if (current?.ready !== true) return;\n for (const [peerSessionId, peer] of communicationEndpoints) {\n if (!peer.ready || !canCommunicate(sessionId, peerSessionId)) continue;\n for (const listener of current.readyListeners) listener(peerSessionId);\n for (const listener of peer.readyListeners) listener(sessionId);\n }\n };\n\n const bindCommunication = (sessionId: string): DoomSessionCommunicationEndpoint => {\n const existing = communicationEndpoints.get(sessionId);\n if (existing !== undefined) return existing.endpoint;\n const listeners = new Map<string, Set<(sourceSessionId: string, payload: unknown) => void>>();\n const readyListeners = new Set<(peerSessionId: string) => void>();\n let endpoint!: DoomSessionCommunicationEndpoint;\n const record = {\n listeners,\n readyListeners,\n ready: false,\n get endpoint() {\n return endpoint;\n },\n };\n endpoint = {\n sessionId,\n publish(targetSessionId, type, payload) {\n if (!canCommunicate(sessionId, targetSessionId)) return false;\n const target = communicationEndpoints.get(targetSessionId);\n if (target?.ready !== true) return false;\n for (const listener of target.listeners.get(type) ?? []) listener(sessionId, payload);\n return true;\n },\n subscribe(type, listener) {\n const current = listeners.get(type) ?? new Set<(sourceSessionId: string, payload: unknown) => void>();\n current.add(listener);\n listeners.set(type, current);\n return () => {\n current.delete(listener);\n if (current.size === 0) listeners.delete(type);\n };\n },\n onPeerReady(listener) {\n readyListeners.add(listener);\n if (!record.ready) {\n record.ready = true;\n announceCommunicationReady(sessionId);\n }\n return () => readyListeners.delete(listener);\n },\n close() {\n if (communicationEndpoints.get(sessionId)?.endpoint !== endpoint) return;\n communicationEndpoints.delete(sessionId);\n listeners.clear();\n readyListeners.clear();\n },\n };\n communicationEndpoints.set(sessionId, record);\n return endpoint;\n };\n\n const emit = (event: HeadlessHubEvent): void => {\n if (closed) return;\n for (const listener of listeners) listener(event);\n };\n const sessionScopes = (): readonly DoomHubSessionScope[] => [...sessions.values()].map(scopeOf);\n const publish = (frameType: string, sessionId: string, payload: unknown, connectionId?: string): void => {\n if (!sessions.has(sessionId)) return;\n emit({ kind: 'channel', frameType, sessionId, payload, ...(connectionId === undefined ? {} : { connectionId }) });\n };\n\n /**\n * Hub-owned calls carry the hub credential, so a session API can tell them from a\n * browser call it must not trust. Browser traffic reaches a session API through\n * requestApi instead, and keeps the headers its caller sent.\n */\n const hubAuthenticated = (request: DoomHubSessionApiRequest): DoomHubSessionApiRequest => {\n const token = options.hubToken?.();\n if (token === undefined || token === '') return request;\n const headers = new Headers(request.headers);\n if (headers.has(AUTHORIZATION_HEADER)) return request;\n headers.set(AUTHORIZATION_HEADER, `Bearer ${token}`);\n return { ...request, headers };\n };\n\n const channelHost = (frameType: string, mount: DoomApiMount): DoomHubChannelHost => ({\n sessions: () =>\n sessionScopes().filter((scope) => {\n const session = sessions.get(scope.sessionId);\n return session !== undefined && serves(mount, frameType, session);\n }),\n sessionService: {\n create: async (request) => {\n if (mount.scope !== 'global') {\n const parent = request.parentSessionId === undefined ? undefined : sessions.get(request.parentSessionId);\n if (!parent || !belongs(mount, parent)) throw new Error('Parent session is outside this mount.');\n }\n return sessionService.create(request);\n },\n close: async (id) => {\n const session = sessions.get(id);\n if (session === undefined) {\n const shutdown = sessionShutdowns.get(id);\n if (shutdown !== undefined) {\n if (!canClose(mount, shutdown.session)) throw new Error('Session is outside this mount.');\n await shutdown.promise;\n }\n return;\n }\n if (!canClose(mount, session)) throw new Error('Session is outside this mount.');\n await sessionService.close(id);\n },\n isLive: (id) => {\n const session = sessions.get(id);\n return session !== undefined && belongs(mount, session);\n },\n canCommunicate: (sourceId, targetId) => {\n const source = sessions.get(sourceId);\n return (\n source !== undefined && belongs(mount, source) && sessionService.canCommunicate?.(sourceId, targetId) === true\n );\n },\n },\n directEvents: {\n publish: (type, id, payload) => {\n const session = sessions.get(id);\n if (session && serves(mount, frameType, session)) directEvents.publish(type, id, payload);\n },\n subscribe: (type, id, listener, subscriptionOptions) => {\n const session = sessions.get(id);\n return session && serves(mount, frameType, session)\n ? directEvents.subscribe(type, id, listener, subscriptionOptions)\n : () => undefined;\n },\n close: () => undefined,\n },\n publish: (sessionId, payload) => {\n const session = sessions.get(sessionId);\n if (session && serves(mount, frameType, session)) publish(frameType, sessionId, payload);\n },\n publishToConnection: (connectionId, sessionId, payload) => {\n const session = sessions.get(sessionId);\n if (connectionId === '' || !session || !serves(mount, frameType, session)) return false;\n publish(frameType, sessionId, payload, connectionId);\n return true;\n },\n requestSessionApi: async (scope, request) => {\n const session = sessions.get(scope.sessionId);\n if (!session || !belongs(mount, session)) return Response.json({ error: 'Session not found.' }, { status: 404 });\n if (options.requestSessionApi === undefined)\n return Response.json({ error: 'Session API unavailable.' }, { status: 404 });\n return options.requestSessionApi(scope, hubAuthenticated(request));\n },\n onNotice: (message) => options.onNotice?.(message),\n });\n\n const startChannel = (channel: DoomHubChannel, mount: DoomApiMount): StartedChannel | undefined => {\n const key = `${mountKey(mount)}:${channel.frameType}`;\n if (channels.has(key)) {\n options.onNotice?.(`hub channel '${channel.frameType}' is already registered`);\n return undefined;\n }\n try {\n const displaced = new Map<string, StartedChannel>();\n for (const session of sessions.values()) {\n if (!belongs(mount, session)) continue;\n const previous = selectedChannel(session, channel.frameType);\n if (previous !== undefined) displaced.set(session.id, previous);\n }\n const source = channel.start(channelHost(channel.frameType, mount));\n const started = { channel, source, mount };\n channels.set(key, started);\n for (const session of sessions.values()) {\n if (selectedChannel(session, channel.frameType) !== started) continue;\n displaced.get(session.id)?.source.sessionRemoved?.(session.id);\n source.sessionAdded?.(scopeOf(session));\n }\n return started;\n } catch (error) {\n options.onNotice?.(`hub channel '${channel.frameType}' failed (${String(error)})`);\n return undefined;\n }\n };\n\n const releaseChannel = (channel: DoomHubChannel, started: StartedChannel): void => {\n const key = `${mountKey(started.mount)}:${channel.frameType}`;\n if (channels.get(key) !== started) return;\n const served = [...sessions.values()].filter((session) => selectedChannel(session, channel.frameType) === started);\n channels.delete(key);\n started.source.close();\n for (const session of served) selectedChannel(session, channel.frameType)?.source.sessionAdded?.(scopeOf(session));\n };\n\n const receiveMountedChannel = (\n mount: DoomApiMount,\n sessionId: string,\n frameType: string,\n payload: unknown,\n connectionId: string,\n ): boolean => {\n if (!connectionId) return false;\n const session = sessions.get(sessionId);\n if (!session || !belongs(mount, session)) return false;\n const started = channels.get(`${mountKey(mount)}:${frameType}`);\n if (!started?.channel.receive) return false;\n started.channel.receive(scopeOf(session), payload, { connectionId });\n return true;\n };\n\n const unregisterSession = (sessionId: string): void => {\n const current = sessions.get(sessionId);\n if (current === undefined) return;\n const cleanup = sessionCleanups.get(sessionId);\n if (cleanup !== undefined) subscriptions.delete(cleanup);\n communicationEndpoints.get(sessionId)?.endpoint.close();\n sessions.delete(sessionId);\n sessionCleanups.delete(sessionId);\n presentationCleanups.get(sessionId)?.();\n presentationCleanups.delete(sessionId);\n for (const { source } of selectedChannels(current)) source.sessionRemoved?.(sessionId);\n directEvents.clearSession?.(sessionId);\n emit({ kind: 'removed', sessionId });\n };\n\n const startSessionShutdown = (session: HeadlessHubSession): Promise<void> => {\n const existing = sessionShutdowns.get(session.id);\n if (existing !== undefined) return existing.promise;\n const shutdown = { session, promise: Promise.resolve(), pending: true };\n const promise = Promise.resolve().then(() => options.manager.closeSession(session.id));\n shutdown.promise = promise;\n sessionShutdowns.set(session.id, shutdown);\n void promise.then(\n () => {\n shutdown.pending = false;\n if (sessionShutdowns.get(session.id) === shutdown) sessionShutdowns.delete(session.id);\n },\n () => {\n shutdown.pending = false;\n },\n );\n return promise;\n };\n\n const register = (session: HeadlessHubSession): void => {\n if (closed) throw new Error('The headless hub is closed.');\n if (sessions.has(session.id)) throw new Error(`Session '${session.id}' is already registered.`);\n if (sessionShutdowns.has(session.id)) throw new Error(`Session '${session.id}' is still shutting down.`);\n let current: HeadlessHubSession = {\n ...session,\n updatedAt: session.updatedAt ?? session.createdAt,\n phase: session.phase ?? 'idle',\n phaseSince: session.phaseSince ?? session.createdAt,\n pendingMessageCount: session.pendingMessageCount ?? 0,\n everPrompted: session.everPrompted ?? false,\n awaitingInput: session.awaitingInput ?? false,\n };\n sessions.set(session.id, current);\n announceCommunicationReady(session.id);\n const stopPresentation = session.host.onPresentationFrame((frame) => {\n if (sessions.get(session.id)?.host !== session.host) return;\n const type = typeof frame.type === 'string' ? frame.type : '';\n // Streaming updates belong to the session presentation. Replicating the\n // hub's growing event list for every token makes model output quadratic.\n if (\n type !== 'agent_start' &&\n type !== 'agent_settled' &&\n type !== 'message_end' &&\n type !== 'session_info_changed' &&\n type !== 'extension_ui_request' &&\n type !== 'extension_ui_answered'\n )\n return;\n const now = new Date().toISOString();\n const phase = type === 'agent_start' ? 'turn' : type === 'agent_settled' ? 'idle' : current.phase;\n const phaseChanged = phase !== current.phase;\n const method = typeof frame.method === 'string' ? frame.method : '';\n const awaitingInput =\n type === 'extension_ui_request' && ['select', 'confirm', 'input', 'editor'].includes(method)\n ? true\n : type === 'extension_ui_answered' || type === 'agent_settled'\n ? false\n : current.awaitingInput;\n current = {\n ...current,\n name: type === 'session_info_changed' && typeof frame.name === 'string' ? frame.name : current.name,\n updatedAt: now,\n phase,\n phaseSince: phaseChanged ? now : current.phaseSince,\n everPrompted: current.everPrompted || type === 'agent_start',\n awaitingInput,\n ...(type === 'agent_settled' ? { lastSettledAt: now } : {}),\n };\n sessions.set(session.id, current);\n emit({ kind: 'upsert', session: current });\n });\n presentationCleanups.set(session.id, stopPresentation);\n for (const { source } of selectedChannels(current)) source.sessionAdded?.(scopeOf(session));\n const active = (): boolean => sessions.get(session.id)?.host === session.host && !closed;\n const cleanup = (): void => {\n subscriptions.delete(cleanup);\n sessionCleanups.delete(session.id);\n if (!active()) return;\n void closeSession(session.id).catch((error: unknown) =>\n options.onNotice?.(\n `session ${session.id} cleanup failed (${error instanceof Error ? error.message : String(error)})`,\n ),\n );\n };\n sessionCleanups.set(session.id, cleanup);\n void session.host.runtime.exited.then(cleanup, cleanup);\n subscriptions.add(cleanup);\n emit({ kind: 'upsert', session: current });\n };\n\n const closeSession = async (sessionId: string): Promise<void> => {\n const pending = sessionShutdowns.get(sessionId);\n if (pending !== undefined) {\n await pending.promise;\n return;\n }\n const session = sessions.get(sessionId);\n if (session === undefined) return;\n unregisterSession(sessionId);\n await startSessionShutdown(session);\n };\n\n sessionService = {\n create: async (request) => {\n if (options.createSession === undefined)\n throw new Error('The cockpit session service cannot create sessions in this host.');\n return options.createSession(request);\n },\n close: closeSession,\n isLive: (sessionId) => !closed && sessions.has(sessionId),\n canCommunicate,\n bindCommunication,\n };\n\n const mountFacets = async (\n facets: readonly (DoomServerFacet | LoadedServerFacet)[],\n suppliedContext?: DoomApiContext,\n ): Promise<void> => {\n if (closed) throw new Error('The headless hub is closed.');\n const context: DoomApiContext = suppliedContext ?? {\n scope: 'global',\n ...(options.hubToken?.() === undefined ? {} : { hubToken: options.hubToken() }),\n sessionService,\n directEvents,\n onNotice: (message) => options.onNotice?.(message),\n };\n if (context.scope === 'session') throw new Error('Session facets belong to their session host.');\n if (context.scope === 'workspace' && (!context.workspaceId || !context.workspaceRoot))\n throw new Error('Workspace mounts require an admitted identity and root.');\n const mount: DoomApiMount =\n context.scope === 'global' ? { scope: 'global' } : { scope: 'workspace', workspaceId: context.workspaceId! };\n const key = mountKey(mount);\n if (mounts.has(key)) throw new Error(`The '${key}' facets are already mounted.`);\n const host = createDoomServerHost({\n scope: context.scope,\n context: {\n ...context,\n sessionService: channelHost('', mount).sessionService,\n directEvents: channelHost('', mount).directEvents,\n receiveChannel: (sessionId, frameType, payload, connectionId) =>\n receiveMountedChannel(mount, sessionId, frameType, payload, connectionId),\n },\n mountChannel: (channel) => {\n const started = startChannel(channel, mount);\n if (started === undefined) return { mounted: false, dispose: () => undefined };\n return { mounted: true, dispose: () => releaseChannel(channel, started) };\n },\n pluginRegistry,\n });\n try {\n const installed = await installServerFacets({ host, facets, onNotice: options.onNotice });\n if (closed) {\n await installed.dispose();\n throw new Error('The headless hub was closed while mounting facets.');\n }\n mounts.set(key, { host, installed });\n if (mount.scope === 'workspace') {\n const workspace = { id: mount.workspaceId, root: context.workspaceRoot!, available: true };\n workspaces.set(mount.workspaceId, workspace);\n emit({ kind: 'workspace_upsert', workspace });\n }\n } catch (error) {\n host.dispose();\n throw error;\n }\n };\n\n return {\n sessionService,\n directEvents,\n snapshot: () => [...sessions.values()],\n session: (sessionId) => sessions.get(sessionId),\n runtime: (sessionId) => sessions.get(sessionId)?.host.runtime,\n onEvent(listener) {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n register,\n notifySessionRemoved(sessionId) {\n emit({ kind: 'removed', sessionId });\n },\n mountFacets,\n workspaces: () => [...workspaces.values()],\n registerWorkspace(workspace) {\n workspaces.set(workspace.id, workspace);\n emit({ kind: 'workspace_upsert', workspace });\n },\n admitWorkspace: async (root) => {\n if (!options.admitWorkspace) throw new Error('Workspace admission is unavailable.');\n return options.admitWorkspace(root);\n },\n async removeWorkspace(id) {\n if ([...sessions.values()].some((session) => session.workspaceId === id))\n throw new Error('Workspace still has live sessions.');\n const key = mountKey({ scope: 'workspace', workspaceId: id });\n const mounted = mounts.get(key);\n if (!workspaces.has(id)) return;\n options.onWorkspaceRemoved?.(id);\n workspaces.delete(id);\n emit({ kind: 'workspace_removed', workspaceId: id });\n if (!mounted) return;\n mounts.delete(key);\n try {\n await mounted.installed.dispose();\n } finally {\n mounted.host.dispose();\n }\n },\n async requestApi(mount, basePath, request) {\n if (closed) return Response.json({ error: 'Server is closed.' }, { status: 503 });\n if (mount.scope === 'session') {\n const session = sessions.get(mount.sessionId);\n if (!session || !options.requestSessionApi)\n return Response.json({ error: 'Session not found.' }, { status: 404 });\n const url = new URL(request.url);\n return options.requestSessionApi(scopeOf(session), {\n basePath,\n path: `${url.pathname}${url.search}`,\n method: request.method,\n headers: request.headers,\n signal: request.signal,\n body: request.body === null ? undefined : new Uint8Array(await request.arrayBuffer()),\n });\n }\n const handler = mounts.get(mountKey(mount))?.host.handlerFor(basePath);\n if (!handler)\n return Response.json({ error: `API '${basePath}' is not mounted in ${mountKey(mount)}.` }, { status: 404 });\n return handler.fetch(request);\n },\n pluginRegistry,\n invokePlugin: (call, direction, caller) => pluginRegistry.invoke(call, direction, caller),\n async create(sessionOptions) {\n if (closed) throw new Error('The headless hub is closed.');\n const host = await options.manager.create(sessionOptions);\n const session: HeadlessHubSession = {\n id: sessionOptions.sessionId,\n workspaceId: sessionOptions.workspaceId,\n webComposition: sessionOptions.webComposition,\n name: sessionOptions.sessionName,\n cwd: sessionOptions.cwd,\n createdAt: new Date().toISOString(),\n ...(sessionOptions.parentSessionId === undefined ? {} : { parentSessionId: sessionOptions.parentSessionId }),\n ...(sessionOptions.sessionProvenance === undefined\n ? {}\n : { sessionProvenance: sessionOptions.sessionProvenance }),\n environment: sessionOptions.environment,\n host,\n };\n register(session);\n return session;\n },\n closeSession,\n async requestSessionApi(scope, request) {\n if (closed || !sessions.has(scope.sessionId))\n return Response.json({ error: 'Session not found.' }, { status: 404 });\n if (options.requestSessionApi === undefined)\n return Response.json({ error: 'Session API unavailable.' }, { status: 404 });\n return options.requestSessionApi(scope, request);\n },\n registerChannel(channel, mount = { scope: 'global' }) {\n const started = startChannel(channel, mount);\n if (started === undefined) return () => undefined;\n return () => releaseChannel(channel, started);\n },\n channelTypes: () => [...new Set([...channels.values()].map(({ channel }) => channel.frameType))],\n channelFrames(sessionId) {\n const session = sessions.get(sessionId);\n if (session === undefined) return [];\n const scope = scopeOf(session);\n const frames: Array<{ type: string; sessionId: string; payload: unknown }> = [];\n for (const { channel, source } of selectedChannels(session)) {\n const type = channel.frameType;\n const payload = source.payloadFor(scope);\n if (payload !== undefined) frames.push({ type, sessionId, payload });\n }\n return frames;\n },\n threadJournal(sessionId, threadId) {\n const session = sessions.get(sessionId);\n if (session === undefined) return undefined;\n const scope = scopeOf(session);\n for (const { source } of selectedChannels(session)) {\n const journal = source.threadJournal?.(scope, threadId);\n if (journal !== undefined) return journal;\n }\n return undefined;\n },\n receiveChannel(sessionId, frameType, payload, connectionId) {\n if (connectionId === '') return;\n const session = sessions.get(sessionId);\n const started = session && selectedChannels(session).find(({ channel }) => channel.frameType === frameType);\n if (session === undefined || started === undefined) return;\n started.channel.receive?.(scopeOf(session), payload, { connectionId } satisfies DoomHubChannelConnection);\n },\n disconnectChannels(connectionId) {\n if (connectionId === '') return;\n for (const { channel } of channels.values()) channel.disconnected?.({ connectionId });\n },\n async close() {\n if (closed) return;\n const ids = [...sessions.keys()];\n closed = true;\n directEvents.close();\n for (const unsubscribe of subscriptions) unsubscribe();\n subscriptions.clear();\n for (const cleanup of presentationCleanups.values()) cleanup();\n presentationCleanups.clear();\n const failures: unknown[] = [];\n for (const mounted of [...mounts.values()].reverse()) {\n try {\n await mounted.installed.dispose();\n } catch (error) {\n failures.push(error);\n }\n try {\n mounted.host.dispose();\n } catch (error) {\n failures.push(error);\n }\n }\n mounts.clear();\n pluginRegistry.dispose();\n workspaces.clear();\n for (const { source } of channels.values()) source.close();\n channels.clear();\n listeners.clear();\n const shutdowns: Promise<void>[] = [...sessionShutdowns.values()]\n .filter(({ pending }) => pending)\n .map(({ promise }) => promise);\n for (const id of ids) {\n const session = sessions.get(id);\n if (session !== undefined && !sessionShutdowns.has(id)) shutdowns.push(startSessionShutdown(session));\n }\n sessions.clear();\n const outcomes = await Promise.allSettled(shutdowns);\n for (const outcome of outcomes) if (outcome.status === 'rejected') failures.push(outcome.reason);\n if (failures.length) throw new AggregateError(failures, 'Mount shutdown failed');\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"headless-hub.cjs","names":["createDoomPluginRegistry","createDoomServerHost","installServerFacets"],"sources":["../src/server/headlessHub.ts"],"sourcesContent":["import type {\n DoomDirectEventBus,\n DoomHubChannel,\n DoomHubChannelConnection,\n DoomHubChannelHost,\n DoomHubSessionApiRequest,\n DoomHubSessionCreateRequest,\n DoomHubSessionScope,\n DoomHubSessionService,\n DoomSessionCommunicationEndpoint,\n DoomHubSessionReservations,\n DoomPendingSessionSetup,\n} from '../exports/hubChannel';\nimport type { DoomWebComposition } from '../exports/packageApi';\nimport type { DoomApiContext, DoomApiMount } from '../exports/packageApi';\nimport {\n createDoomPluginRegistry,\n type DoomPluginCaller,\n type DoomPluginDirection,\n type DoomPluginRegistry,\n} from '../exports/pluginProtocol';\nimport {\n createDoomServerHost,\n installServerFacets,\n type DoomServerFacet,\n type DoomServerHost,\n type InstalledServerFacets,\n type LoadedServerFacet,\n} from '../exports/serverFacet';\nimport type { HeadlessSessionHost, HeadlessSessionHostOptions } from '../systems/main/types/headlessSessionHost';\nimport type { HeadlessSessionManager } from '../systems/main/types/headlessSessionManager';\n\nconst AUTHORIZATION_HEADER = 'authorization';\nconst CHANNEL_PRIORITY = { global: 0, workspace: 1, session: 2 } as const;\n\nexport interface HeadlessHubSession {\n readonly id: string;\n readonly workspaceId?: string;\n webComposition?: DoomWebComposition;\n readonly name: string;\n readonly cwd: string;\n readonly createdAt: string;\n readonly parentSessionId?: string;\n readonly sessionProvenance?: string;\n readonly updatedAt?: string;\n readonly phase?: 'idle' | 'turn' | 'compaction' | 'retry';\n readonly phaseSince?: string;\n readonly pendingMessageCount?: number;\n readonly everPrompted?: boolean;\n readonly awaitingInput?: boolean;\n readonly lastSettledAt?: string;\n readonly pendingSetups?: readonly DoomPendingSessionSetup[];\n /** Environment admitted for this session, when supplied by the host. */\n readonly environment?: Readonly<Record<string, string | undefined>>;\n readonly host: HeadlessSessionHost;\n}\n\nexport interface HeadlessWorkspace {\n readonly id: string;\n readonly root: string;\n readonly available?: boolean;\n}\n\nexport type HeadlessHubEvent =\n | { kind: 'upsert'; session: HeadlessHubSession }\n | { kind: 'removed'; sessionId: string }\n | { kind: 'workspace_upsert'; workspace: HeadlessWorkspace }\n | { kind: 'workspace_removed'; workspaceId: string }\n | { kind: 'channel'; frameType: string; sessionId: string; payload: unknown; connectionId?: string };\n\nexport interface HeadlessHubOptions {\n manager: HeadlessSessionManager;\n /** Resolves the hub credential, because the server reads its token file after the hub exists. */\n hubToken?: () => string | undefined;\n /** Creates a session through the canonical cockpit lifecycle for hub channels. */\n createSession?: (request: DoomHubSessionCreateRequest) => Promise<DoomHubSessionScope>;\n onNotice?: (message: string) => void;\n sessionReservations?: DoomHubSessionReservations;\n requestSessionApi?: (scope: DoomHubSessionScope, request: DoomHubSessionApiRequest) => Promise<Response>;\n admitWorkspace?: (root: string) => Promise<HeadlessWorkspace>;\n onWorkspaceRemoved?: (workspaceId: string) => void;\n}\n\nexport interface HeadlessHub {\n /** Canonical direct lifecycle shared by hub channels, APIs, and sessions. */\n readonly sessionService: DoomHubSessionService;\n /** Lifecycle-owned event path shared by session facets and hub channels. */\n readonly directEvents: DoomDirectEventBus;\n snapshot(): readonly HeadlessHubSession[];\n session(sessionId: string): HeadlessHubSession | undefined;\n runtime(sessionId: string): HeadlessSessionHost['runtime'] | undefined;\n onEvent(listener: (event: HeadlessHubEvent) => void): () => void;\n register(session: HeadlessHubSession): void;\n create(options: HeadlessSessionHostOptions): Promise<HeadlessHubSession>;\n closeSession(sessionId: string): Promise<void>;\n /** Publishes removal of a persisted session that has no live host. */\n notifySessionRemoved(sessionId: string): void;\n /** Publishes setup-only children through the parent's normal session updates. */\n setPendingSessionSetups?(sessionId: string, pending: readonly DoomPendingSessionSetup[]): void;\n /** Dispatches an authenticated package API request inside the owning process. */\n requestSessionApi(scope: DoomHubSessionScope, request: DoomHubSessionApiRequest): Promise<Response>;\n /** Mounts the selected hub facets once, before the headless server accepts clients. */\n mountFacets(facets: readonly (DoomServerFacet | LoadedServerFacet)[], context?: DoomApiContext): Promise<void>;\n workspaces(): readonly HeadlessWorkspace[];\n registerWorkspace(workspace: HeadlessWorkspace): void;\n admitWorkspace(root: string): Promise<HeadlessWorkspace>;\n removeWorkspace(workspaceId: string): Promise<void>;\n requestApi(mount: DoomApiMount, basePath: string, request: Request): Promise<Response>;\n /** The dispatch table hub facets mount into, shared with session hosts this hub serves. */\n readonly pluginRegistry: DoomPluginRegistry;\n invokePlugin(call: unknown, direction: DoomPluginDirection, caller?: DoomPluginCaller): Promise<unknown>;\n registerChannel(channel: DoomHubChannel, mount?: DoomApiMount): () => void;\n channelTypes(): readonly string[];\n channelFrames(sessionId: string): Array<{ type: string; sessionId: string; payload: unknown }>;\n /** Resolves a channel-owned child journal for the browser thread projection. */\n threadJournal(sessionId: string, threadId: string): string | undefined;\n receiveChannel(sessionId: string, frameType: string, payload: unknown, connectionId: string): void;\n disconnectChannels(connectionId: string): void;\n close(): Promise<void>;\n}\n\ninterface StartedChannel {\n readonly channel: DoomHubChannel;\n readonly source: ReturnType<DoomHubChannel['start']>;\n readonly mount: DoomApiMount;\n}\n\nfunction scopeOf(session: HeadlessHubSession): DoomHubSessionScope {\n return {\n sessionId: session.id,\n ...(session.workspaceId === undefined ? {} : { workspaceId: session.workspaceId }),\n cwd: session.cwd,\n ...(session.environment === undefined ? {} : { environment: session.environment }),\n };\n}\n\n/** Owns direct, same-process session and channel state for presentation adapters. */\nexport function createHeadlessHub(options: HeadlessHubOptions): HeadlessHub {\n const sessions = new Map<string, HeadlessHubSession>();\n const pendingSetups = new Map<string, readonly DoomPendingSessionSetup[]>();\n const present = (session: HeadlessHubSession): HeadlessHubSession =>\n pendingSetups.has(session.id) ? { ...session, pendingSetups: pendingSetups.get(session.id) } : session;\n const pluginRegistry = createDoomPluginRegistry();\n const channels = new Map<string, StartedChannel>();\n const listeners = new Set<(event: HeadlessHubEvent) => void>();\n const subscriptions = new Set<() => void>();\n const sessionCleanups = new Map<string, () => void>();\n const presentationCleanups = new Map<string, () => void>();\n const sessionShutdowns = new Map<string, { session: HeadlessHubSession; promise: Promise<void>; pending: boolean }>();\n const directEventListeners = new Map<string, Set<(payload: unknown) => void>>();\n const directEventLatest = new Map<string, unknown>();\n const maxDirectEventLatest = 4096;\n const communicationEndpoints = new Map<\n string,\n {\n listeners: Map<string, Set<(sourceSessionId: string, payload: unknown) => void>>;\n readyListeners: Set<(peerSessionId: string) => void>;\n ready: boolean;\n endpoint: DoomSessionCommunicationEndpoint;\n }\n >();\n let closed = false;\n const mounts = new Map<string, { host: DoomServerHost; installed: InstalledServerFacets }>();\n const workspaces = new Map<string, HeadlessWorkspace>();\n let sessionService: DoomHubSessionService;\n\n const mountKey = (mount: DoomApiMount): string =>\n mount.scope === 'global'\n ? 'global'\n : mount.scope === 'workspace'\n ? `workspace:${mount.workspaceId}`\n : `session:${mount.sessionId}`;\n const belongs = (mount: DoomApiMount, session: HeadlessHubSession): boolean =>\n mount.scope === 'global' ||\n (mount.scope === 'workspace' ? session.workspaceId === mount.workspaceId : session.id === mount.sessionId);\n const canClose = (mount: DoomApiMount, session: HeadlessHubSession): boolean =>\n belongs(mount, session) || (mount.scope === 'session' && session.parentSessionId === mount.sessionId);\n const canCommunicate = (sourceId: string, targetId: string): boolean => {\n if (closed || sourceId === targetId) return false;\n const source = sessions.get(sourceId);\n const target = sessions.get(targetId);\n return (\n source !== undefined &&\n target !== undefined &&\n (source.parentSessionId === target.id || target.parentSessionId === source.id)\n );\n };\n const selectedChannels = (session: HeadlessHubSession): StartedChannel[] => {\n const selected = new Map<string, StartedChannel>();\n for (const started of channels.values()) {\n if (!belongs(started.mount, session)) continue;\n const previous = selected.get(started.channel.frameType);\n if (!previous || CHANNEL_PRIORITY[started.mount.scope] > CHANNEL_PRIORITY[previous.mount.scope])\n selected.set(started.channel.frameType, started);\n }\n return [...selected.values()];\n };\n const selectedChannel = (session: HeadlessHubSession, frameType: string): StartedChannel | undefined =>\n selectedChannels(session).find((started) => started.channel.frameType === frameType);\n /**\n * One channel serves a session: the narrowest mount that reaches it. A package that\n * registers the same channel globally and per workspace would otherwise run two\n * sources against one session, and only the narrower one can publish or receive.\n *\n * Compared by mount scope rather than identity so a channel being started already\n * knows which sessions it is taking over.\n */\n const serves = (mount: DoomApiMount, frameType: string, session: HeadlessHubSession): boolean => {\n if (!belongs(mount, session)) return false;\n const current = selectedChannel(session, frameType);\n return current === undefined || CHANNEL_PRIORITY[mount.scope] >= CHANNEL_PRIORITY[current.mount.scope];\n };\n\n const directEventKey = (frameType: string, sessionId: string): string => `${frameType}\\0${sessionId}`;\n const directEvents: DoomDirectEventBus = {\n publish(frameType, sessionId, payload) {\n if (closed) return;\n const key = directEventKey(frameType, sessionId);\n directEventLatest.set(key, payload);\n while (directEventLatest.size > maxDirectEventLatest) {\n const oldest = directEventLatest.keys().next().value;\n if (oldest === undefined) break;\n directEventLatest.delete(oldest);\n }\n const listeners = directEventListeners.get(key);\n if (listeners === undefined) return;\n for (const listener of listeners) listener(payload);\n },\n subscribe(frameType, sessionId, listener, options) {\n if (closed) return () => undefined;\n const key = directEventKey(frameType, sessionId);\n const listeners = directEventListeners.get(key) ?? new Set<(payload: unknown) => void>();\n listeners.add(listener);\n directEventListeners.set(key, listeners);\n if (options?.replayLatest && directEventLatest.has(key)) listener(directEventLatest.get(key));\n let released = false;\n return () => {\n if (released) return;\n released = true;\n listeners.delete(listener);\n if (listeners.size === 0) directEventListeners.delete(key);\n };\n },\n clearSession(sessionId) {\n const suffix = `\\0${sessionId}`;\n for (const key of directEventLatest.keys()) if (key.endsWith(suffix)) directEventLatest.delete(key);\n },\n close() {\n directEventListeners.clear();\n directEventLatest.clear();\n },\n };\n\n const announceCommunicationReady = (sessionId: string): void => {\n const current = communicationEndpoints.get(sessionId);\n if (current?.ready !== true) return;\n for (const [peerSessionId, peer] of communicationEndpoints) {\n if (!peer.ready || !canCommunicate(sessionId, peerSessionId)) continue;\n for (const listener of current.readyListeners) listener(peerSessionId);\n for (const listener of peer.readyListeners) listener(sessionId);\n }\n };\n\n const bindCommunication = (sessionId: string): DoomSessionCommunicationEndpoint => {\n const existing = communicationEndpoints.get(sessionId);\n if (existing !== undefined) return existing.endpoint;\n const listeners = new Map<string, Set<(sourceSessionId: string, payload: unknown) => void>>();\n const readyListeners = new Set<(peerSessionId: string) => void>();\n let endpoint!: DoomSessionCommunicationEndpoint;\n const record = {\n listeners,\n readyListeners,\n ready: false,\n get endpoint() {\n return endpoint;\n },\n };\n endpoint = {\n sessionId,\n publish(targetSessionId, type, payload) {\n if (!canCommunicate(sessionId, targetSessionId)) return false;\n const target = communicationEndpoints.get(targetSessionId);\n if (target?.ready !== true) return false;\n for (const listener of target.listeners.get(type) ?? []) listener(sessionId, payload);\n return true;\n },\n subscribe(type, listener) {\n const current = listeners.get(type) ?? new Set<(sourceSessionId: string, payload: unknown) => void>();\n current.add(listener);\n listeners.set(type, current);\n return () => {\n current.delete(listener);\n if (current.size === 0) listeners.delete(type);\n };\n },\n onPeerReady(listener) {\n readyListeners.add(listener);\n if (!record.ready) {\n record.ready = true;\n announceCommunicationReady(sessionId);\n }\n return () => readyListeners.delete(listener);\n },\n close() {\n if (communicationEndpoints.get(sessionId)?.endpoint !== endpoint) return;\n communicationEndpoints.delete(sessionId);\n listeners.clear();\n readyListeners.clear();\n },\n };\n communicationEndpoints.set(sessionId, record);\n return endpoint;\n };\n\n const emit = (event: HeadlessHubEvent): void => {\n if (closed) return;\n const projected = event.kind === 'upsert' ? { ...event, session: present(event.session) } : event;\n for (const listener of listeners) listener(projected);\n };\n const sessionScopes = (): readonly DoomHubSessionScope[] => [...sessions.values()].map(scopeOf);\n const publish = (frameType: string, sessionId: string, payload: unknown, connectionId?: string): void => {\n if (!sessions.has(sessionId)) return;\n emit({ kind: 'channel', frameType, sessionId, payload, ...(connectionId === undefined ? {} : { connectionId }) });\n };\n\n /**\n * Hub-owned calls carry the hub credential, so a session API can tell them from a\n * browser call it must not trust. Browser traffic reaches a session API through\n * requestApi instead, and keeps the headers its caller sent.\n */\n const hubAuthenticated = (request: DoomHubSessionApiRequest): DoomHubSessionApiRequest => {\n const token = options.hubToken?.();\n if (token === undefined || token === '') return request;\n const headers = new Headers(request.headers);\n if (headers.has(AUTHORIZATION_HEADER)) return request;\n headers.set(AUTHORIZATION_HEADER, `Bearer ${token}`);\n return { ...request, headers };\n };\n\n const reservationsFor = (mount: DoomApiMount): DoomHubSessionReservations | undefined => {\n const reservations = options.sessionReservations;\n if (!reservations) return undefined;\n const check = (parentId: string): void => {\n const parent = sessions.get(parentId);\n if (!parent || !belongs(mount, parent)) throw new Error('Parent session is outside this mount.');\n };\n return {\n read: (id, parentId) => {\n check(parentId);\n return reservations.read(id, parentId);\n },\n prepare: (id, parentId, cwd) => {\n check(parentId);\n return reservations.prepare(id, parentId, cwd);\n },\n complete: (id, parentId) => {\n check(parentId);\n return reservations.complete(id, parentId);\n },\n };\n };\n const channelHost = (frameType: string, mount: DoomApiMount): DoomHubChannelHost => ({\n sessions: () =>\n sessionScopes().filter((scope) => {\n const session = sessions.get(scope.sessionId);\n return session !== undefined && serves(mount, frameType, session);\n }),\n sessionService: {\n reservations: reservationsFor(mount),\n create: async (request) => {\n if (mount.scope !== 'global') {\n const parent = request.parentSessionId === undefined ? undefined : sessions.get(request.parentSessionId);\n if (!parent || !belongs(mount, parent)) throw new Error('Parent session is outside this mount.');\n }\n return sessionService.create(request);\n },\n close: async (id) => {\n const session = sessions.get(id);\n if (session === undefined) {\n const shutdown = sessionShutdowns.get(id);\n if (shutdown !== undefined) {\n if (!canClose(mount, shutdown.session)) throw new Error('Session is outside this mount.');\n await shutdown.promise;\n }\n return;\n }\n if (!canClose(mount, session)) throw new Error('Session is outside this mount.');\n await sessionService.close(id);\n },\n isLive: (id) => {\n const session = sessions.get(id);\n return session !== undefined && belongs(mount, session);\n },\n canCommunicate: (sourceId, targetId) => {\n const source = sessions.get(sourceId);\n return (\n source !== undefined && belongs(mount, source) && sessionService.canCommunicate?.(sourceId, targetId) === true\n );\n },\n },\n directEvents: {\n publish: (type, id, payload) => {\n const session = sessions.get(id);\n if (session && serves(mount, frameType, session)) directEvents.publish(type, id, payload);\n },\n subscribe: (type, id, listener, subscriptionOptions) => {\n const session = sessions.get(id);\n return session && serves(mount, frameType, session)\n ? directEvents.subscribe(type, id, listener, subscriptionOptions)\n : () => undefined;\n },\n close: () => undefined,\n },\n publish: (sessionId, payload) => {\n const session = sessions.get(sessionId);\n if (session && serves(mount, frameType, session)) publish(frameType, sessionId, payload);\n },\n publishToConnection: (connectionId, sessionId, payload) => {\n const session = sessions.get(sessionId);\n if (connectionId === '' || !session || !serves(mount, frameType, session)) return false;\n publish(frameType, sessionId, payload, connectionId);\n return true;\n },\n requestSessionApi: async (scope, request) => {\n const session = sessions.get(scope.sessionId);\n if (!session || !belongs(mount, session)) return Response.json({ error: 'Session not found.' }, { status: 404 });\n if (options.requestSessionApi === undefined)\n return Response.json({ error: 'Session API unavailable.' }, { status: 404 });\n return options.requestSessionApi(scope, hubAuthenticated(request));\n },\n onNotice: (message) => options.onNotice?.(message),\n });\n\n const startChannel = (channel: DoomHubChannel, mount: DoomApiMount): StartedChannel | undefined => {\n const key = `${mountKey(mount)}:${channel.frameType}`;\n if (channels.has(key)) {\n options.onNotice?.(`hub channel '${channel.frameType}' is already registered`);\n return undefined;\n }\n try {\n const displaced = new Map<string, StartedChannel>();\n for (const session of sessions.values()) {\n if (!belongs(mount, session)) continue;\n const previous = selectedChannel(session, channel.frameType);\n if (previous !== undefined) displaced.set(session.id, previous);\n }\n const source = channel.start(channelHost(channel.frameType, mount));\n const started = { channel, source, mount };\n channels.set(key, started);\n for (const session of sessions.values()) {\n if (selectedChannel(session, channel.frameType) !== started) continue;\n displaced.get(session.id)?.source.sessionRemoved?.(session.id);\n source.sessionAdded?.(scopeOf(session));\n }\n return started;\n } catch (error) {\n options.onNotice?.(`hub channel '${channel.frameType}' failed (${String(error)})`);\n return undefined;\n }\n };\n\n const releaseChannel = (channel: DoomHubChannel, started: StartedChannel): void => {\n const key = `${mountKey(started.mount)}:${channel.frameType}`;\n if (channels.get(key) !== started) return;\n const served = [...sessions.values()].filter((session) => selectedChannel(session, channel.frameType) === started);\n channels.delete(key);\n started.source.close();\n for (const session of served) selectedChannel(session, channel.frameType)?.source.sessionAdded?.(scopeOf(session));\n };\n\n const receiveMountedChannel = (\n mount: DoomApiMount,\n sessionId: string,\n frameType: string,\n payload: unknown,\n connectionId: string,\n ): boolean => {\n if (!connectionId) return false;\n const session = sessions.get(sessionId);\n if (!session || !belongs(mount, session)) return false;\n const started = channels.get(`${mountKey(mount)}:${frameType}`);\n if (!started?.channel.receive) return false;\n started.channel.receive(scopeOf(session), payload, { connectionId });\n return true;\n };\n\n const unregisterSession = (sessionId: string): void => {\n const current = sessions.get(sessionId);\n if (current === undefined) return;\n const cleanup = sessionCleanups.get(sessionId);\n if (cleanup !== undefined) subscriptions.delete(cleanup);\n communicationEndpoints.get(sessionId)?.endpoint.close();\n sessions.delete(sessionId);\n sessionCleanups.delete(sessionId);\n presentationCleanups.get(sessionId)?.();\n presentationCleanups.delete(sessionId);\n for (const { source } of selectedChannels(current)) source.sessionRemoved?.(sessionId);\n directEvents.clearSession?.(sessionId);\n emit({ kind: 'removed', sessionId });\n };\n\n const startSessionShutdown = (session: HeadlessHubSession): Promise<void> => {\n const existing = sessionShutdowns.get(session.id);\n if (existing !== undefined) return existing.promise;\n const shutdown = { session, promise: Promise.resolve(), pending: true };\n const promise = Promise.resolve().then(() => options.manager.closeSession(session.id));\n shutdown.promise = promise;\n sessionShutdowns.set(session.id, shutdown);\n void promise.then(\n () => {\n shutdown.pending = false;\n if (sessionShutdowns.get(session.id) === shutdown) sessionShutdowns.delete(session.id);\n },\n () => {\n shutdown.pending = false;\n },\n );\n return promise;\n };\n\n const register = (session: HeadlessHubSession): void => {\n if (closed) throw new Error('The headless hub is closed.');\n if (sessions.has(session.id)) throw new Error(`Session '${session.id}' is already registered.`);\n if (sessionShutdowns.has(session.id)) throw new Error(`Session '${session.id}' is still shutting down.`);\n let current: HeadlessHubSession = {\n ...session,\n updatedAt: session.updatedAt ?? session.createdAt,\n phase: session.phase ?? 'idle',\n phaseSince: session.phaseSince ?? session.createdAt,\n pendingMessageCount: session.pendingMessageCount ?? 0,\n everPrompted: session.everPrompted ?? false,\n awaitingInput: session.awaitingInput ?? false,\n };\n sessions.set(session.id, current);\n announceCommunicationReady(session.id);\n const stopPresentation = session.host.onPresentationFrame((frame) => {\n if (sessions.get(session.id)?.host !== session.host) return;\n const type = typeof frame.type === 'string' ? frame.type : '';\n // Streaming updates belong to the session presentation. Replicating the\n // hub's growing event list for every token makes model output quadratic.\n if (\n type !== 'agent_start' &&\n type !== 'agent_settled' &&\n type !== 'message_end' &&\n type !== 'session_info_changed' &&\n type !== 'extension_ui_request' &&\n type !== 'extension_ui_answered'\n )\n return;\n const now = new Date().toISOString();\n const phase = type === 'agent_start' ? 'turn' : type === 'agent_settled' ? 'idle' : current.phase;\n const phaseChanged = phase !== current.phase;\n const method = typeof frame.method === 'string' ? frame.method : '';\n const awaitingInput =\n type === 'extension_ui_request' && ['select', 'confirm', 'input', 'editor'].includes(method)\n ? true\n : type === 'extension_ui_answered' || type === 'agent_settled'\n ? false\n : current.awaitingInput;\n current = {\n ...current,\n name: type === 'session_info_changed' && typeof frame.name === 'string' ? frame.name : current.name,\n updatedAt: now,\n phase,\n phaseSince: phaseChanged ? now : current.phaseSince,\n everPrompted: current.everPrompted || type === 'agent_start',\n awaitingInput,\n ...(type === 'agent_settled' ? { lastSettledAt: now } : {}),\n };\n sessions.set(session.id, current);\n emit({ kind: 'upsert', session: current });\n });\n presentationCleanups.set(session.id, stopPresentation);\n for (const { source } of selectedChannels(current)) source.sessionAdded?.(scopeOf(session));\n const active = (): boolean => sessions.get(session.id)?.host === session.host && !closed;\n const cleanup = (): void => {\n subscriptions.delete(cleanup);\n sessionCleanups.delete(session.id);\n if (!active()) return;\n void closeSession(session.id).catch((error: unknown) =>\n options.onNotice?.(\n `session ${session.id} cleanup failed (${error instanceof Error ? error.message : String(error)})`,\n ),\n );\n };\n sessionCleanups.set(session.id, cleanup);\n void session.host.runtime.exited.then(cleanup, cleanup);\n subscriptions.add(cleanup);\n emit({ kind: 'upsert', session: current });\n };\n\n const closeSession = async (sessionId: string): Promise<void> => {\n const pending = sessionShutdowns.get(sessionId);\n if (pending !== undefined) {\n await pending.promise;\n return;\n }\n const session = sessions.get(sessionId);\n if (session === undefined) return;\n unregisterSession(sessionId);\n await startSessionShutdown(session);\n };\n\n sessionService = {\n create: async (request) => {\n if (options.createSession === undefined)\n throw new Error('The cockpit session service cannot create sessions in this host.');\n return options.createSession(request);\n },\n close: closeSession,\n isLive: (sessionId) => !closed && sessions.has(sessionId),\n reservations: options.sessionReservations,\n canCommunicate,\n bindCommunication,\n };\n\n const mountFacets = async (\n facets: readonly (DoomServerFacet | LoadedServerFacet)[],\n suppliedContext?: DoomApiContext,\n ): Promise<void> => {\n if (closed) throw new Error('The headless hub is closed.');\n const context: DoomApiContext = suppliedContext ?? {\n scope: 'global',\n ...(options.hubToken?.() === undefined ? {} : { hubToken: options.hubToken() }),\n sessionService,\n directEvents,\n onNotice: (message) => options.onNotice?.(message),\n };\n if (context.scope === 'session') throw new Error('Session facets belong to their session host.');\n if (context.scope === 'workspace' && (!context.workspaceId || !context.workspaceRoot))\n throw new Error('Workspace mounts require an admitted identity and root.');\n const mount: DoomApiMount =\n context.scope === 'global' ? { scope: 'global' } : { scope: 'workspace', workspaceId: context.workspaceId! };\n const key = mountKey(mount);\n if (mounts.has(key)) throw new Error(`The '${key}' facets are already mounted.`);\n const host = createDoomServerHost({\n scope: context.scope,\n context: {\n ...context,\n sessionService: channelHost('', mount).sessionService,\n directEvents: channelHost('', mount).directEvents,\n receiveChannel: (sessionId, frameType, payload, connectionId) =>\n receiveMountedChannel(mount, sessionId, frameType, payload, connectionId),\n },\n mountChannel: (channel) => {\n const started = startChannel(channel, mount);\n if (started === undefined) return { mounted: false, dispose: () => undefined };\n return { mounted: true, dispose: () => releaseChannel(channel, started) };\n },\n pluginRegistry,\n });\n try {\n const installed = await installServerFacets({ host, facets, onNotice: options.onNotice });\n if (closed) {\n await installed.dispose();\n throw new Error('The headless hub was closed while mounting facets.');\n }\n mounts.set(key, { host, installed });\n if (mount.scope === 'workspace') {\n const workspace = { id: mount.workspaceId, root: context.workspaceRoot!, available: true };\n workspaces.set(mount.workspaceId, workspace);\n emit({ kind: 'workspace_upsert', workspace });\n }\n } catch (error) {\n host.dispose();\n throw error;\n }\n };\n\n return {\n sessionService,\n directEvents,\n snapshot: () => [...sessions.values()].map(present),\n session: (sessionId) => {\n const session = sessions.get(sessionId);\n return session === undefined ? undefined : present(session);\n },\n setPendingSessionSetups(sessionId, pending) {\n if (JSON.stringify(pendingSetups.get(sessionId) ?? []) === JSON.stringify(pending)) return;\n pendingSetups.set(sessionId, Object.freeze([...pending]));\n const session = sessions.get(sessionId);\n if (session) emit({ kind: 'upsert', session });\n },\n runtime: (sessionId) => sessions.get(sessionId)?.host.runtime,\n onEvent(listener) {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n register,\n notifySessionRemoved(sessionId) {\n emit({ kind: 'removed', sessionId });\n },\n mountFacets,\n workspaces: () => [...workspaces.values()],\n registerWorkspace(workspace) {\n workspaces.set(workspace.id, workspace);\n emit({ kind: 'workspace_upsert', workspace });\n },\n admitWorkspace: async (root) => {\n if (!options.admitWorkspace) throw new Error('Workspace admission is unavailable.');\n return options.admitWorkspace(root);\n },\n async removeWorkspace(id) {\n if ([...sessions.values()].some((session) => session.workspaceId === id))\n throw new Error('Workspace still has live sessions.');\n const key = mountKey({ scope: 'workspace', workspaceId: id });\n const mounted = mounts.get(key);\n if (!workspaces.has(id)) return;\n options.onWorkspaceRemoved?.(id);\n workspaces.delete(id);\n emit({ kind: 'workspace_removed', workspaceId: id });\n if (!mounted) return;\n mounts.delete(key);\n try {\n await mounted.installed.dispose();\n } finally {\n mounted.host.dispose();\n }\n },\n async requestApi(mount, basePath, request) {\n if (closed) return Response.json({ error: 'Server is closed.' }, { status: 503 });\n if (mount.scope === 'session') {\n const session = sessions.get(mount.sessionId);\n if (!session || !options.requestSessionApi)\n return Response.json({ error: 'Session not found.' }, { status: 404 });\n const url = new URL(request.url);\n return options.requestSessionApi(scopeOf(session), {\n basePath,\n path: `${url.pathname}${url.search}`,\n method: request.method,\n headers: request.headers,\n signal: request.signal,\n body: request.body === null ? undefined : new Uint8Array(await request.arrayBuffer()),\n });\n }\n const handler = mounts.get(mountKey(mount))?.host.handlerFor(basePath);\n if (!handler)\n return Response.json({ error: `API '${basePath}' is not mounted in ${mountKey(mount)}.` }, { status: 404 });\n return handler.fetch(request);\n },\n pluginRegistry,\n invokePlugin: (call, direction, caller) => pluginRegistry.invoke(call, direction, caller),\n async create(sessionOptions) {\n if (closed) throw new Error('The headless hub is closed.');\n const host = await options.manager.create(sessionOptions);\n const session: HeadlessHubSession = {\n id: sessionOptions.sessionId,\n workspaceId: sessionOptions.workspaceId,\n webComposition: sessionOptions.webComposition,\n name: sessionOptions.sessionName,\n cwd: sessionOptions.cwd,\n createdAt: new Date().toISOString(),\n ...(sessionOptions.parentSessionId === undefined ? {} : { parentSessionId: sessionOptions.parentSessionId }),\n ...(sessionOptions.sessionProvenance === undefined\n ? {}\n : { sessionProvenance: sessionOptions.sessionProvenance }),\n environment: sessionOptions.environment,\n host,\n };\n register(session);\n return session;\n },\n closeSession,\n async requestSessionApi(scope, request) {\n if (closed || !sessions.has(scope.sessionId))\n return Response.json({ error: 'Session not found.' }, { status: 404 });\n if (options.requestSessionApi === undefined)\n return Response.json({ error: 'Session API unavailable.' }, { status: 404 });\n return options.requestSessionApi(scope, request);\n },\n registerChannel(channel, mount = { scope: 'global' }) {\n const started = startChannel(channel, mount);\n if (started === undefined) return () => undefined;\n return () => releaseChannel(channel, started);\n },\n channelTypes: () => [...new Set([...channels.values()].map(({ channel }) => channel.frameType))],\n channelFrames(sessionId) {\n const session = sessions.get(sessionId);\n if (session === undefined) return [];\n const scope = scopeOf(session);\n const frames: Array<{ type: string; sessionId: string; payload: unknown }> = [];\n for (const { channel, source } of selectedChannels(session)) {\n const type = channel.frameType;\n const payload = source.payloadFor(scope);\n if (payload !== undefined) frames.push({ type, sessionId, payload });\n }\n return frames;\n },\n threadJournal(sessionId, threadId) {\n const session = sessions.get(sessionId);\n if (session === undefined) return undefined;\n const scope = scopeOf(session);\n for (const { source } of selectedChannels(session)) {\n const journal = source.threadJournal?.(scope, threadId);\n if (journal !== undefined) return journal;\n }\n return undefined;\n },\n receiveChannel(sessionId, frameType, payload, connectionId) {\n if (connectionId === '') return;\n const session = sessions.get(sessionId);\n const started = session && selectedChannels(session).find(({ channel }) => channel.frameType === frameType);\n if (session === undefined || started === undefined) return;\n started.channel.receive?.(scopeOf(session), payload, { connectionId } satisfies DoomHubChannelConnection);\n },\n disconnectChannels(connectionId) {\n if (connectionId === '') return;\n for (const { channel } of channels.values()) channel.disconnected?.({ connectionId });\n },\n async close() {\n if (closed) return;\n const ids = [...sessions.keys()];\n closed = true;\n directEvents.close();\n for (const unsubscribe of subscriptions) unsubscribe();\n subscriptions.clear();\n for (const cleanup of presentationCleanups.values()) cleanup();\n presentationCleanups.clear();\n const failures: unknown[] = [];\n for (const mounted of [...mounts.values()].reverse()) {\n try {\n await mounted.installed.dispose();\n } catch (error) {\n failures.push(error);\n }\n try {\n mounted.host.dispose();\n } catch (error) {\n failures.push(error);\n }\n }\n mounts.clear();\n pluginRegistry.dispose();\n workspaces.clear();\n for (const { source } of channels.values()) source.close();\n channels.clear();\n listeners.clear();\n const shutdowns: Promise<void>[] = [...sessionShutdowns.values()]\n .filter(({ pending }) => pending)\n .map(({ promise }) => promise);\n for (const id of ids) {\n const session = sessions.get(id);\n if (session !== undefined && !sessionShutdowns.has(id)) shutdowns.push(startSessionShutdown(session));\n }\n sessions.clear();\n const outcomes = await Promise.allSettled(shutdowns);\n for (const outcome of outcomes) if (outcome.status === 'rejected') failures.push(outcome.reason);\n if (failures.length) throw new AggregateError(failures, 'Mount shutdown failed');\n },\n };\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { DoomDirectEventBus, DoomHubChannel, DoomHubSessionApiRequest, DoomHubSessionCreateRequest, DoomHubSessionScope, DoomHubSessionService } from "./schemas/hubChannel.cjs";
1
+ import { DoomDirectEventBus, DoomHubChannel, DoomHubSessionApiRequest, DoomHubSessionCreateRequest, DoomHubSessionReservations, DoomHubSessionScope, DoomHubSessionService, DoomPendingSessionSetup } from "./schemas/hubChannel.cjs";
2
2
  import { DoomApiContext, DoomApiMount, DoomWebComposition } from "./schemas/packageApi.cjs";
3
3
  import { DoomPluginCaller, DoomPluginDirection, DoomPluginRegistry } from "./schemas/pluginProtocol.cjs";
4
4
  import { DoomServerFacet } from "./schemas/serverFacet.cjs";
@@ -25,6 +25,7 @@ export interface HeadlessHubSession {
25
25
  readonly everPrompted?: boolean;
26
26
  readonly awaitingInput?: boolean;
27
27
  readonly lastSettledAt?: string;
28
+ readonly pendingSetups?: readonly DoomPendingSessionSetup[];
28
29
  /** Environment admitted for this session, when supplied by the host. */
29
30
  readonly environment?: Readonly<Record<string, string | undefined>>;
30
31
  readonly host: HeadlessSessionHost;
@@ -60,6 +61,7 @@ export interface HeadlessHubOptions {
60
61
  /** Creates a session through the canonical cockpit lifecycle for hub channels. */
61
62
  createSession?: (request: DoomHubSessionCreateRequest) => Promise<DoomHubSessionScope>;
62
63
  onNotice?: (message: string) => void;
64
+ sessionReservations?: DoomHubSessionReservations;
63
65
  requestSessionApi?: (scope: DoomHubSessionScope, request: DoomHubSessionApiRequest) => Promise<Response>;
64
66
  admitWorkspace?: (root: string) => Promise<HeadlessWorkspace>;
65
67
  onWorkspaceRemoved?: (workspaceId: string) => void;
@@ -78,6 +80,8 @@ export interface HeadlessHub {
78
80
  closeSession(sessionId: string): Promise<void>;
79
81
  /** Publishes removal of a persisted session that has no live host. */
80
82
  notifySessionRemoved(sessionId: string): void;
83
+ /** Publishes setup-only children through the parent's normal session updates. */
84
+ setPendingSessionSetups?(sessionId: string, pending: readonly DoomPendingSessionSetup[]): void;
81
85
  /** Dispatches an authenticated package API request inside the owning process. */
82
86
  requestSessionApi(scope: DoomHubSessionScope, request: DoomHubSessionApiRequest): Promise<Response>;
83
87
  /** Mounts the selected hub facets once, before the headless server accepts clients. */
@@ -1 +1 @@
1
- {"version":3,"file":"headless-hub.d.cts","names":[],"sources":["../src/server/headlessHub.ts"],"mappings":";;;;;;;;;;;iBAiCiB;WACN;WACA;EACT,iBAAiB;WACR;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;;WAEA,cAAc,SAAS;WACvB,MAAM;;iBAGA;WACN;WACA;WACA;;YAGC;EACN;EAAgB,SAAS;;EACzB;EAAiB;;EACjB;EAA0B,WAAW;;EACrC;EAA2B;;EAC3B;EAAiB;EAAmB;EAAmB;EAAkB;;iBAE9D;EACf,SAAS;;EAET;;EAEA,iBAAiB,SAAS,gCAAgC,QAAQ;EAClE,YAAY;EACZ,qBAAqB,OAAO,qBAAqB,SAAS,6BAA6B,QAAQ;EAC/F,kBAAkB,iBAAiB,QAAQ;EAC3C,sBAAsB;;iBAGP;;WAEN,gBAAgB;;WAEhB,cAAc;EACvB,qBAAqB;EACrB,QAAQ,oBAAoB;EAC5B,QAAQ,oBAAoB;EAC5B,QAAQ,WAAW,OAAO;EAC1B,SAAS,SAAS;EAClB,OAAO,SAAS,6BAA6B,QAAQ;EACrD,aAAa,oBAAoB;;EAEjC,qBAAqB;;EAErB,kBAAkB,OAAO,qBAAqB,SAAS,2BAA2B,QAAQ;;EAE1F,YAAY,kBAAkB,kBAAkB,sBAAsB,UAAU,iBAAiB;EACjG,uBAAuB;EACvB,kBAAkB,WAAW;EAC7B,eAAe,eAAe,QAAQ;EACtC,gBAAgB,sBAAsB;EACtC,WAAW,OAAO,cAAc,kBAAkB,SAAS,UAAU,QAAQ;;WAEpE,gBAAgB;EACzB,aAAa,eAAe,WAAW,qBAAqB,SAAS,mBAAmB;EACxF,gBAAgB,SAAS,gBAAgB,QAAQ;EACjD;EACA,cAAc,oBAAoB;IAAQ;IAAc;IAAmB;;;EAE3E,cAAc,mBAAmB;EACjC,eAAe,mBAAmB,mBAAmB,kBAAkB;EACvE,mBAAmB;EACnB,SAAS;;;wBAmBK,kBAAkB,SAAS,qBAAqB"}
1
+ {"version":3,"file":"headless-hub.d.cts","names":[],"sources":["../src/server/headlessHub.ts"],"mappings":";;;;;;;;;;;iBAmCiB;WACN;WACA;EACT,iBAAiB;WACR;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA,yBAAyB;;WAEzB,cAAc,SAAS;WACvB,MAAM;;iBAGA;WACN;WACA;WACA;;YAGC;EACN;EAAgB,SAAS;;EACzB;EAAiB;;EACjB;EAA0B,WAAW;;EACrC;EAA2B;;EAC3B;EAAiB;EAAmB;EAAmB;EAAkB;;iBAE9D;EACf,SAAS;;EAET;;EAEA,iBAAiB,SAAS,gCAAgC,QAAQ;EAClE,YAAY;EACZ,sBAAsB;EACtB,qBAAqB,OAAO,qBAAqB,SAAS,6BAA6B,QAAQ;EAC/F,kBAAkB,iBAAiB,QAAQ;EAC3C,sBAAsB;;iBAGP;;WAEN,gBAAgB;;WAEhB,cAAc;EACvB,qBAAqB;EACrB,QAAQ,oBAAoB;EAC5B,QAAQ,oBAAoB;EAC5B,QAAQ,WAAW,OAAO;EAC1B,SAAS,SAAS;EAClB,OAAO,SAAS,6BAA6B,QAAQ;EACrD,aAAa,oBAAoB;;EAEjC,qBAAqB;;EAErB,yBAAyB,mBAAmB,kBAAkB;;EAE9D,kBAAkB,OAAO,qBAAqB,SAAS,2BAA2B,QAAQ;;EAE1F,YAAY,kBAAkB,kBAAkB,sBAAsB,UAAU,iBAAiB;EACjG,uBAAuB;EACvB,kBAAkB,WAAW;EAC7B,eAAe,eAAe,QAAQ;EACtC,gBAAgB,sBAAsB;EACtC,WAAW,OAAO,cAAc,kBAAkB,SAAS,UAAU,QAAQ;;WAEpE,gBAAgB;EACzB,aAAa,eAAe,WAAW,qBAAqB,SAAS,mBAAmB;EACxF,gBAAgB,SAAS,gBAAgB,QAAQ;EACjD;EACA,cAAc,oBAAoB;IAAQ;IAAc;IAAmB;;;EAE3E,cAAc,mBAAmB;EACjC,eAAe,mBAAmB,mBAAmB,kBAAkB;EACvE,mBAAmB;EACnB,SAAS;;;wBAmBK,kBAAkB,SAAS,qBAAqB"}
@@ -1,4 +1,4 @@
1
- import { DoomDirectEventBus, DoomHubChannel, DoomHubSessionApiRequest, DoomHubSessionCreateRequest, DoomHubSessionScope, DoomHubSessionService } from "./schemas/hubChannel.mjs";
1
+ import { DoomDirectEventBus, DoomHubChannel, DoomHubSessionApiRequest, DoomHubSessionCreateRequest, DoomHubSessionReservations, DoomHubSessionScope, DoomHubSessionService, DoomPendingSessionSetup } from "./schemas/hubChannel.mjs";
2
2
  import { DoomApiContext, DoomApiMount, DoomWebComposition } from "./schemas/packageApi.mjs";
3
3
  import { DoomPluginCaller, DoomPluginDirection, DoomPluginRegistry } from "./schemas/pluginProtocol.mjs";
4
4
  import { DoomServerFacet } from "./schemas/serverFacet.mjs";
@@ -25,6 +25,7 @@ export interface HeadlessHubSession {
25
25
  readonly everPrompted?: boolean;
26
26
  readonly awaitingInput?: boolean;
27
27
  readonly lastSettledAt?: string;
28
+ readonly pendingSetups?: readonly DoomPendingSessionSetup[];
28
29
  /** Environment admitted for this session, when supplied by the host. */
29
30
  readonly environment?: Readonly<Record<string, string | undefined>>;
30
31
  readonly host: HeadlessSessionHost;
@@ -60,6 +61,7 @@ export interface HeadlessHubOptions {
60
61
  /** Creates a session through the canonical cockpit lifecycle for hub channels. */
61
62
  createSession?: (request: DoomHubSessionCreateRequest) => Promise<DoomHubSessionScope>;
62
63
  onNotice?: (message: string) => void;
64
+ sessionReservations?: DoomHubSessionReservations;
63
65
  requestSessionApi?: (scope: DoomHubSessionScope, request: DoomHubSessionApiRequest) => Promise<Response>;
64
66
  admitWorkspace?: (root: string) => Promise<HeadlessWorkspace>;
65
67
  onWorkspaceRemoved?: (workspaceId: string) => void;
@@ -78,6 +80,8 @@ export interface HeadlessHub {
78
80
  closeSession(sessionId: string): Promise<void>;
79
81
  /** Publishes removal of a persisted session that has no live host. */
80
82
  notifySessionRemoved(sessionId: string): void;
83
+ /** Publishes setup-only children through the parent's normal session updates. */
84
+ setPendingSessionSetups?(sessionId: string, pending: readonly DoomPendingSessionSetup[]): void;
81
85
  /** Dispatches an authenticated package API request inside the owning process. */
82
86
  requestSessionApi(scope: DoomHubSessionScope, request: DoomHubSessionApiRequest): Promise<Response>;
83
87
  /** Mounts the selected hub facets once, before the headless server accepts clients. */
@@ -1 +1 @@
1
- {"version":3,"file":"headless-hub.d.mts","names":[],"sources":["../src/server/headlessHub.ts"],"mappings":";;;;;;;;;;;iBAiCiB;WACN;WACA;EACT,iBAAiB;WACR;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;;WAEA,cAAc,SAAS;WACvB,MAAM;;iBAGA;WACN;WACA;WACA;;YAGC;EACN;EAAgB,SAAS;;EACzB;EAAiB;;EACjB;EAA0B,WAAW;;EACrC;EAA2B;;EAC3B;EAAiB;EAAmB;EAAmB;EAAkB;;iBAE9D;EACf,SAAS;;EAET;;EAEA,iBAAiB,SAAS,gCAAgC,QAAQ;EAClE,YAAY;EACZ,qBAAqB,OAAO,qBAAqB,SAAS,6BAA6B,QAAQ;EAC/F,kBAAkB,iBAAiB,QAAQ;EAC3C,sBAAsB;;iBAGP;;WAEN,gBAAgB;;WAEhB,cAAc;EACvB,qBAAqB;EACrB,QAAQ,oBAAoB;EAC5B,QAAQ,oBAAoB;EAC5B,QAAQ,WAAW,OAAO;EAC1B,SAAS,SAAS;EAClB,OAAO,SAAS,6BAA6B,QAAQ;EACrD,aAAa,oBAAoB;;EAEjC,qBAAqB;;EAErB,kBAAkB,OAAO,qBAAqB,SAAS,2BAA2B,QAAQ;;EAE1F,YAAY,kBAAkB,kBAAkB,sBAAsB,UAAU,iBAAiB;EACjG,uBAAuB;EACvB,kBAAkB,WAAW;EAC7B,eAAe,eAAe,QAAQ;EACtC,gBAAgB,sBAAsB;EACtC,WAAW,OAAO,cAAc,kBAAkB,SAAS,UAAU,QAAQ;;WAEpE,gBAAgB;EACzB,aAAa,eAAe,WAAW,qBAAqB,SAAS,mBAAmB;EACxF,gBAAgB,SAAS,gBAAgB,QAAQ;EACjD;EACA,cAAc,oBAAoB;IAAQ;IAAc;IAAmB;;;EAE3E,cAAc,mBAAmB;EACjC,eAAe,mBAAmB,mBAAmB,kBAAkB;EACvE,mBAAmB;EACnB,SAAS;;;wBAmBK,kBAAkB,SAAS,qBAAqB"}
1
+ {"version":3,"file":"headless-hub.d.mts","names":[],"sources":["../src/server/headlessHub.ts"],"mappings":";;;;;;;;;;;iBAmCiB;WACN;WACA;EACT,iBAAiB;WACR;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA,yBAAyB;;WAEzB,cAAc,SAAS;WACvB,MAAM;;iBAGA;WACN;WACA;WACA;;YAGC;EACN;EAAgB,SAAS;;EACzB;EAAiB;;EACjB;EAA0B,WAAW;;EACrC;EAA2B;;EAC3B;EAAiB;EAAmB;EAAmB;EAAkB;;iBAE9D;EACf,SAAS;;EAET;;EAEA,iBAAiB,SAAS,gCAAgC,QAAQ;EAClE,YAAY;EACZ,sBAAsB;EACtB,qBAAqB,OAAO,qBAAqB,SAAS,6BAA6B,QAAQ;EAC/F,kBAAkB,iBAAiB,QAAQ;EAC3C,sBAAsB;;iBAGP;;WAEN,gBAAgB;;WAEhB,cAAc;EACvB,qBAAqB;EACrB,QAAQ,oBAAoB;EAC5B,QAAQ,oBAAoB;EAC5B,QAAQ,WAAW,OAAO;EAC1B,SAAS,SAAS;EAClB,OAAO,SAAS,6BAA6B,QAAQ;EACrD,aAAa,oBAAoB;;EAEjC,qBAAqB;;EAErB,yBAAyB,mBAAmB,kBAAkB;;EAE9D,kBAAkB,OAAO,qBAAqB,SAAS,2BAA2B,QAAQ;;EAE1F,YAAY,kBAAkB,kBAAkB,sBAAsB,UAAU,iBAAiB;EACjG,uBAAuB;EACvB,kBAAkB,WAAW;EAC7B,eAAe,eAAe,QAAQ;EACtC,gBAAgB,sBAAsB;EACtC,WAAW,OAAO,cAAc,kBAAkB,SAAS,UAAU,QAAQ;;WAEpE,gBAAgB;EACzB,aAAa,eAAe,WAAW,qBAAqB,SAAS,mBAAmB;EACxF,gBAAgB,SAAS,gBAAgB,QAAQ;EACjD;EACA,cAAc,oBAAoB;IAAQ;IAAc;IAAmB;;;EAE3E,cAAc,mBAAmB;EACjC,eAAe,mBAAmB,mBAAmB,kBAAkB;EACvE,mBAAmB;EACnB,SAAS;;;wBAmBK,kBAAkB,SAAS,qBAAqB"}
@@ -21,6 +21,11 @@ function scopeOf(session) {
21
21
  /** Owns direct, same-process session and channel state for presentation adapters. */
22
22
  function createHeadlessHub(options) {
23
23
  const sessions = /* @__PURE__ */ new Map();
24
+ const pendingSetups = /* @__PURE__ */ new Map();
25
+ const present = (session) => pendingSetups.has(session.id) ? {
26
+ ...session,
27
+ pendingSetups: pendingSetups.get(session.id)
28
+ } : session;
24
29
  const pluginRegistry = createDoomPluginRegistry();
25
30
  const channels = /* @__PURE__ */ new Map();
26
31
  const listeners = /* @__PURE__ */ new Set();
@@ -168,7 +173,11 @@ function createHeadlessHub(options) {
168
173
  };
169
174
  const emit = (event) => {
170
175
  if (closed) return;
171
- for (const listener of listeners) listener(event);
176
+ const projected = event.kind === "upsert" ? {
177
+ ...event,
178
+ session: present(event.session)
179
+ } : event;
180
+ for (const listener of listeners) listener(projected);
172
181
  };
173
182
  const sessionScopes = () => [...sessions.values()].map(scopeOf);
174
183
  const publish = (frameType, sessionId, payload, connectionId) => {
@@ -197,12 +206,35 @@ function createHeadlessHub(options) {
197
206
  headers
198
207
  };
199
208
  };
209
+ const reservationsFor = (mount) => {
210
+ const reservations = options.sessionReservations;
211
+ if (!reservations) return void 0;
212
+ const check = (parentId) => {
213
+ const parent = sessions.get(parentId);
214
+ if (!parent || !belongs(mount, parent)) throw new Error("Parent session is outside this mount.");
215
+ };
216
+ return {
217
+ read: (id, parentId) => {
218
+ check(parentId);
219
+ return reservations.read(id, parentId);
220
+ },
221
+ prepare: (id, parentId, cwd) => {
222
+ check(parentId);
223
+ return reservations.prepare(id, parentId, cwd);
224
+ },
225
+ complete: (id, parentId) => {
226
+ check(parentId);
227
+ return reservations.complete(id, parentId);
228
+ }
229
+ };
230
+ };
200
231
  const channelHost = (frameType, mount) => ({
201
232
  sessions: () => sessionScopes().filter((scope) => {
202
233
  const session = sessions.get(scope.sessionId);
203
234
  return session !== void 0 && serves(mount, frameType, session);
204
235
  }),
205
236
  sessionService: {
237
+ reservations: reservationsFor(mount),
206
238
  create: async (request) => {
207
239
  if (mount.scope !== "global") {
208
240
  const parent = request.parentSessionId === void 0 ? void 0 : sessions.get(request.parentSessionId);
@@ -425,6 +457,7 @@ function createHeadlessHub(options) {
425
457
  },
426
458
  close: closeSession,
427
459
  isLive: (sessionId) => !closed && sessions.has(sessionId),
460
+ reservations: options.sessionReservations,
428
461
  canCommunicate,
429
462
  bindCommunication
430
463
  };
@@ -500,8 +533,20 @@ function createHeadlessHub(options) {
500
533
  return {
501
534
  sessionService,
502
535
  directEvents,
503
- snapshot: () => [...sessions.values()],
504
- session: (sessionId) => sessions.get(sessionId),
536
+ snapshot: () => [...sessions.values()].map(present),
537
+ session: (sessionId) => {
538
+ const session = sessions.get(sessionId);
539
+ return session === void 0 ? void 0 : present(session);
540
+ },
541
+ setPendingSessionSetups(sessionId, pending) {
542
+ if (JSON.stringify(pendingSetups.get(sessionId) ?? []) === JSON.stringify(pending)) return;
543
+ pendingSetups.set(sessionId, Object.freeze([...pending]));
544
+ const session = sessions.get(sessionId);
545
+ if (session) emit({
546
+ kind: "upsert",
547
+ session
548
+ });
549
+ },
505
550
  runtime: (sessionId) => sessions.get(sessionId)?.host.runtime,
506
551
  onEvent(listener) {
507
552
  listeners.add(listener);