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

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 (165) hide show
  1. package/dist/headless-hub.cjs +107 -7
  2. package/dist/headless-hub.cjs.map +1 -1
  3. package/dist/headless-hub.d.cts +10 -0
  4. package/dist/headless-hub.d.cts.map +1 -1
  5. package/dist/headless-hub.d.mts +10 -0
  6. package/dist/headless-hub.d.mts.map +1 -1
  7. package/dist/headless-hub.mjs +107 -7
  8. package/dist/headless-hub.mjs.map +1 -1
  9. package/dist/headless-server.cjs +63 -6
  10. package/dist/headless-server.cjs.map +1 -1
  11. package/dist/headless-server.d.cts +2 -0
  12. package/dist/headless-server.d.cts.map +1 -1
  13. package/dist/headless-server.d.mts +2 -0
  14. package/dist/headless-server.d.mts.map +1 -1
  15. package/dist/headless-server.mjs +64 -7
  16. package/dist/headless-server.mjs.map +1 -1
  17. package/dist/headless-session-host.d.cts +5 -0
  18. package/dist/headless-session-host.d.cts.map +1 -1
  19. package/dist/headless-session-host.d.mts +5 -0
  20. package/dist/headless-session-host.d.mts.map +1 -1
  21. package/dist/history-import.cjs +1 -1
  22. package/dist/history-import.mjs +1 -1
  23. package/dist/history-ownership.cjs +1 -1
  24. package/dist/history-ownership.mjs +1 -1
  25. package/dist/history.cjs +2 -0
  26. package/dist/history.d.cts +2 -1
  27. package/dist/history.d.mts +2 -1
  28. package/dist/history.mjs +2 -1
  29. package/dist/hub-channel.d.cts +2 -2
  30. package/dist/hub-channel.d.mts +2 -2
  31. package/dist/jsonl-session-repo.cjs +1 -1
  32. package/dist/jsonl-session-repo.mjs +1 -1
  33. package/dist/mcp-facet.cjs +11 -0
  34. package/dist/mcp-facet.d.cts +4 -0
  35. package/dist/mcp-facet.d.mts +4 -0
  36. package/dist/mcp-facet.mjs +4 -0
  37. package/dist/mcp-schema-cost.cjs +2 -2
  38. package/dist/mcp-schema-cost.mjs +1 -1
  39. package/dist/package-api-server.cjs +13 -1
  40. package/dist/package-api-server.cjs.map +1 -1
  41. package/dist/package-api-server.mjs +13 -1
  42. package/dist/package-api-server.mjs.map +1 -1
  43. package/dist/remote-runtime.cjs +11 -3
  44. package/dist/remote-runtime.cjs.map +1 -1
  45. package/dist/remote-runtime.d.cts.map +1 -1
  46. package/dist/remote-runtime.d.mts.map +1 -1
  47. package/dist/remote-runtime.mjs +12 -4
  48. package/dist/remote-runtime.mjs.map +1 -1
  49. package/dist/runtime-identity.cjs +1 -1
  50. package/dist/runtime-identity.mjs +1 -1
  51. package/dist/runtime-json.cjs +1 -1
  52. package/dist/runtime-json.mjs +1 -1
  53. package/dist/schemas/httpApiContracts.cjs +27 -3
  54. package/dist/schemas/httpApiContracts.cjs.map +1 -1
  55. package/dist/schemas/httpApiContracts.mjs +25 -4
  56. package/dist/schemas/httpApiContracts.mjs.map +1 -1
  57. package/dist/schemas/hubChannel.d.cts +12 -0
  58. package/dist/schemas/hubChannel.d.cts.map +1 -1
  59. package/dist/schemas/hubChannel.d.mts +12 -0
  60. package/dist/schemas/hubChannel.d.mts.map +1 -1
  61. package/dist/schemas/mcpBundle.cjs +67 -0
  62. package/dist/schemas/mcpBundle.cjs.map +1 -0
  63. package/dist/schemas/mcpBundle.d.cts +21 -0
  64. package/dist/schemas/mcpBundle.d.cts.map +1 -0
  65. package/dist/schemas/mcpBundle.d.mts +21 -0
  66. package/dist/schemas/mcpBundle.d.mts.map +1 -0
  67. package/dist/schemas/mcpBundle.mjs +65 -0
  68. package/dist/schemas/mcpBundle.mjs.map +1 -0
  69. package/dist/schemas/mcpFacet.cjs +19 -0
  70. package/dist/schemas/mcpFacet.cjs.map +1 -0
  71. package/dist/schemas/mcpFacet.d.cts +40 -0
  72. package/dist/schemas/mcpFacet.d.cts.map +1 -0
  73. package/dist/schemas/mcpFacet.d.mts +40 -0
  74. package/dist/schemas/mcpFacet.d.mts.map +1 -0
  75. package/dist/schemas/mcpFacet.mjs +17 -0
  76. package/dist/schemas/mcpFacet.mjs.map +1 -0
  77. package/dist/schemas/packageApi.cjs.map +1 -1
  78. package/dist/schemas/packageApi.d.cts +3 -1
  79. package/dist/schemas/packageApi.d.cts.map +1 -1
  80. package/dist/schemas/packageApi.d.mts +3 -1
  81. package/dist/schemas/packageApi.d.mts.map +1 -1
  82. package/dist/schemas/packageApi.mjs.map +1 -1
  83. package/dist/schemas/sessionApiContracts.cjs +3 -0
  84. package/dist/schemas/sessionApiContracts.cjs.map +1 -1
  85. package/dist/schemas/sessionApiContracts.d.cts.map +1 -1
  86. package/dist/schemas/sessionApiContracts.d.mts.map +1 -1
  87. package/dist/schemas/sessionApiContracts.mjs +3 -0
  88. package/dist/schemas/sessionApiContracts.mjs.map +1 -1
  89. package/dist/server/headlessProtocol.cjs +26 -0
  90. package/dist/server/headlessProtocol.cjs.map +1 -1
  91. package/dist/server/headlessProtocol.mjs +26 -0
  92. package/dist/server/headlessProtocol.mjs.map +1 -1
  93. package/dist/server/mcpBundleLoader.cjs +56 -0
  94. package/dist/server/mcpBundleLoader.cjs.map +1 -0
  95. package/dist/server/mcpBundleLoader.d.cts +27 -0
  96. package/dist/server/mcpBundleLoader.d.cts.map +1 -0
  97. package/dist/server/mcpBundleLoader.d.mts +27 -0
  98. package/dist/server/mcpBundleLoader.d.mts.map +1 -0
  99. package/dist/server/mcpBundleLoader.mjs +52 -0
  100. package/dist/server/mcpBundleLoader.mjs.map +1 -0
  101. package/dist/server/sessionMcpHandler.cjs +46 -16
  102. package/dist/server/sessionMcpHandler.cjs.map +1 -1
  103. package/dist/server/sessionMcpHandler.d.cts.map +1 -1
  104. package/dist/server/sessionMcpHandler.d.mts.map +1 -1
  105. package/dist/server/sessionMcpHandler.mjs +47 -17
  106. package/dist/server/sessionMcpHandler.mjs.map +1 -1
  107. package/dist/server/sessionMcpRoutes.cjs +14 -7
  108. package/dist/server/sessionMcpRoutes.cjs.map +1 -1
  109. package/dist/server/sessionMcpRoutes.mjs +14 -7
  110. package/dist/server/sessionMcpRoutes.mjs.map +1 -1
  111. package/dist/services/webauthnPolicy/index.cjs +1 -1
  112. package/dist/services/webauthnPolicy/index.cjs.map +1 -1
  113. package/dist/services/webauthnPolicy/index.mjs +1 -1
  114. package/dist/services/webauthnPolicy/index.mjs.map +1 -1
  115. package/dist/services/workspaceRegistry/index.cjs +90 -0
  116. package/dist/services/workspaceRegistry/index.cjs.map +1 -0
  117. package/dist/services/workspaceRegistry/index.d.cts +18 -0
  118. package/dist/services/workspaceRegistry/index.d.cts.map +1 -0
  119. package/dist/services/workspaceRegistry/index.d.mts +18 -0
  120. package/dist/services/workspaceRegistry/index.d.mts.map +1 -0
  121. package/dist/services/workspaceRegistry/index.mjs +87 -0
  122. package/dist/services/workspaceRegistry/index.mjs.map +1 -0
  123. package/dist/sqlite-session-storage.cjs +1 -1
  124. package/dist/sqlite-session-storage.mjs +1 -1
  125. package/dist/sync-location.cjs +2 -2
  126. package/dist/sync-location.mjs +1 -1
  127. package/dist/sync-registration.cjs +19 -5
  128. package/dist/sync-registration.cjs.map +1 -1
  129. package/dist/sync-registration.d.cts +3 -0
  130. package/dist/sync-registration.d.cts.map +1 -1
  131. package/dist/sync-registration.d.mts +3 -0
  132. package/dist/sync-registration.d.mts.map +1 -1
  133. package/dist/sync-registration.mjs +18 -4
  134. package/dist/sync-registration.mjs.map +1 -1
  135. package/dist/systems/child/adapters/headlessChildSessionService.cjs +1 -1
  136. package/dist/systems/child/adapters/headlessChildSessionService.mjs +1 -1
  137. package/dist/systems/main/adapters/headlessHost.cjs +3 -1
  138. package/dist/systems/main/adapters/headlessHost.cjs.map +1 -1
  139. package/dist/systems/main/adapters/headlessHost.d.cts.map +1 -1
  140. package/dist/systems/main/adapters/headlessHost.d.mts.map +1 -1
  141. package/dist/systems/main/adapters/headlessHost.mjs +3 -1
  142. package/dist/systems/main/adapters/headlessHost.mjs.map +1 -1
  143. package/dist/systems/main/adapters/headlessSessionHost.cjs +175 -82
  144. package/dist/systems/main/adapters/headlessSessionHost.cjs.map +1 -1
  145. package/dist/systems/main/adapters/headlessSessionHost.d.cts.map +1 -1
  146. package/dist/systems/main/adapters/headlessSessionHost.d.mts.map +1 -1
  147. package/dist/systems/main/adapters/headlessSessionHost.mjs +175 -82
  148. package/dist/systems/main/adapters/headlessSessionHost.mjs.map +1 -1
  149. package/dist/terminal-pi-child-session-service.cjs +1 -1
  150. package/dist/terminal-pi-child-session-service.mjs +1 -1
  151. package/dist/types/server/sessionToolSurface.d.cts +3 -1
  152. package/dist/types/server/sessionToolSurface.d.cts.map +1 -1
  153. package/dist/types/server/sessionToolSurface.d.mts +3 -1
  154. package/dist/types/server/sessionToolSurface.d.mts.map +1 -1
  155. package/dist/v3-export.cjs +1 -1
  156. package/dist/v3-export.mjs +1 -1
  157. package/dist/web/types/webPlugin.d.cts +8 -0
  158. package/dist/web/types/webPlugin.d.cts.map +1 -1
  159. package/dist/web/types/webPlugin.d.mts +8 -0
  160. package/dist/web/types/webPlugin.d.mts.map +1 -1
  161. package/dist/web-compositions.cjs +2 -2
  162. package/dist/web-compositions.mjs +1 -1
  163. package/dist/web.d.cts +2 -2
  164. package/dist/web.d.mts +2 -2
  165. package/package.json +9 -4
