@agimon-ai/po-session 0.0.0-alpha.52 → 0.0.0-alpha.54
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/README.md +24 -0
- package/dist/extensions/server.cjs +50 -41
- package/dist/extensions/server.cjs.map +1 -1
- package/dist/extensions/server.mjs +10 -1
- package/dist/extensions/server.mjs.map +1 -1
- package/dist/index.cjs +130 -34
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.mts +64 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +95 -1
- package/dist/index.mjs.map +1 -0
- package/dist/{sessionGroupBridge-DQeabG52.mjs → type-BeV2gpe6.mjs} +7 -2
- package/dist/type-BeV2gpe6.mjs.map +1 -0
- package/dist/{sessionGroupBridge-DL3QTIaH.cjs → type-CSkVwA6w.cjs} +18 -1
- package/dist/type-CSkVwA6w.cjs.map +1 -0
- package/package.json +6 -6
- package/dist/sessionGroupBridge-DL3QTIaH.cjs.map +0 -1
- package/dist/sessionGroupBridge-DQeabG52.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -50,3 +50,27 @@ pnpm typecheck
|
|
|
50
50
|
pnpm test
|
|
51
51
|
pnpm lint
|
|
52
52
|
```
|
|
53
|
+
|
|
54
|
+
## Account network peer links
|
|
55
|
+
|
|
56
|
+
`createRosterPeerLink(ports)` adds direct host-key-authenticated sealed links using
|
|
57
|
+
an already verified C6 snapshot. Supply it as `createPeerLink({ network, ... })`
|
|
58
|
+
to expose it alongside the existing invite control as `control.network`.
|
|
59
|
+
`connect(hostId, token, exchange)` and `accept(proof, token)` sign fresh ephemeral
|
|
60
|
+
handshake keys with `signEnvelope` using the roster host key. The receiving host's
|
|
61
|
+
`authorize` port must validate the identity token's issuer, audience, current
|
|
62
|
+
membership, and proof-bound caller host. The `verifyRequest` port must perform
|
|
63
|
+
normal destination C3/C4 verification, including durable replay admission.
|
|
64
|
+
Neither a roster entry, an identity token alone, nor a channel grants application
|
|
65
|
+
permissions. Roster keys use base64url DER P-256 SPKI encoding.
|
|
66
|
+
|
|
67
|
+
Channels recheck roster epoch, membership and keys on each use. Publish C6 changes
|
|
68
|
+
before admitting new traffic; removal or key rotation refuses existing channels.
|
|
69
|
+
Fresh handshakes are required after restart. The account path does not write
|
|
70
|
+
legacy peer secrets or session permission lists. Existing 60-second invites and
|
|
71
|
+
pre-account session links retain their behavior and confer no network capability.
|
|
72
|
+
Direct links do not require an elected coordinator: in a two-voter network with
|
|
73
|
+
one down, existing direct authorized traffic can continue even though neither
|
|
74
|
+
roster writes nor coordinated relay service can progress. Election is CFT, not
|
|
75
|
+
Byzantine-safe. Actual host roster-sync and network activation are composition
|
|
76
|
+
responsibilities, not implicit consequences of enabling session peers.
|
|
@@ -2,9 +2,9 @@ Object.defineProperties(exports, {
|
|
|
2
2
|
__esModule: { value: true },
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
|
-
const
|
|
5
|
+
const require_type = require("../type-CSkVwA6w.cjs");
|
|
6
6
|
let node_path = require("node:path");
|
|
7
|
-
node_path =
|
|
7
|
+
node_path = require_type.__toESM(node_path, 1);
|
|
8
8
|
let _agimon_ai_po_core_platform = require("@agimon-ai/po-core/platform");
|
|
9
9
|
let _agimon_ai_po_core_piSettings = require("@agimon-ai/po-core/piSettings");
|
|
10
10
|
let _agimon_ai_po_core_extensionFile = require("@agimon-ai/po-core/extensionFile");
|
|
@@ -23,18 +23,18 @@ var root_server_default$1 = (0, _agimon_ai_po_core_extensionFile.defineRoot)((pl
|
|
|
23
23
|
], async (scoped) => {
|
|
24
24
|
const homeDirectory = plugin.host.context.homeDirectory;
|
|
25
25
|
if (!homeDirectory) throw new Error("Host links require an explicit host home directory.");
|
|
26
|
-
const peers = await
|
|
26
|
+
const peers = await require_type.createSessionPeers({
|
|
27
27
|
fs: (0, _agimon_ai_po_core_platform.requirePoFs)(scoped),
|
|
28
28
|
homeDirectory
|
|
29
29
|
});
|
|
30
30
|
await peers.identity();
|
|
31
31
|
const directory = node_path.default.join((0, _agimon_ai_po_core_piSettings.poHomeDirectory)(homeDirectory), "session");
|
|
32
|
-
await
|
|
32
|
+
await require_type.ensurePrivateDirectory((0, _agimon_ai_po_core_platform.requirePoFs)(scoped), directory, _agimon_ai_po_core_platform.BACKGROUND_CONTEXT);
|
|
33
33
|
const database = await (0, _agimon_ai_po_core_platform.requirePoSqlite)(scoped).open(node_path.default.join(directory, "groups.sqlite"), _agimon_ai_po_core_platform.BACKGROUND_CONTEXT);
|
|
34
|
-
const groups = await
|
|
34
|
+
const groups = await require_type.createSessionGroupStore({ database });
|
|
35
35
|
await database.run("DELETE FROM session_group_members WHERE group_id IN (SELECT group_id FROM session_groups WHERE owner_authority = 'session-group-bridge')");
|
|
36
36
|
await database.run("DELETE FROM session_groups WHERE owner_authority = 'session-group-bridge'");
|
|
37
|
-
const bridge =
|
|
37
|
+
const bridge = require_type.createSessionGroupBridge({
|
|
38
38
|
groups,
|
|
39
39
|
peers,
|
|
40
40
|
onError: (error) => plugin.host.context.onNotice(`Group bridge failed: ${String(error)}`),
|
|
@@ -42,7 +42,7 @@ var root_server_default$1 = (0, _agimon_ai_po_core_extensionFile.defineRoot)((pl
|
|
|
42
42
|
const config = peers.current();
|
|
43
43
|
const peer = config?.peers.find((entry) => entry.hostId === hostId);
|
|
44
44
|
if (!config || !peer) return;
|
|
45
|
-
await
|
|
45
|
+
await require_type.authenticatedPeerPost({
|
|
46
46
|
config,
|
|
47
47
|
peer,
|
|
48
48
|
path: "/api/plugins/session-peer/control",
|
|
@@ -54,10 +54,10 @@ var root_server_default$1 = (0, _agimon_ai_po_core_extensionFile.defineRoot)((pl
|
|
|
54
54
|
}
|
|
55
55
|
});
|
|
56
56
|
const scope = (0, _agimon_ai_po_core_serverPlugin.requirePoServerScope)(scoped);
|
|
57
|
-
const releasePeers = plugin.host.scope === "global" ? await scope.provide(
|
|
57
|
+
const releasePeers = plugin.host.scope === "global" ? await scope.provide(require_type.PO_SESSION_PEERS_CONTRACT, peers) : scoped.provide(require_type.PO_SESSION_PEERS_SERVICE, peers);
|
|
58
58
|
let releaseBridge;
|
|
59
59
|
try {
|
|
60
|
-
releaseBridge = plugin.host.scope === "global" ? await scope.provide(
|
|
60
|
+
releaseBridge = plugin.host.scope === "global" ? await scope.provide(require_type.PO_SESSION_GROUP_BRIDGE_CONTRACT, bridge) : scoped.provide(require_type.PO_SESSION_GROUP_BRIDGE_SERVICE, bridge);
|
|
61
61
|
} catch (error) {
|
|
62
62
|
await Promise.resolve(releasePeers());
|
|
63
63
|
await bridge.close();
|
|
@@ -93,10 +93,10 @@ async function deliverSessionPeerEnvelope(targetSessionId, sourceKey, type, payl
|
|
|
93
93
|
//#region src/extensions/workspaces/sessions/(backend)/root.server.ts
|
|
94
94
|
var root_server_default = (0, _agimon_ai_po_core_extensionFile.defineRoot)((pluginContext) => ({
|
|
95
95
|
value: void 0,
|
|
96
|
-
services: [
|
|
96
|
+
services: [require_type.provideBackgroundWorkService, (owner) => {
|
|
97
97
|
owner.inject([
|
|
98
|
-
|
|
99
|
-
|
|
98
|
+
require_type.PO_SESSION_PEERS_SERVICE,
|
|
99
|
+
require_type.PO_SESSION_GROUP_BRIDGE_SERVICE,
|
|
100
100
|
_agimon_ai_po_core_platform.PO_FS_SERVICE,
|
|
101
101
|
_agimon_ai_po_core_platform.PO_SQLITE_SERVICE
|
|
102
102
|
], async (context) => {
|
|
@@ -111,46 +111,46 @@ var root_server_default = (0, _agimon_ai_po_core_extensionFile.defineRoot)((plug
|
|
|
111
111
|
const io = _agimon_ai_po_core_platform.BACKGROUND_CONTEXT;
|
|
112
112
|
const sessionDirectory = node_path.default.join((0, _agimon_ai_po_core_piSettings.poHomeDirectory)(homeDirectory), "session");
|
|
113
113
|
const deliveryDirectory = node_path.default.join(sessionDirectory, "delivery");
|
|
114
|
-
await
|
|
115
|
-
const peers =
|
|
114
|
+
await require_type.ensurePrivateDirectory(fs, deliveryDirectory, io);
|
|
115
|
+
const peers = require_type.requireSessionPeers(context);
|
|
116
116
|
const peerConfig = peers.current();
|
|
117
|
-
const peerCommunication =
|
|
117
|
+
const peerCommunication = require_type.createPeerCommunication({
|
|
118
118
|
local: communication,
|
|
119
119
|
config: () => peers.current(),
|
|
120
120
|
canPublish: (target, type, payload) => {
|
|
121
121
|
if (type === "po/session-delivery/ack") return true;
|
|
122
122
|
const metadata = payload?.metadata;
|
|
123
|
-
return bridge?.peersFor(agent.context.sessionId).some((peer) => peer.sessionId === target && peer.sharedGroups.some((name) =>
|
|
123
|
+
return bridge?.peersFor(agent.context.sessionId).some((peer) => peer.sessionId === target && peer.sharedGroups.some((name) => require_type.namedSessionGroupId(name) === metadata?.groupId)) === true;
|
|
124
124
|
},
|
|
125
125
|
onError: (error) => pluginContext.host.context.onNotice(`Peer delivery failed: ${String(error)}`)
|
|
126
126
|
});
|
|
127
127
|
const remotePeerIsGranted = (peerKey) => {
|
|
128
|
-
const peer =
|
|
128
|
+
const peer = require_type.parseRemoteSessionReference(peerKey);
|
|
129
129
|
return peer !== void 0 && peers.current()?.peers.some((candidate) => candidate.hostId === peer.hostId && candidate.allowedSessionIds.includes(agent.context.sessionId)) === true;
|
|
130
130
|
};
|
|
131
131
|
const fallbackAuthorization = (peerKey) => sessionService.canCommunicate?.(agent.context.sessionId, peerKey) === true || remotePeerIsGranted(peerKey);
|
|
132
|
-
const bridge =
|
|
132
|
+
const bridge = require_type.readSessionGroupBridge(context);
|
|
133
133
|
const groups = bridge?.groups;
|
|
134
134
|
await bridge?.updateLocal(agent.context.sessionId, communication.sessionName ?? agent.context.sessionId, communication.groups ?? []);
|
|
135
135
|
const localHostId = peerConfig?.hostId;
|
|
136
|
-
const selfReference = localHostId ?
|
|
137
|
-
const groupAuthorization = groups && selfReference && localHostId ?
|
|
136
|
+
const selfReference = localHostId ? require_type.peerSessionReference(localHostId, agent.context.sessionId) : void 0;
|
|
137
|
+
const groupAuthorization = groups && selfReference && localHostId ? require_type.createSessionGroupDeliveryAuthorizer({
|
|
138
138
|
selfReference,
|
|
139
139
|
groups,
|
|
140
|
-
peerReference: (peerKey) =>
|
|
140
|
+
peerReference: (peerKey) => require_type.parseRemoteSessionReference(peerKey) === void 0 ? require_type.peerSessionReference(localHostId, peerKey) : peerKey,
|
|
141
141
|
fallback: fallbackAuthorization
|
|
142
142
|
}) : void 0;
|
|
143
143
|
let delivery;
|
|
144
144
|
try {
|
|
145
|
-
delivery = await
|
|
145
|
+
delivery = await require_type.provideSessionDeliveryService(context, {
|
|
146
146
|
database: await sqlite.open(node_path.default.join(deliveryDirectory, `${agent.context.sessionId}.sqlite`), io),
|
|
147
147
|
recipientKey: agent.context.sessionId,
|
|
148
148
|
communication: peerCommunication,
|
|
149
149
|
authorizePeer: (peerKey, metadata) => {
|
|
150
|
-
const remote =
|
|
150
|
+
const remote = require_type.parseRemoteSessionReference(peerKey);
|
|
151
151
|
if (remote && !peers.current()?.peers.some((peer) => peer.hostId === remote.hostId)) return false;
|
|
152
152
|
if (remote) {
|
|
153
|
-
if (!bridge?.metadataFor(agent.context.sessionId, peerKey) || !metadata?.groupId || !bridge?.peersFor(agent.context.sessionId).find((peer) => peer.sessionId === peerKey)?.sharedGroups.some((name) =>
|
|
153
|
+
if (!bridge?.metadataFor(agent.context.sessionId, peerKey) || !metadata?.groupId || !bridge?.peersFor(agent.context.sessionId).find((peer) => peer.sessionId === peerKey)?.sharedGroups.some((name) => require_type.namedSessionGroupId(name) === metadata.groupId)) return false;
|
|
154
154
|
return groupAuthorization ? groupAuthorization(peerKey, metadata) : false;
|
|
155
155
|
}
|
|
156
156
|
return groupAuthorization ? groupAuthorization(peerKey, metadata) : fallbackAuthorization(peerKey);
|
|
@@ -176,18 +176,18 @@ var root_server_default = (0, _agimon_ai_po_core_extensionFile.defineRoot)((plug
|
|
|
176
176
|
const { service, close: closeDelivery } = delivery;
|
|
177
177
|
const unprovide = [];
|
|
178
178
|
if (groups && selfReference && peerConfig) {
|
|
179
|
-
const directory =
|
|
180
|
-
const reference =
|
|
179
|
+
const directory = require_type.createSessionDirectory({ authorizeDiscovery: (_caller, subject) => {
|
|
180
|
+
const reference = require_type.peerSessionReference(subject.hostId, subject.sessionId);
|
|
181
181
|
return bridge?.peersFor(agent.context.sessionId).some((peer) => peer.sessionId === reference) ? { capabilities: ["message"] } : void 0;
|
|
182
182
|
} });
|
|
183
183
|
const observedHosts = /* @__PURE__ */ new Set();
|
|
184
184
|
let sequence = 0;
|
|
185
185
|
const observeRemote = () => {
|
|
186
186
|
const remotePeers = bridge?.peersFor(agent.context.sessionId) ?? [];
|
|
187
|
-
for (const peer of remotePeers) observedHosts.add(
|
|
187
|
+
for (const peer of remotePeers) observedHosts.add(require_type.parseRemoteSessionReference(peer.sessionId).hostId);
|
|
188
188
|
sequence += 1;
|
|
189
189
|
for (const hostId of observedHosts) directory.replaceHostSnapshot(hostId, "linked", remotePeers.flatMap((peer) => {
|
|
190
|
-
const remote =
|
|
190
|
+
const remote = require_type.parseRemoteSessionReference(peer.sessionId);
|
|
191
191
|
return remote.hostId !== hostId ? [] : [{
|
|
192
192
|
hostId,
|
|
193
193
|
sessionId: remote.sessionId,
|
|
@@ -213,7 +213,7 @@ var root_server_default = (0, _agimon_ai_po_core_extensionFile.defineRoot)((plug
|
|
|
213
213
|
observeRemote();
|
|
214
214
|
service.reauthorize?.().catch((error) => pluginContext.host.context.onNotice(`Delivery reauthorization failed: ${String(error)}`));
|
|
215
215
|
}));
|
|
216
|
-
unprovide.push(context.provide(
|
|
216
|
+
unprovide.push(context.provide(require_type.PO_SESSION_INTERCOM_SERVICE, require_type.createSessionIntercom({
|
|
217
217
|
selfReference,
|
|
218
218
|
directory,
|
|
219
219
|
groups,
|
|
@@ -248,7 +248,7 @@ function createPeerInboxApi(platform) {
|
|
|
248
248
|
start(context) {
|
|
249
249
|
const homeDirectory = context.homeDirectory;
|
|
250
250
|
let opened;
|
|
251
|
-
const channels =
|
|
251
|
+
const channels = require_type.createSealedPeerChannels();
|
|
252
252
|
let link;
|
|
253
253
|
let announcing = false;
|
|
254
254
|
const announce = () => {
|
|
@@ -267,12 +267,13 @@ function createPeerInboxApi(platform) {
|
|
|
267
267
|
config: void 0,
|
|
268
268
|
replay: void 0
|
|
269
269
|
};
|
|
270
|
-
const config = platform.peers ? platform.peers.current() : await
|
|
271
|
-
const file = node_path.default.join(node_path.default.dirname(
|
|
272
|
-
await
|
|
273
|
-
const replay = await
|
|
274
|
-
if (platform.peers) link =
|
|
270
|
+
const config = platform.peers ? platform.peers.current() : await require_type.readSessionPeerConfigAsync(platform.fs, homeDirectory, _agimon_ai_po_core_platform.BACKGROUND_CONTEXT);
|
|
271
|
+
const file = node_path.default.join(node_path.default.dirname(require_type.sessionPeerConfigPath(homeDirectory)), require_type.PEER_REPLAY_DATABASE_FILE);
|
|
272
|
+
await require_type.ensurePrivateDirectory(platform.fs, node_path.default.dirname(file), _agimon_ai_po_core_platform.BACKGROUND_CONTEXT);
|
|
273
|
+
const replay = await require_type.createPeerReplayGuardAsync(await platform.sqlite.open(file, _agimon_ai_po_core_platform.BACKGROUND_CONTEXT));
|
|
274
|
+
if (platform.peers) link = require_type.createPeerLink({
|
|
275
275
|
peers: platform.peers,
|
|
276
|
+
network: platform.network,
|
|
276
277
|
groups: platform.groups,
|
|
277
278
|
replay,
|
|
278
279
|
channels,
|
|
@@ -349,7 +350,7 @@ function createPeerInboxApi(platform) {
|
|
|
349
350
|
}
|
|
350
351
|
}
|
|
351
352
|
if (!config) return response(503, "Paired Session delivery is not configured.");
|
|
352
|
-
const peer =
|
|
353
|
+
const peer = require_type.verifyPeerRequest(config, request, body);
|
|
353
354
|
if (!peer) return response(401, "Paired Session request is unauthorized.");
|
|
354
355
|
if (!await replay?.admit(request)) return response(409, "Paired Session request was already received.");
|
|
355
356
|
let parsed;
|
|
@@ -370,10 +371,10 @@ function createPeerInboxApi(platform) {
|
|
|
370
371
|
return response(400, "Invalid sealed peer envelope.");
|
|
371
372
|
}
|
|
372
373
|
}
|
|
373
|
-
const envelope =
|
|
374
|
+
const envelope = require_type.parseSessionPeerEnvelope(parsed);
|
|
374
375
|
if (!envelope || !DELIVERY_TYPES.has(envelope.type)) return response(400, "Paired Session envelope is invalid.");
|
|
375
376
|
if (!peer.linkGeneration && !peer.allowedSessionIds.includes(envelope.targetSessionId)) return response(403, "Peer is not granted this session.");
|
|
376
|
-
const state = await deliverSessionPeerEnvelope(envelope.targetSessionId,
|
|
377
|
+
const state = await deliverSessionPeerEnvelope(envelope.targetSessionId, require_type.peerSessionReference(peer.hostId, envelope.sourceSessionId), envelope.type, envelope.payload);
|
|
377
378
|
if (state !== void 0 && peerChannel) {
|
|
378
379
|
const sealed = peerChannel.seal(Buffer.from(JSON.stringify({
|
|
379
380
|
accepted: true,
|
|
@@ -381,7 +382,7 @@ function createPeerInboxApi(platform) {
|
|
|
381
382
|
})));
|
|
382
383
|
if (!sealed.ok) return response(503, "Peer acknowledgement could not be sealed.");
|
|
383
384
|
const text = JSON.stringify(sealed.envelope);
|
|
384
|
-
return new Response(text, { headers:
|
|
385
|
+
return new Response(text, { headers: require_type.peerRequestHeaders(config.hostId, peer, "RESPONSE", "/inbox", text) });
|
|
385
386
|
}
|
|
386
387
|
return state === void 0 ? response(404, "Target Session is unavailable.") : Response.json({
|
|
387
388
|
accepted: true,
|
|
@@ -402,6 +403,13 @@ function createPeerInboxApi(platform) {
|
|
|
402
403
|
//#region src/extensions/(backend)/api/session-peer/route.server.ts
|
|
403
404
|
var route_server_default = (0, _agimon_ai_po_core_extensionFile.defineRoute)(({ context }) => {
|
|
404
405
|
let origin;
|
|
406
|
+
let network;
|
|
407
|
+
context.inject([require_type.PO_ROSTER_PEER_SERVICE], (scoped) => {
|
|
408
|
+
network = scoped.get(require_type.PO_ROSTER_PEER_SERVICE);
|
|
409
|
+
return () => {
|
|
410
|
+
network = void 0;
|
|
411
|
+
};
|
|
412
|
+
});
|
|
405
413
|
context.inject([_agimon_ai_po_core_remoteOrigin.PO_REMOTE_ORIGIN_SERVICE], (scoped) => {
|
|
406
414
|
origin = scoped.get(_agimon_ai_po_core_remoteOrigin.PO_REMOTE_ORIGIN_SERVICE);
|
|
407
415
|
return () => {
|
|
@@ -412,8 +420,9 @@ var route_server_default = (0, _agimon_ai_po_core_extensionFile.defineRoute)(({
|
|
|
412
420
|
return createPeerInboxApi({
|
|
413
421
|
fs: (0, _agimon_ai_po_core_platform.requirePoFs)(context),
|
|
414
422
|
sqlite: (0, _agimon_ai_po_core_platform.requirePoSqlite)(context),
|
|
415
|
-
peers:
|
|
416
|
-
|
|
423
|
+
peers: require_type.requireSessionPeers(context),
|
|
424
|
+
network,
|
|
425
|
+
groups: () => require_type.readSessionGroupBridge(context),
|
|
417
426
|
...hasOrigin ? { origin: () => origin?.current() } : {}
|
|
418
427
|
});
|
|
419
428
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.cjs","names":["defineRoot","PO_FS_SERVICE","PO_SQLITE_SERVICE","PO_SERVER_SCOPE_SERVICE","createSessionPeers","requirePoFs","path","poHomeDirectory","ensurePrivateDirectory","BACKGROUND_CONTEXT","requirePoSqlite","createSessionGroupStore","createSessionGroupBridge","authenticatedPeerPost","requirePoServerScope","PO_SESSION_PEERS_CONTRACT","PO_SESSION_PEERS_SERVICE","PO_SESSION_GROUP_BRIDGE_CONTRACT","PO_SESSION_GROUP_BRIDGE_SERVICE","defineRoot","provideBackgroundWorkService","PO_SESSION_PEERS_SERVICE","PO_SESSION_GROUP_BRIDGE_SERVICE","PO_FS_SERVICE","PO_SQLITE_SERVICE","requirePoFs","requirePoSqlite","BACKGROUND_CONTEXT","path","poHomeDirectory","ensurePrivateDirectory","requireSessionPeers","createPeerCommunication","namedSessionGroupId","parseRemoteSessionReference","readSessionGroupBridge","peerSessionReference","createSessionGroupDeliveryAuthorizer","provideSessionDeliveryService","createSessionDirectory","PO_SESSION_INTERCOM_SERVICE","createSessionIntercom","PO_SESSION_ATTENTION_EVENT","createSealedPeerChannels","readSessionPeerConfigAsync","BACKGROUND_CONTEXT","path","sessionPeerConfigPath","PEER_REPLAY_DATABASE_FILE","ensurePrivateDirectory","createPeerReplayGuardAsync","createPeerLink","poApiCallerFrom","verifyPeerRequest","parseSessionPeerEnvelope","peerSessionReference","peerRequestHeaders","defineRoute","PO_REMOTE_ORIGIN_SERVICE","requirePoFs","requirePoSqlite","requireSessionPeers","readSessionGroupBridge","defineServerPlugin","composeServerProviderOverrides","rootGlobal","rootSession","composeRootInjects","composeRootHooks","apiRoute"],"sources":["../../src/extensions/(backend)/root.server.ts","../../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 path from 'node:path';\n\nimport { defineRoot } from '@agimon-ai/po-core/extensionFile';\nimport { poHomeDirectory } from '@agimon-ai/po-core/piSettings';\nimport {\n PO_FS_SERVICE,\n PO_SQLITE_SERVICE,\n requirePoFs,\n requirePoSqlite,\n BACKGROUND_CONTEXT,\n} from '@agimon-ai/po-core/platform';\nimport {\n PO_SERVER_SCOPE_SERVICE,\n requirePoServerScope,\n type PoServerPluginContext,\n} from '@agimon-ai/po-core/serverPlugin';\n\nimport { ensurePrivateDirectory } from '../../services/peerTransport';\nimport { authenticatedPeerPost } from '../../services/sealedPeerChannel';\nimport {\n createSessionGroupBridge,\n PO_SESSION_GROUP_BRIDGE_CONTRACT,\n PO_SESSION_GROUP_BRIDGE_SERVICE,\n} from '../../services/sessionGroupBridge';\nimport { createSessionGroupStore } from '../../services/sessionGroups';\nimport { createSessionPeers, PO_SESSION_PEERS_CONTRACT, PO_SESSION_PEERS_SERVICE } from '../../services/sessionPeers';\n\nexport default defineRoot((plugin: PoServerPluginContext) => ({\n value: undefined,\n services: [\n async (context) => {\n // Routes require these bindings as soon as the service plugin finishes loading.\n await context.inject([PO_FS_SERVICE, PO_SQLITE_SERVICE, PO_SERVER_SCOPE_SERVICE], async (scoped) => {\n const homeDirectory = plugin.host.context.homeDirectory;\n if (!homeDirectory) throw new Error('Host links require an explicit host home directory.');\n const peers = await createSessionPeers({ fs: requirePoFs(scoped), homeDirectory });\n await peers.identity();\n const directory = path.join(poHomeDirectory(homeDirectory), 'session');\n await ensurePrivateDirectory(requirePoFs(scoped), directory, BACKGROUND_CONTEXT);\n const database = await requirePoSqlite(scoped).open(path.join(directory, 'groups.sqlite'), BACKGROUND_CONTEXT);\n const groups = await createSessionGroupStore({ database });\n // Snapshots are rebuilt from live sessions and fresh authenticated adverts after a restart.\n await database.run(\n \"DELETE FROM session_group_members WHERE group_id IN (SELECT group_id FROM session_groups WHERE owner_authority = 'session-group-bridge')\",\n );\n await database.run(\"DELETE FROM session_groups WHERE owner_authority = 'session-group-bridge'\");\n const bridge = createSessionGroupBridge({\n groups,\n peers,\n onError: (error) => plugin.host.context.onNotice(`Group bridge failed: ${String(error)}`),\n send: async (hostId, advert) => {\n const config = peers.current();\n const peer = config?.peers.find((entry) => entry.hostId === hostId);\n if (!config || !peer) return;\n await authenticatedPeerPost({\n config,\n peer,\n path: '/api/plugins/session-peer/control',\n value: { type: 'groups', groups: advert },\n });\n },\n });\n const scope = requirePoServerScope(scoped);\n const releasePeers =\n plugin.host.scope === 'global'\n ? await scope.provide(PO_SESSION_PEERS_CONTRACT, peers)\n : scoped.provide(PO_SESSION_PEERS_SERVICE, peers);\n let releaseBridge: () => void | Promise<void>;\n try {\n releaseBridge =\n plugin.host.scope === 'global'\n ? await scope.provide(PO_SESSION_GROUP_BRIDGE_CONTRACT, bridge)\n : scoped.provide(PO_SESSION_GROUP_BRIDGE_SERVICE, bridge);\n } catch (error) {\n await Promise.resolve(releasePeers());\n await bridge.close();\n throw error;\n }\n return async () => {\n await releaseBridge();\n await Promise.resolve(releasePeers());\n await bridge.close();\n };\n });\n },\n ],\n}));\n","import type { PoSessionDeliveryInboxState } from '../sessionDelivery';\n\nexport type SessionPeerInbox = (\n sourceKey: string,\n type: string,\n payload: unknown,\n) => Promise<PoSessionDeliveryInboxState | 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<PoSessionDeliveryInboxState | 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 { PO_SESSION_ATTENTION_EVENT } from '@agimon-ai/po-core/hubChannel';\nimport { poHomeDirectory } from '@agimon-ai/po-core/piSettings';\nimport {\n BACKGROUND_CONTEXT,\n PO_FS_SERVICE,\n PO_SQLITE_SERVICE,\n requirePoFs,\n requirePoSqlite,\n} from '@agimon-ai/po-core/platform';\nimport type { PoServerPluginContext } from '@agimon-ai/po-core/serverPlugin';\n\nimport { provideBackgroundWorkService } from '../../../../services/backgroundWorkService';\nimport { registerSessionPeerInbox } from '../../../../services/peerInbox';\nimport {\n createPeerCommunication,\n ensurePrivateDirectory,\n parseRemoteSessionReference,\n peerSessionReference,\n} from '../../../../services/peerTransport';\nimport { provideSessionDeliveryService } from '../../../../services/sessionDelivery';\nimport { createSessionDirectory } from '../../../../services/sessionDirectory';\nimport {\n PO_SESSION_GROUP_BRIDGE_SERVICE,\n readSessionGroupBridge,\n namedSessionGroupId,\n} from '../../../../services/sessionGroupBridge';\nimport {\n PO_SESSION_INTERCOM_SERVICE,\n createSessionGroupDeliveryAuthorizer,\n createSessionIntercom,\n} from '../../../../services/sessionIntercom';\nimport { PO_SESSION_PEERS_SERVICE, requireSessionPeers } from '../../../../services/sessionPeers';\n\nexport default defineRoot((pluginContext: PoServerPluginContext) => ({\n value: undefined,\n services: [\n provideBackgroundWorkService,\n (owner) => {\n owner.inject(\n [PO_SESSION_PEERS_SERVICE, PO_SESSION_GROUP_BRIDGE_SERVICE, PO_FS_SERVICE, PO_SQLITE_SERVICE],\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 = requirePoFs(context);\n const sqlite = requirePoSqlite(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 peers = requireSessionPeers(context);\n const peerConfig = peers.current();\n const peerCommunication = createPeerCommunication({\n local: communication,\n config: () => peers.current(),\n canPublish: (target, type, payload) => {\n if (type === 'po/session-delivery/ack') return true;\n const metadata = (payload as { metadata?: { groupId?: string } } | null)?.metadata;\n return (\n bridge\n ?.peersFor(agent.context.sessionId)\n .some(\n (peer) =>\n peer.sessionId === target &&\n peer.sharedGroups.some((name) => namedSessionGroupId(name) === metadata?.groupId),\n ) === true\n );\n },\n onError: (error) => pluginContext.host.context.onNotice(`Peer delivery failed: ${String(error)}`),\n });\n const remotePeerIsGranted = (peerKey: string): boolean => {\n const peer = parseRemoteSessionReference(peerKey);\n return (\n peer !== undefined &&\n peers\n .current()\n ?.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 bridge = readSessionGroupBridge(context);\n const groups = bridge?.groups;\n await bridge?.updateLocal(\n agent.context.sessionId,\n communication.sessionName ?? agent.context.sessionId,\n communication.groups ?? [],\n );\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 const remote = parseRemoteSessionReference(peerKey);\n if (remote && !peers.current()?.peers.some((peer) => peer.hostId === remote.hostId)) return false;\n if (remote) {\n // Local membership is authoritative even before the SQLite mirror update finishes.\n const expected = bridge?.metadataFor(agent.context.sessionId, peerKey);\n if (\n !expected ||\n !metadata?.groupId ||\n !bridge\n ?.peersFor(agent.context.sessionId)\n .find((peer) => peer.sessionId === peerKey)\n ?.sharedGroups.some((name) => namedSessionGroupId(name) === metadata.groupId)\n )\n return false;\n return groupAuthorization ? groupAuthorization(peerKey, metadata) : false;\n }\n return groupAuthorization ? groupAuthorization(peerKey, metadata) : fallbackAuthorization(peerKey);\n },\n admitPrompt: async (prompt, admission, requestId, metadata) => {\n if (!metadata.customType) {\n await session.admitPrompt!(prompt, admission, undefined, requestId);\n return;\n }\n if (!session.admitMessage) throw new Error('The session cannot admit a custom delivery.');\n await session.admitMessage(\n {\n type: metadata.customType,\n text: prompt,\n display: true,\n details: metadata.customDetails === undefined ? undefined : JSON.parse(metadata.customDetails),\n },\n admission === 'steer' ? 'steer' : 'followUp',\n requestId,\n );\n },\n });\n } catch (error) {\n await bridge?.removeLocal(agent.context.sessionId);\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: (_caller, subject) => {\n const reference = peerSessionReference(subject.hostId, subject.sessionId);\n return bridge?.peersFor(agent.context.sessionId).some((peer) => peer.sessionId === reference)\n ? { capabilities: ['message'] }\n : undefined;\n },\n });\n const observedHosts = new Set<string>();\n let sequence = 0;\n const observeRemote = (): void => {\n const remotePeers = bridge?.peersFor(agent.context.sessionId) ?? [];\n for (const peer of remotePeers) observedHosts.add(parseRemoteSessionReference(peer.sessionId)!.hostId);\n sequence += 1;\n for (const hostId of observedHosts)\n directory.replaceHostSnapshot(\n hostId,\n 'linked',\n remotePeers.flatMap((peer) => {\n const remote = parseRemoteSessionReference(peer.sessionId)!;\n return remote.hostId !== hostId\n ? []\n : [\n {\n hostId,\n sessionId: remote.sessionId,\n label: peer.name,\n hostIncarnation: 'linked',\n sessionIncarnation: 'live',\n sequence,\n observedAt: Date.now(),\n staleAfterMs: 120_000,\n deliveryTarget: peer.sessionId,\n reachability: 'unknown' as const,\n runtime: 'unknown' as const,\n activity: 'unknown' as const,\n voice: { eligible: 'unknown' as const, readiness: 'unknown' as const },\n },\n ];\n }),\n );\n };\n observeRemote();\n unprovide.push(\n bridge!.subscribe(() => {\n observeRemote();\n void service\n .reauthorize?.()\n .catch((error: unknown) =>\n pluginContext.host.context.onNotice(`Delivery reauthorization failed: ${String(error)}`),\n );\n }),\n );\n unprovide.push(\n context.provide(\n PO_SESSION_INTERCOM_SERVICE,\n createSessionIntercom({ selfReference, directory, groups, delivery: service }),\n ),\n );\n }\n const releaseGroups = pluginContext.host.context.directEvents?.subscribe(\n PO_SESSION_ATTENTION_EVENT,\n agent.context.sessionId,\n () => {\n void bridge\n ?.updateLocal(\n agent.context.sessionId,\n communication.sessionName ?? agent.context.sessionId,\n communication.groups ?? [],\n )\n .catch((error: unknown) =>\n pluginContext.host.context.onNotice(`Group update failed: ${String(error)}`),\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 releaseGroups?.();\n await bridge?.removeLocal(agent.context.sessionId);\n for (const release of unprovide.reverse()) release();\n await closeDelivery();\n };\n },\n );\n },\n ],\n}));\n","import path from 'node:path';\n\nimport { poApiCallerFrom, type PoApi, type PoApiHandler } from '@agimon-ai/po-core/packageApi';\nimport { BACKGROUND_CONTEXT, type PoSqliteService, type FileSystem } from '@agimon-ai/po-core/platform';\n\nimport { deliverSessionPeerEnvelope } from '../peerInbox';\nimport { createPeerLink, type PeerLinkControl } from '../peerLink';\nimport {\n PEER_REPLAY_DATABASE_FILE,\n createPeerReplayGuardAsync,\n ensurePrivateDirectory,\n parseSessionPeerEnvelope,\n peerSessionReference,\n peerRequestHeaders,\n readSessionPeerConfigAsync,\n sessionPeerConfigPath,\n verifyPeerRequest,\n type SessionPeerConfig,\n type SessionPeerReplayGuardAsync,\n} from '../peerTransport';\nimport { createSealedPeerChannels } from '../sealedPeerChannel';\nimport type { SessionGroupBridge } from '../sessionGroupBridge';\nimport type { SessionPeers } from '../sessionPeers';\n\nconst MAX_BODY_BYTES = 64 * 1024;\nconst DELIVERY_TYPES = new Set(['po/session-delivery/envelope', 'po/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: {\n readonly fs: FileSystem;\n readonly sqlite: PoSqliteService;\n readonly peers?: SessionPeers;\n readonly groups?: () => SessionGroupBridge | undefined;\n readonly origin?: () => string | undefined;\n readonly fetch?: typeof fetch;\n}): PoApi {\n return {\n basePath: 'session-peer',\n start(context): PoApiHandler {\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 channels = createSealedPeerChannels();\n let link: PeerLinkControl | undefined;\n let announcing = false;\n const announce = (): void => {\n if (!link || announcing) return;\n announcing = true;\n void link\n .announce()\n .catch((error: unknown) => context.onNotice(`Peer URL announcement failed: ${String(error)}`))\n .finally(() => {\n announcing = false;\n });\n };\n // Retry announcements as well as detecting origin revisions. A peer may be offline at rotation.\n const timer =\n platform.peers && platform.origin\n ? setInterval(() => {\n void open()\n .then(announce)\n .catch((error: unknown) => context.onNotice(`Host links could not start: ${String(error)}`));\n }, 1_000)\n : undefined;\n timer?.unref?.();\n const open = (): Promise<PeerInboxState> =>\n (opened ??= (async () => {\n if (homeDirectory === undefined) return { config: undefined, replay: undefined };\n const config = platform.peers\n ? platform.peers.current()\n : 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 if (platform.peers)\n link = createPeerLink({\n peers: platform.peers,\n groups: platform.groups,\n replay,\n channels,\n origin: platform.origin ?? (() => undefined),\n fetch: platform.fetch,\n });\n announce();\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 if (platform.peers)\n void open().catch((error: unknown) => context.onNotice(`Host links could not start: ${String(error)}`));\n return {\n async fetch(request) {\n const pathname = new URL(request.url).pathname;\n const openedState = await open();\n const config = platform.peers ? platform.peers.current() : openedState.config;\n const replay = openedState.replay;\n if (request.method === 'GET' && pathname === '/links' && platform.peers) {\n if (!poApiCallerFrom(request.headers)) return response(403, 'An authenticated caller is required.');\n return Response.json({\n hostId: config?.hostId,\n peers: config?.peers.map(({ hostId, url, urlGeneration }) => ({ hostId, url, urlGeneration })) ?? [],\n });\n }\n if (\n request.method !== 'POST' ||\n !['/inbox', '/control', '/link/prepare', '/link/accept', '/link/unlink', '/link/hint'].includes(pathname)\n )\n return response(404, 'Not found.');\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 if (pathname.startsWith('/link/')) {\n const caller = poApiCallerFrom(request.headers);\n if (!caller || (caller.locality === 'remote' && pathname !== '/link/hint' && caller.stepUp !== 'verified'))\n return response(403, 'Linking hosts needs a fresh peer.link step-up.');\n if (!link) return response(503, 'Host links are unavailable.');\n try {\n const input = (body === '' ? {} : JSON.parse(body)) as {\n invite?: unknown;\n hostId?: unknown;\n url?: unknown;\n };\n if (pathname === '/link/prepare') return Response.json(await link.prepare());\n if (pathname === '/link/accept') {\n await link.accept(input.invite as Parameters<PeerLinkControl['accept']>[0]);\n return Response.json({ linked: true });\n }\n if (typeof input.hostId !== 'string') return response(400, 'A host identity is required.');\n if (pathname === '/link/unlink') {\n await link.unlink(input.hostId);\n return Response.json({ unlinked: true });\n }\n if (typeof input.url !== 'string') return response(400, 'A URL hint is required.');\n return Response.json({ rebound: await link.hint(input.hostId, input.url) });\n } catch (error) {\n return response(400, error instanceof Error ? error.message : 'Host link failed.');\n }\n }\n if (pathname === '/control') {\n if (!link) return response(503, 'Host links are unavailable.');\n try {\n return await link.control(request, body);\n } catch (error) {\n return response(400, error instanceof Error ? error.message : 'Invalid peer control.');\n }\n }\n if (!config) return response(503, 'Paired Session delivery is not configured.');\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 let peerChannel: ReturnType<typeof channels.take>;\n if (peer.linkGeneration) {\n const sealed = parsed as { epochId?: unknown; envelope?: unknown } | null;\n peerChannel = typeof sealed?.epochId === 'string' ? channels.take(peer, sealed.epochId) : undefined;\n const opened = peerChannel?.open(sealed?.envelope);\n if (!opened?.ok) return response(401, 'A fresh sealed peer channel is required.');\n try {\n parsed = JSON.parse(Buffer.from(opened.plaintext).toString('utf8')) as unknown;\n } catch {\n return response(400, 'Invalid sealed peer envelope.');\n }\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.linkGeneration && !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 if (state !== undefined && peerChannel) {\n const sealed = peerChannel.seal(Buffer.from(JSON.stringify({ accepted: true, state })));\n if (!sealed.ok) return response(503, 'Peer acknowledgement could not be sealed.');\n const text = JSON.stringify(sealed.envelope);\n return new Response(text, { headers: peerRequestHeaders(config.hostId, peer, 'RESPONSE', '/inbox', text) });\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 if (timer) clearInterval(timer);\n const state = await opened?.catch(() => undefined);\n link?.close();\n await state?.replay?.close();\n },\n };\n },\n };\n}\n","import { defineRoute } from '@agimon-ai/po-core/extensionFile';\nimport { requirePoFs, requirePoSqlite } from '@agimon-ai/po-core/platform';\nimport { PO_REMOTE_ORIGIN_SERVICE, type RemoteOrigin } from '@agimon-ai/po-core/remoteOrigin';\nimport type { PoServerPluginContext } from '@agimon-ai/po-core/serverPlugin';\n\nimport { createPeerInboxApi } from '../../../../services/peerInboxApi';\nimport { readSessionGroupBridge } from '../../../../services/sessionGroupBridge';\nimport { requireSessionPeers } from '../../../../services/sessionPeers';\n\nexport default defineRoute(({ context }: PoServerPluginContext) => {\n let origin: RemoteOrigin | undefined;\n context.inject([PO_REMOTE_ORIGIN_SERVICE], (scoped) => {\n origin = scoped.get(PO_REMOTE_ORIGIN_SERVICE);\n return () => {\n origin = undefined;\n };\n });\n // Hosts without public routing have nothing to announce and must not keep a polling timer alive.\n const hasOrigin = origin !== undefined;\n return createPeerInboxApi({\n fs: requirePoFs(context),\n sqlite: requirePoSqlite(context),\n peers: requireSessionPeers(context),\n groups: () => readSessionGroupBridge(context),\n ...(hasOrigin ? { origin: () => origin?.current() } : {}),\n });\n});\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineServerPlugin, type PoServerSessionPlugin } from '@agimon-ai/po-core/serverPlugin';\nimport { composeServerProviderOverrides } from '@agimon-ai/po-core/serverPlugin';\nimport { composeRootHooks, composeRootInjects } from '@agimon-ai/po-core/extensionFile';\n\nimport rootGlobal from '../src/extensions/(backend)/root.server';\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/* v8 ignore next -- generated normalization branch */\nconst defined = <T>(values: readonly (T | undefined)[]): T[] =>\n values.filter((value): value is T => value !== undefined);\n\nexport const serverPlugin = defineServerPlugin({\n name: '@agimon-ai/po-session',\n providerOverrides: composeServerProviderOverrides(rootGlobal, rootSession),\n injects: composeRootInjects({ global: [rootGlobal], workspace: [rootGlobal], session: [rootGlobal, rootSession] }),\n global: async (context) => {\n const scopeGlobal = await context.scopeRoot('global', rootGlobal, context);\n const contextGlobal = { ...context, root: scopeGlobal.value };\n return {\n ...composeRootHooks(scopeGlobal),\n get api(): PoServerSessionPlugin['api'] { return [at(apiRoute, contextGlobal)]; },\n services: [...(scopeGlobal.services ?? [])],\n };\n },\n workspace: async (context) => {\n const scopeGlobal = await context.scopeRoot('global', rootGlobal, context);\n const contextGlobal = { ...context, root: scopeGlobal.value };\n return {\n ...composeRootHooks(scopeGlobal),\n get api(): PoServerSessionPlugin['api'] { return defined([context.hosted('global') ? undefined : at(apiRoute, contextGlobal)]); },\n services: [...(scopeGlobal.services ?? [])],\n };\n },\n session: async (context) => {\n const scopeGlobal = await context.scopeRoot('global', rootGlobal, context);\n const contextGlobal = { ...context, root: scopeGlobal.value };\n const scopeSession = await context.scopeRoot('session', rootSession, contextGlobal);\n return {\n ...composeRootHooks(scopeGlobal, scopeSession),\n get api(): PoServerSessionPlugin['api'] { return defined([context.hosted('global') ? undefined : at(apiRoute, contextGlobal)]); },\n services: [...(scopeGlobal.services ?? []), ...(scopeSession.services ?? [])],\n get activities(): PoServerSessionPlugin['activities'] { return [...(scopeGlobal.activities ?? []), ...(scopeSession.activities ?? [])]; },\n };\n },\n});\n\nexport default serverPlugin;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"server.cjs","names":["defineRoot","PO_FS_SERVICE","PO_SQLITE_SERVICE","PO_SERVER_SCOPE_SERVICE","createSessionPeers","requirePoFs","path","poHomeDirectory","ensurePrivateDirectory","BACKGROUND_CONTEXT","requirePoSqlite","createSessionGroupStore","createSessionGroupBridge","authenticatedPeerPost","requirePoServerScope","PO_SESSION_PEERS_CONTRACT","PO_SESSION_PEERS_SERVICE","PO_SESSION_GROUP_BRIDGE_CONTRACT","PO_SESSION_GROUP_BRIDGE_SERVICE","defineRoot","provideBackgroundWorkService","PO_SESSION_PEERS_SERVICE","PO_SESSION_GROUP_BRIDGE_SERVICE","PO_FS_SERVICE","PO_SQLITE_SERVICE","requirePoFs","requirePoSqlite","BACKGROUND_CONTEXT","path","poHomeDirectory","ensurePrivateDirectory","requireSessionPeers","createPeerCommunication","namedSessionGroupId","parseRemoteSessionReference","readSessionGroupBridge","peerSessionReference","createSessionGroupDeliveryAuthorizer","provideSessionDeliveryService","createSessionDirectory","PO_SESSION_INTERCOM_SERVICE","createSessionIntercom","PO_SESSION_ATTENTION_EVENT","createSealedPeerChannels","readSessionPeerConfigAsync","BACKGROUND_CONTEXT","path","sessionPeerConfigPath","PEER_REPLAY_DATABASE_FILE","ensurePrivateDirectory","createPeerReplayGuardAsync","createPeerLink","poApiCallerFrom","verifyPeerRequest","parseSessionPeerEnvelope","peerSessionReference","peerRequestHeaders","defineRoute","PO_ROSTER_PEER_SERVICE","PO_REMOTE_ORIGIN_SERVICE","requirePoFs","requirePoSqlite","requireSessionPeers","readSessionGroupBridge","defineServerPlugin","composeServerProviderOverrides","rootGlobal","rootSession","composeRootInjects","composeRootHooks","apiRoute"],"sources":["../../src/extensions/(backend)/root.server.ts","../../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 path from 'node:path';\n\nimport { defineRoot } from '@agimon-ai/po-core/extensionFile';\nimport { poHomeDirectory } from '@agimon-ai/po-core/piSettings';\nimport {\n PO_FS_SERVICE,\n PO_SQLITE_SERVICE,\n requirePoFs,\n requirePoSqlite,\n BACKGROUND_CONTEXT,\n} from '@agimon-ai/po-core/platform';\nimport {\n PO_SERVER_SCOPE_SERVICE,\n requirePoServerScope,\n type PoServerPluginContext,\n} from '@agimon-ai/po-core/serverPlugin';\n\nimport { ensurePrivateDirectory } from '../../services/peerTransport';\nimport { authenticatedPeerPost } from '../../services/sealedPeerChannel';\nimport {\n createSessionGroupBridge,\n PO_SESSION_GROUP_BRIDGE_CONTRACT,\n PO_SESSION_GROUP_BRIDGE_SERVICE,\n} from '../../services/sessionGroupBridge';\nimport { createSessionGroupStore } from '../../services/sessionGroups';\nimport { createSessionPeers, PO_SESSION_PEERS_CONTRACT, PO_SESSION_PEERS_SERVICE } from '../../services/sessionPeers';\n\nexport default defineRoot((plugin: PoServerPluginContext) => ({\n value: undefined,\n services: [\n async (context) => {\n // Routes require these bindings as soon as the service plugin finishes loading.\n await context.inject([PO_FS_SERVICE, PO_SQLITE_SERVICE, PO_SERVER_SCOPE_SERVICE], async (scoped) => {\n const homeDirectory = plugin.host.context.homeDirectory;\n if (!homeDirectory) throw new Error('Host links require an explicit host home directory.');\n const peers = await createSessionPeers({ fs: requirePoFs(scoped), homeDirectory });\n await peers.identity();\n const directory = path.join(poHomeDirectory(homeDirectory), 'session');\n await ensurePrivateDirectory(requirePoFs(scoped), directory, BACKGROUND_CONTEXT);\n const database = await requirePoSqlite(scoped).open(path.join(directory, 'groups.sqlite'), BACKGROUND_CONTEXT);\n const groups = await createSessionGroupStore({ database });\n // Snapshots are rebuilt from live sessions and fresh authenticated adverts after a restart.\n await database.run(\n \"DELETE FROM session_group_members WHERE group_id IN (SELECT group_id FROM session_groups WHERE owner_authority = 'session-group-bridge')\",\n );\n await database.run(\"DELETE FROM session_groups WHERE owner_authority = 'session-group-bridge'\");\n const bridge = createSessionGroupBridge({\n groups,\n peers,\n onError: (error) => plugin.host.context.onNotice(`Group bridge failed: ${String(error)}`),\n send: async (hostId, advert) => {\n const config = peers.current();\n const peer = config?.peers.find((entry) => entry.hostId === hostId);\n if (!config || !peer) return;\n await authenticatedPeerPost({\n config,\n peer,\n path: '/api/plugins/session-peer/control',\n value: { type: 'groups', groups: advert },\n });\n },\n });\n const scope = requirePoServerScope(scoped);\n const releasePeers =\n plugin.host.scope === 'global'\n ? await scope.provide(PO_SESSION_PEERS_CONTRACT, peers)\n : scoped.provide(PO_SESSION_PEERS_SERVICE, peers);\n let releaseBridge: () => void | Promise<void>;\n try {\n releaseBridge =\n plugin.host.scope === 'global'\n ? await scope.provide(PO_SESSION_GROUP_BRIDGE_CONTRACT, bridge)\n : scoped.provide(PO_SESSION_GROUP_BRIDGE_SERVICE, bridge);\n } catch (error) {\n await Promise.resolve(releasePeers());\n await bridge.close();\n throw error;\n }\n return async () => {\n await releaseBridge();\n await Promise.resolve(releasePeers());\n await bridge.close();\n };\n });\n },\n ],\n}));\n","import type { PoSessionDeliveryInboxState } from '../sessionDelivery';\n\nexport type SessionPeerInbox = (\n sourceKey: string,\n type: string,\n payload: unknown,\n) => Promise<PoSessionDeliveryInboxState | 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<PoSessionDeliveryInboxState | 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 { PO_SESSION_ATTENTION_EVENT } from '@agimon-ai/po-core/hubChannel';\nimport { poHomeDirectory } from '@agimon-ai/po-core/piSettings';\nimport {\n BACKGROUND_CONTEXT,\n PO_FS_SERVICE,\n PO_SQLITE_SERVICE,\n requirePoFs,\n requirePoSqlite,\n} from '@agimon-ai/po-core/platform';\nimport type { PoServerPluginContext } from '@agimon-ai/po-core/serverPlugin';\n\nimport { provideBackgroundWorkService } from '../../../../services/backgroundWorkService';\nimport { registerSessionPeerInbox } from '../../../../services/peerInbox';\nimport {\n createPeerCommunication,\n ensurePrivateDirectory,\n parseRemoteSessionReference,\n peerSessionReference,\n} from '../../../../services/peerTransport';\nimport { provideSessionDeliveryService } from '../../../../services/sessionDelivery';\nimport { createSessionDirectory } from '../../../../services/sessionDirectory';\nimport {\n PO_SESSION_GROUP_BRIDGE_SERVICE,\n readSessionGroupBridge,\n namedSessionGroupId,\n} from '../../../../services/sessionGroupBridge';\nimport {\n PO_SESSION_INTERCOM_SERVICE,\n createSessionGroupDeliveryAuthorizer,\n createSessionIntercom,\n} from '../../../../services/sessionIntercom';\nimport { PO_SESSION_PEERS_SERVICE, requireSessionPeers } from '../../../../services/sessionPeers';\n\nexport default defineRoot((pluginContext: PoServerPluginContext) => ({\n value: undefined,\n services: [\n provideBackgroundWorkService,\n (owner) => {\n owner.inject(\n [PO_SESSION_PEERS_SERVICE, PO_SESSION_GROUP_BRIDGE_SERVICE, PO_FS_SERVICE, PO_SQLITE_SERVICE],\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 = requirePoFs(context);\n const sqlite = requirePoSqlite(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 peers = requireSessionPeers(context);\n const peerConfig = peers.current();\n const peerCommunication = createPeerCommunication({\n local: communication,\n config: () => peers.current(),\n canPublish: (target, type, payload) => {\n if (type === 'po/session-delivery/ack') return true;\n const metadata = (payload as { metadata?: { groupId?: string } } | null)?.metadata;\n return (\n bridge\n ?.peersFor(agent.context.sessionId)\n .some(\n (peer) =>\n peer.sessionId === target &&\n peer.sharedGroups.some((name) => namedSessionGroupId(name) === metadata?.groupId),\n ) === true\n );\n },\n onError: (error) => pluginContext.host.context.onNotice(`Peer delivery failed: ${String(error)}`),\n });\n const remotePeerIsGranted = (peerKey: string): boolean => {\n const peer = parseRemoteSessionReference(peerKey);\n return (\n peer !== undefined &&\n peers\n .current()\n ?.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 bridge = readSessionGroupBridge(context);\n const groups = bridge?.groups;\n await bridge?.updateLocal(\n agent.context.sessionId,\n communication.sessionName ?? agent.context.sessionId,\n communication.groups ?? [],\n );\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 const remote = parseRemoteSessionReference(peerKey);\n if (remote && !peers.current()?.peers.some((peer) => peer.hostId === remote.hostId)) return false;\n if (remote) {\n // Local membership is authoritative even before the SQLite mirror update finishes.\n const expected = bridge?.metadataFor(agent.context.sessionId, peerKey);\n if (\n !expected ||\n !metadata?.groupId ||\n !bridge\n ?.peersFor(agent.context.sessionId)\n .find((peer) => peer.sessionId === peerKey)\n ?.sharedGroups.some((name) => namedSessionGroupId(name) === metadata.groupId)\n )\n return false;\n return groupAuthorization ? groupAuthorization(peerKey, metadata) : false;\n }\n return groupAuthorization ? groupAuthorization(peerKey, metadata) : fallbackAuthorization(peerKey);\n },\n admitPrompt: async (prompt, admission, requestId, metadata) => {\n if (!metadata.customType) {\n await session.admitPrompt!(prompt, admission, undefined, requestId);\n return;\n }\n if (!session.admitMessage) throw new Error('The session cannot admit a custom delivery.');\n await session.admitMessage(\n {\n type: metadata.customType,\n text: prompt,\n display: true,\n details: metadata.customDetails === undefined ? undefined : JSON.parse(metadata.customDetails),\n },\n admission === 'steer' ? 'steer' : 'followUp',\n requestId,\n );\n },\n });\n } catch (error) {\n await bridge?.removeLocal(agent.context.sessionId);\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: (_caller, subject) => {\n const reference = peerSessionReference(subject.hostId, subject.sessionId);\n return bridge?.peersFor(agent.context.sessionId).some((peer) => peer.sessionId === reference)\n ? { capabilities: ['message'] }\n : undefined;\n },\n });\n const observedHosts = new Set<string>();\n let sequence = 0;\n const observeRemote = (): void => {\n const remotePeers = bridge?.peersFor(agent.context.sessionId) ?? [];\n for (const peer of remotePeers) observedHosts.add(parseRemoteSessionReference(peer.sessionId)!.hostId);\n sequence += 1;\n for (const hostId of observedHosts)\n directory.replaceHostSnapshot(\n hostId,\n 'linked',\n remotePeers.flatMap((peer) => {\n const remote = parseRemoteSessionReference(peer.sessionId)!;\n return remote.hostId !== hostId\n ? []\n : [\n {\n hostId,\n sessionId: remote.sessionId,\n label: peer.name,\n hostIncarnation: 'linked',\n sessionIncarnation: 'live',\n sequence,\n observedAt: Date.now(),\n staleAfterMs: 120_000,\n deliveryTarget: peer.sessionId,\n reachability: 'unknown' as const,\n runtime: 'unknown' as const,\n activity: 'unknown' as const,\n voice: { eligible: 'unknown' as const, readiness: 'unknown' as const },\n },\n ];\n }),\n );\n };\n observeRemote();\n unprovide.push(\n bridge!.subscribe(() => {\n observeRemote();\n void service\n .reauthorize?.()\n .catch((error: unknown) =>\n pluginContext.host.context.onNotice(`Delivery reauthorization failed: ${String(error)}`),\n );\n }),\n );\n unprovide.push(\n context.provide(\n PO_SESSION_INTERCOM_SERVICE,\n createSessionIntercom({ selfReference, directory, groups, delivery: service }),\n ),\n );\n }\n const releaseGroups = pluginContext.host.context.directEvents?.subscribe(\n PO_SESSION_ATTENTION_EVENT,\n agent.context.sessionId,\n () => {\n void bridge\n ?.updateLocal(\n agent.context.sessionId,\n communication.sessionName ?? agent.context.sessionId,\n communication.groups ?? [],\n )\n .catch((error: unknown) =>\n pluginContext.host.context.onNotice(`Group update failed: ${String(error)}`),\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 releaseGroups?.();\n await bridge?.removeLocal(agent.context.sessionId);\n for (const release of unprovide.reverse()) release();\n await closeDelivery();\n };\n },\n );\n },\n ],\n}));\n","import path from 'node:path';\n\nimport { poApiCallerFrom, type PoApi, type PoApiHandler } from '@agimon-ai/po-core/packageApi';\nimport { BACKGROUND_CONTEXT, type PoSqliteService, type FileSystem } from '@agimon-ai/po-core/platform';\n\nimport { deliverSessionPeerEnvelope } from '../peerInbox';\nimport { createPeerLink, type PeerLinkControl } from '../peerLink';\nimport type { RosterPeerLinks } from '../peerLink/type';\nimport {\n PEER_REPLAY_DATABASE_FILE,\n createPeerReplayGuardAsync,\n ensurePrivateDirectory,\n parseSessionPeerEnvelope,\n peerSessionReference,\n peerRequestHeaders,\n readSessionPeerConfigAsync,\n sessionPeerConfigPath,\n verifyPeerRequest,\n type SessionPeerConfig,\n type SessionPeerReplayGuardAsync,\n} from '../peerTransport';\nimport { createSealedPeerChannels } from '../sealedPeerChannel';\nimport type { SessionGroupBridge } from '../sessionGroupBridge';\nimport type { SessionPeers } from '../sessionPeers';\n\nconst MAX_BODY_BYTES = 64 * 1024;\nconst DELIVERY_TYPES = new Set(['po/session-delivery/envelope', 'po/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: {\n readonly fs: FileSystem;\n readonly sqlite: PoSqliteService;\n readonly peers?: SessionPeers;\n readonly network?: RosterPeerLinks;\n readonly groups?: () => SessionGroupBridge | undefined;\n readonly origin?: () => string | undefined;\n readonly fetch?: typeof fetch;\n}): PoApi {\n return {\n basePath: 'session-peer',\n start(context): PoApiHandler {\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 channels = createSealedPeerChannels();\n let link: PeerLinkControl | undefined;\n let announcing = false;\n const announce = (): void => {\n if (!link || announcing) return;\n announcing = true;\n void link\n .announce()\n .catch((error: unknown) => context.onNotice(`Peer URL announcement failed: ${String(error)}`))\n .finally(() => {\n announcing = false;\n });\n };\n // Retry announcements as well as detecting origin revisions. A peer may be offline at rotation.\n const timer =\n platform.peers && platform.origin\n ? setInterval(() => {\n void open()\n .then(announce)\n .catch((error: unknown) => context.onNotice(`Host links could not start: ${String(error)}`));\n }, 1_000)\n : undefined;\n timer?.unref?.();\n const open = (): Promise<PeerInboxState> =>\n (opened ??= (async () => {\n if (homeDirectory === undefined) return { config: undefined, replay: undefined };\n const config = platform.peers\n ? platform.peers.current()\n : 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 if (platform.peers)\n link = createPeerLink({\n peers: platform.peers,\n network: platform.network,\n groups: platform.groups,\n replay,\n channels,\n origin: platform.origin ?? (() => undefined),\n fetch: platform.fetch,\n });\n announce();\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 if (platform.peers)\n void open().catch((error: unknown) => context.onNotice(`Host links could not start: ${String(error)}`));\n return {\n async fetch(request) {\n const pathname = new URL(request.url).pathname;\n const openedState = await open();\n const config = platform.peers ? platform.peers.current() : openedState.config;\n const replay = openedState.replay;\n if (request.method === 'GET' && pathname === '/links' && platform.peers) {\n if (!poApiCallerFrom(request.headers)) return response(403, 'An authenticated caller is required.');\n return Response.json({\n hostId: config?.hostId,\n peers: config?.peers.map(({ hostId, url, urlGeneration }) => ({ hostId, url, urlGeneration })) ?? [],\n });\n }\n if (\n request.method !== 'POST' ||\n !['/inbox', '/control', '/link/prepare', '/link/accept', '/link/unlink', '/link/hint'].includes(pathname)\n )\n return response(404, 'Not found.');\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 if (pathname.startsWith('/link/')) {\n const caller = poApiCallerFrom(request.headers);\n if (!caller || (caller.locality === 'remote' && pathname !== '/link/hint' && caller.stepUp !== 'verified'))\n return response(403, 'Linking hosts needs a fresh peer.link step-up.');\n if (!link) return response(503, 'Host links are unavailable.');\n try {\n const input = (body === '' ? {} : JSON.parse(body)) as {\n invite?: unknown;\n hostId?: unknown;\n url?: unknown;\n };\n if (pathname === '/link/prepare') return Response.json(await link.prepare());\n if (pathname === '/link/accept') {\n await link.accept(input.invite as Parameters<PeerLinkControl['accept']>[0]);\n return Response.json({ linked: true });\n }\n if (typeof input.hostId !== 'string') return response(400, 'A host identity is required.');\n if (pathname === '/link/unlink') {\n await link.unlink(input.hostId);\n return Response.json({ unlinked: true });\n }\n if (typeof input.url !== 'string') return response(400, 'A URL hint is required.');\n return Response.json({ rebound: await link.hint(input.hostId, input.url) });\n } catch (error) {\n return response(400, error instanceof Error ? error.message : 'Host link failed.');\n }\n }\n if (pathname === '/control') {\n if (!link) return response(503, 'Host links are unavailable.');\n try {\n return await link.control(request, body);\n } catch (error) {\n return response(400, error instanceof Error ? error.message : 'Invalid peer control.');\n }\n }\n if (!config) return response(503, 'Paired Session delivery is not configured.');\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 let peerChannel: ReturnType<typeof channels.take>;\n if (peer.linkGeneration) {\n const sealed = parsed as { epochId?: unknown; envelope?: unknown } | null;\n peerChannel = typeof sealed?.epochId === 'string' ? channels.take(peer, sealed.epochId) : undefined;\n const opened = peerChannel?.open(sealed?.envelope);\n if (!opened?.ok) return response(401, 'A fresh sealed peer channel is required.');\n try {\n parsed = JSON.parse(Buffer.from(opened.plaintext).toString('utf8')) as unknown;\n } catch {\n return response(400, 'Invalid sealed peer envelope.');\n }\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.linkGeneration && !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 if (state !== undefined && peerChannel) {\n const sealed = peerChannel.seal(Buffer.from(JSON.stringify({ accepted: true, state })));\n if (!sealed.ok) return response(503, 'Peer acknowledgement could not be sealed.');\n const text = JSON.stringify(sealed.envelope);\n return new Response(text, { headers: peerRequestHeaders(config.hostId, peer, 'RESPONSE', '/inbox', text) });\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 if (timer) clearInterval(timer);\n const state = await opened?.catch(() => undefined);\n link?.close();\n await state?.replay?.close();\n },\n };\n },\n };\n}\n","import { defineRoute } from '@agimon-ai/po-core/extensionFile';\nimport { requirePoFs, requirePoSqlite } from '@agimon-ai/po-core/platform';\nimport { PO_REMOTE_ORIGIN_SERVICE, type RemoteOrigin } from '@agimon-ai/po-core/remoteOrigin';\nimport type { PoServerPluginContext } from '@agimon-ai/po-core/serverPlugin';\n\nimport { createPeerInboxApi } from '../../../../services/peerInboxApi';\nimport { PO_ROSTER_PEER_SERVICE, type RosterPeerLinks } from '../../../../services/peerLink/type';\nimport { readSessionGroupBridge } from '../../../../services/sessionGroupBridge';\nimport { requireSessionPeers } from '../../../../services/sessionPeers';\n\nexport default defineRoute(({ context }: PoServerPluginContext) => {\n let origin: RemoteOrigin | undefined;\n let network: RosterPeerLinks | undefined;\n context.inject([PO_ROSTER_PEER_SERVICE], (scoped) => {\n network = scoped.get(PO_ROSTER_PEER_SERVICE);\n return () => {\n network = undefined;\n };\n });\n context.inject([PO_REMOTE_ORIGIN_SERVICE], (scoped) => {\n origin = scoped.get(PO_REMOTE_ORIGIN_SERVICE);\n return () => {\n origin = undefined;\n };\n });\n // Hosts without public routing have nothing to announce and must not keep a polling timer alive.\n const hasOrigin = origin !== undefined;\n return createPeerInboxApi({\n fs: requirePoFs(context),\n sqlite: requirePoSqlite(context),\n peers: requireSessionPeers(context),\n network,\n groups: () => readSessionGroupBridge(context),\n ...(hasOrigin ? { origin: () => origin?.current() } : {}),\n });\n});\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineServerPlugin, type PoServerSessionPlugin } from '@agimon-ai/po-core/serverPlugin';\nimport { composeServerProviderOverrides } from '@agimon-ai/po-core/serverPlugin';\nimport { composeRootHooks, composeRootInjects } from '@agimon-ai/po-core/extensionFile';\n\nimport rootGlobal from '../src/extensions/(backend)/root.server';\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/* v8 ignore next -- generated normalization branch */\nconst defined = <T>(values: readonly (T | undefined)[]): T[] =>\n values.filter((value): value is T => value !== undefined);\n\nexport const serverPlugin = defineServerPlugin({\n name: '@agimon-ai/po-session',\n providerOverrides: composeServerProviderOverrides(rootGlobal, rootSession),\n injects: composeRootInjects({ global: [rootGlobal], workspace: [rootGlobal], session: [rootGlobal, rootSession] }),\n global: async (context) => {\n const scopeGlobal = await context.scopeRoot('global', rootGlobal, context);\n const contextGlobal = { ...context, root: scopeGlobal.value };\n return {\n ...composeRootHooks(scopeGlobal),\n get api(): PoServerSessionPlugin['api'] { return [at(apiRoute, contextGlobal)]; },\n services: [...(scopeGlobal.services ?? [])],\n };\n },\n workspace: async (context) => {\n const scopeGlobal = await context.scopeRoot('global', rootGlobal, context);\n const contextGlobal = { ...context, root: scopeGlobal.value };\n return {\n ...composeRootHooks(scopeGlobal),\n get api(): PoServerSessionPlugin['api'] { return defined([context.hosted('global') ? undefined : at(apiRoute, contextGlobal)]); },\n services: [...(scopeGlobal.services ?? [])],\n };\n },\n session: async (context) => {\n const scopeGlobal = await context.scopeRoot('global', rootGlobal, context);\n const contextGlobal = { ...context, root: scopeGlobal.value };\n const scopeSession = await context.scopeRoot('session', rootSession, contextGlobal);\n return {\n ...composeRootHooks(scopeGlobal, scopeSession),\n get api(): PoServerSessionPlugin['api'] { return defined([context.hosted('global') ? undefined : at(apiRoute, contextGlobal)]); },\n services: [...(scopeGlobal.services ?? []), ...(scopeSession.services ?? [])],\n get activities(): PoServerSessionPlugin['activities'] { return [...(scopeGlobal.activities ?? []), ...(scopeSession.activities ?? [])]; },\n };\n },\n});\n\nexport default serverPlugin;\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as
|
|
1
|
+
import { A as parseSessionPeerEnvelope, B as sessionPeerConfigPath, C as createSessionGroupStore, D as createPeerReplayGuardAsync, E as createPeerCommunication, H as provideBackgroundWorkService, L as authenticatedPeerPost, M as peerSessionReference, N as readSessionPeerConfigAsync, O as ensurePrivateDirectory, P as verifyPeerRequest, R as createSealedPeerChannels, T as createSessionDirectory, a as createSessionGroupBridge, b as PO_SESSION_INTERCOM_SERVICE, c as createPeerLink, d as createSessionPeers, h as provideSessionDeliveryService, i as PO_SESSION_GROUP_BRIDGE_SERVICE, j as peerRequestHeaders, k as parseRemoteSessionReference, l as PO_SESSION_PEERS_CONTRACT, n as PO_ROSTER_PEER_SERVICE, o as namedSessionGroupId, p as requireSessionPeers, r as PO_SESSION_GROUP_BRIDGE_CONTRACT, s as readSessionGroupBridge, u as PO_SESSION_PEERS_SERVICE, v as createSessionGroupDeliveryAuthorizer, y as createSessionIntercom, z as PEER_REPLAY_DATABASE_FILE } from "../type-BeV2gpe6.mjs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { BACKGROUND_CONTEXT, PO_FS_SERVICE, PO_SQLITE_SERVICE, requirePoFs, requirePoSqlite } from "@agimon-ai/po-core/platform";
|
|
4
4
|
import { poHomeDirectory } from "@agimon-ai/po-core/piSettings";
|
|
@@ -268,6 +268,7 @@ function createPeerInboxApi(platform) {
|
|
|
268
268
|
const replay = await createPeerReplayGuardAsync(await platform.sqlite.open(file, BACKGROUND_CONTEXT));
|
|
269
269
|
if (platform.peers) link = createPeerLink({
|
|
270
270
|
peers: platform.peers,
|
|
271
|
+
network: platform.network,
|
|
271
272
|
groups: platform.groups,
|
|
272
273
|
replay,
|
|
273
274
|
channels,
|
|
@@ -397,6 +398,13 @@ function createPeerInboxApi(platform) {
|
|
|
397
398
|
//#region src/extensions/(backend)/api/session-peer/route.server.ts
|
|
398
399
|
var route_server_default = defineRoute(({ context }) => {
|
|
399
400
|
let origin;
|
|
401
|
+
let network;
|
|
402
|
+
context.inject([PO_ROSTER_PEER_SERVICE], (scoped) => {
|
|
403
|
+
network = scoped.get(PO_ROSTER_PEER_SERVICE);
|
|
404
|
+
return () => {
|
|
405
|
+
network = void 0;
|
|
406
|
+
};
|
|
407
|
+
});
|
|
400
408
|
context.inject([PO_REMOTE_ORIGIN_SERVICE], (scoped) => {
|
|
401
409
|
origin = scoped.get(PO_REMOTE_ORIGIN_SERVICE);
|
|
402
410
|
return () => {
|
|
@@ -408,6 +416,7 @@ var route_server_default = defineRoute(({ context }) => {
|
|
|
408
416
|
fs: requirePoFs(context),
|
|
409
417
|
sqlite: requirePoSqlite(context),
|
|
410
418
|
peers: requireSessionPeers(context),
|
|
419
|
+
network,
|
|
411
420
|
groups: () => readSessionGroupBridge(context),
|
|
412
421
|
...hasOrigin ? { origin: () => origin?.current() } : {}
|
|
413
422
|
});
|