@agimon-ai/po-session 0.0.0-alpha.2 → 0.0.0-alpha.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,55 +2,58 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_sessionDelivery = require("../sessionDelivery-CkuX6jlw.cjs");
6
- let node_fs = require("node:fs");
7
- node_fs = require_sessionDelivery.__toESM(node_fs, 1);
5
+ const require_sessionDelivery = require("../sessionDelivery-CkKlRYAK.cjs");
8
6
  let node_path = require("node:path");
9
7
  node_path = require_sessionDelivery.__toESM(node_path, 1);
8
+ let _agimon_ai_po_core_platform = require("@agimon-ai/po-core/platform");
10
9
  let _agimon_ai_po_core_piSettings = require("@agimon-ai/po-core/piSettings");
11
10
  let _agimon_ai_po_core_serverFacet = require("@agimon-ai/po-core/serverFacet");
12
11
  let _agimon_ai_po_core_extensionFile = require("@agimon-ai/po-core/extensionFile");
13
12
  //#region src/services/peerInbox/index.ts
14
13
  const inboxes = /* @__PURE__ */ new Map();
15
- /** Registers one live session-owned peer inbox. This registry is reachable only by the Session route. */
14
+ /**
15
+ * Registers one live session-owned peer inbox. This registry is reachable only by the Session route.
16
+ * The newest registration wins: a Durable Object evicted without disposal leaves its entry in the
17
+ * isolate's module state, and the restarted session must replace it. The hub still rejects two live
18
+ * sessions with one id, and an older unregister cannot remove a newer inbox.
19
+ */
16
20
  function registerSessionPeerInbox(sessionId, inbox) {
17
- if (inboxes.has(sessionId)) throw new Error(`Session peer inbox '${sessionId}' is already registered.`);
18
21
  inboxes.set(sessionId, inbox);
19
22
  return () => {
20
23
  if (inboxes.get(sessionId) === inbox) inboxes.delete(sessionId);
21
24
  };
22
25
  }