@@ -32,6 +32,7 @@ function createHeadlessHub(options) {
32
32
  const directEventListeners = /* @__PURE__ */ new Map();
33
33
  const directEventLatest = /* @__PURE__ */ new Map();
34
34
  const maxDirectEventLatest = 4096;
35
+ const communicationEndpoints = /* @__PURE__ */ new Map();
35
36
  let closed = false;
36
37
  const mounts = /* @__PURE__ */ new Map();
37
38
  const workspaces = /* @__PURE__ */ new Map();
@@ -39,6 +40,12 @@ function createHeadlessHub(options) {
39
40
  const mountKey = (mount) => mount.scope === "global" ? "global" : mount.scope === "workspace" ? `workspace:${mount.workspaceId}` : `session:${mount.sessionId}`;
40
41
  const belongs = (mount, session) => mount.scope === "global" || (mount.scope === "workspace" ? session.workspaceId === mount.workspaceId : session.id === mount.sessionId);
41
42
  const canClose = (mount, session) => belongs(mount, session) || mount.scope === "session" && session.parentSessionId === mount.sessionId;
43
+ const canCommunicate = (sourceId, targetId) => {
44
+ if (closed || sourceId === targetId) return false;
45
+ const source = sessions.get(sourceId);
46
+ const target = sessions.get(targetId);
47
+ return source !== void 0 && target !== void 0 && (source.parentSessionId === target.id || target.parentSessionId === source.id);
48
+ };
42
49
  const selectedChannels = (session) => {
43
50
  const selected = /* @__PURE__ */ new Map();
44
51
  for (const started of channels.values()) {
@@ -101,6 +108,65 @@ function createHeadlessHub(options) {
101
108
  directEventLatest.clear();
102
109
  }
103
110
  };
111
+ const announceCommunicationReady = (sessionId) => {
112
+ const current = communicationEndpoints.get(sessionId);
113
+ if (current?.ready !== true) return;
114
+ for (const [peerSessionId, peer] of communicationEndpoints) {
115
+ if (!peer.ready || !canCommunicate(sessionId, peerSessionId)) continue;
116
+ for (const listener of current.readyListeners) listener(peerSessionId);
117
+ for (const listener of peer.readyListeners) listener(sessionId);
118
+ }
119
+ };
120
+ const bindCommunication = (sessionId) => {
121
+ const existing = communicationEndpoints.get(sessionId);
122
+ if (existing !== void 0) return existing.endpoint;
123
+ const listeners = /* @__PURE__ */ new Map();
124
+ const readyListeners = /* @__PURE__ */ new Set();
125
+ let endpoint;
126
+ const record = {
127
+ listeners,
128
+ readyListeners,
129
+ ready: false,
130
+ get endpoint() {
131
+ return endpoint;
132
+ }
133
+ };
134
+ endpoint = {
135
+ sessionId,
136
+ publish(targetSessionId, type, payload) {
137
+ if (!canCommunicate(sessionId, targetSessionId)) return false;
138
+ const target = communicationEndpoints.get(targetSessionId);
139
+ if (target?.ready !== true) return false;
140
+ for (const listener of target.listeners.get(type) ?? []) listener(sessionId, payload);
141
+ return true;
142
+ },
143
+ subscribe(type, listener) {
144
+ const current = listeners.get(type) ?? /* @__PURE__ */ new Set();
145
+ current.add(listener);
146
+ listeners.set(type, current);
147
+ return () => {
148
+ current.delete(listener);
149
+ if (current.size === 0) listeners.delete(type);
150
+ };
151
+ },
152
+ onPeerReady(listener) {
153
+ readyListeners.add(listener);
154
+ if (!record.ready) {
155
+ record.ready = true;
156
+ announceCommunicationReady(sessionId);
157
+ }
158
+ return () => readyListeners.delete(listener);
159
+ },
160
+ close() {
161
+ if (communicationEndpoints.get(sessionId)?.endpoint !== endpoint) return;
162
+ communicationEndpoints.delete(sessionId);
163
+ listeners.clear();
164
+ readyListeners.clear();
165
+ }
166
+ };
167
+ communicationEndpoints.set(sessionId, record);
168
+ return endpoint;
169
+ };
104
170
  const emit = (event) => {
105
171
  if (closed) return;
106
172
  for (const listener of listeners) listener(event);
@@ -161,6 +227,10 @@ function createHeadlessHub(options) {
161
227
  isLive: (id) => {
162
228
  const session = sessions.get(id);
163
229
  return session !== void 0 && belongs(mount, session);
230
+ },
231
+ canCommunicate: (sourceId, targetId) => {
232
+ const source = sessions.get(sourceId);
233
+ return source !== void 0 && belongs(mount, source) && sessionService.canCommunicate?.(sourceId, targetId) === true;
164
234
  }
165
235
  },
166
236
  directEvents: {
@@ -245,6 +315,7 @@ function createHeadlessHub(options) {
245
315
  if (current === void 0) return;
246
316
  const cleanup = sessionCleanups.get(sessionId);
247
317
  if (cleanup !== void 0) subscriptions.delete(cleanup);
318
+ communicationEndpoints.get(sessionId)?.endpoint.close();
248
319
  sessions.delete(sessionId);
249
320
  sessionCleanups.delete(sessionId);
250
321
  presentationCleanups.get(sessionId)?.();
@@ -289,6 +360,7 @@ function createHeadlessHub(options) {
289
360
  awaitingInput: session.awaitingInput ?? false
290
361
  };
291
362
  sessions.set(session.id, current);
363
+ announceCommunicationReady(session.id);
292
364
  const stopPresentation = session.host.onPresentationFrame((frame) => {
293
365
  if (sessions.get(session.id)?.host !== session.host) return;
294
366
  const type = typeof frame.type === "string" ? frame.type : "";
@@ -353,7 +425,9 @@ function createHeadlessHub(options) {
353
425
  return options.createSession(request);
354
426
  },
355
427
  close: closeSession,
356
- isLive: (sessionId) => !closed && sessions.has(sessionId)
428
+ isLive: (sessionId) => !closed && sessions.has(sessionId),
429
+ canCommunicate,
430
+ bindCommunication
357
431
  };
358
432
  const mountFacets = async (facets, suppliedContext) => {
359
433
  if (closed) throw new Error("The headless hub is closed.");
@@ -407,10 +481,18 @@ function createHeadlessHub(options) {
407
481
  host,
408
482
  installed
409
483
  });
410
- if (mount.scope === "workspace") workspaces.set(mount.workspaceId, {
411
- id: mount.workspaceId,
412
- root: context.workspaceRoot
413
- });
484
+ if (mount.scope === "workspace") {
485
+ const workspace = {
486
+ id: mount.workspaceId,
487
+ root: context.workspaceRoot,
488
+ available: true
489
+ };
490
+ workspaces.set(mount.workspaceId, workspace);
491
+ emit({
492
+ kind: "workspace_upsert",
493
+ workspace
494
+ });
495
+ }
414
496
  } catch (error) {
415
497
  host.dispose();
416
498
  throw error;
@@ -427,8 +509,21 @@ function createHeadlessHub(options) {
427
509
  return () => listeners.delete(listener);
428
510
  },
429
511
  register,
512
+ notifySessionRemoved(sessionId) {
513
+ emit({
514
+ kind: "removed",
515
+ sessionId
516
+ });
517
+ },
430
518
  mountFacets,
431
519
  workspaces: () => [...workspaces.values()],
520
+ registerWorkspace(workspace) {
521
+ workspaces.set(workspace.id, workspace);
522
+ emit({
523
+ kind: "workspace_upsert",
524
+ workspace
525
+ });
526
+ },
432
527
  admitWorkspace: async (root) => {
433
528
  if (!options.admitWorkspace) throw new Error("Workspace admission is unavailable.");
434
529
  return options.admitWorkspace(root);
@@ -440,10 +535,15 @@ function createHeadlessHub(options) {
440
535
  workspaceId: id
441
536
  });
442
537
  const mounted = mounts.get(key);
538
+ if (!workspaces.has(id)) return;
539
+ options.onWorkspaceRemoved?.(id);
540
+ workspaces.delete(id);
541
+ emit({
542
+ kind: "workspace_removed",
543
+ workspaceId: id
544
+ });
443
545
  if (!mounted) return;
444
546
  mounts.delete(key);
445
- workspaces.delete(id);
446
- options.onWorkspaceRemoved?.(id);
447
547
  try {
448
548
  await mounted.installed.dispose();
449
549
  } finally {
@@ -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} 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}\n\nexport type HeadlessHubEvent =\n | { kind: 'upsert'; session: HeadlessHubSession }\n | { kind: 'removed'; sessionId: 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 /** 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 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 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 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 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 },\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 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 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 };\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 workspaces.set(mount.workspaceId, { id: mount.workspaceId, root: context.workspaceRoot! });\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 mountFacets,\n workspaces: () => [...workspaces.values()],\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 (!mounted) return;\n mounts.delete(key);\n workspaces.delete(id);\n options.onWorkspaceRemoved?.(id);\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} 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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"}
@@ -32,6 +32,7 @@ export interface HeadlessHubSession {
32
32
  export interface HeadlessWorkspace {
33
33
  readonly id: string;
34
34
  readonly root: string;
35
+ readonly available?: boolean;
35
36
  }
36
37
  export type HeadlessHubEvent = {
37
38
  kind: 'upsert';
@@ -39,6 +40,12 @@ export type HeadlessHubEvent = {
39
40
  } | {
40
41
  kind: 'removed';
41
42
  sessionId: string;
43
+ } | {
44
+ kind: 'workspace_upsert';
45
+ workspace: HeadlessWorkspace;
46
+ } | {
47
+ kind: 'workspace_removed';
48
+ workspaceId: string;
42
49
  } | {
43
50
  kind: 'channel';
44
51
  frameType: string;
@@ -69,11 +76,14 @@ export interface HeadlessHub {
69
76
  register(session: HeadlessHubSession): void;
70
77
  create(options: HeadlessSessionHostOptions): Promise<HeadlessHubSession>;
71
78
  closeSession(sessionId: string): Promise<void>;
79
+ /** Publishes removal of a persisted session that has no live host. */
80
+ notifySessionRemoved(sessionId: string): void;
72
81
  /** Dispatches an authenticated package API request inside the owning process. */
73
82
  requestSessionApi(scope: DoomHubSessionScope, request: DoomHubSessionApiRequest): Promise<Response>;
74
83
  /** Mounts the selected hub facets once, before the headless server accepts clients. */
75
84
  mountFacets(facets: readonly (DoomServerFacet | LoadedServerFacet)[], context?: DoomApiContext): Promise<void>;
76
85
  workspaces(): readonly HeadlessWorkspace[];
86
+ registerWorkspace(workspace: HeadlessWorkspace): void;
77
87
  admitWorkspace(root: string): Promise<HeadlessWorkspace>;
78
88
  removeWorkspace(workspaceId: string): Promise<void>;
79
89
  requestApi(mount: DoomApiMount, basePath: string, request: Request): Promise<Response>;
@@ -1 +1 @@
1
- {"version":3,"file":"headless-hub.d.cts","names":[],"sources":["../src/server/headlessHub.ts"],"mappings":";;;;;;;;;;;iBAgCiB;WACN;WACA;EACT,iBAAiB;WACR;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;;WAEA,cAAc,SAAS;WACvB,MAAM;;iBAGA;WACN;WACA;;YAGC;EACN;EAAgB,SAAS;;EACzB;EAAiB;;EACjB;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,kBAAkB,OAAO,qBAAqB,SAAS,2BAA2B,QAAQ;;EAE1F,YAAY,kBAAkB,kBAAkB,sBAAsB,UAAU,iBAAiB;EACjG,uBAAuB;EACvB,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":";;;;;;;;;;;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"}
@@ -32,6 +32,7 @@ export interface HeadlessHubSession {
32
32
  export interface HeadlessWorkspace {
33
33
  readonly id: string;
34
34
  readonly root: string;
35
+ readonly available?: boolean;
35
36
  }
36
37
  export type HeadlessHubEvent = {
37
38
  kind: 'upsert';
@@ -39,6 +40,12 @@ export type HeadlessHubEvent = {
39
40
  } | {
40
41
  kind: 'removed';
41
42
  sessionId: string;
43
+ } | {
44
+ kind: 'workspace_upsert';
45
+ workspace: HeadlessWorkspace;
46
+ } | {
47
+ kind: 'workspace_removed';
48
+ workspaceId: string;
42
49
  } | {
43
50
  kind: 'channel';
44
51
  frameType: string;
@@ -69,11 +76,14 @@ export interface HeadlessHub {
69
76
  register(session: HeadlessHubSession): void;
70
77
  create(options: HeadlessSessionHostOptions): Promise<HeadlessHubSession>;
71
78
  closeSession(sessionId: string): Promise<void>;
79
+ /** Publishes removal of a persisted session that has no live host. */
80
+ notifySessionRemoved(sessionId: string): void;
72
81
  /** Dispatches an authenticated package API request inside the owning process. */
73
82
  requestSessionApi(scope: DoomHubSessionScope, request: DoomHubSessionApiRequest): Promise<Response>;
74
83
  /** Mounts the selected hub facets once, before the headless server accepts clients. */
75
84
  mountFacets(facets: readonly (DoomServerFacet | LoadedServerFacet)[], context?: DoomApiContext): Promise<void>;
76
85
  workspaces(): readonly HeadlessWorkspace[];
86
+ registerWorkspace(workspace: HeadlessWorkspace): void;
77
87
  admitWorkspace(root: string): Promise<HeadlessWorkspace>;
78
88
  removeWorkspace(workspaceId: string): Promise<void>;
79
89
  requestApi(mount: DoomApiMount, basePath: string, request: Request): Promise<Response>;
@@ -1 +1 @@
1
- {"version":3,"file":"headless-hub.d.mts","names":[],"sources":["../src/server/headlessHub.ts"],"mappings":";;;;;;;;;;;iBAgCiB;WACN;WACA;EACT,iBAAiB;WACR;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;;WAEA,cAAc,SAAS;WACvB,MAAM;;iBAGA;WACN;WACA;;YAGC;EACN;EAAgB,SAAS;;EACzB;EAAiB;;EACjB;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,kBAAkB,OAAO,qBAAqB,SAAS,2BAA2B,QAAQ;;EAE1F,YAAY,kBAAkB,kBAAkB,sBAAsB,UAAU,iBAAiB;EACjG,uBAAuB;EACvB,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":";;;;;;;;;;;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"}