@agimon-ai/po-session 0.0.0-alpha.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Vuong Ngo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,44 @@
1
+ # @agimon-ai/po-session
2
+
3
+ Shared session coordination services for Po.
4
+
5
+ The foundation owns the session-scoped background-work coordinator used by optional execution packages. It also provides SQLite-backed delivery with host-bound sender identities, durable inbox and outbox state, acknowledgements, keyed duplicate suppression, and prompt admission that wakes idle recipients. Interrupted admission is marked for recovery and is never replayed automatically.
6
+
7
+ Its public services also include:
8
+
9
+ - An in-memory, permission-filtered presence directory with qualified references, separate reachability, runtime, activity, and Voice facts, incarnation and sequence tracking, expiry, and snapshot recovery after sequence gaps.
10
+ - A SQLite communication-group registry with one owner authority, explicit qualified members, enrollment policy, revision conflicts, and current-membership authorization.
11
+ - An agent-facing intercom service for `discover`, attributed direct `send`, `messages`, and correlated `report` operations. Compositions acquire this service explicitly. Group membership grants only discovery and messaging, never session controls or Voice ownership.
12
+
13
+ Session delivery rechecks current authority before each initial send and queued retry. Removing a member therefore blocks later publication while preserving already accepted delivery evidence.
14
+
15
+ ## Paired hosts
16
+
17
+ A paired host is configured privately in `~/.po/session/peers.json`. The file must be owner-only (`0600`). Each peer needs an HTTPS tunnel URL, a shared secret of at least 32 characters, and explicit operation-specific session allowlists. Pairing alone grants no Session or Voice access.
18
+
19
+ ```json
20
+ {
21
+ "version": 1,
22
+ "hostId": "home-host",
23
+ "peers": [
24
+ {
25
+ "hostId": "work-host",
26
+ "url": "https://work.example.net/",
27
+ "secret": "replace-with-a-private-32-character-minimum-secret",
28
+ "allowedSessionIds": ["approved-message-session-id"],
29
+ "allowedVoiceSessionIds": ["approved-voice-session-id"]
30
+ }
31
+ ]
32
+ }
33
+ ```
34
+
35
+ Use `peer/<host-id>/<session-id>` as a qualified target. `allowedSessionIds` grants durable messages and acknowledgements. `allowedVoiceSessionIds` separately grants bounded Voice client-media relay operations. Neither grant exposes owner, `/host/*`, or `/hub/*` routes.
36
+
37
+ ## Development
38
+
39
+ ```bash
40
+ pnpm build
41
+ pnpm typecheck
42
+ pnpm test
43
+ pnpm lint
44
+ ```
@@ -0,0 +1,37 @@
1
+ Object.defineProperties(exports, {
2
+ __esModule: { value: true },
3
+ [Symbol.toStringTag]: { value: "Module" }
4
+ });
5
+ let _agimon_ai_po_core_apiContracts = require("@agimon-ai/po-core/apiContracts");
6
+ let typebox = require("typebox");
7
+ //#region src/schemas/apiContracts.ts
8
+ /** The endpoint validates its own HMAC peer credential because tunnel device authentication is intentionally bypassed. */
9
+ const http = [{
10
+ id: "peer-inbox",
11
+ scope: "global",
12
+ basePath: "session-peer",
13
+ method: "POST",
14
+ path: "/inbox",
15
+ authentication: "none",
16
+ description: "Accept an HMAC-authenticated durable Session delivery envelope from an explicitly configured tunnel peer.",
17
+ body: {
18
+ contentType: "application/json",
19
+ required: true,
20
+ schema: typebox.Type.Unknown()
21
+ },
22
+ responses: (0, _agimon_ai_po_core_apiContracts.jsonApiResponses)(typebox.Type.Object({
23
+ accepted: typebox.Type.Boolean(),
24
+ state: typebox.Type.String()
25
+ }))
26
+ }];
27
+ const apiContracts = (0, _agimon_ai_po_core_apiContracts.defineApiContract)({
28
+ version: 1,
29
+ http,
30
+ sockets: [],
31
+ dynamic: []
32
+ });
33
+ //#endregion
34
+ exports.apiContracts = apiContracts;
35
+ exports.default = apiContracts;
36
+
37
+ //# sourceMappingURL=apiContracts.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apiContracts.cjs","names":["Type","jsonApiResponses","defineApiContract"],"sources":["../src/schemas/apiContracts.ts"],"sourcesContent":["import { defineApiContract, jsonApiResponses, type DoomHttpContract } from '@agimon-ai/po-core/apiContracts';\nimport { Type } from 'typebox';\n\n/** The endpoint validates its own HMAC peer credential because tunnel device authentication is intentionally bypassed. */\nconst http: DoomHttpContract[] = [\n {\n id: 'peer-inbox',\n scope: 'global',\n basePath: 'session-peer',\n method: 'POST',\n path: '/inbox',\n authentication: 'none',\n description:\n 'Accept an HMAC-authenticated durable Session delivery envelope from an explicitly configured tunnel peer.',\n body: { contentType: 'application/json', required: true, schema: Type.Unknown() },\n responses: jsonApiResponses(Type.Object({ accepted: Type.Boolean(), state: Type.String() })),\n },\n];\n\nexport const apiContracts = defineApiContract({ version: 1, http, sockets: [], dynamic: [] });\nexport default apiContracts;\n"],"mappings":";;;;;;;;AAIA,MAAM,OAA2B,CAC/B;CACE,IAAI;CACJ,OAAO;CACP,UAAU;CACV,QAAQ;CACR,MAAM;CACN,gBAAgB;CAChB,aACE;CACF,MAAM;EAAE,aAAa;EAAoB,UAAU;EAAM,QAAQA,QAAAA,KAAK,QAAQ;CAAE;CAChF,YAAA,GAAWC,gCAAAA,iBAAAA,CAAiBD,QAAAA,KAAK,OAAO;EAAE,UAAUA,QAAAA,KAAK,QAAQ;EAAG,OAAOA,QAAAA,KAAK,OAAO;CAAE,CAAC,CAAC;AAC7F,CACF;AAEA,MAAa,gBAAA,GAAeE,gCAAAA,kBAAAA,CAAkB;CAAE,SAAS;CAAG;CAAM,SAAS,CAAC;CAAG,SAAS,CAAC;AAAE,CAAC"}
@@ -0,0 +1,52 @@
1
+ //#region src/schemas/apiContracts.d.ts
2
+ export declare const apiContracts: {
3
+ version: 1;
4
+ protocols?: Record<string, string> | undefined;
5
+ schemas?: Record<string, boolean | object> | undefined;
6
+ http: {
7
+ id: string;
8
+ scope: "global" | "session" | "workspace";
9
+ basePath?: string | undefined;
10
+ path: string;
11
+ method: "DELETE" | "GET" | "HEAD" | "OPTIONS" | "PATCH" | "POST" | "PUT";
12
+ description: string;
13
+ availability?: string | undefined;
14
+ authentication: "device" | "none" | "owner";
15
+ parameters?: {
16
+ name: string;
17
+ in: "header" | "path" | "query";
18
+ required: boolean;
19
+ schema: boolean | object;
20
+ }[] | undefined;
21
+ body?: {
22
+ contentType: string;
23
+ contentTypes?: string[] | undefined;
24
+ required: boolean;
25
+ schema: boolean | object;
26
+ } | undefined;
27
+ responses: Record<string, {
28
+ description: string;
29
+ contentType?: string | undefined;
30
+ schema?: boolean | object | undefined;
31
+ events?: Record<string, boolean | object> | undefined;
32
+ }>;
33
+ }[];
34
+ sockets: {
35
+ id: string;
36
+ scope: "global" | "session" | "workspace";
37
+ description: string;
38
+ availability?: string | undefined;
39
+ service: string;
40
+ member: string;
41
+ direction: "client-to-server" | "server-to-client";
42
+ kind: "channel" | "method" | "state";
43
+ input: boolean | object;
44
+ output?: boolean | object | undefined;
45
+ errors?: boolean | object | undefined;
46
+ }[];
47
+ dynamic: string[];
48
+ gaps?: string[] | undefined;
49
+ };
50
+ //#endregion
51
+ export { apiContracts as default };
52
+ //# sourceMappingURL=apiContracts.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apiContracts.d.mts","names":[],"sources":["../src/schemas/apiContracts.ts"],"mappings":";qBAmBa"}
@@ -0,0 +1,32 @@
1
+ import { defineApiContract, jsonApiResponses } from "@agimon-ai/po-core/apiContracts";
2
+ import { Type } from "typebox";
3
+ //#region src/schemas/apiContracts.ts
4
+ /** The endpoint validates its own HMAC peer credential because tunnel device authentication is intentionally bypassed. */
5
+ const http = [{
6
+ id: "peer-inbox",
7
+ scope: "global",
8
+ basePath: "session-peer",
9
+ method: "POST",
10
+ path: "/inbox",
11
+ authentication: "none",
12
+ description: "Accept an HMAC-authenticated durable Session delivery envelope from an explicitly configured tunnel peer.",
13
+ body: {
14
+ contentType: "application/json",
15
+ required: true,
16
+ schema: Type.Unknown()
17
+ },
18
+ responses: jsonApiResponses(Type.Object({
19
+ accepted: Type.Boolean(),
20
+ state: Type.String()
21
+ }))
22
+ }];
23
+ const apiContracts = defineApiContract({
24
+ version: 1,
25
+ http,
26
+ sockets: [],
27
+ dynamic: []
28
+ });
29
+ //#endregion
30
+ export { apiContracts, apiContracts as default };
31
+
32
+ //# sourceMappingURL=apiContracts.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apiContracts.mjs","names":[],"sources":["../src/schemas/apiContracts.ts"],"sourcesContent":["import { defineApiContract, jsonApiResponses, type DoomHttpContract } from '@agimon-ai/po-core/apiContracts';\nimport { Type } from 'typebox';\n\n/** The endpoint validates its own HMAC peer credential because tunnel device authentication is intentionally bypassed. */\nconst http: DoomHttpContract[] = [\n {\n id: 'peer-inbox',\n scope: 'global',\n basePath: 'session-peer',\n method: 'POST',\n path: '/inbox',\n authentication: 'none',\n description:\n 'Accept an HMAC-authenticated durable Session delivery envelope from an explicitly configured tunnel peer.',\n body: { contentType: 'application/json', required: true, schema: Type.Unknown() },\n responses: jsonApiResponses(Type.Object({ accepted: Type.Boolean(), state: Type.String() })),\n },\n];\n\nexport const apiContracts = defineApiContract({ version: 1, http, sockets: [], dynamic: [] });\nexport default apiContracts;\n"],"mappings":";;;;AAIA,MAAM,OAA2B,CAC/B;CACE,IAAI;CACJ,OAAO;CACP,UAAU;CACV,QAAQ;CACR,MAAM;CACN,gBAAgB;CAChB,aACE;CACF,MAAM;EAAE,aAAa;EAAoB,UAAU;EAAM,QAAQ,KAAK,QAAQ;CAAE;CAChF,WAAW,iBAAiB,KAAK,OAAO;EAAE,UAAU,KAAK,QAAQ;EAAG,OAAO,KAAK,OAAO;CAAE,CAAC,CAAC;AAC7F,CACF;AAEA,MAAa,eAAe,kBAAkB;CAAE,SAAS;CAAG;CAAM,SAAS,CAAC;CAAG,SAAS,CAAC;AAAE,CAAC"}
@@ -0,0 +1,182 @@
1
+ Object.defineProperties(exports, {
2
+ __esModule: { value: true },
3
+ [Symbol.toStringTag]: { value: "Module" }
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);
8
+ let node_path = require("node:path");
9
+ node_path = require_sessionDelivery.__toESM(node_path, 1);
10
+ let _agimon_ai_po_core_piSettings = require("@agimon-ai/po-core/piSettings");
11
+ let _agimon_ai_po_core_serverFacet = require("@agimon-ai/po-core/serverFacet");
12
+ let _agimon_ai_po_core_extensionFile = require("@agimon-ai/po-core/extensionFile");
13
+ //#region src/services/peerInbox/index.ts
14
+ const inboxes = /* @__PURE__ */ new Map();
15
+ /** Registers one live session-owned peer inbox. This registry is reachable only by the Session route. */
16
+ function registerSessionPeerInbox(sessionId, inbox) {
17
+ if (inboxes.has(sessionId)) throw new Error(`Session peer inbox '${sessionId}' is already registered.`);
18
+ inboxes.set(sessionId, inbox);
19
+ return () => {
20
+ if (inboxes.get(sessionId) === inbox) inboxes.delete(sessionId);
21
+ };
22
+ }
23
+ function deliverSessionPeerEnvelope(targetSessionId, sourceKey, type, payload) {
24
+ return inboxes.get(targetSessionId)?.(sourceKey, type, payload);
25
+ }
26
+ //#endregion
27
+ //#region src/extensions/workspaces/sessions/(backend)/root.server.ts
28
+ var root_server_default = (0, _agimon_ai_po_core_extensionFile.defineRoot)((pluginContext) => ({
29
+ value: void 0,
30
+ services: [require_sessionDelivery.provideBackgroundWorkService, (context) => {
31
+ const agent = pluginContext.agent;
32
+ const session = agent?.context.session;
33
+ const communication = pluginContext.host.context.sessionCommunication;
34
+ const homeDirectory = pluginContext.host.context.homeDirectory;
35
+ const sessionService = pluginContext.host.context.sessionService;
36
+ 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.");
37
+ const sessionDirectory = node_path.default.join((0, _agimon_ai_po_core_piSettings.poHomeDirectory)(homeDirectory), "session");
38
+ const deliveryDirectory = node_path.default.join(sessionDirectory, "delivery");
39
+ node_fs.default.mkdirSync(deliveryDirectory, {
40
+ recursive: true,
41
+ mode: 448
42
+ });
43
+ const peerCommunication = require_sessionDelivery.createPeerCommunication({
44
+ local: communication,
45
+ homeDirectory
46
+ });
47
+ const peerConfig = require_sessionDelivery.readSessionPeerConfig(homeDirectory);
48
+ const remotePeerIsGranted = (peerKey) => {
49
+ const peer = require_sessionDelivery.parseRemoteSessionReference(peerKey);
50
+ return peer !== void 0 && peerConfig?.peers.some((candidate) => candidate.hostId === peer.hostId && candidate.allowedSessionIds.includes(agent.context.sessionId)) === true;
51
+ };
52
+ 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;
54
+ const localHostId = peerConfig?.hostId;
55
+ const selfReference = localHostId ? require_sessionDelivery.peerSessionReference(localHostId, agent.context.sessionId) : void 0;
56
+ const groupAuthorization = groups && selfReference && localHostId ? require_sessionDelivery.createSessionGroupDeliveryAuthorizer({
57
+ selfReference,
58
+ groups,
59
+ peerReference: (peerKey) => require_sessionDelivery.parseRemoteSessionReference(peerKey) === void 0 ? require_sessionDelivery.peerSessionReference(localHostId, peerKey) : peerKey,
60
+ fallback: fallbackAuthorization
61
+ }) : 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
+ });
69
+ const unprovide = [];
70
+ if (groups && selfReference && peerConfig) {
71
+ const directory = require_sessionDelivery.createSessionDirectory({ authorizeDiscovery: (caller, subject) => {
72
+ const reference = require_sessionDelivery.peerSessionReference(subject.hostId, subject.sessionId);
73
+ return groups.groupsFor(caller).some((group) => group.members.includes(reference)) ? { capabilities: ["message"] } : void 0;
74
+ } });
75
+ for (const peer of peerConfig.peers) for (const sessionId of peer.allowedSessionIds) directory.observe({
76
+ hostId: peer.hostId,
77
+ sessionId,
78
+ hostIncarnation: "unknown",
79
+ sessionIncarnation: "unknown",
80
+ sequence: 0,
81
+ observedAt: 0,
82
+ staleAfterMs: 0,
83
+ deliveryTarget: require_sessionDelivery.peerSessionReference(peer.hostId, sessionId),
84
+ reachability: "unknown",
85
+ runtime: "unknown",
86
+ activity: "unknown",
87
+ voice: {
88
+ eligible: "unknown",
89
+ readiness: "unknown"
90
+ }
91
+ });
92
+ unprovide.push(context.provide(require_sessionDelivery.DOOM_SESSION_INTERCOM_SERVICE, require_sessionDelivery.createSessionIntercom({
93
+ selfReference,
94
+ directory,
95
+ groups,
96
+ delivery: service
97
+ })));
98
+ }
99
+ const unregister = registerSessionPeerInbox(agent.context.sessionId, (sourceKey, kind, payload) => service.receive?.(sourceKey, kind, payload));
100
+ return async () => {
101
+ unregister();
102
+ for (const release of unprovide.reverse()) release();
103
+ await closeDelivery();
104
+ groups?.close();
105
+ };
106
+ }]
107
+ }));
108
+ //#endregion
109
+ //#region src/services/peerInboxApi/index.ts
110
+ const MAX_BODY_BYTES = 65536;
111
+ const DELIVERY_TYPES = /* @__PURE__ */ new Set(["doom/session-delivery/envelope", "doom/session-delivery/ack"]);
112
+ function response(status, error) {
113
+ return Response.json({ error }, { status });
114
+ }
115
+ //#endregion
116
+ //#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
+ });
152
+ //#endregion
153
+ //#region generated/server.ts
154
+ const at = (value, context) => typeof value === "function" ? value(context) : value;
155
+ const facet = (0, _agimon_ai_po_core_serverFacet.defineServerPlugin)({
156
+ name: "@agimon-ai/po-session",
157
+ providerOverrides: (0, _agimon_ai_po_core_serverFacet.composeServerProviderOverrides)(root_server_default),
158
+ global: (context) => ({ get api() {
159
+ return [at(route_server_default, context)];
160
+ } }),
161
+ workspace: (context) => ({ get api() {
162
+ return [at(route_server_default, context)];
163
+ } }),
164
+ session: async (context) => {
165
+ const scopeSession = await root_server_default(context);
166
+ return {
167
+ ...(0, _agimon_ai_po_core_extensionFile.composeRootHooks)(scopeSession),
168
+ get api() {
169
+ return [at(route_server_default, context)];
170
+ },
171
+ services: [...scopeSession.services ?? []],
172
+ get activities() {
173
+ return [...scopeSession.activities ?? []];
174
+ }
175
+ };
176
+ }
177
+ });
178
+ //#endregion
179
+ exports.default = facet;
180
+ exports.facet = facet;
181
+
182
+ //# sourceMappingURL=server.cjs.map
@@ -0,0 +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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"}
@@ -0,0 +1,5 @@
1
+ //#region generated/server.d.ts
2
+ export declare const facet: import("@agimon-ai/po-core/serverFacet").DoomServerFacet;
3
+ //#endregion
4
+ export { facet as default };
5
+ //# sourceMappingURL=server.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.mts","names":[],"sources":["../../generated/server.ts"],"mappings":";qBAYa,gDAAK"}
@@ -0,0 +1,175 @@
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";
3
+ import path from "node:path";
4
+ import { poHomeDirectory } from "@agimon-ai/po-core/piSettings";
5
+ import { composeServerProviderOverrides, defineServerPlugin } from "@agimon-ai/po-core/serverFacet";
6
+ import { composeRootHooks, defineRoot, defineRoute } from "@agimon-ai/po-core/extensionFile";
7
+ //#region src/services/peerInbox/index.ts
8
+ const inboxes = /* @__PURE__ */ new Map();
9
+ /** Registers one live session-owned peer inbox. This registry is reachable only by the Session route. */
10
+ function registerSessionPeerInbox(sessionId, inbox) {
11
+ if (inboxes.has(sessionId)) throw new Error(`Session peer inbox '${sessionId}' is already registered.`);
12
+ inboxes.set(sessionId, inbox);
13
+ return () => {
14
+ if (inboxes.get(sessionId) === inbox) inboxes.delete(sessionId);
15
+ };
16
+ }
17
+ function deliverSessionPeerEnvelope(targetSessionId, sourceKey, type, payload) {
18
+ return inboxes.get(targetSessionId)?.(sourceKey, type, payload);
19
+ }
20
+ //#endregion
21
+ //#region src/extensions/workspaces/sessions/(backend)/root.server.ts
22
+ var root_server_default = defineRoot((pluginContext) => ({
23
+ value: void 0,
24
+ services: [provideBackgroundWorkService, (context) => {
25
+ const agent = pluginContext.agent;
26
+ const session = agent?.context.session;
27
+ const communication = pluginContext.host.context.sessionCommunication;
28
+ const homeDirectory = pluginContext.host.context.homeDirectory;
29
+ const sessionService = pluginContext.host.context.sessionService;
30
+ 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.");
31
+ const sessionDirectory = path.join(poHomeDirectory(homeDirectory), "session");
32
+ const deliveryDirectory = path.join(sessionDirectory, "delivery");
33
+ fs.mkdirSync(deliveryDirectory, {
34
+ recursive: true,
35
+ mode: 448
36
+ });
37
+ const peerCommunication = createPeerCommunication({
38
+ local: communication,
39
+ homeDirectory
40
+ });
41
+ const peerConfig = readSessionPeerConfig(homeDirectory);
42
+ const remotePeerIsGranted = (peerKey) => {
43
+ const peer = parseRemoteSessionReference(peerKey);
44
+ return peer !== void 0 && peerConfig?.peers.some((candidate) => candidate.hostId === peer.hostId && candidate.allowedSessionIds.includes(agent.context.sessionId)) === true;
45
+ };
46
+ 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;
48
+ const localHostId = peerConfig?.hostId;
49
+ const selfReference = localHostId ? peerSessionReference(localHostId, agent.context.sessionId) : void 0;
50
+ const groupAuthorization = groups && selfReference && localHostId ? createSessionGroupDeliveryAuthorizer({
51
+ selfReference,
52
+ groups,
53
+ peerReference: (peerKey) => parseRemoteSessionReference(peerKey) === void 0 ? peerSessionReference(localHostId, peerKey) : peerKey,
54
+ fallback: fallbackAuthorization
55
+ }) : 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
+ });
63
+ const unprovide = [];
64
+ if (groups && selfReference && peerConfig) {
65
+ const directory = createSessionDirectory({ authorizeDiscovery: (caller, subject) => {
66
+ const reference = peerSessionReference(subject.hostId, subject.sessionId);
67
+ return groups.groupsFor(caller).some((group) => group.members.includes(reference)) ? { capabilities: ["message"] } : void 0;
68
+ } });
69
+ for (const peer of peerConfig.peers) for (const sessionId of peer.allowedSessionIds) directory.observe({
70
+ hostId: peer.hostId,
71
+ sessionId,
72
+ hostIncarnation: "unknown",
73
+ sessionIncarnation: "unknown",
74
+ sequence: 0,
75
+ observedAt: 0,
76
+ staleAfterMs: 0,
77
+ deliveryTarget: peerSessionReference(peer.hostId, sessionId),
78
+ reachability: "unknown",
79
+ runtime: "unknown",
80
+ activity: "unknown",
81
+ voice: {
82
+ eligible: "unknown",
83
+ readiness: "unknown"
84
+ }
85
+ });
86
+ unprovide.push(context.provide(DOOM_SESSION_INTERCOM_SERVICE, createSessionIntercom({
87
+ selfReference,
88
+ directory,
89
+ groups,
90
+ delivery: service
91
+ })));
92
+ }
93
+ const unregister = registerSessionPeerInbox(agent.context.sessionId, (sourceKey, kind, payload) => service.receive?.(sourceKey, kind, payload));
94
+ return async () => {
95
+ unregister();
96
+ for (const release of unprovide.reverse()) release();
97
+ await closeDelivery();
98
+ groups?.close();
99
+ };
100
+ }]
101
+ }));
102
+ //#endregion
103
+ //#region src/services/peerInboxApi/index.ts
104
+ const MAX_BODY_BYTES = 65536;
105
+ const DELIVERY_TYPES = /* @__PURE__ */ new Set(["doom/session-delivery/envelope", "doom/session-delivery/ack"]);
106
+ function response(status, error) {
107
+ return Response.json({ error }, { status });
108
+ }
109
+ //#endregion
110
+ //#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
+ });
146
+ //#endregion
147
+ //#region generated/server.ts
148
+ const at = (value, context) => typeof value === "function" ? value(context) : value;
149
+ const facet = defineServerPlugin({
150
+ name: "@agimon-ai/po-session",
151
+ providerOverrides: composeServerProviderOverrides(root_server_default),
152
+ global: (context) => ({ get api() {
153
+ return [at(route_server_default, context)];
154
+ } }),
155
+ workspace: (context) => ({ get api() {
156
+ return [at(route_server_default, context)];
157
+ } }),
158
+ session: async (context) => {
159
+ const scopeSession = await root_server_default(context);
160
+ return {
161
+ ...composeRootHooks(scopeSession),
162
+ get api() {
163
+ return [at(route_server_default, context)];
164
+ },
165
+ services: [...scopeSession.services ?? []],
166
+ get activities() {
167
+ return [...scopeSession.activities ?? []];
168
+ }
169
+ };
170
+ }
171
+ });
172
+ //#endregion
173
+ export { facet as default, facet };
174
+
175
+ //# sourceMappingURL=server.mjs.map