@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.
- package/dist/headless-hub.cjs +48 -3
- package/dist/headless-hub.cjs.map +1 -1
- package/dist/headless-hub.d.cts +5 -1
- package/dist/headless-hub.d.cts.map +1 -1
- package/dist/headless-hub.d.mts +5 -1
- package/dist/headless-hub.d.mts.map +1 -1
- package/dist/headless-hub.mjs +48 -3
- package/dist/headless-hub.mjs.map +1 -1
- package/dist/headless-server.cjs +17 -2
- package/dist/headless-server.cjs.map +1 -1
- package/dist/headless-server.d.cts +5 -0
- package/dist/headless-server.d.cts.map +1 -1
- package/dist/headless-server.d.mts +5 -0
- package/dist/headless-server.d.mts.map +1 -1
- package/dist/headless-server.mjs +17 -2
- package/dist/headless-server.mjs.map +1 -1
- package/dist/headless.d.cts +2 -2
- package/dist/headless.d.mts +2 -2
- package/dist/hub-channel.d.cts +2 -2
- package/dist/hub-channel.d.mts +2 -2
- package/dist/mcp-facet.d.cts +2 -2
- package/dist/mcp-facet.d.mts +2 -2
- package/dist/runtime-cordis-host.cjs.map +1 -1
- package/dist/runtime-cordis-host.d.cts +1 -1
- package/dist/runtime-cordis-host.d.cts.map +1 -1
- package/dist/runtime-cordis-host.d.mts +1 -1
- package/dist/runtime-cordis-host.d.mts.map +1 -1
- package/dist/runtime-cordis-host.mjs.map +1 -1
- package/dist/runtime-pi-extension.cjs +6 -2
- package/dist/runtime-pi-extension.cjs.map +1 -1
- package/dist/runtime-pi-extension.d.cts.map +1 -1
- package/dist/runtime-pi-extension.d.mts.map +1 -1
- package/dist/runtime-pi-extension.mjs +6 -2
- package/dist/runtime-pi-extension.mjs.map +1 -1
- package/dist/schemas/builtinApiContracts.cjs +1 -1
- package/dist/schemas/builtinApiContracts.cjs.map +1 -1
- package/dist/schemas/builtinApiContracts.mjs +1 -1
- package/dist/schemas/builtinApiContracts.mjs.map +1 -1
- package/dist/schemas/headless.cjs.map +1 -1
- package/dist/schemas/headless.d.cts +15 -0
- package/dist/schemas/headless.d.cts.map +1 -1
- package/dist/schemas/headless.d.mts +15 -0
- package/dist/schemas/headless.d.mts.map +1 -1
- package/dist/schemas/headless.mjs.map +1 -1
- package/dist/schemas/httpApiContracts.cjs +6 -0
- package/dist/schemas/httpApiContracts.cjs.map +1 -1
- package/dist/schemas/httpApiContracts.mjs +6 -0
- package/dist/schemas/httpApiContracts.mjs.map +1 -1
- package/dist/schemas/hubChannel.d.cts +21 -0
- package/dist/schemas/hubChannel.d.cts.map +1 -1
- package/dist/schemas/hubChannel.d.mts +21 -0
- package/dist/schemas/hubChannel.d.mts.map +1 -1
- package/dist/schemas/mcpFacet.cjs.map +1 -1
- package/dist/schemas/mcpFacet.d.cts +27 -0
- package/dist/schemas/mcpFacet.d.cts.map +1 -1
- package/dist/schemas/mcpFacet.d.mts +27 -0
- package/dist/schemas/mcpFacet.d.mts.map +1 -1
- package/dist/schemas/mcpFacet.mjs.map +1 -1
- package/dist/server/headlessProtocol.cjs +7 -1
- package/dist/server/headlessProtocol.cjs.map +1 -1
- package/dist/server/headlessProtocol.mjs +7 -1
- package/dist/server/headlessProtocol.mjs.map +1 -1
- package/dist/server/sessionMcpHandler.cjs +113 -21
- package/dist/server/sessionMcpHandler.cjs.map +1 -1
- package/dist/server/sessionMcpHandler.d.cts +6 -1
- package/dist/server/sessionMcpHandler.d.cts.map +1 -1
- package/dist/server/sessionMcpHandler.d.mts +6 -1
- package/dist/server/sessionMcpHandler.d.mts.map +1 -1
- package/dist/server/sessionMcpHandler.mjs +113 -21
- package/dist/server/sessionMcpHandler.mjs.map +1 -1
- package/dist/server/sessionMcpRoutes.cjs +224 -15
- package/dist/server/sessionMcpRoutes.cjs.map +1 -1
- package/dist/server/sessionMcpRoutes.mjs +213 -7
- package/dist/server/sessionMcpRoutes.mjs.map +1 -1
- package/dist/services/historyCreationFileSystem/index.cjs +3 -2
- package/dist/services/historyCreationFileSystem/index.cjs.map +1 -1
- package/dist/services/historyCreationFileSystem/index.mjs +3 -2
- package/dist/services/historyCreationFileSystem/index.mjs.map +1 -1
- package/dist/services/openSessionRegistry/index.cjs +13 -6
- package/dist/services/openSessionRegistry/index.cjs.map +1 -1
- package/dist/services/openSessionRegistry/index.d.cts +2 -1
- package/dist/services/openSessionRegistry/index.d.cts.map +1 -1
- package/dist/services/openSessionRegistry/index.d.mts +2 -1
- package/dist/services/openSessionRegistry/index.d.mts.map +1 -1
- package/dist/services/openSessionRegistry/index.mjs +13 -6
- package/dist/services/openSessionRegistry/index.mjs.map +1 -1
- package/dist/services/piExtensionHost/index.cjs +20 -8
- package/dist/services/piExtensionHost/index.cjs.map +1 -1
- package/dist/services/piExtensionHost/index.mjs +20 -8
- package/dist/services/piExtensionHost/index.mjs.map +1 -1
- package/dist/services/pluginLifecycle/index.cjs +1 -1
- package/dist/services/pluginLifecycle/index.cjs.map +1 -1
- package/dist/services/pluginLifecycle/index.mjs +1 -1
- package/dist/services/pluginLifecycle/index.mjs.map +1 -1
- package/dist/services/sessionMcpAuthorization/index.cjs +56 -0
- package/dist/services/sessionMcpAuthorization/index.cjs.map +1 -1
- package/dist/services/sessionMcpAuthorization/index.d.cts +12 -0
- package/dist/services/sessionMcpAuthorization/index.d.cts.map +1 -1
- package/dist/services/sessionMcpAuthorization/index.d.mts +12 -0
- package/dist/services/sessionMcpAuthorization/index.d.mts.map +1 -1
- package/dist/services/sessionMcpAuthorization/index.mjs +56 -1
- package/dist/services/sessionMcpAuthorization/index.mjs.map +1 -1
- package/dist/services/sessionMcpConversations/index.cjs +170 -0
- package/dist/services/sessionMcpConversations/index.cjs.map +1 -0
- package/dist/services/sessionMcpConversations/index.mjs +165 -0
- package/dist/services/sessionMcpConversations/index.mjs.map +1 -0
- package/dist/services/sessionMcpRegistrationStore/index.cjs +85 -0
- package/dist/services/sessionMcpRegistrationStore/index.cjs.map +1 -0
- package/dist/services/sessionMcpRegistrationStore/index.mjs +82 -0
- package/dist/services/sessionMcpRegistrationStore/index.mjs.map +1 -0
- package/dist/systems/main/adapters/headlessSessionHost.cjs +82 -12
- package/dist/systems/main/adapters/headlessSessionHost.cjs.map +1 -1
- package/dist/systems/main/adapters/headlessSessionHost.d.cts.map +1 -1
- package/dist/systems/main/adapters/headlessSessionHost.d.mts.map +1 -1
- package/dist/systems/main/adapters/headlessSessionHost.mjs +81 -12
- package/dist/systems/main/adapters/headlessSessionHost.mjs.map +1 -1
- package/dist/testing/pi/testHost.cjs +6 -0
- package/dist/testing/pi/testHost.cjs.map +1 -1
- package/dist/testing/pi/testHost.mjs +6 -0
- package/dist/testing/pi/testHost.mjs.map +1 -1
- package/dist/types/server/sessionToolSurface.d.cts +5 -2
- package/dist/types/server/sessionToolSurface.d.cts.map +1 -1
- package/dist/types/server/sessionToolSurface.d.mts +5 -2
- package/dist/types/server/sessionToolSurface.d.mts.map +1 -1
- package/dist/web/types/webPlugin.d.cts +8 -2
- package/dist/web/types/webPlugin.d.cts.map +1 -1
- package/dist/web/types/webPlugin.d.mts +8 -2
- package/dist/web/types/webPlugin.d.mts.map +1 -1
- package/package.json +14 -14
package/dist/headless-hub.cjs
CHANGED
|
@@ -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
|
-
|
|
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) =>
|
|
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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"}
|
package/dist/headless-hub.d.cts
CHANGED
|
@@ -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":";;;;;;;;;;;
|
|
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"}
|
package/dist/headless-hub.d.mts
CHANGED
|
@@ -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":";;;;;;;;;;;
|
|
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"}
|
package/dist/headless-hub.mjs
CHANGED
|
@@ -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
|
-
|
|
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) =>
|
|
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);
|