@jungjaehoon/mama-os 0.74.0 → 0.76.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/CHANGELOG.md +84 -0
- package/README.md +6 -1
- package/dist/api/code-act-actions.d.ts +3 -1
- package/dist/api/code-act-actions.d.ts.map +1 -1
- package/dist/api/code-act-actions.js +16 -7
- package/dist/api/code-act-actions.js.map +1 -1
- package/dist/api/document-actions.d.ts +13 -0
- package/dist/api/document-actions.d.ts.map +1 -0
- package/dist/api/document-actions.js +55 -0
- package/dist/api/document-actions.js.map +1 -0
- package/dist/api/drive-actions.d.ts +3 -1
- package/dist/api/drive-actions.d.ts.map +1 -1
- package/dist/api/drive-actions.js +6 -1
- package/dist/api/drive-actions.js.map +1 -1
- package/dist/api/member-enrollment.d.ts +7 -4
- package/dist/api/member-enrollment.d.ts.map +1 -1
- package/dist/api/member-enrollment.js +65 -33
- package/dist/api/member-enrollment.js.map +1 -1
- package/dist/api/member-export.d.ts.map +1 -1
- package/dist/api/member-export.js +14 -6
- package/dist/api/member-export.js.map +1 -1
- package/dist/api/member-lifecycle.d.ts +23 -4
- package/dist/api/member-lifecycle.d.ts.map +1 -1
- package/dist/api/member-lifecycle.js +90 -19
- package/dist/api/member-lifecycle.js.map +1 -1
- package/dist/api/member-notice-actions.d.ts +18 -0
- package/dist/api/member-notice-actions.d.ts.map +1 -0
- package/dist/api/member-notice-actions.js +184 -0
- package/dist/api/member-notice-actions.js.map +1 -0
- package/dist/api/member-records.d.ts +17 -2
- package/dist/api/member-records.d.ts.map +1 -1
- package/dist/api/member-records.js +440 -82
- package/dist/api/member-records.js.map +1 -1
- package/dist/api/member-share-actions.d.ts +3 -3
- package/dist/api/member-share-actions.d.ts.map +1 -1
- package/dist/api/member-share-actions.js +49 -22
- package/dist/api/member-share-actions.js.map +1 -1
- package/dist/api/member-smoke-state.d.ts +3 -0
- package/dist/api/member-smoke-state.d.ts.map +1 -0
- package/dist/api/member-smoke-state.js +20 -0
- package/dist/api/member-smoke-state.js.map +1 -0
- package/dist/api/owner-policy-actions.d.ts +1 -0
- package/dist/api/owner-policy-actions.d.ts.map +1 -1
- package/dist/api/owner-policy-actions.js +8 -4
- package/dist/api/owner-policy-actions.js.map +1 -1
- package/dist/api/owner-timezone-actions.d.ts +3 -2
- package/dist/api/owner-timezone-actions.d.ts.map +1 -1
- package/dist/api/owner-timezone-actions.js +4 -6
- package/dist/api/owner-timezone-actions.js.map +1 -1
- package/dist/api/personal-job-actions.d.ts +6 -0
- package/dist/api/personal-job-actions.d.ts.map +1 -0
- package/dist/api/personal-job-actions.js +72 -0
- package/dist/api/personal-job-actions.js.map +1 -0
- package/dist/api/security-events.d.ts +6 -1
- package/dist/api/security-events.d.ts.map +1 -1
- package/dist/api/security-events.js +34 -15
- package/dist/api/security-events.js.map +1 -1
- package/dist/api/skill-actions.d.ts +5 -0
- package/dist/api/skill-actions.d.ts.map +1 -0
- package/dist/api/skill-actions.js +66 -0
- package/dist/api/skill-actions.js.map +1 -0
- package/dist/api/source-actions.d.ts.map +1 -1
- package/dist/api/source-actions.js +4 -2
- package/dist/api/source-actions.js.map +1 -1
- package/dist/api/stored-source-reader.d.ts.map +1 -1
- package/dist/api/stored-source-reader.js +9 -2
- package/dist/api/stored-source-reader.js.map +1 -1
- package/dist/api/trello-actions.d.ts +1 -1
- package/dist/api/trello-actions.d.ts.map +1 -1
- package/dist/api/trello-actions.js +19 -4
- package/dist/api/trello-actions.js.map +1 -1
- package/dist/api/work-actions.d.ts +12 -2
- package/dist/api/work-actions.d.ts.map +1 -1
- package/dist/api/work-actions.js +57 -10
- package/dist/api/work-actions.js.map +1 -1
- package/dist/cli/commands/daemon.d.ts +12 -2
- package/dist/cli/commands/daemon.d.ts.map +1 -1
- package/dist/cli/commands/daemon.js +80 -13
- package/dist/cli/commands/daemon.js.map +1 -1
- package/dist/cli/commands/init.d.ts +4 -0
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +27 -5
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/register-owner.d.ts +14 -5
- package/dist/cli/commands/register-owner.d.ts.map +1 -1
- package/dist/cli/commands/register-owner.js +145 -42
- package/dist/cli/commands/register-owner.js.map +1 -1
- package/dist/cli/commands/tools.d.ts +12 -0
- package/dist/cli/commands/tools.d.ts.map +1 -0
- package/dist/cli/commands/tools.js +109 -0
- package/dist/cli/commands/tools.js.map +1 -0
- package/dist/cli/index.js +8 -4
- package/dist/cli/index.js.map +1 -1
- package/dist/connectors/framework/polling-scheduler.d.ts +6 -1
- package/dist/connectors/framework/polling-scheduler.d.ts.map +1 -1
- package/dist/connectors/framework/polling-scheduler.js +25 -3
- package/dist/connectors/framework/polling-scheduler.js.map +1 -1
- package/dist/connectors/framework/stored-index-read.d.ts +2 -2
- package/dist/connectors/framework/stored-index-read.d.ts.map +1 -1
- package/dist/connectors/framework/stored-index-read.js +10 -3
- package/dist/connectors/framework/stored-index-read.js.map +1 -1
- package/dist/gateways/base-gateway.d.ts +7 -0
- package/dist/gateways/base-gateway.d.ts.map +1 -1
- package/dist/gateways/base-gateway.js +24 -0
- package/dist/gateways/base-gateway.js.map +1 -1
- package/dist/gateways/discord.d.ts +4 -0
- package/dist/gateways/discord.d.ts.map +1 -1
- package/dist/gateways/discord.js +24 -6
- package/dist/gateways/discord.js.map +1 -1
- package/dist/gateways/slack.d.ts +3 -0
- package/dist/gateways/slack.d.ts.map +1 -1
- package/dist/gateways/slack.js +15 -2
- package/dist/gateways/slack.js.map +1 -1
- package/dist/gateways/telegram-errors.d.ts +3 -0
- package/dist/gateways/telegram-errors.d.ts.map +1 -1
- package/dist/gateways/telegram-errors.js +5 -0
- package/dist/gateways/telegram-errors.js.map +1 -1
- package/dist/gateways/telegram-message-ledger.d.ts +7 -0
- package/dist/gateways/telegram-message-ledger.d.ts.map +1 -1
- package/dist/gateways/telegram-message-ledger.js +27 -2
- package/dist/gateways/telegram-message-ledger.js.map +1 -1
- package/dist/gateways/telegram.d.ts +33 -7
- package/dist/gateways/telegram.d.ts.map +1 -1
- package/dist/gateways/telegram.js +297 -59
- package/dist/gateways/telegram.js.map +1 -1
- package/dist/gateways/turn-contract.d.ts +8 -0
- package/dist/gateways/turn-contract.d.ts.map +1 -1
- package/dist/runtime/action-surface.d.ts +28 -3
- package/dist/runtime/action-surface.d.ts.map +1 -1
- package/dist/runtime/action-surface.js +316 -39
- package/dist/runtime/action-surface.js.map +1 -1
- package/dist/runtime/backend-security.d.ts +4 -0
- package/dist/runtime/backend-security.d.ts.map +1 -1
- package/dist/runtime/backend-security.js +8 -1
- package/dist/runtime/backend-security.js.map +1 -1
- package/dist/runtime/config.d.ts +1 -1
- package/dist/runtime/config.d.ts.map +1 -1
- package/dist/runtime/config.js +18 -11
- package/dist/runtime/config.js.map +1 -1
- package/dist/runtime/connectors.js +1 -1
- package/dist/runtime/connectors.js.map +1 -1
- package/dist/runtime/egress-proxy.d.ts +3 -3
- package/dist/runtime/egress-proxy.d.ts.map +1 -1
- package/dist/runtime/egress-proxy.js +16 -3
- package/dist/runtime/egress-proxy.js.map +1 -1
- package/dist/runtime/member-paths.d.ts +9 -1
- package/dist/runtime/member-paths.d.ts.map +1 -1
- package/dist/runtime/member-paths.js +67 -5
- package/dist/runtime/member-paths.js.map +1 -1
- package/dist/runtime/member-session.d.ts +21 -21
- package/dist/runtime/member-session.d.ts.map +1 -1
- package/dist/runtime/member-session.js +289 -34
- package/dist/runtime/member-session.js.map +1 -1
- package/dist/runtime/member-system-prompt.d.ts +1 -0
- package/dist/runtime/member-system-prompt.d.ts.map +1 -1
- package/dist/runtime/member-system-prompt.js +26 -4
- package/dist/runtime/member-system-prompt.js.map +1 -1
- package/dist/runtime/native-session.d.ts +6 -0
- package/dist/runtime/native-session.d.ts.map +1 -1
- package/dist/runtime/native-session.js +80 -13
- package/dist/runtime/native-session.js.map +1 -1
- package/dist/runtime/outbound-attempts.d.ts +5 -2
- package/dist/runtime/outbound-attempts.d.ts.map +1 -1
- package/dist/runtime/outbound-attempts.js +3 -2
- package/dist/runtime/outbound-attempts.js.map +1 -1
- package/dist/runtime/owner-authority.d.ts +15 -13
- package/dist/runtime/owner-authority.d.ts.map +1 -1
- package/dist/runtime/owner-authority.js +164 -8
- package/dist/runtime/owner-authority.js.map +1 -1
- package/dist/runtime/owner-runtime.d.ts +19 -4
- package/dist/runtime/owner-runtime.d.ts.map +1 -1
- package/dist/runtime/owner-runtime.js +446 -44
- package/dist/runtime/owner-runtime.js.map +1 -1
- package/dist/runtime/owner-system-prompt.d.ts +3 -1
- package/dist/runtime/owner-system-prompt.d.ts.map +1 -1
- package/dist/runtime/owner-system-prompt.js +15 -5
- package/dist/runtime/owner-system-prompt.js.map +1 -1
- package/dist/runtime/personal-job-cron.d.ts +6 -0
- package/dist/runtime/personal-job-cron.d.ts.map +1 -0
- package/dist/runtime/personal-job-cron.js +154 -0
- package/dist/runtime/personal-job-cron.js.map +1 -0
- package/dist/runtime/personal-jobs.d.ts +107 -0
- package/dist/runtime/personal-jobs.d.ts.map +1 -0
- package/dist/runtime/personal-jobs.js +353 -0
- package/dist/runtime/personal-jobs.js.map +1 -0
- package/dist/runtime/personal-notices.d.ts +38 -0
- package/dist/runtime/personal-notices.d.ts.map +1 -0
- package/dist/runtime/personal-notices.js +137 -0
- package/dist/runtime/personal-notices.js.map +1 -0
- package/dist/runtime/personal-turns.d.ts +23 -0
- package/dist/runtime/personal-turns.d.ts.map +1 -0
- package/dist/runtime/personal-turns.js +51 -0
- package/dist/runtime/personal-turns.js.map +1 -0
- package/dist/runtime/principal-access.d.ts +8 -0
- package/dist/runtime/principal-access.d.ts.map +1 -1
- package/dist/runtime/principal-access.js +99 -4
- package/dist/runtime/principal-access.js.map +1 -1
- package/dist/runtime/principal-sessions.d.ts +16 -2
- package/dist/runtime/principal-sessions.d.ts.map +1 -1
- package/dist/runtime/principal-sessions.js +104 -21
- package/dist/runtime/principal-sessions.js.map +1 -1
- package/dist/runtime/skills.d.ts +86 -0
- package/dist/runtime/skills.d.ts.map +1 -0
- package/dist/runtime/skills.js +560 -0
- package/dist/runtime/skills.js.map +1 -0
- package/dist/runtime/stimulus-delivery.d.ts +22 -1
- package/dist/runtime/stimulus-delivery.d.ts.map +1 -1
- package/dist/runtime/stimulus-delivery.js +192 -19
- package/dist/runtime/stimulus-delivery.js.map +1 -1
- package/dist/runtime/tools-paths.d.ts +9 -0
- package/dist/runtime/tools-paths.d.ts.map +1 -0
- package/dist/runtime/tools-paths.js +98 -0
- package/dist/runtime/tools-paths.js.map +1 -0
- package/dist/runtime/turn-orders.d.ts +22 -2
- package/dist/runtime/turn-orders.d.ts.map +1 -1
- package/dist/runtime/turn-orders.js +60 -6
- package/dist/runtime/turn-orders.js.map +1 -1
- package/dist/storage/chat-sources.d.ts +21 -0
- package/dist/storage/chat-sources.d.ts.map +1 -1
- package/dist/storage/chat-sources.js +66 -4
- package/dist/storage/chat-sources.js.map +1 -1
- package/dist/storage/source-archive.d.ts +13 -0
- package/dist/storage/source-archive.d.ts.map +1 -1
- package/dist/storage/source-archive.js +103 -0
- package/dist/storage/source-archive.js.map +1 -1
- package/dist/tools/command-runner.d.ts +12 -0
- package/dist/tools/command-runner.d.ts.map +1 -0
- package/dist/tools/command-runner.js +65 -0
- package/dist/tools/command-runner.js.map +1 -0
- package/dist/tools/document-render.d.ts +24 -0
- package/dist/tools/document-render.d.ts.map +1 -0
- package/dist/tools/document-render.js +208 -0
- package/dist/tools/document-render.js.map +1 -0
- package/dist/tools/document-toolkit.d.ts +15 -0
- package/dist/tools/document-toolkit.d.ts.map +1 -0
- package/dist/tools/document-toolkit.js +501 -0
- package/dist/tools/document-toolkit.js.map +1 -0
- package/dist/tools/office-render.d.ts +28 -0
- package/dist/tools/office-render.d.ts.map +1 -0
- package/dist/tools/office-render.js +98 -0
- package/dist/tools/office-render.js.map +1 -0
- package/dist/tools/toolkit-process.d.ts +9 -0
- package/dist/tools/toolkit-process.d.ts.map +1 -0
- package/dist/tools/toolkit-process.js +148 -0
- package/dist/tools/toolkit-process.js.map +1 -0
- package/dist/tools/toolkit-state.d.ts +22 -0
- package/dist/tools/toolkit-state.d.ts.map +1 -0
- package/dist/tools/toolkit-state.js +116 -0
- package/dist/tools/toolkit-state.js.map +1 -0
- package/package.json +3 -2
- package/toolkit/inspect-office-zip.py +16 -0
- package/toolkit/rasterize.py +29 -0
- package/toolkit/requirements.lock +664 -0
- package/toolkit/smoke.py +32 -0
|
@@ -4,8 +4,14 @@ exports.readOwnerMemoryRecords = readOwnerMemoryRecords;
|
|
|
4
4
|
exports.runtimeModelRun = runtimeModelRun;
|
|
5
5
|
exports.guidanceInSearchOrder = guidanceInSearchOrder;
|
|
6
6
|
exports.createOwnerRuntime = createOwnerRuntime;
|
|
7
|
+
const personal_jobs_js_1 = require("./personal-jobs.js");
|
|
8
|
+
const skills_js_1 = require("./skills.js");
|
|
9
|
+
const node_os_1 = require("node:os");
|
|
7
10
|
const mama_core_1 = require("@jungjaehoon/mama-core");
|
|
11
|
+
const personal_notices_js_1 = require("./personal-notices.js");
|
|
12
|
+
const runtime_1 = require("@jungjaehoon/mama-core/runtime/runtime");
|
|
8
13
|
const principal_sessions_js_1 = require("./principal-sessions.js");
|
|
14
|
+
const telegram_message_ledger_js_1 = require("../gateways/telegram-message-ledger.js");
|
|
9
15
|
const member_session_js_1 = require("./member-session.js");
|
|
10
16
|
const backend_security_js_1 = require("./backend-security.js");
|
|
11
17
|
const member_paths_js_1 = require("./member-paths.js");
|
|
@@ -13,7 +19,9 @@ const principal_access_js_1 = require("./principal-access.js");
|
|
|
13
19
|
const mama_core_2 = require("@jungjaehoon/mama-core");
|
|
14
20
|
const node_crypto_1 = require("node:crypto");
|
|
15
21
|
const node_path_1 = require("node:path");
|
|
22
|
+
const node_fs_1 = require("node:fs");
|
|
16
23
|
const stored_source_reader_js_1 = require("../api/stored-source-reader.js");
|
|
24
|
+
const backend_security_js_2 = require("./backend-security.js");
|
|
17
25
|
const report_persistence_js_1 = require("../api/report-persistence.js");
|
|
18
26
|
const obsidian_writer_js_1 = require("../wiki/obsidian-writer.js");
|
|
19
27
|
const source_archive_js_1 = require("../storage/source-archive.js");
|
|
@@ -84,7 +92,7 @@ function isOwnerGuidanceRecord(record) {
|
|
|
84
92
|
return owner_authority_js_1.RULE_KINDS.includes(record.kind);
|
|
85
93
|
}
|
|
86
94
|
/** Active guidance among the search hits, in the search's own order. */
|
|
87
|
-
function guidanceInSearchOrder(hitIds, records, limit, ownerRuleRecordIds) {
|
|
95
|
+
function guidanceInSearchOrder(hitIds, records, limit, ownerRuleRecordIds, skillScopes = []) {
|
|
88
96
|
const active = new Map(records
|
|
89
97
|
.filter((record) => isOwnerGuidanceRecord(record) && record.status === 'active')
|
|
90
98
|
.map((record) => [record.id, record]));
|
|
@@ -98,6 +106,10 @@ function guidanceInSearchOrder(hitIds, records, limit, ownerRuleRecordIds) {
|
|
|
98
106
|
topic: record.topic,
|
|
99
107
|
summary: record.summary,
|
|
100
108
|
...(record.applies_when ? { appliesWhen: record.applies_when } : {}),
|
|
109
|
+
...(record.kind === 'workflow' &&
|
|
110
|
+
record.scopes.some((scope) => skillScopes.some((admitted) => admitted.kind === scope.kind && admitted.id === scope.id))
|
|
111
|
+
? { skillId: record.id }
|
|
112
|
+
: {}),
|
|
101
113
|
ownerRule: ownerRuleRecordIds.has(record.id),
|
|
102
114
|
},
|
|
103
115
|
]
|
|
@@ -119,7 +131,15 @@ async function createOwnerRuntime(options) {
|
|
|
119
131
|
let stopping = false;
|
|
120
132
|
const sessions = (0, principal_sessions_js_1.createPrincipalSessions)(options.ownerPrincipalId, {
|
|
121
133
|
onUncertain: options.onStimulusUncertain,
|
|
122
|
-
onDead:
|
|
134
|
+
onDead: async (row, reason) => {
|
|
135
|
+
if ((0, personal_notices_js_1.personalNoticePayload)(row)) {
|
|
136
|
+
await actionSurface?.personalNotices.close(row);
|
|
137
|
+
const ledger = options.messageLedger?.();
|
|
138
|
+
if (ledger)
|
|
139
|
+
(0, telegram_message_ledger_js_1.retirePersonalTurnReceipt)(ledger, row.stimulusId);
|
|
140
|
+
}
|
|
141
|
+
await options.onStimulusDead?.(row, reason);
|
|
142
|
+
},
|
|
123
143
|
}, (id) => {
|
|
124
144
|
if (stopping)
|
|
125
145
|
throw new Error('Runtime is stopping');
|
|
@@ -133,13 +153,12 @@ async function createOwnerRuntime(options) {
|
|
|
133
153
|
const reportSseClients = new Set();
|
|
134
154
|
let wikiRoot = null;
|
|
135
155
|
try {
|
|
156
|
+
const principals = (0, mama_core_1.createPrincipalRepository)(database.adapter);
|
|
136
157
|
const knowledge = (0, mama_core_2.createKnowledge)({
|
|
137
158
|
adapter: database.adapter,
|
|
138
159
|
embedder: runtimeEmbedder(options),
|
|
139
160
|
});
|
|
140
|
-
const activeMembers = (
|
|
141
|
-
.listMembers()
|
|
142
|
-
.filter((member) => member.status === 'active');
|
|
161
|
+
const activeMembers = principals.listMembers().filter((member) => member.status === 'active');
|
|
143
162
|
if (activeMembers.length && options.memberRoot === undefined)
|
|
144
163
|
throw new Error('Active members require member_root');
|
|
145
164
|
const ownerDeniedPaths = options.memberRoot === undefined ? [] : (0, backend_security_js_1.ownerDataReadPaths)(options);
|
|
@@ -150,6 +169,15 @@ async function createOwnerRuntime(options) {
|
|
|
150
169
|
rawStore = new source_archive_js_1.RawStore(options.rawPath);
|
|
151
170
|
const chat = new chat_sources_js_1.ChatSources(rawStore, database.adapter, options.ownerPrincipalId, options.agentId);
|
|
152
171
|
const sourceStore = rawStore;
|
|
172
|
+
// A member's personal copy of a host exchange; the common management copy is separate.
|
|
173
|
+
const writeMemberHostExchange = (id, { message, reply }) => {
|
|
174
|
+
const archive = new chat_sources_js_1.ChatSources(sourceStore, database.adapter, id, `member-agent:${id}`);
|
|
175
|
+
archive.saveOwnerMessage({ ...message, senderPrincipalId: id });
|
|
176
|
+
archive.saveReply({ ...reply, messageRef: message.id, author: 'host' });
|
|
177
|
+
};
|
|
178
|
+
// A member's chat archive: its personal records for a DM, the common records for a room
|
|
179
|
+
// (as its room session keeps them).
|
|
180
|
+
const memberChat = (id, room) => new chat_sources_js_1.ChatSources(sourceStore, database.adapter, room === undefined ? id : options.ownerPrincipalId, `member-agent:${id}`);
|
|
153
181
|
const storedSourceReader = (0, stored_source_reader_js_1.createStoredSourceReader)({
|
|
154
182
|
adapter: database.adapter,
|
|
155
183
|
rawStore: () => sourceStore,
|
|
@@ -182,7 +210,41 @@ async function createOwnerRuntime(options) {
|
|
|
182
210
|
})()
|
|
183
211
|
: {};
|
|
184
212
|
const surface = (0, action_surface_js_1.createActionSurface)({
|
|
213
|
+
commonChannelId: options.commonChannelId,
|
|
185
214
|
runtimeRoot: options.runtimeRoot,
|
|
215
|
+
skillPaths: {
|
|
216
|
+
toolsRoot: options.toolsRoot,
|
|
217
|
+
workspace: (id) => {
|
|
218
|
+
if (id === options.ownerPrincipalId)
|
|
219
|
+
return options.workspaceDir;
|
|
220
|
+
if (memberRoot === undefined)
|
|
221
|
+
throw new Error('Member skills require member_root');
|
|
222
|
+
return (0, member_paths_js_1.memberPaths)(memberRoot, id).workspaceDir;
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
documentPorts: {
|
|
226
|
+
toolsRoot: options.toolsRoot,
|
|
227
|
+
workspaceDir: options.workspaceDir,
|
|
228
|
+
deniedReadPaths: [
|
|
229
|
+
(0, node_os_1.homedir)(),
|
|
230
|
+
...(0, backend_security_js_2.credentialReadPaths)(options.runtimeRoot, options.codexHome, options.replayKeyFile),
|
|
231
|
+
...(0, backend_security_js_1.ownerNativeDataReadPaths)(options),
|
|
232
|
+
],
|
|
233
|
+
...(memberRoot === undefined
|
|
234
|
+
? {}
|
|
235
|
+
: {
|
|
236
|
+
principalPaths: (id) => {
|
|
237
|
+
sessions.get(id);
|
|
238
|
+
return members.get(id).activePaths();
|
|
239
|
+
},
|
|
240
|
+
// Linked document resources have the same private-data boundary as native tools.
|
|
241
|
+
principalDeniedReadPaths: (id) => {
|
|
242
|
+
sessions.get(id);
|
|
243
|
+
return members.get(id).activeDeniedReadPaths();
|
|
244
|
+
},
|
|
245
|
+
}),
|
|
246
|
+
},
|
|
247
|
+
memberDirectoryChange: sessions.exclusive,
|
|
186
248
|
adapter: database.adapter,
|
|
187
249
|
...(options.outboundAttempts === undefined
|
|
188
250
|
? {}
|
|
@@ -195,14 +257,43 @@ async function createOwnerRuntime(options) {
|
|
|
195
257
|
storedSourceReader,
|
|
196
258
|
timeZone: options.timeZone,
|
|
197
259
|
configPath: (0, node_path_1.join)(options.runtimeRoot, 'config.yaml'),
|
|
198
|
-
isOwnerMessageTurn: (sourceMessageRef, principalId
|
|
260
|
+
isOwnerMessageTurn: (sourceMessageRef, principalId) => ownerMailbox?.readInput(sourceMessageRef, principalId)?.kind === 'owner_message',
|
|
261
|
+
recordManagementReceipt: ({ callerPrincipalId, operationId, action, input, result }) => {
|
|
262
|
+
const at = Date.now();
|
|
263
|
+
const ref = `host:management:${operationId}`;
|
|
264
|
+
chat.saveOwnerMessage({
|
|
265
|
+
id: ref,
|
|
266
|
+
channelKey: 'management',
|
|
267
|
+
...(callerPrincipalId === undefined ? {} : { senderPrincipalId: callerPrincipalId }),
|
|
268
|
+
occurredAt: at,
|
|
269
|
+
text: action,
|
|
270
|
+
payload: { managementAction: action },
|
|
271
|
+
});
|
|
272
|
+
chat.saveReply({
|
|
273
|
+
messageRef: ref,
|
|
274
|
+
text: JSON.stringify({ action, input, result }),
|
|
275
|
+
occurredAt: at,
|
|
276
|
+
deliveryVerified: true,
|
|
277
|
+
author: 'host',
|
|
278
|
+
});
|
|
279
|
+
},
|
|
199
280
|
// Without member_root no member can be served or set aside, so no change may be committed.
|
|
200
281
|
...(memberRoot === undefined
|
|
201
282
|
? {}
|
|
202
283
|
: {
|
|
284
|
+
memberNotices: {
|
|
285
|
+
mailbox: () => ownerMailbox,
|
|
286
|
+
wake: () => {
|
|
287
|
+
// The recipient delivery prepares service outside the common turn.
|
|
288
|
+
void intakeRuntime
|
|
289
|
+
.drainOnce()
|
|
290
|
+
.catch((error) => options.onPersonalJobError?.(error));
|
|
291
|
+
},
|
|
292
|
+
},
|
|
203
293
|
memberRecords: {
|
|
204
294
|
root: memberRoot,
|
|
205
295
|
isStopping: () => stopping,
|
|
296
|
+
isReady: () => options.deliveryReady?.() ?? true,
|
|
206
297
|
resumeQueued: () => {
|
|
207
298
|
void intakeRuntime
|
|
208
299
|
.drainOnce()
|
|
@@ -218,7 +309,12 @@ async function createOwnerRuntime(options) {
|
|
|
218
309
|
serve: (id) => {
|
|
219
310
|
serveMember(id);
|
|
220
311
|
},
|
|
312
|
+
writeHostExchange: (id, exchange) => writeMemberHostExchange(id, exchange),
|
|
221
313
|
recordExchange: (id, kind, ref, text, deliveryVerified) => {
|
|
314
|
+
if (database.adapter
|
|
315
|
+
.prepare("SELECT 1 FROM connector_event_index WHERE source_connector='chat' AND source_entity_id=? AND memory_scope_id=?")
|
|
316
|
+
.get(`${ref}:reply`, id))
|
|
317
|
+
return;
|
|
222
318
|
const memberChat = new chat_sources_js_1.ChatSources(sourceStore, database.adapter, id, `member-agent:${id}`);
|
|
223
319
|
const at = Date.now();
|
|
224
320
|
memberChat.saveOwnerMessage({
|
|
@@ -237,25 +333,39 @@ async function createOwnerRuntime(options) {
|
|
|
237
333
|
},
|
|
238
334
|
},
|
|
239
335
|
memberLifecycle: {
|
|
336
|
+
mailbox: () => ownerMailbox,
|
|
337
|
+
isStopping: () => stopping,
|
|
338
|
+
isServed: (id) => members.has(id),
|
|
339
|
+
resumeQueued: () => {
|
|
340
|
+
void intakeRuntime
|
|
341
|
+
.drainOnce()
|
|
342
|
+
.catch((error) => console.error('[Member management] delivery failed', error));
|
|
343
|
+
},
|
|
344
|
+
turnChain: sessions.turnChain,
|
|
240
345
|
resetSession: async (id) => {
|
|
241
346
|
const member = members.get(id);
|
|
242
347
|
if (!member)
|
|
243
348
|
throw new Error(`Member session is not served: ${id}`);
|
|
244
349
|
await member.native.resetSession(member.native.sessionKey);
|
|
245
350
|
},
|
|
246
|
-
cancelQueued: (id, reason) =>
|
|
351
|
+
cancelQueued: (id, reason) => {
|
|
352
|
+
if (reason === 'member_suspend')
|
|
353
|
+
options.memberEnrollment?.cancelSelection?.(id);
|
|
354
|
+
return ownerMailbox.cancelQueued(id, reason);
|
|
355
|
+
},
|
|
247
356
|
retire: (id) => retireMember(id),
|
|
248
357
|
resume: async (id) => {
|
|
249
358
|
// A suspended member may retain a session after an earlier stop/move failure.
|
|
250
359
|
await retireMember(id);
|
|
251
|
-
const
|
|
252
|
-
|
|
360
|
+
const resumedAt = Date.now();
|
|
361
|
+
await surface.personalJobs.resume(id, resumedAt);
|
|
362
|
+
principals.resume(id, resumedAt);
|
|
253
363
|
try {
|
|
254
364
|
serveMember(id);
|
|
255
365
|
}
|
|
256
366
|
catch (error) {
|
|
257
367
|
// A failed preparation never leaves an active registry entry with a partial runtime.
|
|
258
|
-
|
|
368
|
+
principals.suspend(id, Date.now());
|
|
259
369
|
throw error;
|
|
260
370
|
}
|
|
261
371
|
},
|
|
@@ -268,7 +378,14 @@ async function createOwnerRuntime(options) {
|
|
|
268
378
|
...options.memberEnrollment,
|
|
269
379
|
memberRoot,
|
|
270
380
|
serveMember: (id) => {
|
|
381
|
+
if (stopping)
|
|
382
|
+
return 'next_start';
|
|
383
|
+
// Serving recreates the member's paths; an erasure checks them against the
|
|
384
|
+
// delivered export and serves the member itself when it ends.
|
|
385
|
+
if (surface.memberRecords.excludePrincipals().includes(id))
|
|
386
|
+
return 'after_erasure';
|
|
271
387
|
serveMember(id);
|
|
388
|
+
return undefined;
|
|
272
389
|
},
|
|
273
390
|
},
|
|
274
391
|
}),
|
|
@@ -286,7 +403,7 @@ async function createOwnerRuntime(options) {
|
|
|
286
403
|
downloadsDir: options.attachmentPorts?.downloadsDir ?? '',
|
|
287
404
|
};
|
|
288
405
|
sessions.get(principalId);
|
|
289
|
-
return members.get(principalId).
|
|
406
|
+
return members.get(principalId).activePaths();
|
|
290
407
|
},
|
|
291
408
|
},
|
|
292
409
|
...(options.driveDelivery === undefined
|
|
@@ -313,6 +430,7 @@ async function createOwnerRuntime(options) {
|
|
|
313
430
|
model: options.model,
|
|
314
431
|
workspaceDir: options.workspaceDir,
|
|
315
432
|
runtimeRoot: options.runtimeRoot,
|
|
433
|
+
toolsRoot: options.toolsRoot,
|
|
316
434
|
deniedReadPaths: (0, backend_security_js_1.ownerNativeDataReadPaths)(options),
|
|
317
435
|
replayKeyFile: options.replayKeyFile,
|
|
318
436
|
...(options.sandboxNetworkProxy === undefined
|
|
@@ -345,9 +463,61 @@ async function createOwnerRuntime(options) {
|
|
|
345
463
|
...(options.onRecordOrderEvent === undefined ? {} : { onEvent: options.onRecordOrderEvent }),
|
|
346
464
|
});
|
|
347
465
|
delivery = (0, stimulus_delivery_js_1.createStimulusDelivery)({
|
|
348
|
-
|
|
466
|
+
skills: (0, skills_js_1.createSkillTurnContext)(() => surface.skills.list(options.ownerPrincipalId)),
|
|
467
|
+
turnChain: sessions.turnChain(options.ownerPrincipalId),
|
|
349
468
|
backend: options.backend,
|
|
350
469
|
timeZone: options.timeZone,
|
|
470
|
+
principalRoles: (principalId) => principals.listRoles(principalId).map(({ role }) => role),
|
|
471
|
+
principalDisplayName: (principalId) => principals.findById(principalId)?.displayName,
|
|
472
|
+
commonPrincipalId: options.ownerPrincipalId,
|
|
473
|
+
ownerMessageSenderActive: (id) => {
|
|
474
|
+
const person = principals.findById(id);
|
|
475
|
+
return (person?.kind === 'member' && person.status === 'active' && (0, owner_authority_js_1.holdsOwnerRole)(principals, id));
|
|
476
|
+
},
|
|
477
|
+
rerouteOwnerMessage: async (row, sender) => {
|
|
478
|
+
const person = principals.findById(sender);
|
|
479
|
+
if (person?.kind !== 'member' ||
|
|
480
|
+
person.status !== 'active' ||
|
|
481
|
+
!row.stimulusId.startsWith(`telegram:${row.channelKey}:`) ||
|
|
482
|
+
!options.roomChannels?.includes(row.channelKey))
|
|
483
|
+
return false;
|
|
484
|
+
await serveMemberOnLane(sender);
|
|
485
|
+
// The room session cannot read the common downloads; its files go to the room's own.
|
|
486
|
+
const payload = row.payload;
|
|
487
|
+
let input = payload.input;
|
|
488
|
+
if (input?.attachments?.length) {
|
|
489
|
+
const target = (0, node_path_1.join)(members.get(sender).roomSession(row.channelKey).paths.downloadsDir, 'telegram');
|
|
490
|
+
(0, node_fs_1.mkdirSync)(target, { recursive: true });
|
|
491
|
+
input = {
|
|
492
|
+
...input,
|
|
493
|
+
attachments: input.attachments.map((attachment) => {
|
|
494
|
+
if (!('path' in attachment))
|
|
495
|
+
return attachment;
|
|
496
|
+
const path = (0, node_path_1.join)(target, (0, node_path_1.basename)(attachment.path));
|
|
497
|
+
(0, node_fs_1.copyFileSync)(attachment.path, path);
|
|
498
|
+
return { ...attachment, path };
|
|
499
|
+
}),
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
// A deterministic handoff ref survives a crash between this append and the common
|
|
503
|
+
// acknowledgement. Mailbox ref identities are global; keep the evidence handles.
|
|
504
|
+
intakeRuntime.accept({
|
|
505
|
+
id: row.stimulusId,
|
|
506
|
+
kind: 'owner_message',
|
|
507
|
+
principalId: sender,
|
|
508
|
+
channelKey: row.channelKey,
|
|
509
|
+
occurredAt: row.occurredAt,
|
|
510
|
+
refs: row.refs.map((ref) => ({ ...ref, refId: `room-handoff:${sender}:${ref.refId}` })),
|
|
511
|
+
replyTo: row.replyTo,
|
|
512
|
+
payload: {
|
|
513
|
+
...row.payload,
|
|
514
|
+
...(input === undefined ? {} : { input }),
|
|
515
|
+
senderPrincipalId: sender,
|
|
516
|
+
roomChannel: row.channelKey,
|
|
517
|
+
},
|
|
518
|
+
});
|
|
519
|
+
return true;
|
|
520
|
+
},
|
|
351
521
|
wikiEnabled: options.wiki?.enabled ?? false,
|
|
352
522
|
formattingRoutes: options.formattingRoutes ?? {
|
|
353
523
|
reports: 'telegram',
|
|
@@ -360,7 +530,13 @@ async function createOwnerRuntime(options) {
|
|
|
360
530
|
? {}
|
|
361
531
|
: { onUncertain: options.onStimulusUncertain }),
|
|
362
532
|
...(options.onStimulusDead === undefined ? {} : { onDead: options.onStimulusDead }),
|
|
363
|
-
|
|
533
|
+
onSkipped: async (row, reason) => {
|
|
534
|
+
if (row.kind === 'owner_message')
|
|
535
|
+
database.adapter
|
|
536
|
+
.prepare('UPDATE mailbox_inputs SET last_error = ? WHERE id = ?')
|
|
537
|
+
.run(reason, row.id);
|
|
538
|
+
await options.onStimulusSkipped?.(row, reason);
|
|
539
|
+
},
|
|
364
540
|
...(options.closeUncertain === undefined ? {} : { closeUncertain: options.closeUncertain }),
|
|
365
541
|
sessionStart: (row) => (0, session_start_context_js_1.readSessionStartInput)({
|
|
366
542
|
exchanges: chat.recentExchanges(row.stimulusId),
|
|
@@ -405,7 +581,7 @@ async function createOwnerRuntime(options) {
|
|
|
405
581
|
});
|
|
406
582
|
// Ten in search order: the session filter drops lessons already shown and keeps three.
|
|
407
583
|
const hitIds = hits.map((hit) => hit.id);
|
|
408
|
-
return guidanceInSearchOrder(hitIds, active, 10, (0, owner_authority_js_1.ownerRuleIds)(database.adapter, hitIds, options.ownerPrincipalId));
|
|
584
|
+
return guidanceInSearchOrder(hitIds, active, 10, (0, owner_authority_js_1.ownerRuleIds)(database.adapter, hitIds, options.ownerPrincipalId), surface.skills.workflowScopes(options.ownerPrincipalId));
|
|
409
585
|
}),
|
|
410
586
|
recordOrders,
|
|
411
587
|
...(options.onOwnerResult === undefined ? {} : { onOwnerResult: options.onOwnerResult }),
|
|
@@ -422,19 +598,19 @@ async function createOwnerRuntime(options) {
|
|
|
422
598
|
const prepareMember = (principalId) => {
|
|
423
599
|
if (memberRoot === undefined)
|
|
424
600
|
throw new Error('Active members require member_root');
|
|
425
|
-
const principal =
|
|
601
|
+
const principal = principals.findById(principalId);
|
|
426
602
|
if (principal?.kind !== 'member' || principal.status !== 'active')
|
|
427
603
|
throw new Error(`serveMember requires an active member: ${principalId}`);
|
|
428
604
|
const member = (0, member_session_js_1.createMemberSession)(principalId, memberRoot, {
|
|
429
605
|
options,
|
|
606
|
+
personalJobs: surface.personalJobs,
|
|
607
|
+
ownerPolicyProvider,
|
|
430
608
|
ownerDeniedPaths,
|
|
431
|
-
registeredMemberIds: () => (
|
|
432
|
-
.listMembers()
|
|
433
|
-
.map((row) => row.principalId),
|
|
609
|
+
registeredMemberIds: () => principals.listMembers().map((row) => row.principalId),
|
|
434
610
|
database,
|
|
435
611
|
rawStore: sourceStore,
|
|
436
612
|
surface,
|
|
437
|
-
turnChain: sessions.turnChain,
|
|
613
|
+
turnChain: sessions.turnChain(principalId),
|
|
438
614
|
readResult: (row) => row.nativeDelivery?.receipt
|
|
439
615
|
? intakeRuntime.mailbox.nativeInputs.resultForReceipt(row.nativeDelivery.receipt, row.principalId)
|
|
440
616
|
: null,
|
|
@@ -444,16 +620,22 @@ async function createOwnerRuntime(options) {
|
|
|
444
620
|
return member;
|
|
445
621
|
};
|
|
446
622
|
for (const member of activeMembers)
|
|
447
|
-
|
|
448
|
-
|
|
623
|
+
if (!surface.memberRecords.isBlocked(member.principalId))
|
|
624
|
+
prepareMember(member.principalId);
|
|
625
|
+
const executionAccess = (principalId, sessionKey) => principalId === options.ownerPrincipalId
|
|
449
626
|
? access
|
|
450
627
|
: (0, principal_access_js_1.resolvePrincipalAccess)(principalId, {
|
|
451
628
|
adapter: database.adapter,
|
|
452
629
|
ownerAccess: access,
|
|
453
630
|
agentId: `member-agent:${principalId}`,
|
|
631
|
+
sessionKey,
|
|
632
|
+
commonChannelId: options.commonChannelId,
|
|
454
633
|
});
|
|
455
634
|
const socketDispatch = Object.assign(async (...[call, context]) => {
|
|
456
|
-
context = {
|
|
635
|
+
context = {
|
|
636
|
+
...context,
|
|
637
|
+
access: executionAccess(context.access.principalId, members.get(context.access.principalId)?.activeSessionKey()),
|
|
638
|
+
};
|
|
457
639
|
const caller = context.session?.nativeCaller;
|
|
458
640
|
if (caller !== undefined) {
|
|
459
641
|
const principalSession = sessions.get(context.access.principalId).native;
|
|
@@ -480,6 +662,7 @@ async function createOwnerRuntime(options) {
|
|
|
480
662
|
executionAccess(callerAccess.principalId);
|
|
481
663
|
const ceiling = sessions.replaySourceEndMs(callerAccess.principalId);
|
|
482
664
|
return {
|
|
665
|
+
...members.get(callerAccess.principalId)?.activeSessionFacts(),
|
|
483
666
|
...(ceiling === undefined ? {} : { replaySourceEndMs: ceiling }),
|
|
484
667
|
...(request.session?.nativeCaller === undefined
|
|
485
668
|
? {}
|
|
@@ -490,8 +673,80 @@ async function createOwnerRuntime(options) {
|
|
|
490
673
|
nativeSession: sessions.native,
|
|
491
674
|
delivery: {
|
|
492
675
|
...sessions.delivery,
|
|
676
|
+
deliverHost: surface.deliverManagement,
|
|
677
|
+
prepareNative: async (row) => {
|
|
678
|
+
const payload = row.payload;
|
|
679
|
+
if (row.kind === 'owner_message' && payload?.deferredMemberMessage) {
|
|
680
|
+
if (!intakeRuntime.servesPrincipal(row.principalId))
|
|
681
|
+
await serveMemberOnLane(row.principalId);
|
|
682
|
+
// A room message belongs to its room session's common archive, never to the
|
|
683
|
+
// member's personal records.
|
|
684
|
+
const room = payload.roomChannel;
|
|
685
|
+
const archive = memberChat(row.principalId, room);
|
|
686
|
+
const existing = database.adapter
|
|
687
|
+
.prepare("SELECT current_observation_id, metadata_json FROM connector_event_index WHERE source_connector='chat' AND source_id=? AND memory_scope_id=?")
|
|
688
|
+
.get(row.stimulusId, room === undefined ? row.principalId : options.ownerPrincipalId);
|
|
689
|
+
let observation;
|
|
690
|
+
let input = payload.input;
|
|
691
|
+
if (existing) {
|
|
692
|
+
observation = existing.current_observation_id;
|
|
693
|
+
input = JSON.parse(existing.metadata_json).input ?? undefined;
|
|
694
|
+
}
|
|
695
|
+
else {
|
|
696
|
+
let message = {
|
|
697
|
+
id: row.stimulusId,
|
|
698
|
+
channelKey: row.channelKey,
|
|
699
|
+
occurredAt: row.occurredAt,
|
|
700
|
+
text: payload.text ?? '',
|
|
701
|
+
replyTo: row.replyTo,
|
|
702
|
+
...(payload.input === undefined ? {} : { payload: payload.input }),
|
|
703
|
+
...(payload.senderPrincipalId === undefined
|
|
704
|
+
? {}
|
|
705
|
+
: { senderPrincipalId: payload.senderPrincipalId }),
|
|
706
|
+
};
|
|
707
|
+
if (message.payload &&
|
|
708
|
+
typeof message.payload === 'object' &&
|
|
709
|
+
!Array.isArray(message.payload) &&
|
|
710
|
+
'pendingTelegramMessage' in message.payload) {
|
|
711
|
+
if (!options.prepareDeferredMemberInput)
|
|
712
|
+
throw new Error('Deferred Telegram input requires its gateway preparer');
|
|
713
|
+
message = await options.prepareDeferredMemberInput(row.principalId, message);
|
|
714
|
+
}
|
|
715
|
+
observation = archive.saveOwnerMessage(message);
|
|
716
|
+
input = message.payload;
|
|
717
|
+
}
|
|
718
|
+
intakeRuntime.mailbox.bindInputObservation(row.id, row.stimulusId, observation);
|
|
719
|
+
for (const ref of row.refs)
|
|
720
|
+
if (ref.refId === row.stimulusId)
|
|
721
|
+
ref.observationRef = observation;
|
|
722
|
+
// The immutable accepted payload remains the dedupe identity. Native delivery sees
|
|
723
|
+
// the stored projection's prepared attachments rather than downloading them again.
|
|
724
|
+
row.payload = { ...payload, ...(input === undefined ? {} : { input }) };
|
|
725
|
+
}
|
|
726
|
+
if ((0, personal_notices_js_1.personalNoticePayload)(row)) {
|
|
727
|
+
if (principals.findById(row.principalId)?.status !== 'active')
|
|
728
|
+
throw new runtime_1.StimulusQuarantine('Personal notice person is no longer active');
|
|
729
|
+
// Core calls this port before native admission, including scheduled inputs.
|
|
730
|
+
// Serve-on-demand may wait here, in this recipient's delivery, never in the common
|
|
731
|
+
// action. The one durable notice remains claimed without a native dispatch meanwhile.
|
|
732
|
+
try {
|
|
733
|
+
if (!intakeRuntime.servesPrincipal(row.principalId))
|
|
734
|
+
await serveMemberOnLane(row.principalId);
|
|
735
|
+
}
|
|
736
|
+
catch (error) {
|
|
737
|
+
// Suspend/offboard can finish while service waits for the directory lane.
|
|
738
|
+
// That lifecycle cancellation is final; an active person's preparation error retries.
|
|
739
|
+
if (principals.findById(row.principalId)?.status !== 'active')
|
|
740
|
+
throw new runtime_1.StimulusQuarantine('Personal notice person is no longer active');
|
|
741
|
+
throw error;
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
},
|
|
745
|
+
hostReady: () => !stopping,
|
|
746
|
+
excludePrincipals: surface.memberRecords.excludePrincipals,
|
|
493
747
|
onSettled: surface.memberRecords.onSettled,
|
|
494
748
|
onDead: (row, reason) => {
|
|
749
|
+
surface.onManagementDead(row, reason);
|
|
495
750
|
surface.memberRecords.onDead(row, reason);
|
|
496
751
|
return sessions.delivery.onDead?.(row, reason);
|
|
497
752
|
},
|
|
@@ -499,27 +754,75 @@ async function createOwnerRuntime(options) {
|
|
|
499
754
|
surface.memberRecords.onUncertain(row, reason);
|
|
500
755
|
return sessions.delivery.onUncertain?.(row, reason);
|
|
501
756
|
},
|
|
502
|
-
ready: () => !stopping &&
|
|
757
|
+
ready: () => !stopping && (options.deliveryReady?.() ?? true),
|
|
503
758
|
},
|
|
504
759
|
reclaimStaleSocket: true,
|
|
505
760
|
});
|
|
506
761
|
ownerMailbox = intakeRuntime.mailbox;
|
|
507
762
|
recordOrders.recover();
|
|
763
|
+
// Recovered host inputs need no new Telegram update to run.
|
|
764
|
+
void intakeRuntime
|
|
765
|
+
.drainOnce()
|
|
766
|
+
.catch((error) => console.error('[Member management] recovery failed', error));
|
|
508
767
|
const intake = (0, stimulus_delivery_js_1.createStimulusIntake)(intakeRuntime, options.ownerPrincipalId, chat);
|
|
509
|
-
const createMemberIntake = (id, member
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
768
|
+
const createMemberIntake = (id, member, room) => {
|
|
769
|
+
const archive = member?.chat ?? memberChat(id, room);
|
|
770
|
+
// A room intake made while the member was retired stays a room intake after the erasure
|
|
771
|
+
// ends: its message runs in the room session and is kept in the room's common archive.
|
|
772
|
+
const direct = (0, stimulus_delivery_js_1.createStimulusIntake)(intakeRuntime, id, {
|
|
773
|
+
saveOwnerMessage: (input) => {
|
|
774
|
+
executionAccess(id);
|
|
775
|
+
if (!intakeRuntime.servesPrincipal(id))
|
|
776
|
+
throw new Error(`Member session is not served: ${id}`);
|
|
777
|
+
return archive.saveOwnerMessage(input);
|
|
778
|
+
},
|
|
779
|
+
saveReply: (input) => archive.saveReply(input),
|
|
780
|
+
hasOwnerMessage: (ref) => archive.hasOwnerMessage(ref),
|
|
781
|
+
}, room);
|
|
782
|
+
const deferred = () => surface.memberRecords.isBlocked(id) && !intakeRuntime.servesPrincipal(id);
|
|
783
|
+
return {
|
|
784
|
+
...direct,
|
|
785
|
+
deferIncoming: deferred,
|
|
786
|
+
acceptOwnerMessage: (input) => {
|
|
787
|
+
if (stopping)
|
|
788
|
+
throw new Error('Runtime is stopping');
|
|
789
|
+
if (!deferred())
|
|
790
|
+
return direct.acceptOwnerMessage(input);
|
|
791
|
+
executionAccess(id);
|
|
792
|
+
// New work received after retirement is durable, but creates no personal projection
|
|
793
|
+
// while an idempotent erasure is finishing its already exported snapshot.
|
|
794
|
+
const rowId = intakeRuntime.mailbox.enqueue({
|
|
795
|
+
id: input.id,
|
|
796
|
+
kind: 'owner_message',
|
|
797
|
+
principalId: id,
|
|
798
|
+
channelKey: input.channelKey,
|
|
799
|
+
occurredAt: input.occurredAt,
|
|
800
|
+
refs: [{ refId: input.id, observationRef: null }],
|
|
801
|
+
replyTo: input.replyTo,
|
|
802
|
+
payload: {
|
|
803
|
+
text: input.text,
|
|
804
|
+
deferredMemberMessage: true,
|
|
805
|
+
// A room message waits with its room, so it runs in that room session.
|
|
806
|
+
...(room === undefined ? {} : { roomChannel: room }),
|
|
807
|
+
...(input.payload === undefined ? {} : { input: input.payload }),
|
|
808
|
+
...(input.senderPrincipalId === undefined
|
|
809
|
+
? {}
|
|
810
|
+
: { senderPrincipalId: input.senderPrincipalId }),
|
|
811
|
+
},
|
|
812
|
+
});
|
|
813
|
+
return {
|
|
814
|
+
inputId: rowId === null ? null : String(rowId),
|
|
815
|
+
state: rowId === null ? 'duplicate' : 'accepted',
|
|
816
|
+
};
|
|
817
|
+
},
|
|
818
|
+
};
|
|
819
|
+
};
|
|
518
820
|
for (const [id, member] of members)
|
|
519
821
|
memberIntakes.set(id, createMemberIntake(id, member));
|
|
520
822
|
const retireMember = async (id, setAside = true) => {
|
|
521
823
|
if (memberRoot === undefined)
|
|
522
824
|
throw new Error('Member lifecycle requires member_root');
|
|
825
|
+
surface.releasePrincipal(id);
|
|
523
826
|
intakeRuntime.unservePrincipal(id);
|
|
524
827
|
const member = members.get(id);
|
|
525
828
|
if (member) {
|
|
@@ -569,7 +872,53 @@ async function createOwnerRuntime(options) {
|
|
|
569
872
|
memberIntakes.set(principalId, memberIntake);
|
|
570
873
|
return memberIntake;
|
|
571
874
|
};
|
|
875
|
+
// Gateway DMs and personal jobs serve active unserved members through the directory lane. A
|
|
876
|
+
// served intake needs no directory work; P9 restores its own known paths on the member chain.
|
|
877
|
+
const serveMemberOnLane = (id) => {
|
|
878
|
+
executionAccess(id);
|
|
879
|
+
if (stopping)
|
|
880
|
+
return Promise.reject(new Error('Cannot serve a member after runtime stop'));
|
|
881
|
+
if (surface.memberRecords.isBlocked(id) && !intakeRuntime.servesPrincipal(id))
|
|
882
|
+
return Promise.resolve(createMemberIntake(id));
|
|
883
|
+
if (intakeRuntime.servesPrincipal(id))
|
|
884
|
+
return Promise.resolve(memberIntakes.get(id));
|
|
885
|
+
return sessions.exclusive(async () => {
|
|
886
|
+
if (stopping)
|
|
887
|
+
throw new Error('Cannot serve a member after runtime stop');
|
|
888
|
+
if (surface.memberRecords.isBlocked(id) && !intakeRuntime.servesPrincipal(id))
|
|
889
|
+
return createMemberIntake(id);
|
|
890
|
+
return serveMember(id);
|
|
891
|
+
});
|
|
892
|
+
};
|
|
893
|
+
const personalJobs = (0, personal_jobs_js_1.createPersonalJobScheduler)({
|
|
894
|
+
adapter: database.adapter,
|
|
895
|
+
store: surface.personalJobs,
|
|
896
|
+
mailbox: intakeRuntime.mailbox,
|
|
897
|
+
intake: serveMemberOnLane,
|
|
898
|
+
blocked: surface.memberRecords.isBlocked,
|
|
899
|
+
onError: (error) => options.onPersonalJobError?.(error),
|
|
900
|
+
});
|
|
901
|
+
let schedulerStop;
|
|
902
|
+
const beginStop = () => {
|
|
903
|
+
if (stopping)
|
|
904
|
+
return;
|
|
905
|
+
stopping = true;
|
|
906
|
+
schedulerStop = personalJobs.stop();
|
|
907
|
+
recordOrders.stop();
|
|
908
|
+
surface.memberRecords.stopRecovery();
|
|
909
|
+
intakeRuntime.beginStop();
|
|
910
|
+
};
|
|
911
|
+
let drainPromise;
|
|
912
|
+
const drain = () => (drainPromise ??= (async () => {
|
|
913
|
+
beginStop();
|
|
914
|
+
await intakeRuntime.stop();
|
|
915
|
+
await schedulerStop;
|
|
916
|
+
await surface.memberRecords.idle();
|
|
917
|
+
await sessions.idle();
|
|
918
|
+
})());
|
|
919
|
+
surface.memberRecords.startRecovery();
|
|
572
920
|
return {
|
|
921
|
+
personalJobs,
|
|
573
922
|
runtime: intakeRuntime,
|
|
574
923
|
database,
|
|
575
924
|
knowledge,
|
|
@@ -579,36 +928,89 @@ async function createOwnerRuntime(options) {
|
|
|
579
928
|
wikiRoot,
|
|
580
929
|
intake,
|
|
581
930
|
acceptSourceDelta: intake.acceptSourceDelta,
|
|
582
|
-
serveMember:
|
|
931
|
+
serveMember: serveMemberOnLane,
|
|
932
|
+
memberIntake: (id, room) => {
|
|
583
933
|
// Erasure owns restoration once it unserves the member. Intake callers must not
|
|
584
934
|
// recreate a writer while the host takes and delivers that member's snapshot.
|
|
585
935
|
if (surface.memberRecords.isBlocked(id) && !intakeRuntime.servesPrincipal(id))
|
|
936
|
+
return createMemberIntake(id, undefined, room);
|
|
937
|
+
if (stopped)
|
|
938
|
+
throw new Error('Cannot obtain member intake after runtime stop');
|
|
939
|
+
executionAccess(id);
|
|
940
|
+
const intake = memberIntakes.get(id);
|
|
941
|
+
if (!intake || !intakeRuntime.servesPrincipal(id))
|
|
586
942
|
throw new Error(`Member session is not served: ${id}`);
|
|
587
|
-
|
|
943
|
+
if (room === undefined)
|
|
944
|
+
return intake;
|
|
945
|
+
const session = members.get(id).roomSession(room);
|
|
946
|
+
return (0, stimulus_delivery_js_1.createStimulusIntake)(intakeRuntime, id, {
|
|
947
|
+
saveOwnerMessage: (input) => {
|
|
948
|
+
executionAccess(id, session.native.sessionKey);
|
|
949
|
+
return session.chat.saveOwnerMessage(input);
|
|
950
|
+
},
|
|
951
|
+
saveReply: (input) => session.chat.saveReply(input),
|
|
952
|
+
hasOwnerMessage: (ref) => session.chat.hasOwnerMessage(ref),
|
|
953
|
+
}, room);
|
|
588
954
|
},
|
|
589
|
-
|
|
590
|
-
if (
|
|
591
|
-
|
|
955
|
+
principalPaths: (principalId, room) => {
|
|
956
|
+
if (principalId === options.ownerPrincipalId) {
|
|
957
|
+
const downloadsDir = options.attachmentPorts?.downloadsDir;
|
|
958
|
+
if (!downloadsDir?.trim())
|
|
959
|
+
throw new Error('Owner downloads directory is required');
|
|
960
|
+
return {
|
|
961
|
+
workspaceDir: options.workspaceDir,
|
|
962
|
+
downloadsDir,
|
|
963
|
+
};
|
|
964
|
+
}
|
|
965
|
+
executionAccess(principalId);
|
|
966
|
+
sessions.get(principalId);
|
|
967
|
+
const member = members.get(principalId);
|
|
968
|
+
return room === undefined ? member.paths : member.roomSession(room).paths;
|
|
969
|
+
},
|
|
970
|
+
completeMemberEnrollment: (selection) => {
|
|
971
|
+
if (stopping)
|
|
972
|
+
return Promise.reject(new Error('Cannot enroll a member after runtime stop'));
|
|
592
973
|
return surface.completeMemberEnrollment(selection);
|
|
593
|
-
}
|
|
974
|
+
},
|
|
594
975
|
recordHostExchange: ({ message, reply }) => {
|
|
595
|
-
|
|
596
|
-
|
|
976
|
+
const person = message.id.startsWith('telegram:')
|
|
977
|
+
? principals.resolveByExternal('telegram', 'private', message.channelKey)
|
|
978
|
+
: null;
|
|
979
|
+
if (person?.kind !== 'member') {
|
|
980
|
+
chat.saveOwnerMessage({
|
|
981
|
+
...message,
|
|
982
|
+
...(person ? { senderPrincipalId: person.principalId } : {}),
|
|
983
|
+
});
|
|
984
|
+
chat.saveReply({ ...reply, messageRef: message.id, author: 'host' });
|
|
985
|
+
}
|
|
986
|
+
else {
|
|
987
|
+
// A member being erased gets its personal copy after the erasure, never inside it.
|
|
988
|
+
if (!surface.memberRecords.deferWhileErasing(person.principalId, { message, reply }))
|
|
989
|
+
writeMemberHostExchange(person.principalId, { message, reply });
|
|
990
|
+
const ref = `host:management:${message.id}`;
|
|
991
|
+
chat.saveOwnerMessage({
|
|
992
|
+
...message,
|
|
993
|
+
id: ref,
|
|
994
|
+
channelKey: 'management',
|
|
995
|
+
senderPrincipalId: person.principalId,
|
|
996
|
+
});
|
|
997
|
+
chat.saveReply({ ...reply, messageRef: ref, author: 'host' });
|
|
998
|
+
}
|
|
597
999
|
},
|
|
598
1000
|
stop: () => {
|
|
599
1001
|
if (stopPromise)
|
|
600
1002
|
return stopPromise;
|
|
601
|
-
|
|
1003
|
+
beginStop();
|
|
602
1004
|
stopPromise = (async () => {
|
|
603
|
-
await
|
|
1005
|
+
await drain();
|
|
604
1006
|
stopped = true;
|
|
605
|
-
recordOrders.stop();
|
|
606
|
-
await intakeRuntime.stop();
|
|
607
1007
|
rawStore?.close();
|
|
608
1008
|
await database.close();
|
|
609
1009
|
})();
|
|
610
1010
|
return stopPromise;
|
|
611
1011
|
},
|
|
1012
|
+
beginStop,
|
|
1013
|
+
drain,
|
|
612
1014
|
};
|
|
613
1015
|
}
|
|
614
1016
|
catch (error) {
|