23
- function deliverSessionPeerEnvelope(targetSessionId, sourceKey, type, payload) {
26
+ async function deliverSessionPeerEnvelope(targetSessionId, sourceKey, type, payload) {
24
27
  return inboxes.get(targetSessionId)?.(sourceKey, type, payload);
25
28
  }
26
29
  //#endregion
27
30
  //#region src/extensions/workspaces/sessions/(backend)/root.server.ts
28
31
  var root_server_default = (0, _agimon_ai_po_core_extensionFile.defineRoot)((pluginContext) => ({
29
32
  value: void 0,
30
- services: [require_sessionDelivery.provideBackgroundWorkService, (context) => {
33
+ services: [require_sessionDelivery.provideBackgroundWorkService, async (context) => {
31
34
  const agent = pluginContext.agent;
32
35
  const session = agent?.context.session;
33
36
  const communication = pluginContext.host.context.sessionCommunication;
34
37
  const homeDirectory = pluginContext.host.context.homeDirectory;
35
38
  const sessionService = pluginContext.host.context.sessionService;
36
39
  if (!agent || !session?.admitPrompt || !communication || !homeDirectory || !sessionService?.canCommunicate) throw new Error("Session delivery requires the headless agent, authenticated session communication, host home directory, and communication policy.");
40
+ const fs = (0, _agimon_ai_po_core_platform.requireDoomFs)(pluginContext.context);
41
+ const sqlite = (0, _agimon_ai_po_core_platform.requireDoomSqlite)(pluginContext.context);
42
+ const io = _agimon_ai_po_core_platform.BACKGROUND_CONTEXT;
37
43
  const sessionDirectory = node_path.default.join((0, _agimon_ai_po_core_piSettings.poHomeDirectory)(homeDirectory), "session");
38
44
  const deliveryDirectory = node_path.default.join(sessionDirectory, "delivery");
39
- node_fs.default.mkdirSync(deliveryDirectory, {
40
- recursive: true,
41
- mode: 448
42
- });
45
+ await require_sessionDelivery.ensurePrivateDirectory(fs, deliveryDirectory, io);
46
+ const peerConfig = await require_sessionDelivery.readSessionPeerConfigAsync(fs, homeDirectory, io);
43
47
  const peerCommunication = require_sessionDelivery.createPeerCommunication({
44
48
  local: communication,
45
- homeDirectory
49
+ config: peerConfig
46
50
  });
47
- const peerConfig = require_sessionDelivery.readSessionPeerConfig(homeDirectory);
48
51
  const remotePeerIsGranted = (peerKey) => {
49
52
  const peer = require_sessionDelivery.parseRemoteSessionReference(peerKey);
50
53
  return peer !== void 0 && peerConfig?.peers.some((candidate) => candidate.hostId === peer.hostId && candidate.allowedSessionIds.includes(agent.context.sessionId)) === true;
51
54
  };
52
55
  const fallbackAuthorization = (peerKey) => sessionService.canCommunicate?.(agent.context.sessionId, peerKey) === true || remotePeerIsGranted(peerKey);
53
- const groups = peerConfig ? require_sessionDelivery.createSessionGroupStore({ databasePath: node_path.default.join(sessionDirectory, "groups.sqlite") }) : void 0;
56
+ const groups = peerConfig ? await require_sessionDelivery.createSessionGroupStore({ database: await sqlite.open(node_path.default.join(sessionDirectory, "groups.sqlite"), io) }) : void 0;
54
57
  const localHostId = peerConfig?.hostId;
55
58
  const selfReference = localHostId ? require_sessionDelivery.peerSessionReference(localHostId, agent.context.sessionId) : void 0;
56
59
  const groupAuthorization = groups && selfReference && localHostId ? require_sessionDelivery.createSessionGroupDeliveryAuthorizer({
@@ -59,18 +62,25 @@ var root_server_default = (0, _agimon_ai_po_core_extensionFile.defineRoot)((plug
59
62
  peerReference: (peerKey) => require_sessionDelivery.parseRemoteSessionReference(peerKey) === void 0 ? require_sessionDelivery.peerSessionReference(localHostId, peerKey) : peerKey,
60
63
  fallback: fallbackAuthorization
61
64
  }) : void 0;
62
- const { service, close: closeDelivery } = require_sessionDelivery.provideSessionDeliveryService(context, {
63
- databasePath: node_path.default.join(deliveryDirectory, `${agent.context.sessionId}.sqlite`),
64
- recipientKey: agent.context.sessionId,
65
- communication: peerCommunication,
66
- authorizePeer: (peerKey, metadata) => groupAuthorization?.(peerKey, metadata) ?? fallbackAuthorization(peerKey),
67
- admitPrompt: (prompt, delivery) => session.admitPrompt(prompt, delivery)
68
- });
65
+ let delivery;
66
+ try {
67
+ delivery = await require_sessionDelivery.provideSessionDeliveryService(context, {
68
+ database: await sqlite.open(node_path.default.join(deliveryDirectory, `${agent.context.sessionId}.sqlite`), io),
69
+ recipientKey: agent.context.sessionId,
70
+ communication: peerCommunication,
71
+ authorizePeer: (peerKey, metadata) => groupAuthorization ? groupAuthorization(peerKey, metadata) : fallbackAuthorization(peerKey),
72
+ admitPrompt: (prompt, admission) => session.admitPrompt(prompt, admission)
73
+ });
74
+ } catch (error) {
75
+ await groups?.close();
76
+ throw error;
77
+ }
78
+ const { service, close: closeDelivery } = delivery;
69
79
  const unprovide = [];
70
80
  if (groups && selfReference && peerConfig) {
71
- const directory = require_sessionDelivery.createSessionDirectory({ authorizeDiscovery: (caller, subject) => {
81
+ const directory = require_sessionDelivery.createSessionDirectory({ authorizeDiscovery: async (caller, subject) => {
72
82
  const reference = require_sessionDelivery.peerSessionReference(subject.hostId, subject.sessionId);
73
- return groups.groupsFor(caller).some((group) => group.members.includes(reference)) ? { capabilities: ["message"] } : void 0;
83
+ return (await groups.groupsFor(caller)).some((group) => group.members.includes(reference)) ? { capabilities: ["message"] } : void 0;
74
84
  } });
75
85
  for (const peer of peerConfig.peers) for (const sessionId of peer.allowedSessionIds) directory.observe({
76
86
  hostId: peer.hostId,
@@ -101,7 +111,7 @@ var root_server_default = (0, _agimon_ai_po_core_extensionFile.defineRoot)((plug
101
111
  unregister();
102
112
  for (const release of unprovide.reverse()) release();
103
113
  await closeDelivery();
104
- groups?.close();
114
+ await groups?.close();
105
115
  };
106
116
  }]
107
117
  }));
@@ -112,43 +122,69 @@ const DELIVERY_TYPES = /* @__PURE__ */ new Set(["doom/session-delivery/envelope"
112
122
  function response(status, error) {
113
123
  return Response.json({ error }, { status });
114
124
  }
125
+ /** Narrow HMAC-authenticated endpoint that a trusted tunnel may forward without device authentication. */
126
+ function createPeerInboxApi(platform) {
127
+ return {
128
+ basePath: "session-peer",
129
+ start(context) {
130
+ const homeDirectory = context.homeDirectory;
131
+ let opened;
132
+ const open = () => opened ??= (async () => {
133
+ if (homeDirectory === void 0) return {
134
+ config: void 0,
135
+ replay: void 0
136
+ };
137
+ const config = await require_sessionDelivery.readSessionPeerConfigAsync(platform.fs, homeDirectory, _agimon_ai_po_core_platform.BACKGROUND_CONTEXT);
138
+ const file = node_path.default.join(node_path.default.dirname(require_sessionDelivery.sessionPeerConfigPath(homeDirectory)), require_sessionDelivery.PEER_REPLAY_DATABASE_FILE);
139
+ await require_sessionDelivery.ensurePrivateDirectory(platform.fs, node_path.default.dirname(file), _agimon_ai_po_core_platform.BACKGROUND_CONTEXT);
140
+ return {
141
+ config,
142
+ replay: await require_sessionDelivery.createPeerReplayGuardAsync(await platform.sqlite.open(file, _agimon_ai_po_core_platform.BACKGROUND_CONTEXT))
143
+ };
144
+ })().catch((error) => {
145
+ opened = void 0;
146
+ throw error;
147
+ });
148
+ return {
149
+ async fetch(request) {
150
+ if (request.method !== "POST" || new URL(request.url).pathname !== "/inbox") return response(404, "Not found.");
151
+ const { config, replay } = await open();
152
+ if (!config) return response(503, "Paired Session delivery is not configured.");
153
+ const length = request.headers.get("content-length");
154
+ if (length !== null && (!/^\d+$/u.test(length) || Number(length) > MAX_BODY_BYTES)) return response(413, "Request body is too large.");
155
+ const body = await request.text();
156
+ if (Buffer.byteLength(body) > MAX_BODY_BYTES) return response(413, "Request body is too large.");
157
+ const peer = require_sessionDelivery.verifyPeerRequest(config, request, body);
158
+ if (!peer) return response(401, "Paired Session request is unauthorized.");
159
+ if (!await replay?.admit(request)) return response(409, "Paired Session request was already received.");
160
+ let parsed;
161
+ try {
162
+ parsed = JSON.parse(body);
163
+ } catch {
164
+ return response(400, "Paired Session request is malformed.");
165
+ }
166
+ const envelope = require_sessionDelivery.parseSessionPeerEnvelope(parsed);
167
+ if (!envelope || !DELIVERY_TYPES.has(envelope.type)) return response(400, "Paired Session envelope is invalid.");
168
+ if (!peer.allowedSessionIds.includes(envelope.targetSessionId)) return response(403, "Peer is not granted this session.");
169
+ const state = await deliverSessionPeerEnvelope(envelope.targetSessionId, require_sessionDelivery.peerSessionReference(peer.hostId, envelope.sourceSessionId), envelope.type, envelope.payload);
170
+ return state === void 0 ? response(404, "Target Session is unavailable.") : Response.json({
171
+ accepted: true,
172
+ state
173
+ }, { status: 202 });
174
+ },
175
+ async close() {
176
+ await (await opened?.catch(() => void 0))?.replay?.close();
177
+ }
178
+ };
179
+ }
180
+ };
181
+ }
115
182
  //#endregion
116
183
  //#region src/extensions/(backend)/api/session-peer/route.server.ts
117
- var route_server_default = (0, _agimon_ai_po_core_extensionFile.defineRoute)({
118
- basePath: "session-peer",
119
- start(context) {
120
- const config = context.homeDirectory === void 0 ? void 0 : require_sessionDelivery.readSessionPeerConfig(context.homeDirectory);
121
- const replay = context.homeDirectory === void 0 ? void 0 : require_sessionDelivery.createPeerReplayGuard(context.homeDirectory);
122
- return {
123
- async fetch(request) {
124
- if (request.method !== "POST" || new URL(request.url).pathname !== "/inbox") return response(404, "Not found.");
125
- if (!config) return response(503, "Paired Session delivery is not configured.");
126
- const length = request.headers.get("content-length");
127
- if (length !== null && (!/^\d+$/u.test(length) || Number(length) > MAX_BODY_BYTES)) return response(413, "Request body is too large.");
128
- const body = await request.text();
129
- if (Buffer.byteLength(body) > MAX_BODY_BYTES) return response(413, "Request body is too large.");
130
- const peer = require_sessionDelivery.verifyPeerRequest(config, request, body);
131
- if (!peer) return response(401, "Paired Session request is unauthorized.");
132
- if (!replay?.admit(request)) return response(409, "Paired Session request was already received.");
133
- let parsed;
134
- try {
135
- parsed = JSON.parse(body);
136
- } catch {
137
- return response(400, "Paired Session request is malformed.");
138
- }
139
- const envelope = require_sessionDelivery.parseSessionPeerEnvelope(parsed);
140
- if (!envelope || !DELIVERY_TYPES.has(envelope.type)) return response(400, "Paired Session envelope is invalid.");
141
- if (!peer.allowedSessionIds.includes(envelope.targetSessionId)) return response(403, "Peer is not granted this session.");
142
- const state = deliverSessionPeerEnvelope(envelope.targetSessionId, require_sessionDelivery.peerSessionReference(peer.hostId, envelope.sourceSessionId), envelope.type, envelope.payload);
143
- return state === void 0 ? response(404, "Target Session is unavailable.") : Response.json({
144
- accepted: true,
145
- state
146
- }, { status: 202 });
147
- },
148
- close: () => replay?.close()
149
- };
150
- }
151
- });
184
+ var route_server_default = (0, _agimon_ai_po_core_extensionFile.defineRoute)(({ context }) => createPeerInboxApi({
185
+ fs: (0, _agimon_ai_po_core_platform.requireDoomFs)(context),
186
+ sqlite: (0, _agimon_ai_po_core_platform.requireDoomSqlite)(context)
187
+ }));
152
188
  //#endregion
153
189
  //#region generated/server.ts
154
190
  const at = (value, context) => typeof value === "function" ? value(context) : value;
@@ -1 +1 @@
1
- {"version":3,"file":"server.cjs","names":["defineRoot","provideBackgroundWorkService","path","poHomeDirectory","createPeerCommunication","readSessionPeerConfig","parseRemoteSessionReference","createSessionGroupStore","peerSessionReference","createSessionGroupDeliveryAuthorizer","provideSessionDeliveryService","createSessionDirectory","DOOM_SESSION_INTERCOM_SERVICE","createSessionIntercom","readSessionPeerConfig","createPeerReplayGuard","verifyPeerRequest","parseSessionPeerEnvelope","peerSessionReference","defineRoute","defineServerPlugin","composeServerProviderOverrides","rootSession","apiRoute","composeRootHooks"],"sources":["../../src/services/peerInbox/index.ts","../../src/extensions/workspaces/sessions/(backend)/root.server.ts","../../src/services/peerInboxApi/index.ts","../../src/extensions/(backend)/api/session-peer/route.server.ts","../../generated/server.ts"],"sourcesContent":["import type { DoomSessionDeliveryInboxState } from '../sessionDelivery';\n\nexport type SessionPeerInbox = (\n sourceKey: string,\n type: string,\n payload: unknown,\n) => DoomSessionDeliveryInboxState | undefined;\n\nconst inboxes = new Map<string, SessionPeerInbox>();\n\n/** Registers one live session-owned peer inbox. This registry is reachable only by the Session route. */\nexport function registerSessionPeerInbox(sessionId: string, inbox: SessionPeerInbox): () => void {\n if (inboxes.has(sessionId)) throw new Error(`Session peer inbox '${sessionId}' is already registered.`);\n inboxes.set(sessionId, inbox);\n return () => {\n if (inboxes.get(sessionId) === inbox) inboxes.delete(sessionId);\n };\n}\n\nexport function deliverSessionPeerEnvelope(\n targetSessionId: string,\n sourceKey: string,\n type: string,\n payload: unknown,\n): DoomSessionDeliveryInboxState | undefined {\n return inboxes.get(targetSessionId)?.(sourceKey, type, payload);\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nimport { defineRoot } from '@agimon-ai/po-core/extensionFile';\nimport { poHomeDirectory } from '@agimon-ai/po-core/piSettings';\nimport type { DoomServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport { provideBackgroundWorkService } from '../../../../services/backgroundWorkService';\nimport { registerSessionPeerInbox } from '../../../../services/peerInbox';\nimport {\n createPeerCommunication,\n parseRemoteSessionReference,\n peerSessionReference,\n readSessionPeerConfig,\n} from '../../../../services/peerTransport';\nimport { provideSessionDeliveryService } from '../../../../services/sessionDelivery';\nimport { createSessionDirectory } from '../../../../services/sessionDirectory';\nimport { createSessionGroupStore } from '../../../../services/sessionGroups';\nimport {\n DOOM_SESSION_INTERCOM_SERVICE,\n createSessionGroupDeliveryAuthorizer,\n createSessionIntercom,\n} from '../../../../services/sessionIntercom';\n\nexport default defineRoot((pluginContext: DoomServerPluginContext) => ({\n value: undefined,\n services: [\n provideBackgroundWorkService,\n (context) => {\n const agent = pluginContext.agent;\n const session = agent?.context.session;\n const communication = pluginContext.host.context.sessionCommunication;\n const homeDirectory = pluginContext.host.context.homeDirectory;\n const sessionService = pluginContext.host.context.sessionService;\n if (!agent || !session?.admitPrompt || !communication || !homeDirectory || !sessionService?.canCommunicate) {\n throw new Error(\n 'Session delivery requires the headless agent, authenticated session communication, host home directory, and communication policy.',\n );\n }\n const sessionDirectory = path.join(poHomeDirectory(homeDirectory), 'session');\n const deliveryDirectory = path.join(sessionDirectory, 'delivery');\n fs.mkdirSync(deliveryDirectory, { recursive: true, mode: 0o700 });\n const peerCommunication = createPeerCommunication({ local: communication, homeDirectory });\n const peerConfig = readSessionPeerConfig(homeDirectory);\n const remotePeerIsGranted = (peerKey: string): boolean => {\n const peer = parseRemoteSessionReference(peerKey);\n return (\n peer !== undefined &&\n peerConfig?.peers.some(\n (candidate) =>\n candidate.hostId === peer.hostId && candidate.allowedSessionIds.includes(agent.context.sessionId),\n ) === true\n );\n };\n const fallbackAuthorization = (peerKey: string): boolean =>\n sessionService.canCommunicate?.(agent.context.sessionId, peerKey) === true || remotePeerIsGranted(peerKey);\n const groups = peerConfig\n ? createSessionGroupStore({ databasePath: path.join(sessionDirectory, 'groups.sqlite') })\n : undefined;\n const localHostId = peerConfig?.hostId;\n const selfReference = localHostId ? peerSessionReference(localHostId, agent.context.sessionId) : undefined;\n const groupAuthorization =\n groups && selfReference && localHostId\n ? createSessionGroupDeliveryAuthorizer({\n selfReference,\n groups,\n peerReference: (peerKey) =>\n parseRemoteSessionReference(peerKey) === undefined\n ? peerSessionReference(localHostId, peerKey)\n : peerKey,\n fallback: fallbackAuthorization,\n })\n : undefined;\n const { service, close: closeDelivery } = provideSessionDeliveryService(context, {\n databasePath: path.join(deliveryDirectory, `${agent.context.sessionId}.sqlite`),\n recipientKey: agent.context.sessionId,\n communication: peerCommunication,\n authorizePeer: (peerKey, metadata) => groupAuthorization?.(peerKey, metadata) ?? fallbackAuthorization(peerKey),\n admitPrompt: (prompt, delivery) => session.admitPrompt!(prompt, delivery),\n });\n const unprovide: (() => void)[] = [];\n if (groups && selfReference && peerConfig) {\n const directory = createSessionDirectory({\n authorizeDiscovery: (caller, subject) => {\n const reference = peerSessionReference(subject.hostId, subject.sessionId);\n const permitted = groups.groupsFor(caller).some((group) => group.members.includes(reference));\n return permitted ? { capabilities: ['message'] } : undefined;\n },\n });\n for (const peer of peerConfig.peers)\n for (const sessionId of peer.allowedSessionIds)\n directory.observe({\n hostId: peer.hostId,\n sessionId,\n hostIncarnation: 'unknown',\n sessionIncarnation: 'unknown',\n sequence: 0,\n observedAt: 0,\n staleAfterMs: 0,\n deliveryTarget: peerSessionReference(peer.hostId, sessionId),\n reachability: 'unknown',\n runtime: 'unknown',\n activity: 'unknown',\n voice: { eligible: 'unknown', readiness: 'unknown' },\n });\n unprovide.push(\n context.provide(\n DOOM_SESSION_INTERCOM_SERVICE,\n createSessionIntercom({ selfReference, directory, groups, delivery: service }),\n ),\n );\n }\n const unregister = registerSessionPeerInbox(agent.context.sessionId, (sourceKey, kind, payload) =>\n service.receive?.(sourceKey, kind, payload),\n );\n return async () => {\n unregister();\n for (const release of unprovide.reverse()) release();\n await closeDelivery();\n groups?.close();\n };\n },\n ],\n}));\n","import type { DoomApi, DoomApiHandler } from '@agimon-ai/po-core/packageApi';\n\nimport { deliverSessionPeerEnvelope } from '../peerInbox';\nimport {\n createPeerReplayGuard,\n parseSessionPeerEnvelope,\n peerSessionReference,\n readSessionPeerConfig,\n verifyPeerRequest,\n} from '../peerTransport';\n\nconst MAX_BODY_BYTES = 64 * 1024;\nconst DELIVERY_TYPES = new Set(['doom/session-delivery/envelope', 'doom/session-delivery/ack']);\n\nfunction response(status: number, error: string): Response {\n return Response.json({ error }, { status });\n}\n\n/** Narrow HMAC-authenticated endpoint that a trusted tunnel may forward without device authentication. */\nexport const peerInboxApi: DoomApi = {\n basePath: 'session-peer',\n start(context): DoomApiHandler {\n const config = context.homeDirectory === undefined ? undefined : readSessionPeerConfig(context.homeDirectory);\n const replay = context.homeDirectory === undefined ? undefined : createPeerReplayGuard(context.homeDirectory);\n return {\n async fetch(request) {\n if (request.method !== 'POST' || new URL(request.url).pathname !== '/inbox') return response(404, 'Not found.');\n if (!config) return response(503, 'Paired Session delivery is not configured.');\n const length = request.headers.get('content-length');\n if (length !== null && (!/^\\d+$/u.test(length) || Number(length) > MAX_BODY_BYTES))\n return response(413, 'Request body is too large.');\n const body = await request.text();\n if (Buffer.byteLength(body) > MAX_BODY_BYTES) return response(413, 'Request body is too large.');\n const peer = verifyPeerRequest(config, request, body);\n if (!peer) return response(401, 'Paired Session request is unauthorized.');\n if (!replay?.admit(request)) return response(409, 'Paired Session request was already received.');\n let parsed: unknown;\n try {\n parsed = JSON.parse(body) as unknown;\n } catch {\n return response(400, 'Paired Session request is malformed.');\n }\n const envelope = parseSessionPeerEnvelope(parsed);\n if (!envelope || !DELIVERY_TYPES.has(envelope.type))\n return response(400, 'Paired Session envelope is invalid.');\n if (!peer.allowedSessionIds.includes(envelope.targetSessionId))\n return response(403, 'Peer is not granted this session.');\n const state = deliverSessionPeerEnvelope(\n envelope.targetSessionId,\n peerSessionReference(peer.hostId, envelope.sourceSessionId),\n envelope.type,\n envelope.payload,\n );\n return state === undefined\n ? response(404, 'Target Session is unavailable.')\n : Response.json({ accepted: true, state }, { status: 202 });\n },\n close: () => replay?.close(),\n };\n },\n};\n","import { defineRoute } from '@agimon-ai/po-core/extensionFile';\n\nimport { peerInboxApi } from '../../../../services/peerInboxApi';\n\nexport default defineRoute(peerInboxApi);\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineServerPlugin, type DoomServerSessionPlugin } from '@agimon-ai/po-core/serverFacet';\nimport { composeServerProviderOverrides } from '@agimon-ai/po-core/serverFacet';\nimport { composeRootHooks } from '@agimon-ai/po-core/extensionFile';\n\nimport rootSession from '../src/extensions/workspaces/sessions/(backend)/root.server';\nimport apiRoute from '../src/extensions/(backend)/api/session-peer/route.server';\n\ntype Factory<T, C> = (context: C) => T;\nconst at = <T, C>(value: T | Factory<T, C>, context: C): T =>\n typeof value === 'function' ? (value as Factory<T, C>)(context) : value;\n\nexport const facet = defineServerPlugin({\n name: '@agimon-ai/po-session',\n providerOverrides: composeServerProviderOverrides(rootSession),\n global: (context) => ({\n get api(): DoomServerSessionPlugin['api'] { return [at(apiRoute, context)]; },\n }),\n workspace: (context) => ({\n get api(): DoomServerSessionPlugin['api'] { return [at(apiRoute, context)]; },\n }),\n session: async (context) => {\n const scopeSession = await rootSession(context);\n return {\n ...composeRootHooks(scopeSession),\n get api(): DoomServerSessionPlugin['api'] { return [at(apiRoute, context)]; },\n services: [...(scopeSession.services ?? [])],\n get activities(): DoomServerSessionPlugin['activities'] { return [...(scopeSession.activities ?? [])]; },\n };\n },\n});\n\nexport default facet;\n"],"mappings":"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"}
1
+ {"version":3,"file":"server.cjs","names":["defineRoot","provideBackgroundWorkService","requireDoomFs","requireDoomSqlite","BACKGROUND_CONTEXT","path","poHomeDirectory","ensurePrivateDirectory","readSessionPeerConfigAsync","createPeerCommunication","parseRemoteSessionReference","createSessionGroupStore","peerSessionReference","createSessionGroupDeliveryAuthorizer","provideSessionDeliveryService","createSessionDirectory","DOOM_SESSION_INTERCOM_SERVICE","createSessionIntercom","readSessionPeerConfigAsync","BACKGROUND_CONTEXT","path","sessionPeerConfigPath","PEER_REPLAY_DATABASE_FILE","ensurePrivateDirectory","createPeerReplayGuardAsync","verifyPeerRequest","parseSessionPeerEnvelope","peerSessionReference","defineRoute","requireDoomFs","requireDoomSqlite","defineServerPlugin","composeServerProviderOverrides","rootSession","apiRoute","composeRootHooks"],"sources":["../../src/services/peerInbox/index.ts","../../src/extensions/workspaces/sessions/(backend)/root.server.ts","../../src/services/peerInboxApi/index.ts","../../src/extensions/(backend)/api/session-peer/route.server.ts","../../generated/server.ts"],"sourcesContent":["import type { DoomSessionDeliveryInboxState } from '../sessionDelivery';\n\nexport type SessionPeerInbox = (\n sourceKey: string,\n type: string,\n payload: unknown,\n) => Promise<DoomSessionDeliveryInboxState | undefined> | undefined;\n\nconst inboxes = new Map<string, SessionPeerInbox>();\n\n/**\n * Registers one live session-owned peer inbox. This registry is reachable only by the Session route.\n * The newest registration wins: a Durable Object evicted without disposal leaves its entry in the\n * isolate's module state, and the restarted session must replace it. The hub still rejects two live\n * sessions with one id, and an older unregister cannot remove a newer inbox.\n */\nexport function registerSessionPeerInbox(sessionId: string, inbox: SessionPeerInbox): () => void {\n inboxes.set(sessionId, inbox);\n return () => {\n if (inboxes.get(sessionId) === inbox) inboxes.delete(sessionId);\n };\n}\n\nexport async function deliverSessionPeerEnvelope(\n targetSessionId: string,\n sourceKey: string,\n type: string,\n payload: unknown,\n): Promise<DoomSessionDeliveryInboxState | undefined> {\n return inboxes.get(targetSessionId)?.(sourceKey, type, payload);\n}\n","import path from 'node:path';\n\nimport { defineRoot } from '@agimon-ai/po-core/extensionFile';\nimport { poHomeDirectory } from '@agimon-ai/po-core/piSettings';\nimport { BACKGROUND_CONTEXT, requireDoomFs, requireDoomSqlite } from '@agimon-ai/po-core/platform';\nimport type { DoomServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport { provideBackgroundWorkService } from '../../../../services/backgroundWorkService';\nimport { registerSessionPeerInbox } from '../../../../services/peerInbox';\nimport {\n createPeerCommunication,\n ensurePrivateDirectory,\n parseRemoteSessionReference,\n peerSessionReference,\n readSessionPeerConfigAsync,\n} from '../../../../services/peerTransport';\nimport { provideSessionDeliveryService } from '../../../../services/sessionDelivery';\nimport { createSessionDirectory } from '../../../../services/sessionDirectory';\nimport { createSessionGroupStore } from '../../../../services/sessionGroups';\nimport {\n DOOM_SESSION_INTERCOM_SERVICE,\n createSessionGroupDeliveryAuthorizer,\n createSessionIntercom,\n} from '../../../../services/sessionIntercom';\n\nexport default defineRoot((pluginContext: DoomServerPluginContext) => ({\n value: undefined,\n services: [\n provideBackgroundWorkService,\n async (context) => {\n const agent = pluginContext.agent;\n const session = agent?.context.session;\n const communication = pluginContext.host.context.sessionCommunication;\n const homeDirectory = pluginContext.host.context.homeDirectory;\n const sessionService = pluginContext.host.context.sessionService;\n if (!agent || !session?.admitPrompt || !communication || !homeDirectory || !sessionService?.canCommunicate) {\n throw new Error(\n 'Session delivery requires the headless agent, authenticated session communication, host home directory, and communication policy.',\n );\n }\n const fs = requireDoomFs(pluginContext.context);\n const sqlite = requireDoomSqlite(pluginContext.context);\n const io = BACKGROUND_CONTEXT;\n const sessionDirectory = path.join(poHomeDirectory(homeDirectory), 'session');\n const deliveryDirectory = path.join(sessionDirectory, 'delivery');\n await ensurePrivateDirectory(fs, deliveryDirectory, io);\n const peerConfig = await readSessionPeerConfigAsync(fs, homeDirectory, io);\n const peerCommunication = createPeerCommunication({ local: communication, config: peerConfig });\n const remotePeerIsGranted = (peerKey: string): boolean => {\n const peer = parseRemoteSessionReference(peerKey);\n return (\n peer !== undefined &&\n peerConfig?.peers.some(\n (candidate) =>\n candidate.hostId === peer.hostId && candidate.allowedSessionIds.includes(agent.context.sessionId),\n ) === true\n );\n };\n const fallbackAuthorization = (peerKey: string): boolean =>\n sessionService.canCommunicate?.(agent.context.sessionId, peerKey) === true || remotePeerIsGranted(peerKey);\n const groups = peerConfig\n ? await createSessionGroupStore({\n database: await sqlite.open(path.join(sessionDirectory, 'groups.sqlite'), io),\n })\n : undefined;\n const localHostId = peerConfig?.hostId;\n const selfReference = localHostId ? peerSessionReference(localHostId, agent.context.sessionId) : undefined;\n const groupAuthorization =\n groups && selfReference && localHostId\n ? createSessionGroupDeliveryAuthorizer({\n selfReference,\n groups,\n peerReference: (peerKey) =>\n parseRemoteSessionReference(peerKey) === undefined\n ? peerSessionReference(localHostId, peerKey)\n : peerKey,\n fallback: fallbackAuthorization,\n })\n : undefined;\n let delivery: Awaited<ReturnType<typeof provideSessionDeliveryService>>;\n try {\n delivery = await provideSessionDeliveryService(context, {\n database: await sqlite.open(path.join(deliveryDirectory, `${agent.context.sessionId}.sqlite`), io),\n recipientKey: agent.context.sessionId,\n communication: peerCommunication,\n authorizePeer: (peerKey, metadata) =>\n groupAuthorization ? groupAuthorization(peerKey, metadata) : fallbackAuthorization(peerKey),\n admitPrompt: (prompt, admission) => session.admitPrompt!(prompt, admission),\n });\n } catch (error) {\n await groups?.close();\n throw error;\n }\n const { service, close: closeDelivery } = delivery;\n const unprovide: (() => void)[] = [];\n if (groups && selfReference && peerConfig) {\n const directory = createSessionDirectory({\n authorizeDiscovery: async (caller, subject) => {\n const reference = peerSessionReference(subject.hostId, subject.sessionId);\n const permitted = (await groups.groupsFor(caller)).some((group) => group.members.includes(reference));\n return permitted ? { capabilities: ['message'] } : undefined;\n },\n });\n for (const peer of peerConfig.peers)\n for (const sessionId of peer.allowedSessionIds)\n directory.observe({\n hostId: peer.hostId,\n sessionId,\n hostIncarnation: 'unknown',\n sessionIncarnation: 'unknown',\n sequence: 0,\n observedAt: 0,\n staleAfterMs: 0,\n deliveryTarget: peerSessionReference(peer.hostId, sessionId),\n reachability: 'unknown',\n runtime: 'unknown',\n activity: 'unknown',\n voice: { eligible: 'unknown', readiness: 'unknown' },\n });\n unprovide.push(\n context.provide(\n DOOM_SESSION_INTERCOM_SERVICE,\n createSessionIntercom({ selfReference, directory, groups, delivery: service }),\n ),\n );\n }\n const unregister = registerSessionPeerInbox(agent.context.sessionId, (sourceKey, kind, payload) =>\n service.receive?.(sourceKey, kind, payload),\n );\n return async () => {\n unregister();\n for (const release of unprovide.reverse()) release();\n await closeDelivery();\n await groups?.close();\n };\n },\n ],\n}));\n","import path from 'node:path';\n\nimport type { DoomApi, DoomApiHandler } from '@agimon-ai/po-core/packageApi';\nimport { BACKGROUND_CONTEXT, type DoomSqliteService, type FileSystem } from '@agimon-ai/po-core/platform';\n\nimport { deliverSessionPeerEnvelope } from '../peerInbox';\nimport {\n PEER_REPLAY_DATABASE_FILE,\n createPeerReplayGuardAsync,\n ensurePrivateDirectory,\n parseSessionPeerEnvelope,\n peerSessionReference,\n readSessionPeerConfigAsync,\n sessionPeerConfigPath,\n verifyPeerRequest,\n type SessionPeerConfig,\n type SessionPeerReplayGuardAsync,\n} from '../peerTransport';\n\nconst MAX_BODY_BYTES = 64 * 1024;\nconst DELIVERY_TYPES = new Set(['doom/session-delivery/envelope', 'doom/session-delivery/ack']);\n\nfunction response(status: number, error: string): Response {\n return Response.json({ error }, { status });\n}\n\ninterface PeerInboxState {\n readonly config: SessionPeerConfig | undefined;\n readonly replay: SessionPeerReplayGuardAsync | undefined;\n}\n\n/** Narrow HMAC-authenticated endpoint that a trusted tunnel may forward without device authentication. */\nexport function createPeerInboxApi(platform: { readonly fs: FileSystem; readonly sqlite: DoomSqliteService }): DoomApi {\n return {\n basePath: 'session-peer',\n start(context): DoomApiHandler {\n const homeDirectory = context.homeDirectory;\n // Opened once on first use: the handler starts synchronously, the platform services are async.\n let opened: Promise<PeerInboxState> | undefined;\n const open = (): Promise<PeerInboxState> =>\n (opened ??= (async () => {\n if (homeDirectory === undefined) return { config: undefined, replay: undefined };\n const config = await readSessionPeerConfigAsync(platform.fs, homeDirectory, BACKGROUND_CONTEXT);\n const file = path.join(path.dirname(sessionPeerConfigPath(homeDirectory)), PEER_REPLAY_DATABASE_FILE);\n await ensurePrivateDirectory(platform.fs, path.dirname(file), BACKGROUND_CONTEXT);\n const replay = await createPeerReplayGuardAsync(await platform.sqlite.open(file, BACKGROUND_CONTEXT));\n return { config, replay };\n })().catch((error: unknown) => {\n // A failed open is retried by the next request rather than cached.\n opened = undefined;\n throw error;\n }));\n return {\n async fetch(request) {\n if (request.method !== 'POST' || new URL(request.url).pathname !== '/inbox')\n return response(404, 'Not found.');\n const { config, replay } = await open();\n if (!config) return response(503, 'Paired Session delivery is not configured.');\n const length = request.headers.get('content-length');\n if (length !== null && (!/^\\d+$/u.test(length) || Number(length) > MAX_BODY_BYTES))\n return response(413, 'Request body is too large.');\n const body = await request.text();\n if (Buffer.byteLength(body) > MAX_BODY_BYTES) return response(413, 'Request body is too large.');\n const peer = verifyPeerRequest(config, request, body);\n if (!peer) return response(401, 'Paired Session request is unauthorized.');\n if (!(await replay?.admit(request))) return response(409, 'Paired Session request was already received.');\n let parsed: unknown;\n try {\n parsed = JSON.parse(body) as unknown;\n } catch {\n return response(400, 'Paired Session request is malformed.');\n }\n const envelope = parseSessionPeerEnvelope(parsed);\n if (!envelope || !DELIVERY_TYPES.has(envelope.type))\n return response(400, 'Paired Session envelope is invalid.');\n if (!peer.allowedSessionIds.includes(envelope.targetSessionId))\n return response(403, 'Peer is not granted this session.');\n const state = await deliverSessionPeerEnvelope(\n envelope.targetSessionId,\n peerSessionReference(peer.hostId, envelope.sourceSessionId),\n envelope.type,\n envelope.payload,\n );\n return state === undefined\n ? response(404, 'Target Session is unavailable.')\n : Response.json({ accepted: true, state }, { status: 202 });\n },\n async close() {\n const state = await opened?.catch(() => undefined);\n await state?.replay?.close();\n },\n };\n },\n };\n}\n","import { defineRoute } from '@agimon-ai/po-core/extensionFile';\nimport { requireDoomFs, requireDoomSqlite } from '@agimon-ai/po-core/platform';\nimport type { DoomServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport { createPeerInboxApi } from '../../../../services/peerInboxApi';\n\nexport default defineRoute(({ context }: DoomServerPluginContext) =>\n createPeerInboxApi({ fs: requireDoomFs(context), sqlite: requireDoomSqlite(context) }),\n);\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineServerPlugin, type DoomServerSessionPlugin } from '@agimon-ai/po-core/serverFacet';\nimport { composeServerProviderOverrides } from '@agimon-ai/po-core/serverFacet';\nimport { composeRootHooks } from '@agimon-ai/po-core/extensionFile';\n\nimport rootSession from '../src/extensions/workspaces/sessions/(backend)/root.server';\nimport apiRoute from '../src/extensions/(backend)/api/session-peer/route.server';\n\ntype Factory<T, C> = (context: C) => T;\nconst at = <T, C>(value: T | Factory<T, C>, context: C): T =>\n typeof value === 'function' ? (value as Factory<T, C>)(context) : value;\n\nexport const facet = defineServerPlugin({\n name: '@agimon-ai/po-session',\n providerOverrides: composeServerProviderOverrides(rootSession),\n global: (context) => ({\n get api(): DoomServerSessionPlugin['api'] { return [at(apiRoute, context)]; },\n }),\n workspace: (context) => ({\n get api(): DoomServerSessionPlugin['api'] { return [at(apiRoute, context)]; },\n }),\n session: async (context) => {\n const scopeSession = await rootSession(context);\n return {\n ...composeRootHooks(scopeSession),\n get api(): DoomServerSessionPlugin['api'] { return [at(apiRoute, context)]; },\n services: [...(scopeSession.services ?? [])],\n get activities(): DoomServerSessionPlugin['activities'] { return [...(scopeSession.activities ?? [])]; },\n };\n },\n});\n\nexport default facet;\n"],"mappings":";;;;;;;;;;;;AAQA,MAAM,0BAAU,IAAI,IAA8B;;;;;;;AAQlD,SAAgB,yBAAyB,WAAmB,OAAqC;CAC/F,QAAQ,IAAI,WAAW,KAAK;CAC5B,aAAa;EACX,IAAI,QAAQ,IAAI,SAAS,MAAM,OAAO,QAAQ,OAAO,SAAS;CAChE;AACF;AAEA,eAAsB,2BACpB,iBACA,WACA,MACA,SACoD;CACpD,OAAO,QAAQ,IAAI,eAAe,CAAC,GAAG,WAAW,MAAM,OAAO;AAChE;;;ACLA,IAAA,uBAAA,GAAeA,iCAAAA,WAAAA,EAAY,mBAA4C;CACrE,OAAO,KAAA;CACP,UAAU,CACRC,wBAAAA,8BACA,OAAO,YAAY;EACjB,MAAM,QAAQ,cAAc;EAC5B,MAAM,UAAU,OAAO,QAAQ;EAC/B,MAAM,gBAAgB,cAAc,KAAK,QAAQ;EACjD,MAAM,gBAAgB,cAAc,KAAK,QAAQ;EACjD,MAAM,iBAAiB,cAAc,KAAK,QAAQ;EAClD,IAAI,CAAC,SAAS,CAAC,SAAS,eAAe,CAAC,iBAAiB,CAAC,iBAAiB,CAAC,gBAAgB,gBAC1F,MAAM,IAAI,MACR,mIACF;EAEF,MAAM,MAAA,GAAKC,4BAAAA,cAAAA,CAAc,cAAc,OAAO;EAC9C,MAAM,UAAA,GAASC,4BAAAA,kBAAAA,CAAkB,cAAc,OAAO;EACtD,MAAM,KAAKC,4BAAAA;EACX,MAAM,mBAAmBC,UAAAA,QAAK,MAAA,GAAKC,8BAAAA,gBAAAA,CAAgB,aAAa,GAAG,SAAS;EAC5E,MAAM,oBAAoBD,UAAAA,QAAK,KAAK,kBAAkB,UAAU;EAChE,MAAME,wBAAAA,uBAAuB,IAAI,mBAAmB,EAAE;EACtD,MAAM,aAAa,MAAMC,wBAAAA,2BAA2B,IAAI,eAAe,EAAE;EACzE,MAAM,oBAAoBC,wBAAAA,wBAAwB;GAAE,OAAO;GAAe,QAAQ;EAAW,CAAC;EAC9F,MAAM,uBAAuB,YAA6B;GACxD,MAAM,OAAOC,wBAAAA,4BAA4B,OAAO;GAChD,OACE,SAAS,KAAA,KACT,YAAY,MAAM,MACf,cACC,UAAU,WAAW,KAAK,UAAU,UAAU,kBAAkB,SAAS,MAAM,QAAQ,SAAS,CACpG,MAAM;EAEV;EACA,MAAM,yBAAyB,YAC7B,eAAe,iBAAiB,MAAM,QAAQ,WAAW,OAAO,MAAM,QAAQ,oBAAoB,OAAO;EAC3G,MAAM,SAAS,aACX,MAAMC,wBAAAA,wBAAwB,EAC5B,UAAU,MAAM,OAAO,KAAKN,UAAAA,QAAK,KAAK,kBAAkB,eAAe,GAAG,EAAE,EAC9E,CAAC,IACD,KAAA;EACJ,MAAM,cAAc,YAAY;EAChC,MAAM,gBAAgB,cAAcO,wBAAAA,qBAAqB,aAAa,MAAM,QAAQ,SAAS,IAAI,KAAA;EACjG,MAAM,qBACJ,UAAU,iBAAiB,cACvBC,wBAAAA,qCAAqC;GACnC;GACA;GACA,gBAAgB,YACdH,wBAAAA,4BAA4B,OAAO,MAAM,KAAA,IACrCE,wBAAAA,qBAAqB,aAAa,OAAO,IACzC;GACN,UAAU;EACZ,CAAC,IACD,KAAA;EACN,IAAI;EACJ,IAAI;GACF,WAAW,MAAME,wBAAAA,8BAA8B,SAAS;IACtD,UAAU,MAAM,OAAO,KAAKT,UAAAA,QAAK,KAAK,mBAAmB,GAAG,MAAM,QAAQ,UAAU,QAAQ,GAAG,EAAE;IACjG,cAAc,MAAM,QAAQ;IAC5B,eAAe;IACf,gBAAgB,SAAS,aACvB,qBAAqB,mBAAmB,SAAS,QAAQ,IAAI,sBAAsB,OAAO;IAC5F,cAAc,QAAQ,cAAc,QAAQ,YAAa,QAAQ,SAAS;GAC5E,CAAC;EACH,SAAS,OAAO;GACd,MAAM,QAAQ,MAAM;GACpB,MAAM;EACR;EACA,MAAM,EAAE,SAAS,OAAO,kBAAkB;EAC1C,MAAM,YAA4B,CAAC;EACnC,IAAI,UAAU,iBAAiB,YAAY;GACzC,MAAM,YAAYU,wBAAAA,uBAAuB,EACvC,oBAAoB,OAAO,QAAQ,YAAY;IAC7C,MAAM,YAAYH,wBAAAA,qBAAqB,QAAQ,QAAQ,QAAQ,SAAS;IAExE,QADmB,MAAM,OAAO,UAAU,MAAM,EAAA,CAAG,MAAM,UAAU,MAAM,QAAQ,SAAS,SAAS,CACpF,IAAI,EAAE,cAAc,CAAC,SAAS,EAAE,IAAI,KAAA;GACrD,EACF,CAAC;GACD,KAAK,MAAM,QAAQ,WAAW,OAC5B,KAAK,MAAM,aAAa,KAAK,mBAC3B,UAAU,QAAQ;IAChB,QAAQ,KAAK;IACb;IACA,iBAAiB;IACjB,oBAAoB;IACpB,UAAU;IACV,YAAY;IACZ,cAAc;IACd,gBAAgBA,wBAAAA,qBAAqB,KAAK,QAAQ,SAAS;IAC3D,cAAc;IACd,SAAS;IACT,UAAU;IACV,OAAO;KAAE,UAAU;KAAW,WAAW;IAAU;GACrD,CAAC;GACL,UAAU,KACR,QAAQ,QACNI,wBAAAA,+BACAC,wBAAAA,sBAAsB;IAAE;IAAe;IAAW;IAAQ,UAAU;GAAQ,CAAC,CAC/E,CACF;EACF;EACA,MAAM,aAAa,yBAAyB,MAAM,QAAQ,YAAY,WAAW,MAAM,YACrF,QAAQ,UAAU,WAAW,MAAM,OAAO,CAC5C;EACA,OAAO,YAAY;GACjB,WAAW;GACX,KAAK,MAAM,WAAW,UAAU,QAAQ,GAAG,QAAQ;GACnD,MAAM,cAAc;GACpB,MAAM,QAAQ,MAAM;EACtB;CACF,CACF;AACF,EAAE;;;ACtHF,MAAM,iBAAiB;AACvB,MAAM,iCAAiB,IAAI,IAAI,CAAC,kCAAkC,2BAA2B,CAAC;AAE9F,SAAS,SAAS,QAAgB,OAAyB;CACzD,OAAO,SAAS,KAAK,EAAE,MAAM,GAAG,EAAE,OAAO,CAAC;AAC5C;;AAQA,SAAgB,mBAAmB,UAAoF;CACrH,OAAO;EACL,UAAU;EACV,MAAM,SAAyB;GAC7B,MAAM,gBAAgB,QAAQ;GAE9B,IAAI;GACJ,MAAM,aACH,YAAY,YAAY;IACvB,IAAI,kBAAkB,KAAA,GAAW,OAAO;KAAE,QAAQ,KAAA;KAAW,QAAQ,KAAA;IAAU;IAC/E,MAAM,SAAS,MAAMC,wBAAAA,2BAA2B,SAAS,IAAI,eAAeC,4BAAAA,kBAAkB;IAC9F,MAAM,OAAOC,UAAAA,QAAK,KAAKA,UAAAA,QAAK,QAAQC,wBAAAA,sBAAsB,aAAa,CAAC,GAAGC,wBAAAA,yBAAyB;IACpG,MAAMC,wBAAAA,uBAAuB,SAAS,IAAIH,UAAAA,QAAK,QAAQ,IAAI,GAAGD,4BAAAA,kBAAkB;IAEhF,OAAO;KAAE;KAAQ,QAAA,MADIK,wBAAAA,2BAA2B,MAAM,SAAS,OAAO,KAAK,MAAML,4BAAAA,kBAAkB,CAAC;IAC5E;GAC1B,EAAA,CAAG,CAAC,CAAC,OAAO,UAAmB;IAE7B,SAAS,KAAA;IACT,MAAM;GACR,CAAC;GACH,OAAO;IACL,MAAM,MAAM,SAAS;KACnB,IAAI,QAAQ,WAAW,UAAU,IAAI,IAAI,QAAQ,GAAG,CAAC,CAAC,aAAa,UACjE,OAAO,SAAS,KAAK,YAAY;KACnC,MAAM,EAAE,QAAQ,WAAW,MAAM,KAAK;KACtC,IAAI,CAAC,QAAQ,OAAO,SAAS,KAAK,4CAA4C;KAC9E,MAAM,SAAS,QAAQ,QAAQ,IAAI,gBAAgB;KACnD,IAAI,WAAW,SAAS,CAAC,SAAS,KAAK,MAAM,KAAK,OAAO,MAAM,IAAI,iBACjE,OAAO,SAAS,KAAK,4BAA4B;KACnD,MAAM,OAAO,MAAM,QAAQ,KAAK;KAChC,IAAI,OAAO,WAAW,IAAI,IAAI,gBAAgB,OAAO,SAAS,KAAK,4BAA4B;KAC/F,MAAM,OAAOM,wBAAAA,kBAAkB,QAAQ,SAAS,IAAI;KACpD,IAAI,CAAC,MAAM,OAAO,SAAS,KAAK,yCAAyC;KACzE,IAAI,CAAE,MAAM,QAAQ,MAAM,OAAO,GAAI,OAAO,SAAS,KAAK,8CAA8C;KACxG,IAAI;KACJ,IAAI;MACF,SAAS,KAAK,MAAM,IAAI;KAC1B,QAAQ;MACN,OAAO,SAAS,KAAK,sCAAsC;KAC7D;KACA,MAAM,WAAWC,wBAAAA,yBAAyB,MAAM;KAChD,IAAI,CAAC,YAAY,CAAC,eAAe,IAAI,SAAS,IAAI,GAChD,OAAO,SAAS,KAAK,qCAAqC;KAC5D,IAAI,CAAC,KAAK,kBAAkB,SAAS,SAAS,eAAe,GAC3D,OAAO,SAAS,KAAK,mCAAmC;KAC1D,MAAM,QAAQ,MAAM,2BAClB,SAAS,iBACTC,wBAAAA,qBAAqB,KAAK,QAAQ,SAAS,eAAe,GAC1D,SAAS,MACT,SAAS,OACX;KACA,OAAO,UAAU,KAAA,IACb,SAAS,KAAK,gCAAgC,IAC9C,SAAS,KAAK;MAAE,UAAU;MAAM;KAAM,GAAG,EAAE,QAAQ,IAAI,CAAC;IAC9D;IACA,MAAM,QAAQ;KAEZ,OAAM,MADc,QAAQ,YAAY,KAAA,CAAS,EAAA,EACpC,QAAQ,MAAM;IAC7B;GACF;EACF;CACF;AACF;;;ACxFA,IAAA,wBAAA,GAAeC,iCAAAA,YAAAA,EAAa,EAAE,cAC5B,mBAAmB;CAAE,KAAA,GAAIC,4BAAAA,cAAAA,CAAc,OAAO;CAAG,SAAA,GAAQC,4BAAAA,kBAAAA,CAAkB,OAAO;AAAE,CAAC,CACvF;;;ACCA,MAAM,MAAY,OAA0B,YAC1C,OAAO,UAAU,aAAc,MAAwB,OAAO,IAAI;AAEpE,MAAa,SAAA,GAAQC,+BAAAA,mBAAAA,CAAmB;CACtC,MAAM;CACN,oBAAA,GAAmBC,+BAAAA,+BAAAA,CAA+BC,mBAAW;CAC7D,SAAS,aAAa,EACpB,IAAI,MAAsC;EAAE,OAAO,CAAC,GAAGC,sBAAU,OAAO,CAAC;CAAG,EAC9E;CACA,YAAY,aAAa,EACvB,IAAI,MAAsC;EAAE,OAAO,CAAC,GAAGA,sBAAU,OAAO,CAAC;CAAG,EAC9E;CACA,SAAS,OAAO,YAAY;EAC1B,MAAM,eAAe,MAAMD,oBAAY,OAAO;EAC9C,OAAO;GACL,IAAA,GAAGE,iCAAAA,iBAAAA,CAAiB,YAAY;GAChC,IAAI,MAAsC;IAAE,OAAO,CAAC,GAAGD,sBAAU,OAAO,CAAC;GAAG;GAC5E,UAAU,CAAC,GAAI,aAAa,YAAY,CAAC,CAAE;GAC3C,IAAI,aAAoD;IAAE,OAAO,CAAC,GAAI,aAAa,cAAc,CAAC,CAAE;GAAG;EACzG;CACF;AACF,CAAC"}
@@ -1,50 +1,54 @@
1
- import { C as provideBackgroundWorkService, a as createSessionGroupDeliveryAuthorizer, f as createSessionDirectory, g as parseSessionPeerEnvelope, h as parseRemoteSessionReference, m as createPeerReplayGuard, n as provideSessionDeliveryService, o as createSessionIntercom, p as createPeerCommunication, s as DOOM_SESSION_INTERCOM_SERVICE, u as createSessionGroupStore, v as peerSessionReference, x as verifyPeerRequest, y as readSessionPeerConfig } from "../sessionDelivery-DpLWlaum.mjs";
2
- import fs from "node:fs";
1
+ import { D as provideBackgroundWorkService, T as sessionPeerConfigPath, _ as parseSessionPeerEnvelope, a as createSessionGroupDeliveryAuthorizer, b as readSessionPeerConfigAsync, f as createSessionDirectory, g as parseRemoteSessionReference, h as ensurePrivateDirectory, m as createPeerReplayGuardAsync, n as provideSessionDeliveryService, o as createSessionIntercom, p as createPeerCommunication, s as DOOM_SESSION_INTERCOM_SERVICE, u as createSessionGroupStore, w as PEER_REPLAY_DATABASE_FILE, x as verifyPeerRequest, y as peerSessionReference } from "../sessionDelivery-BzPl0sCk.mjs";
3
2
  import path from "node:path";
3
+ import { BACKGROUND_CONTEXT, requireDoomFs, requireDoomSqlite } from "@agimon-ai/po-core/platform";
4
4
  import { poHomeDirectory } from "@agimon-ai/po-core/piSettings";
5
5
  import { composeServerProviderOverrides, defineServerPlugin } from "@agimon-ai/po-core/serverFacet";
6
6
  import { composeRootHooks, defineRoot, defineRoute } from "@agimon-ai/po-core/extensionFile";
7
7
  //#region src/services/peerInbox/index.ts
8
8
  const inboxes = /* @__PURE__ */ new Map();
9
- /** Registers one live session-owned peer inbox. This registry is reachable only by the Session route. */
9
+ /**
10
+ * Registers one live session-owned peer inbox. This registry is reachable only by the Session route.
11
+ * The newest registration wins: a Durable Object evicted without disposal leaves its entry in the
12
+ * isolate's module state, and the restarted session must replace it. The hub still rejects two live
13
+ * sessions with one id, and an older unregister cannot remove a newer inbox.
14
+ */
10
15
  function registerSessionPeerInbox(sessionId, inbox) {
11
- if (inboxes.has(sessionId)) throw new Error(`Session peer inbox '${sessionId}' is already registered.`);
12
16
  inboxes.set(sessionId, inbox);
13
17
  return () => {
14
18
  if (inboxes.get(sessionId) === inbox) inboxes.delete(sessionId);
15
19
  };
16
20
  }
17
- function deliverSessionPeerEnvelope(targetSessionId, sourceKey, type, payload) {
21
+ async function deliverSessionPeerEnvelope(targetSessionId, sourceKey, type, payload) {
18
22
  return inboxes.get(targetSessionId)?.(sourceKey, type, payload);
19
23
  }
20
24
  //#endregion
21
25
  //#region src/extensions/workspaces/sessions/(backend)/root.server.ts
22
26
  var root_server_default = defineRoot((pluginContext) => ({
23
27
  value: void 0,
24
- services: [provideBackgroundWorkService, (context) => {
28
+ services: [provideBackgroundWorkService, async (context) => {
25
29
  const agent = pluginContext.agent;
26
30
  const session = agent?.context.session;
27
31
  const communication = pluginContext.host.context.sessionCommunication;
28
32
  const homeDirectory = pluginContext.host.context.homeDirectory;
29
33
  const sessionService = pluginContext.host.context.sessionService;
30
34
  if (!agent || !session?.admitPrompt || !communication || !homeDirectory || !sessionService?.canCommunicate) throw new Error("Session delivery requires the headless agent, authenticated session communication, host home directory, and communication policy.");
35
+ const fs = requireDoomFs(pluginContext.context);
36
+ const sqlite = requireDoomSqlite(pluginContext.context);
37
+ const io = BACKGROUND_CONTEXT;
31
38
  const sessionDirectory = path.join(poHomeDirectory(homeDirectory), "session");
32
39
  const deliveryDirectory = path.join(sessionDirectory, "delivery");
33
- fs.mkdirSync(deliveryDirectory, {
34
- recursive: true,
35
- mode: 448
36
- });
40
+ await ensurePrivateDirectory(fs, deliveryDirectory, io);
41
+ const peerConfig = await readSessionPeerConfigAsync(fs, homeDirectory, io);
37
42
  const peerCommunication = createPeerCommunication({
38
43
  local: communication,
39
- homeDirectory
44
+ config: peerConfig
40
45
  });
41
- const peerConfig = readSessionPeerConfig(homeDirectory);
42
46
  const remotePeerIsGranted = (peerKey) => {
43
47
  const peer = parseRemoteSessionReference(peerKey);
44
48
  return peer !== void 0 && peerConfig?.peers.some((candidate) => candidate.hostId === peer.hostId && candidate.allowedSessionIds.includes(agent.context.sessionId)) === true;
45
49
  };
46
50
  const fallbackAuthorization = (peerKey) => sessionService.canCommunicate?.(agent.context.sessionId, peerKey) === true || remotePeerIsGranted(peerKey);
47
- const groups = peerConfig ? createSessionGroupStore({ databasePath: path.join(sessionDirectory, "groups.sqlite") }) : void 0;
51
+ const groups = peerConfig ? await createSessionGroupStore({ database: await sqlite.open(path.join(sessionDirectory, "groups.sqlite"), io) }) : void 0;
48
52
  const localHostId = peerConfig?.hostId;
49
53
  const selfReference = localHostId ? peerSessionReference(localHostId, agent.context.sessionId) : void 0;
50
54
  const groupAuthorization = groups && selfReference && localHostId ? createSessionGroupDeliveryAuthorizer({
@@ -53,18 +57,25 @@ var root_server_default = defineRoot((pluginContext) => ({
53
57
  peerReference: (peerKey) => parseRemoteSessionReference(peerKey) === void 0 ? peerSessionReference(localHostId, peerKey) : peerKey,
54
58
  fallback: fallbackAuthorization
55
59
  }) : void 0;
56
- const { service, close: closeDelivery } = provideSessionDeliveryService(context, {
57
- databasePath: path.join(deliveryDirectory, `${agent.context.sessionId}.sqlite`),
58
- recipientKey: agent.context.sessionId,
59
- communication: peerCommunication,
60
- authorizePeer: (peerKey, metadata) => groupAuthorization?.(peerKey, metadata) ?? fallbackAuthorization(peerKey),
61
- admitPrompt: (prompt, delivery) => session.admitPrompt(prompt, delivery)
62
- });
60
+ let delivery;
61
+ try {
62
+ delivery = await provideSessionDeliveryService(context, {
63
+ database: await sqlite.open(path.join(deliveryDirectory, `${agent.context.sessionId}.sqlite`), io),
64
+ recipientKey: agent.context.sessionId,
65
+ communication: peerCommunication,
66
+ authorizePeer: (peerKey, metadata) => groupAuthorization ? groupAuthorization(peerKey, metadata) : fallbackAuthorization(peerKey),
67
+ admitPrompt: (prompt, admission) => session.admitPrompt(prompt, admission)
68
+ });
69
+ } catch (error) {
70
+ await groups?.close();
71
+ throw error;
72
+ }
73
+ const { service, close: closeDelivery } = delivery;
63
74
  const unprovide = [];
64
75
  if (groups && selfReference && peerConfig) {
65
- const directory = createSessionDirectory({ authorizeDiscovery: (caller, subject) => {
76
+ const directory = createSessionDirectory({ authorizeDiscovery: async (caller, subject) => {
66
77
  const reference = peerSessionReference(subject.hostId, subject.sessionId);
67
- return groups.groupsFor(caller).some((group) => group.members.includes(reference)) ? { capabilities: ["message"] } : void 0;
78
+ return (await groups.groupsFor(caller)).some((group) => group.members.includes(reference)) ? { capabilities: ["message"] } : void 0;
68
79
  } });
69
80
  for (const peer of peerConfig.peers) for (const sessionId of peer.allowedSessionIds) directory.observe({
70
81
  hostId: peer.hostId,
@@ -95,7 +106,7 @@ var root_server_default = defineRoot((pluginContext) => ({
95
106
  unregister();
96
107
  for (const release of unprovide.reverse()) release();
97
108
  await closeDelivery();
98
- groups?.close();
109
+ await groups?.close();
99
110
  };
100
111
  }]
101
112
  }));
@@ -106,43 +117,69 @@ const DELIVERY_TYPES = /* @__PURE__ */ new Set(["doom/session-delivery/envelope"
106
117
  function response(status, error) {
107
118
  return Response.json({ error }, { status });
108
119
  }
120
+ /** Narrow HMAC-authenticated endpoint that a trusted tunnel may forward without device authentication. */
121
+ function createPeerInboxApi(platform) {
122
+ return {
123
+ basePath: "session-peer",
124
+ start(context) {
125
+ const homeDirectory = context.homeDirectory;
126
+ let opened;
127
+ const open = () => opened ??= (async () => {
128
+ if (homeDirectory === void 0) return {
129
+ config: void 0,
130
+ replay: void 0
131
+ };
132
+ const config = await readSessionPeerConfigAsync(platform.fs, homeDirectory, BACKGROUND_CONTEXT);
133
+ const file = path.join(path.dirname(sessionPeerConfigPath(homeDirectory)), PEER_REPLAY_DATABASE_FILE);
134
+ await ensurePrivateDirectory(platform.fs, path.dirname(file), BACKGROUND_CONTEXT);
135
+ return {
136
+ config,
137
+ replay: await createPeerReplayGuardAsync(await platform.sqlite.open(file, BACKGROUND_CONTEXT))
138
+ };
139
+ })().catch((error) => {
140
+ opened = void 0;
141
+ throw error;
142
+ });
143
+ return {
144
+ async fetch(request) {
145
+ if (request.method !== "POST" || new URL(request.url).pathname !== "/inbox") return response(404, "Not found.");
146
+ const { config, replay } = await open();
147
+ if (!config) return response(503, "Paired Session delivery is not configured.");
148
+ const length = request.headers.get("content-length");
149
+ if (length !== null && (!/^\d+$/u.test(length) || Number(length) > MAX_BODY_BYTES)) return response(413, "Request body is too large.");
150
+ const body = await request.text();
151
+ if (Buffer.byteLength(body) > MAX_BODY_BYTES) return response(413, "Request body is too large.");
152
+ const peer = verifyPeerRequest(config, request, body);
153
+ if (!peer) return response(401, "Paired Session request is unauthorized.");
154
+ if (!await replay?.admit(request)) return response(409, "Paired Session request was already received.");
155
+ let parsed;
156
+ try {
157
+ parsed = JSON.parse(body);
158
+ } catch {
159
+ return response(400, "Paired Session request is malformed.");
160
+ }
161
+ const envelope = parseSessionPeerEnvelope(parsed);
162
+ if (!envelope || !DELIVERY_TYPES.has(envelope.type)) return response(400, "Paired Session envelope is invalid.");
163
+ if (!peer.allowedSessionIds.includes(envelope.targetSessionId)) return response(403, "Peer is not granted this session.");
164
+ const state = await deliverSessionPeerEnvelope(envelope.targetSessionId, peerSessionReference(peer.hostId, envelope.sourceSessionId), envelope.type, envelope.payload);
165
+ return state === void 0 ? response(404, "Target Session is unavailable.") : Response.json({
166
+ accepted: true,
167
+ state
168
+ }, { status: 202 });
169
+ },
170
+ async close() {
171
+ await (await opened?.catch(() => void 0))?.replay?.close();
172
+ }
173
+ };
174
+ }
175
+ };
176
+ }
109
177
  //#endregion
110
178
  //#region src/extensions/(backend)/api/session-peer/route.server.ts
111
- var route_server_default = defineRoute({
112
- basePath: "session-peer",
113
- start(context) {
114
- const config = context.homeDirectory === void 0 ? void 0 : readSessionPeerConfig(context.homeDirectory);
115
- const replay = context.homeDirectory === void 0 ? void 0 : createPeerReplayGuard(context.homeDirectory);
116
- return {
117
- async fetch(request) {
118
- if (request.method !== "POST" || new URL(request.url).pathname !== "/inbox") return response(404, "Not found.");
119
- if (!config) return response(503, "Paired Session delivery is not configured.");
120
- const length = request.headers.get("content-length");
121
- if (length !== null && (!/^\d+$/u.test(length) || Number(length) > MAX_BODY_BYTES)) return response(413, "Request body is too large.");
122
- const body = await request.text();
123
- if (Buffer.byteLength(body) > MAX_BODY_BYTES) return response(413, "Request body is too large.");
124
- const peer = verifyPeerRequest(config, request, body);
125
- if (!peer) return response(401, "Paired Session request is unauthorized.");
126
- if (!replay?.admit(request)) return response(409, "Paired Session request was already received.");
127
- let parsed;
128
- try {
129
- parsed = JSON.parse(body);
130
- } catch {
131
- return response(400, "Paired Session request is malformed.");
132
- }
133
- const envelope = parseSessionPeerEnvelope(parsed);
134
- if (!envelope || !DELIVERY_TYPES.has(envelope.type)) return response(400, "Paired Session envelope is invalid.");
135
- if (!peer.allowedSessionIds.includes(envelope.targetSessionId)) return response(403, "Peer is not granted this session.");
136
- const state = deliverSessionPeerEnvelope(envelope.targetSessionId, peerSessionReference(peer.hostId, envelope.sourceSessionId), envelope.type, envelope.payload);
137
- return state === void 0 ? response(404, "Target Session is unavailable.") : Response.json({
138
- accepted: true,
139
- state
140
- }, { status: 202 });
141
- },
142
- close: () => replay?.close()
143
- };
144
- }
145
- });
179
+ var route_server_default = defineRoute(({ context }) => createPeerInboxApi({
180
+ fs: requireDoomFs(context),
181
+ sqlite: requireDoomSqlite(context)
182
+ }));
146
183
  //#endregion
147
184
  //#region generated/server.ts
148
185
  const at = (value, context) => typeof value === "function" ? value(context) : value;
@@ -1 +1 @@
1
- {"version":3,"file":"server.mjs","names":["rootSession","apiRoute"],"sources":["../../src/services/peerInbox/index.ts","../../src/extensions/workspaces/sessions/(backend)/root.server.ts","../../src/services/peerInboxApi/index.ts","../../src/extensions/(backend)/api/session-peer/route.server.ts","../../generated/server.ts"],"sourcesContent":["import type { DoomSessionDeliveryInboxState } from '../sessionDelivery';\n\nexport type SessionPeerInbox = (\n sourceKey: string,\n type: string,\n payload: unknown,\n) => DoomSessionDeliveryInboxState | undefined;\n\nconst inboxes = new Map<string, SessionPeerInbox>();\n\n/** Registers one live session-owned peer inbox. This registry is reachable only by the Session route. */\nexport function registerSessionPeerInbox(sessionId: string, inbox: SessionPeerInbox): () => void {\n if (inboxes.has(sessionId)) throw new Error(`Session peer inbox '${sessionId}' is already registered.`);\n inboxes.set(sessionId, inbox);\n return () => {\n if (inboxes.get(sessionId) === inbox) inboxes.delete(sessionId);\n };\n}\n\nexport function deliverSessionPeerEnvelope(\n targetSessionId: string,\n sourceKey: string,\n type: string,\n payload: unknown,\n): DoomSessionDeliveryInboxState | undefined {\n return inboxes.get(targetSessionId)?.(sourceKey, type, payload);\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nimport { defineRoot } from '@agimon-ai/po-core/extensionFile';\nimport { poHomeDirectory } from '@agimon-ai/po-core/piSettings';\nimport type { DoomServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport { provideBackgroundWorkService } from '../../../../services/backgroundWorkService';\nimport { registerSessionPeerInbox } from '../../../../services/peerInbox';\nimport {\n createPeerCommunication,\n parseRemoteSessionReference,\n peerSessionReference,\n readSessionPeerConfig,\n} from '../../../../services/peerTransport';\nimport { provideSessionDeliveryService } from '../../../../services/sessionDelivery';\nimport { createSessionDirectory } from '../../../../services/sessionDirectory';\nimport { createSessionGroupStore } from '../../../../services/sessionGroups';\nimport {\n DOOM_SESSION_INTERCOM_SERVICE,\n createSessionGroupDeliveryAuthorizer,\n createSessionIntercom,\n} from '../../../../services/sessionIntercom';\n\nexport default defineRoot((pluginContext: DoomServerPluginContext) => ({\n value: undefined,\n services: [\n provideBackgroundWorkService,\n (context) => {\n const agent = pluginContext.agent;\n const session = agent?.context.session;\n const communication = pluginContext.host.context.sessionCommunication;\n const homeDirectory = pluginContext.host.context.homeDirectory;\n const sessionService = pluginContext.host.context.sessionService;\n if (!agent || !session?.admitPrompt || !communication || !homeDirectory || !sessionService?.canCommunicate) {\n throw new Error(\n 'Session delivery requires the headless agent, authenticated session communication, host home directory, and communication policy.',\n );\n }\n const sessionDirectory = path.join(poHomeDirectory(homeDirectory), 'session');\n const deliveryDirectory = path.join(sessionDirectory, 'delivery');\n fs.mkdirSync(deliveryDirectory, { recursive: true, mode: 0o700 });\n const peerCommunication = createPeerCommunication({ local: communication, homeDirectory });\n const peerConfig = readSessionPeerConfig(homeDirectory);\n const remotePeerIsGranted = (peerKey: string): boolean => {\n const peer = parseRemoteSessionReference(peerKey);\n return (\n peer !== undefined &&\n peerConfig?.peers.some(\n (candidate) =>\n candidate.hostId === peer.hostId && candidate.allowedSessionIds.includes(agent.context.sessionId),\n ) === true\n );\n };\n const fallbackAuthorization = (peerKey: string): boolean =>\n sessionService.canCommunicate?.(agent.context.sessionId, peerKey) === true || remotePeerIsGranted(peerKey);\n const groups = peerConfig\n ? createSessionGroupStore({ databasePath: path.join(sessionDirectory, 'groups.sqlite') })\n : undefined;\n const localHostId = peerConfig?.hostId;\n const selfReference = localHostId ? peerSessionReference(localHostId, agent.context.sessionId) : undefined;\n const groupAuthorization =\n groups && selfReference && localHostId\n ? createSessionGroupDeliveryAuthorizer({\n selfReference,\n groups,\n peerReference: (peerKey) =>\n parseRemoteSessionReference(peerKey) === undefined\n ? peerSessionReference(localHostId, peerKey)\n : peerKey,\n fallback: fallbackAuthorization,\n })\n : undefined;\n const { service, close: closeDelivery } = provideSessionDeliveryService(context, {\n databasePath: path.join(deliveryDirectory, `${agent.context.sessionId}.sqlite`),\n recipientKey: agent.context.sessionId,\n communication: peerCommunication,\n authorizePeer: (peerKey, metadata) => groupAuthorization?.(peerKey, metadata) ?? fallbackAuthorization(peerKey),\n admitPrompt: (prompt, delivery) => session.admitPrompt!(prompt, delivery),\n });\n const unprovide: (() => void)[] = [];\n if (groups && selfReference && peerConfig) {\n const directory = createSessionDirectory({\n authorizeDiscovery: (caller, subject) => {\n const reference = peerSessionReference(subject.hostId, subject.sessionId);\n const permitted = groups.groupsFor(caller).some((group) => group.members.includes(reference));\n return permitted ? { capabilities: ['message'] } : undefined;\n },\n });\n for (const peer of peerConfig.peers)\n for (const sessionId of peer.allowedSessionIds)\n directory.observe({\n hostId: peer.hostId,\n sessionId,\n hostIncarnation: 'unknown',\n sessionIncarnation: 'unknown',\n sequence: 0,\n observedAt: 0,\n staleAfterMs: 0,\n deliveryTarget: peerSessionReference(peer.hostId, sessionId),\n reachability: 'unknown',\n runtime: 'unknown',\n activity: 'unknown',\n voice: { eligible: 'unknown', readiness: 'unknown' },\n });\n unprovide.push(\n context.provide(\n DOOM_SESSION_INTERCOM_SERVICE,\n createSessionIntercom({ selfReference, directory, groups, delivery: service }),\n ),\n );\n }\n const unregister = registerSessionPeerInbox(agent.context.sessionId, (sourceKey, kind, payload) =>\n service.receive?.(sourceKey, kind, payload),\n );\n return async () => {\n unregister();\n for (const release of unprovide.reverse()) release();\n await closeDelivery();\n groups?.close();\n };\n },\n ],\n}));\n","import type { DoomApi, DoomApiHandler } from '@agimon-ai/po-core/packageApi';\n\nimport { deliverSessionPeerEnvelope } from '../peerInbox';\nimport {\n createPeerReplayGuard,\n parseSessionPeerEnvelope,\n peerSessionReference,\n readSessionPeerConfig,\n verifyPeerRequest,\n} from '../peerTransport';\n\nconst MAX_BODY_BYTES = 64 * 1024;\nconst DELIVERY_TYPES = new Set(['doom/session-delivery/envelope', 'doom/session-delivery/ack']);\n\nfunction response(status: number, error: string): Response {\n return Response.json({ error }, { status });\n}\n\n/** Narrow HMAC-authenticated endpoint that a trusted tunnel may forward without device authentication. */\nexport const peerInboxApi: DoomApi = {\n basePath: 'session-peer',\n start(context): DoomApiHandler {\n const config = context.homeDirectory === undefined ? undefined : readSessionPeerConfig(context.homeDirectory);\n const replay = context.homeDirectory === undefined ? undefined : createPeerReplayGuard(context.homeDirectory);\n return {\n async fetch(request) {\n if (request.method !== 'POST' || new URL(request.url).pathname !== '/inbox') return response(404, 'Not found.');\n if (!config) return response(503, 'Paired Session delivery is not configured.');\n const length = request.headers.get('content-length');\n if (length !== null && (!/^\\d+$/u.test(length) || Number(length) > MAX_BODY_BYTES))\n return response(413, 'Request body is too large.');\n const body = await request.text();\n if (Buffer.byteLength(body) > MAX_BODY_BYTES) return response(413, 'Request body is too large.');\n const peer = verifyPeerRequest(config, request, body);\n if (!peer) return response(401, 'Paired Session request is unauthorized.');\n if (!replay?.admit(request)) return response(409, 'Paired Session request was already received.');\n let parsed: unknown;\n try {\n parsed = JSON.parse(body) as unknown;\n } catch {\n return response(400, 'Paired Session request is malformed.');\n }\n const envelope = parseSessionPeerEnvelope(parsed);\n if (!envelope || !DELIVERY_TYPES.has(envelope.type))\n return response(400, 'Paired Session envelope is invalid.');\n if (!peer.allowedSessionIds.includes(envelope.targetSessionId))\n return response(403, 'Peer is not granted this session.');\n const state = deliverSessionPeerEnvelope(\n envelope.targetSessionId,\n peerSessionReference(peer.hostId, envelope.sourceSessionId),\n envelope.type,\n envelope.payload,\n );\n return state === undefined\n ? response(404, 'Target Session is unavailable.')\n : Response.json({ accepted: true, state }, { status: 202 });\n },\n close: () => replay?.close(),\n };\n },\n};\n","import { defineRoute } from '@agimon-ai/po-core/extensionFile';\n\nimport { peerInboxApi } from '../../../../services/peerInboxApi';\n\nexport default defineRoute(peerInboxApi);\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineServerPlugin, type DoomServerSessionPlugin } from '@agimon-ai/po-core/serverFacet';\nimport { composeServerProviderOverrides } from '@agimon-ai/po-core/serverFacet';\nimport { composeRootHooks } from '@agimon-ai/po-core/extensionFile';\n\nimport rootSession from '../src/extensions/workspaces/sessions/(backend)/root.server';\nimport apiRoute from '../src/extensions/(backend)/api/session-peer/route.server';\n\ntype Factory<T, C> = (context: C) => T;\nconst at = <T, C>(value: T | Factory<T, C>, context: C): T =>\n typeof value === 'function' ? (value as Factory<T, C>)(context) : value;\n\nexport const facet = defineServerPlugin({\n name: '@agimon-ai/po-session',\n providerOverrides: composeServerProviderOverrides(rootSession),\n global: (context) => ({\n get api(): DoomServerSessionPlugin['api'] { return [at(apiRoute, context)]; },\n }),\n workspace: (context) => ({\n get api(): DoomServerSessionPlugin['api'] { return [at(apiRoute, context)]; },\n }),\n session: async (context) => {\n const scopeSession = await rootSession(context);\n return {\n ...composeRootHooks(scopeSession),\n get api(): DoomServerSessionPlugin['api'] { return [at(apiRoute, context)]; },\n services: [...(scopeSession.services ?? [])],\n get activities(): DoomServerSessionPlugin['activities'] { return [...(scopeSession.activities ?? [])]; },\n };\n },\n});\n\nexport default facet;\n"],"mappings":"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"}
1
+ {"version":3,"file":"server.mjs","names":["rootSession","apiRoute"],"sources":["../../src/services/peerInbox/index.ts","../../src/extensions/workspaces/sessions/(backend)/root.server.ts","../../src/services/peerInboxApi/index.ts","../../src/extensions/(backend)/api/session-peer/route.server.ts","../../generated/server.ts"],"sourcesContent":["import type { DoomSessionDeliveryInboxState } from '../sessionDelivery';\n\nexport type SessionPeerInbox = (\n sourceKey: string,\n type: string,\n payload: unknown,\n) => Promise<DoomSessionDeliveryInboxState | undefined> | undefined;\n\nconst inboxes = new Map<string, SessionPeerInbox>();\n\n/**\n * Registers one live session-owned peer inbox. This registry is reachable only by the Session route.\n * The newest registration wins: a Durable Object evicted without disposal leaves its entry in the\n * isolate's module state, and the restarted session must replace it. The hub still rejects two live\n * sessions with one id, and an older unregister cannot remove a newer inbox.\n */\nexport function registerSessionPeerInbox(sessionId: string, inbox: SessionPeerInbox): () => void {\n inboxes.set(sessionId, inbox);\n return () => {\n if (inboxes.get(sessionId) === inbox) inboxes.delete(sessionId);\n };\n}\n\nexport async function deliverSessionPeerEnvelope(\n targetSessionId: string,\n sourceKey: string,\n type: string,\n payload: unknown,\n): Promise<DoomSessionDeliveryInboxState | undefined> {\n return inboxes.get(targetSessionId)?.(sourceKey, type, payload);\n}\n","import path from 'node:path';\n\nimport { defineRoot } from '@agimon-ai/po-core/extensionFile';\nimport { poHomeDirectory } from '@agimon-ai/po-core/piSettings';\nimport { BACKGROUND_CONTEXT, requireDoomFs, requireDoomSqlite } from '@agimon-ai/po-core/platform';\nimport type { DoomServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport { provideBackgroundWorkService } from '../../../../services/backgroundWorkService';\nimport { registerSessionPeerInbox } from '../../../../services/peerInbox';\nimport {\n createPeerCommunication,\n ensurePrivateDirectory,\n parseRemoteSessionReference,\n peerSessionReference,\n readSessionPeerConfigAsync,\n} from '../../../../services/peerTransport';\nimport { provideSessionDeliveryService } from '../../../../services/sessionDelivery';\nimport { createSessionDirectory } from '../../../../services/sessionDirectory';\nimport { createSessionGroupStore } from '../../../../services/sessionGroups';\nimport {\n DOOM_SESSION_INTERCOM_SERVICE,\n createSessionGroupDeliveryAuthorizer,\n createSessionIntercom,\n} from '../../../../services/sessionIntercom';\n\nexport default defineRoot((pluginContext: DoomServerPluginContext) => ({\n value: undefined,\n services: [\n provideBackgroundWorkService,\n async (context) => {\n const agent = pluginContext.agent;\n const session = agent?.context.session;\n const communication = pluginContext.host.context.sessionCommunication;\n const homeDirectory = pluginContext.host.context.homeDirectory;\n const sessionService = pluginContext.host.context.sessionService;\n if (!agent || !session?.admitPrompt || !communication || !homeDirectory || !sessionService?.canCommunicate) {\n throw new Error(\n 'Session delivery requires the headless agent, authenticated session communication, host home directory, and communication policy.',\n );\n }\n const fs = requireDoomFs(pluginContext.context);\n const sqlite = requireDoomSqlite(pluginContext.context);\n const io = BACKGROUND_CONTEXT;\n const sessionDirectory = path.join(poHomeDirectory(homeDirectory), 'session');\n const deliveryDirectory = path.join(sessionDirectory, 'delivery');\n await ensurePrivateDirectory(fs, deliveryDirectory, io);\n const peerConfig = await readSessionPeerConfigAsync(fs, homeDirectory, io);\n const peerCommunication = createPeerCommunication({ local: communication, config: peerConfig });\n const remotePeerIsGranted = (peerKey: string): boolean => {\n const peer = parseRemoteSessionReference(peerKey);\n return (\n peer !== undefined &&\n peerConfig?.peers.some(\n (candidate) =>\n candidate.hostId === peer.hostId && candidate.allowedSessionIds.includes(agent.context.sessionId),\n ) === true\n );\n };\n const fallbackAuthorization = (peerKey: string): boolean =>\n sessionService.canCommunicate?.(agent.context.sessionId, peerKey) === true || remotePeerIsGranted(peerKey);\n const groups = peerConfig\n ? await createSessionGroupStore({\n database: await sqlite.open(path.join(sessionDirectory, 'groups.sqlite'), io),\n })\n : undefined;\n const localHostId = peerConfig?.hostId;\n const selfReference = localHostId ? peerSessionReference(localHostId, agent.context.sessionId) : undefined;\n const groupAuthorization =\n groups && selfReference && localHostId\n ? createSessionGroupDeliveryAuthorizer({\n selfReference,\n groups,\n peerReference: (peerKey) =>\n parseRemoteSessionReference(peerKey) === undefined\n ? peerSessionReference(localHostId, peerKey)\n : peerKey,\n fallback: fallbackAuthorization,\n })\n : undefined;\n let delivery: Awaited<ReturnType<typeof provideSessionDeliveryService>>;\n try {\n delivery = await provideSessionDeliveryService(context, {\n database: await sqlite.open(path.join(deliveryDirectory, `${agent.context.sessionId}.sqlite`), io),\n recipientKey: agent.context.sessionId,\n communication: peerCommunication,\n authorizePeer: (peerKey, metadata) =>\n groupAuthorization ? groupAuthorization(peerKey, metadata) : fallbackAuthorization(peerKey),\n admitPrompt: (prompt, admission) => session.admitPrompt!(prompt, admission),\n });\n } catch (error) {\n await groups?.close();\n throw error;\n }\n const { service, close: closeDelivery } = delivery;\n const unprovide: (() => void)[] = [];\n if (groups && selfReference && peerConfig) {\n const directory = createSessionDirectory({\n authorizeDiscovery: async (caller, subject) => {\n const reference = peerSessionReference(subject.hostId, subject.sessionId);\n const permitted = (await groups.groupsFor(caller)).some((group) => group.members.includes(reference));\n return permitted ? { capabilities: ['message'] } : undefined;\n },\n });\n for (const peer of peerConfig.peers)\n for (const sessionId of peer.allowedSessionIds)\n directory.observe({\n hostId: peer.hostId,\n sessionId,\n hostIncarnation: 'unknown',\n sessionIncarnation: 'unknown',\n sequence: 0,\n observedAt: 0,\n staleAfterMs: 0,\n deliveryTarget: peerSessionReference(peer.hostId, sessionId),\n reachability: 'unknown',\n runtime: 'unknown',\n activity: 'unknown',\n voice: { eligible: 'unknown', readiness: 'unknown' },\n });\n unprovide.push(\n context.provide(\n DOOM_SESSION_INTERCOM_SERVICE,\n createSessionIntercom({ selfReference, directory, groups, delivery: service }),\n ),\n );\n }\n const unregister = registerSessionPeerInbox(agent.context.sessionId, (sourceKey, kind, payload) =>\n service.receive?.(sourceKey, kind, payload),\n );\n return async () => {\n unregister();\n for (const release of unprovide.reverse()) release();\n await closeDelivery();\n await groups?.close();\n };\n },\n ],\n}));\n","import path from 'node:path';\n\nimport type { DoomApi, DoomApiHandler } from '@agimon-ai/po-core/packageApi';\nimport { BACKGROUND_CONTEXT, type DoomSqliteService, type FileSystem } from '@agimon-ai/po-core/platform';\n\nimport { deliverSessionPeerEnvelope } from '../peerInbox';\nimport {\n PEER_REPLAY_DATABASE_FILE,\n createPeerReplayGuardAsync,\n ensurePrivateDirectory,\n parseSessionPeerEnvelope,\n peerSessionReference,\n readSessionPeerConfigAsync,\n sessionPeerConfigPath,\n verifyPeerRequest,\n type SessionPeerConfig,\n type SessionPeerReplayGuardAsync,\n} from '../peerTransport';\n\nconst MAX_BODY_BYTES = 64 * 1024;\nconst DELIVERY_TYPES = new Set(['doom/session-delivery/envelope', 'doom/session-delivery/ack']);\n\nfunction response(status: number, error: string): Response {\n return Response.json({ error }, { status });\n}\n\ninterface PeerInboxState {\n readonly config: SessionPeerConfig | undefined;\n readonly replay: SessionPeerReplayGuardAsync | undefined;\n}\n\n/** Narrow HMAC-authenticated endpoint that a trusted tunnel may forward without device authentication. */\nexport function createPeerInboxApi(platform: { readonly fs: FileSystem; readonly sqlite: DoomSqliteService }): DoomApi {\n return {\n basePath: 'session-peer',\n start(context): DoomApiHandler {\n const homeDirectory = context.homeDirectory;\n // Opened once on first use: the handler starts synchronously, the platform services are async.\n let opened: Promise<PeerInboxState> | undefined;\n const open = (): Promise<PeerInboxState> =>\n (opened ??= (async () => {\n if (homeDirectory === undefined) return { config: undefined, replay: undefined };\n const config = await readSessionPeerConfigAsync(platform.fs, homeDirectory, BACKGROUND_CONTEXT);\n const file = path.join(path.dirname(sessionPeerConfigPath(homeDirectory)), PEER_REPLAY_DATABASE_FILE);\n await ensurePrivateDirectory(platform.fs, path.dirname(file), BACKGROUND_CONTEXT);\n const replay = await createPeerReplayGuardAsync(await platform.sqlite.open(file, BACKGROUND_CONTEXT));\n return { config, replay };\n })().catch((error: unknown) => {\n // A failed open is retried by the next request rather than cached.\n opened = undefined;\n throw error;\n }));\n return {\n async fetch(request) {\n if (request.method !== 'POST' || new URL(request.url).pathname !== '/inbox')\n return response(404, 'Not found.');\n const { config, replay } = await open();\n if (!config) return response(503, 'Paired Session delivery is not configured.');\n const length = request.headers.get('content-length');\n if (length !== null && (!/^\\d+$/u.test(length) || Number(length) > MAX_BODY_BYTES))\n return response(413, 'Request body is too large.');\n const body = await request.text();\n if (Buffer.byteLength(body) > MAX_BODY_BYTES) return response(413, 'Request body is too large.');\n const peer = verifyPeerRequest(config, request, body);\n if (!peer) return response(401, 'Paired Session request is unauthorized.');\n if (!(await replay?.admit(request))) return response(409, 'Paired Session request was already received.');\n let parsed: unknown;\n try {\n parsed = JSON.parse(body) as unknown;\n } catch {\n return response(400, 'Paired Session request is malformed.');\n }\n const envelope = parseSessionPeerEnvelope(parsed);\n if (!envelope || !DELIVERY_TYPES.has(envelope.type))\n return response(400, 'Paired Session envelope is invalid.');\n if (!peer.allowedSessionIds.includes(envelope.targetSessionId))\n return response(403, 'Peer is not granted this session.');\n const state = await deliverSessionPeerEnvelope(\n envelope.targetSessionId,\n peerSessionReference(peer.hostId, envelope.sourceSessionId),\n envelope.type,\n envelope.payload,\n );\n return state === undefined\n ? response(404, 'Target Session is unavailable.')\n : Response.json({ accepted: true, state }, { status: 202 });\n },\n async close() {\n const state = await opened?.catch(() => undefined);\n await state?.replay?.close();\n },\n };\n },\n };\n}\n","import { defineRoute } from '@agimon-ai/po-core/extensionFile';\nimport { requireDoomFs, requireDoomSqlite } from '@agimon-ai/po-core/platform';\nimport type { DoomServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport { createPeerInboxApi } from '../../../../services/peerInboxApi';\n\nexport default defineRoute(({ context }: DoomServerPluginContext) =>\n createPeerInboxApi({ fs: requireDoomFs(context), sqlite: requireDoomSqlite(context) }),\n);\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineServerPlugin, type DoomServerSessionPlugin } from '@agimon-ai/po-core/serverFacet';\nimport { composeServerProviderOverrides } from '@agimon-ai/po-core/serverFacet';\nimport { composeRootHooks } from '@agimon-ai/po-core/extensionFile';\n\nimport rootSession from '../src/extensions/workspaces/sessions/(backend)/root.server';\nimport apiRoute from '../src/extensions/(backend)/api/session-peer/route.server';\n\ntype Factory<T, C> = (context: C) => T;\nconst at = <T, C>(value: T | Factory<T, C>, context: C): T =>\n typeof value === 'function' ? (value as Factory<T, C>)(context) : value;\n\nexport const facet = defineServerPlugin({\n name: '@agimon-ai/po-session',\n providerOverrides: composeServerProviderOverrides(rootSession),\n global: (context) => ({\n get api(): DoomServerSessionPlugin['api'] { return [at(apiRoute, context)]; },\n }),\n workspace: (context) => ({\n get api(): DoomServerSessionPlugin['api'] { return [at(apiRoute, context)]; },\n }),\n session: async (context) => {\n const scopeSession = await rootSession(context);\n return {\n ...composeRootHooks(scopeSession),\n get api(): DoomServerSessionPlugin['api'] { return [at(apiRoute, context)]; },\n services: [...(scopeSession.services ?? [])],\n get activities(): DoomServerSessionPlugin['activities'] { return [...(scopeSession.activities ?? [])]; },\n };\n },\n});\n\nexport default facet;\n"],"mappings":"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"}