@jungjaehoon/mama-os 0.75.0 → 0.77.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 +112 -0
- package/README.md +1 -1
- package/dist/api/attachment-actions.d.ts.map +1 -1
- package/dist/api/attachment-actions.js +8 -7
- package/dist/api/attachment-actions.js.map +1 -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 +21 -9
- 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 +4 -0
- package/dist/api/drive-actions.d.ts.map +1 -1
- package/dist/api/drive-actions.js +15 -7
- package/dist/api/drive-actions.js.map +1 -1
- package/dist/api/drive-delivery.d.ts.map +1 -1
- package/dist/api/drive-delivery.js +8 -3
- package/dist/api/drive-delivery.js.map +1 -1
- package/dist/api/member-enrollment.d.ts +9 -3
- package/dist/api/member-enrollment.d.ts.map +1 -1
- package/dist/api/member-enrollment.js +64 -16
- 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 +25 -4
- package/dist/api/member-lifecycle.d.ts.map +1 -1
- package/dist/api/member-lifecycle.js +88 -21
- 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 +47 -20
- package/dist/api/member-share-actions.js.map +1 -1
- 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 +5 -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.map +1 -1
- package/dist/api/trello-actions.js +8 -2
- 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 +85 -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 +23 -0
- package/dist/cli/commands/init.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 +6 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/runtime/claude-caller-config.d.ts +3 -1
- package/dist/cli/runtime/claude-caller-config.d.ts.map +1 -1
- package/dist/cli/runtime/claude-caller-config.js +14 -2
- package/dist/cli/runtime/claude-caller-config.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 +24 -6
- package/dist/gateways/telegram.d.ts.map +1 -1
- package/dist/gateways/telegram.js +258 -44
- package/dist/gateways/telegram.js.map +1 -1
- package/dist/gateways/turn-contract.d.ts +6 -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 -34
- 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/claude-caller-hook.d.ts +14 -0
- package/dist/runtime/claude-caller-hook.d.ts.map +1 -1
- package/dist/runtime/claude-caller-hook.js +21 -1
- package/dist/runtime/claude-caller-hook.js.map +1 -1
- package/dist/runtime/config.d.ts +6 -0
- package/dist/runtime/config.d.ts.map +1 -1
- package/dist/runtime/config.js +24 -0
- 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 +297 -34
- package/dist/runtime/member-session.js.map +1 -1
- package/dist/runtime/member-system-prompt.d.ts +3 -0
- package/dist/runtime/member-system-prompt.d.ts.map +1 -1
- package/dist/runtime/member-system-prompt.js +30 -4
- package/dist/runtime/member-system-prompt.js.map +1 -1
- package/dist/runtime/native-session.d.ts +8 -0
- package/dist/runtime/native-session.d.ts.map +1 -1
- package/dist/runtime/native-session.js +105 -17
- 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 +16 -14
- package/dist/runtime/owner-authority.d.ts.map +1 -1
- package/dist/runtime/owner-authority.js +163 -7
- package/dist/runtime/owner-authority.js.map +1 -1
- package/dist/runtime/owner-runtime.d.ts +18 -5
- package/dist/runtime/owner-runtime.d.ts.map +1 -1
- package/dist/runtime/owner-runtime.js +429 -43
- 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 +16 -6
- 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 +70 -2
- 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 +21 -1
- package/dist/runtime/stimulus-delivery.d.ts.map +1 -1
- package/dist/runtime/stimulus-delivery.js +185 -18
- 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/runtime/web-fetch-scope.d.ts +20 -0
- package/dist/runtime/web-fetch-scope.d.ts.map +1 -0
- package/dist/runtime/web-fetch-scope.js +68 -0
- package/dist/runtime/web-fetch-scope.js.map +1 -0
- 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 +65 -3
- package/dist/storage/chat-sources.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 +101 -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.in +11 -0
- package/toolkit/requirements.lock +1643 -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');
|
|
@@ -149,6 +169,15 @@ async function createOwnerRuntime(options) {
|
|
|
149
169
|
rawStore = new source_archive_js_1.RawStore(options.rawPath);
|
|
150
170
|
const chat = new chat_sources_js_1.ChatSources(rawStore, database.adapter, options.ownerPrincipalId, options.agentId);
|
|
151
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}`);
|
|
152
181
|
const storedSourceReader = (0, stored_source_reader_js_1.createStoredSourceReader)({
|
|
153
182
|
adapter: database.adapter,
|
|
154
183
|
rawStore: () => sourceStore,
|
|
@@ -181,7 +210,41 @@ async function createOwnerRuntime(options) {
|
|
|
181
210
|
})()
|
|
182
211
|
: {};
|
|
183
212
|
const surface = (0, action_surface_js_1.createActionSurface)({
|
|
213
|
+
commonChannelId: options.commonChannelId,
|
|
184
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,
|
|
185
248
|
adapter: database.adapter,
|
|
186
249
|
...(options.outboundAttempts === undefined
|
|
187
250
|
? {}
|
|
@@ -194,14 +257,43 @@ async function createOwnerRuntime(options) {
|
|
|
194
257
|
storedSourceReader,
|
|
195
258
|
timeZone: options.timeZone,
|
|
196
259
|
configPath: (0, node_path_1.join)(options.runtimeRoot, 'config.yaml'),
|
|
197
|
-
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
|
+
},
|
|
198
280
|
// Without member_root no member can be served or set aside, so no change may be committed.
|
|
199
281
|
...(memberRoot === undefined
|
|
200
282
|
? {}
|
|
201
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
|
+
},
|
|
202
293
|
memberRecords: {
|
|
203
294
|
root: memberRoot,
|
|
204
295
|
isStopping: () => stopping,
|
|
296
|
+
isReady: () => options.deliveryReady?.() ?? true,
|
|
205
297
|
resumeQueued: () => {
|
|
206
298
|
void intakeRuntime
|
|
207
299
|
.drainOnce()
|
|
@@ -217,7 +309,12 @@ async function createOwnerRuntime(options) {
|
|
|
217
309
|
serve: (id) => {
|
|
218
310
|
serveMember(id);
|
|
219
311
|
},
|
|
312
|
+
writeHostExchange: (id, exchange) => writeMemberHostExchange(id, exchange),
|
|
220
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;
|
|
221
318
|
const memberChat = new chat_sources_js_1.ChatSources(sourceStore, database.adapter, id, `member-agent:${id}`);
|
|
222
319
|
const at = Date.now();
|
|
223
320
|
memberChat.saveOwnerMessage({
|
|
@@ -236,18 +333,33 @@ async function createOwnerRuntime(options) {
|
|
|
236
333
|
},
|
|
237
334
|
},
|
|
238
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,
|
|
239
345
|
resetSession: async (id) => {
|
|
240
346
|
const member = members.get(id);
|
|
241
347
|
if (!member)
|
|
242
348
|
throw new Error(`Member session is not served: ${id}`);
|
|
243
349
|
await member.native.resetSession(member.native.sessionKey);
|
|
244
350
|
},
|
|
245
|
-
cancelQueued: (id, reason) =>
|
|
351
|
+
cancelQueued: (id, reason) => {
|
|
352
|
+
if (reason === 'member_suspend')
|
|
353
|
+
options.memberEnrollment?.cancelSelection?.(id);
|
|
354
|
+
return ownerMailbox.cancelQueued(id, reason);
|
|
355
|
+
},
|
|
246
356
|
retire: (id) => retireMember(id),
|
|
247
357
|
resume: async (id) => {
|
|
248
358
|
// A suspended member may retain a session after an earlier stop/move failure.
|
|
249
359
|
await retireMember(id);
|
|
250
|
-
|
|
360
|
+
const resumedAt = Date.now();
|
|
361
|
+
await surface.personalJobs.resume(id, resumedAt);
|
|
362
|
+
principals.resume(id, resumedAt);
|
|
251
363
|
try {
|
|
252
364
|
serveMember(id);
|
|
253
365
|
}
|
|
@@ -266,7 +378,14 @@ async function createOwnerRuntime(options) {
|
|
|
266
378
|
...options.memberEnrollment,
|
|
267
379
|
memberRoot,
|
|
268
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';
|
|
269
387
|
serveMember(id);
|
|
388
|
+
return undefined;
|
|
270
389
|
},
|
|
271
390
|
},
|
|
272
391
|
}),
|
|
@@ -284,7 +403,7 @@ async function createOwnerRuntime(options) {
|
|
|
284
403
|
downloadsDir: options.attachmentPorts?.downloadsDir ?? '',
|
|
285
404
|
};
|
|
286
405
|
sessions.get(principalId);
|
|
287
|
-
return members.get(principalId).
|
|
406
|
+
return members.get(principalId).activePaths();
|
|
288
407
|
},
|
|
289
408
|
},
|
|
290
409
|
...(options.driveDelivery === undefined
|
|
@@ -311,6 +430,7 @@ async function createOwnerRuntime(options) {
|
|
|
311
430
|
model: options.model,
|
|
312
431
|
workspaceDir: options.workspaceDir,
|
|
313
432
|
runtimeRoot: options.runtimeRoot,
|
|
433
|
+
toolsRoot: options.toolsRoot,
|
|
314
434
|
deniedReadPaths: (0, backend_security_js_1.ownerNativeDataReadPaths)(options),
|
|
315
435
|
replayKeyFile: options.replayKeyFile,
|
|
316
436
|
...(options.sandboxNetworkProxy === undefined
|
|
@@ -322,6 +442,9 @@ async function createOwnerRuntime(options) {
|
|
|
322
442
|
ownerSystemPrompt: standingText,
|
|
323
443
|
ownerPolicyProvider,
|
|
324
444
|
...(options.effort === undefined ? {} : { effort: options.effort }),
|
|
445
|
+
...(options.autonomousWebFetch === undefined
|
|
446
|
+
? {}
|
|
447
|
+
: { autonomousWebFetch: options.autonomousWebFetch }),
|
|
325
448
|
timeout: options.timeout,
|
|
326
449
|
...(options.maxTurnMs === undefined ? {} : { maxTurnMs: options.maxTurnMs }),
|
|
327
450
|
maxTurns: options.maxTurns,
|
|
@@ -343,9 +466,61 @@ async function createOwnerRuntime(options) {
|
|
|
343
466
|
...(options.onRecordOrderEvent === undefined ? {} : { onEvent: options.onRecordOrderEvent }),
|
|
344
467
|
});
|
|
345
468
|
delivery = (0, stimulus_delivery_js_1.createStimulusDelivery)({
|
|
346
|
-
|
|
469
|
+
skills: (0, skills_js_1.createSkillTurnContext)(() => surface.skills.list(options.ownerPrincipalId)),
|
|
470
|
+
turnChain: sessions.turnChain(options.ownerPrincipalId),
|
|
347
471
|
backend: options.backend,
|
|
348
472
|
timeZone: options.timeZone,
|
|
473
|
+
principalRoles: (principalId) => principals.listRoles(principalId).map(({ role }) => role),
|
|
474
|
+
principalDisplayName: (principalId) => principals.findById(principalId)?.displayName,
|
|
475
|
+
commonPrincipalId: options.ownerPrincipalId,
|
|
476
|
+
ownerMessageSenderActive: (id) => {
|
|
477
|
+
const person = principals.findById(id);
|
|
478
|
+
return (person?.kind === 'member' && person.status === 'active' && (0, owner_authority_js_1.holdsOwnerRole)(principals, id));
|
|
479
|
+
},
|
|
480
|
+
rerouteOwnerMessage: async (row, sender) => {
|
|
481
|
+
const person = principals.findById(sender);
|
|
482
|
+
if (person?.kind !== 'member' ||
|
|
483
|
+
person.status !== 'active' ||
|
|
484
|
+
!row.stimulusId.startsWith(`telegram:${row.channelKey}:`) ||
|
|
485
|
+
!options.roomChannels?.includes(row.channelKey))
|
|
486
|
+
return false;
|
|
487
|
+
await serveMemberOnLane(sender);
|
|
488
|
+
// The room session cannot read the common downloads; its files go to the room's own.
|
|
489
|
+
const payload = row.payload;
|
|
490
|
+
let input = payload.input;
|
|
491
|
+
if (input?.attachments?.length) {
|
|
492
|
+
const target = (0, node_path_1.join)(members.get(sender).roomSession(row.channelKey).paths.downloadsDir, 'telegram');
|
|
493
|
+
(0, node_fs_1.mkdirSync)(target, { recursive: true });
|
|
494
|
+
input = {
|
|
495
|
+
...input,
|
|
496
|
+
attachments: input.attachments.map((attachment) => {
|
|
497
|
+
if (!('path' in attachment))
|
|
498
|
+
return attachment;
|
|
499
|
+
const path = (0, node_path_1.join)(target, (0, node_path_1.basename)(attachment.path));
|
|
500
|
+
(0, node_fs_1.copyFileSync)(attachment.path, path);
|
|
501
|
+
return { ...attachment, path };
|
|
502
|
+
}),
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
// A deterministic handoff ref survives a crash between this append and the common
|
|
506
|
+
// acknowledgement. Mailbox ref identities are global; keep the evidence handles.
|
|
507
|
+
intakeRuntime.accept({
|
|
508
|
+
id: row.stimulusId,
|
|
509
|
+
kind: 'owner_message',
|
|
510
|
+
principalId: sender,
|
|
511
|
+
channelKey: row.channelKey,
|
|
512
|
+
occurredAt: row.occurredAt,
|
|
513
|
+
refs: row.refs.map((ref) => ({ ...ref, refId: `room-handoff:${sender}:${ref.refId}` })),
|
|
514
|
+
replyTo: row.replyTo,
|
|
515
|
+
payload: {
|
|
516
|
+
...row.payload,
|
|
517
|
+
...(input === undefined ? {} : { input }),
|
|
518
|
+
senderPrincipalId: sender,
|
|
519
|
+
roomChannel: row.channelKey,
|
|
520
|
+
},
|
|
521
|
+
});
|
|
522
|
+
return true;
|
|
523
|
+
},
|
|
349
524
|
wikiEnabled: options.wiki?.enabled ?? false,
|
|
350
525
|
formattingRoutes: options.formattingRoutes ?? {
|
|
351
526
|
reports: 'telegram',
|
|
@@ -358,7 +533,13 @@ async function createOwnerRuntime(options) {
|
|
|
358
533
|
? {}
|
|
359
534
|
: { onUncertain: options.onStimulusUncertain }),
|
|
360
535
|
...(options.onStimulusDead === undefined ? {} : { onDead: options.onStimulusDead }),
|
|
361
|
-
|
|
536
|
+
onSkipped: async (row, reason) => {
|
|
537
|
+
if (row.kind === 'owner_message')
|
|
538
|
+
database.adapter
|
|
539
|
+
.prepare('UPDATE mailbox_inputs SET last_error = ? WHERE id = ?')
|
|
540
|
+
.run(reason, row.id);
|
|
541
|
+
await options.onStimulusSkipped?.(row, reason);
|
|
542
|
+
},
|
|
362
543
|
...(options.closeUncertain === undefined ? {} : { closeUncertain: options.closeUncertain }),
|
|
363
544
|
sessionStart: (row) => (0, session_start_context_js_1.readSessionStartInput)({
|
|
364
545
|
exchanges: chat.recentExchanges(row.stimulusId),
|
|
@@ -403,7 +584,7 @@ async function createOwnerRuntime(options) {
|
|
|
403
584
|
});
|
|
404
585
|
// Ten in search order: the session filter drops lessons already shown and keeps three.
|
|
405
586
|
const hitIds = hits.map((hit) => hit.id);
|
|
406
|
-
return guidanceInSearchOrder(hitIds, active, 10, (0, owner_authority_js_1.ownerRuleIds)(database.adapter, hitIds, options.ownerPrincipalId));
|
|
587
|
+
return guidanceInSearchOrder(hitIds, active, 10, (0, owner_authority_js_1.ownerRuleIds)(database.adapter, hitIds, options.ownerPrincipalId), surface.skills.workflowScopes(options.ownerPrincipalId));
|
|
407
588
|
}),
|
|
408
589
|
recordOrders,
|
|
409
590
|
...(options.onOwnerResult === undefined ? {} : { onOwnerResult: options.onOwnerResult }),
|
|
@@ -425,12 +606,14 @@ async function createOwnerRuntime(options) {
|
|
|
425
606
|
throw new Error(`serveMember requires an active member: ${principalId}`);
|
|
426
607
|
const member = (0, member_session_js_1.createMemberSession)(principalId, memberRoot, {
|
|
427
608
|
options,
|
|
609
|
+
personalJobs: surface.personalJobs,
|
|
610
|
+
ownerPolicyProvider,
|
|
428
611
|
ownerDeniedPaths,
|
|
429
612
|
registeredMemberIds: () => principals.listMembers().map((row) => row.principalId),
|
|
430
613
|
database,
|
|
431
614
|
rawStore: sourceStore,
|
|
432
615
|
surface,
|
|
433
|
-
turnChain: sessions.turnChain,
|
|
616
|
+
turnChain: sessions.turnChain(principalId),
|
|
434
617
|
readResult: (row) => row.nativeDelivery?.receipt
|
|
435
618
|
? intakeRuntime.mailbox.nativeInputs.resultForReceipt(row.nativeDelivery.receipt, row.principalId)
|
|
436
619
|
: null,
|
|
@@ -440,16 +623,22 @@ async function createOwnerRuntime(options) {
|
|
|
440
623
|
return member;
|
|
441
624
|
};
|
|
442
625
|
for (const member of activeMembers)
|
|
443
|
-
|
|
444
|
-
|
|
626
|
+
if (!surface.memberRecords.isBlocked(member.principalId))
|
|
627
|
+
prepareMember(member.principalId);
|
|
628
|
+
const executionAccess = (principalId, sessionKey) => principalId === options.ownerPrincipalId
|
|
445
629
|
? access
|
|
446
630
|
: (0, principal_access_js_1.resolvePrincipalAccess)(principalId, {
|
|
447
631
|
adapter: database.adapter,
|
|
448
632
|
ownerAccess: access,
|
|
449
633
|
agentId: `member-agent:${principalId}`,
|
|
634
|
+
sessionKey,
|
|
635
|
+
commonChannelId: options.commonChannelId,
|
|
450
636
|
});
|
|
451
637
|
const socketDispatch = Object.assign(async (...[call, context]) => {
|
|
452
|
-
context = {
|
|
638
|
+
context = {
|
|
639
|
+
...context,
|
|
640
|
+
access: executionAccess(context.access.principalId, members.get(context.access.principalId)?.activeSessionKey()),
|
|
641
|
+
};
|
|
453
642
|
const caller = context.session?.nativeCaller;
|
|
454
643
|
if (caller !== undefined) {
|
|
455
644
|
const principalSession = sessions.get(context.access.principalId).native;
|
|
@@ -476,6 +665,7 @@ async function createOwnerRuntime(options) {
|
|
|
476
665
|
executionAccess(callerAccess.principalId);
|
|
477
666
|
const ceiling = sessions.replaySourceEndMs(callerAccess.principalId);
|
|
478
667
|
return {
|
|
668
|
+
...members.get(callerAccess.principalId)?.activeSessionFacts(),
|
|
479
669
|
...(ceiling === undefined ? {} : { replaySourceEndMs: ceiling }),
|
|
480
670
|
...(request.session?.nativeCaller === undefined
|
|
481
671
|
? {}
|
|
@@ -486,8 +676,80 @@ async function createOwnerRuntime(options) {
|
|
|
486
676
|
nativeSession: sessions.native,
|
|
487
677
|
delivery: {
|
|
488
678
|
...sessions.delivery,
|
|
679
|
+
deliverHost: surface.deliverManagement,
|
|
680
|
+
prepareNative: async (row) => {
|
|
681
|
+
const payload = row.payload;
|
|
682
|
+
if (row.kind === 'owner_message' && payload?.deferredMemberMessage) {
|
|
683
|
+
if (!intakeRuntime.servesPrincipal(row.principalId))
|
|
684
|
+
await serveMemberOnLane(row.principalId);
|
|
685
|
+
// A room message belongs to its room session's common archive, never to the
|
|
686
|
+
// member's personal records.
|
|
687
|
+
const room = payload.roomChannel;
|
|
688
|
+
const archive = memberChat(row.principalId, room);
|
|
689
|
+
const existing = database.adapter
|
|
690
|
+
.prepare("SELECT current_observation_id, metadata_json FROM connector_event_index WHERE source_connector='chat' AND source_id=? AND memory_scope_id=?")
|
|
691
|
+
.get(row.stimulusId, room === undefined ? row.principalId : options.ownerPrincipalId);
|
|
692
|
+
let observation;
|
|
693
|
+
let input = payload.input;
|
|
694
|
+
if (existing) {
|
|
695
|
+
observation = existing.current_observation_id;
|
|
696
|
+
input = JSON.parse(existing.metadata_json).input ?? undefined;
|
|
697
|
+
}
|
|
698
|
+
else {
|
|
699
|
+
let message = {
|
|
700
|
+
id: row.stimulusId,
|
|
701
|
+
channelKey: row.channelKey,
|
|
702
|
+
occurredAt: row.occurredAt,
|
|
703
|
+
text: payload.text ?? '',
|
|
704
|
+
replyTo: row.replyTo,
|
|
705
|
+
...(payload.input === undefined ? {} : { payload: payload.input }),
|
|
706
|
+
...(payload.senderPrincipalId === undefined
|
|
707
|
+
? {}
|
|
708
|
+
: { senderPrincipalId: payload.senderPrincipalId }),
|
|
709
|
+
};
|
|
710
|
+
if (message.payload &&
|
|
711
|
+
typeof message.payload === 'object' &&
|
|
712
|
+
!Array.isArray(message.payload) &&
|
|
713
|
+
'pendingTelegramMessage' in message.payload) {
|
|
714
|
+
if (!options.prepareDeferredMemberInput)
|
|
715
|
+
throw new Error('Deferred Telegram input requires its gateway preparer');
|
|
716
|
+
message = await options.prepareDeferredMemberInput(row.principalId, message);
|
|
717
|
+
}
|
|
718
|
+
observation = archive.saveOwnerMessage(message);
|
|
719
|
+
input = message.payload;
|
|
720
|
+
}
|
|
721
|
+
intakeRuntime.mailbox.bindInputObservation(row.id, row.stimulusId, observation);
|
|
722
|
+
for (const ref of row.refs)
|
|
723
|
+
if (ref.refId === row.stimulusId)
|
|
724
|
+
ref.observationRef = observation;
|
|
725
|
+
// The immutable accepted payload remains the dedupe identity. Native delivery sees
|
|
726
|
+
// the stored projection's prepared attachments rather than downloading them again.
|
|
727
|
+
row.payload = { ...payload, ...(input === undefined ? {} : { input }) };
|
|
728
|
+
}
|
|
729
|
+
if ((0, personal_notices_js_1.personalNoticePayload)(row)) {
|
|
730
|
+
if (principals.findById(row.principalId)?.status !== 'active')
|
|
731
|
+
throw new runtime_1.StimulusQuarantine('Personal notice person is no longer active');
|
|
732
|
+
// Core calls this port before native admission, including scheduled inputs.
|
|
733
|
+
// Serve-on-demand may wait here, in this recipient's delivery, never in the common
|
|
734
|
+
// action. The one durable notice remains claimed without a native dispatch meanwhile.
|
|
735
|
+
try {
|
|
736
|
+
if (!intakeRuntime.servesPrincipal(row.principalId))
|
|
737
|
+
await serveMemberOnLane(row.principalId);
|
|
738
|
+
}
|
|
739
|
+
catch (error) {
|
|
740
|
+
// Suspend/offboard can finish while service waits for the directory lane.
|
|
741
|
+
// That lifecycle cancellation is final; an active person's preparation error retries.
|
|
742
|
+
if (principals.findById(row.principalId)?.status !== 'active')
|
|
743
|
+
throw new runtime_1.StimulusQuarantine('Personal notice person is no longer active');
|
|
744
|
+
throw error;
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
},
|
|
748
|
+
hostReady: () => !stopping,
|
|
749
|
+
excludePrincipals: surface.memberRecords.excludePrincipals,
|
|
489
750
|
onSettled: surface.memberRecords.onSettled,
|
|
490
751
|
onDead: (row, reason) => {
|
|
752
|
+
surface.onManagementDead(row, reason);
|
|
491
753
|
surface.memberRecords.onDead(row, reason);
|
|
492
754
|
return sessions.delivery.onDead?.(row, reason);
|
|
493
755
|
},
|
|
@@ -495,27 +757,75 @@ async function createOwnerRuntime(options) {
|
|
|
495
757
|
surface.memberRecords.onUncertain(row, reason);
|
|
496
758
|
return sessions.delivery.onUncertain?.(row, reason);
|
|
497
759
|
},
|
|
498
|
-
ready: () => !stopping &&
|
|
760
|
+
ready: () => !stopping && (options.deliveryReady?.() ?? true),
|
|
499
761
|
},
|
|
500
762
|
reclaimStaleSocket: true,
|
|
501
763
|
});
|
|
502
764
|
ownerMailbox = intakeRuntime.mailbox;
|
|
503
765
|
recordOrders.recover();
|
|
766
|
+
// Recovered host inputs need no new Telegram update to run.
|
|
767
|
+
void intakeRuntime
|
|
768
|
+
.drainOnce()
|
|
769
|
+
.catch((error) => console.error('[Member management] recovery failed', error));
|
|
504
770
|
const intake = (0, stimulus_delivery_js_1.createStimulusIntake)(intakeRuntime, options.ownerPrincipalId, chat);
|
|
505
|
-
const createMemberIntake = (id, member
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
771
|
+
const createMemberIntake = (id, member, room) => {
|
|
772
|
+
const archive = member?.chat ?? memberChat(id, room);
|
|
773
|
+
// A room intake made while the member was retired stays a room intake after the erasure
|
|
774
|
+
// ends: its message runs in the room session and is kept in the room's common archive.
|
|
775
|
+
const direct = (0, stimulus_delivery_js_1.createStimulusIntake)(intakeRuntime, id, {
|
|
776
|
+
saveOwnerMessage: (input) => {
|
|
777
|
+
executionAccess(id);
|
|
778
|
+
if (!intakeRuntime.servesPrincipal(id))
|
|
779
|
+
throw new Error(`Member session is not served: ${id}`);
|
|
780
|
+
return archive.saveOwnerMessage(input);
|
|
781
|
+
},
|
|
782
|
+
saveReply: (input) => archive.saveReply(input),
|
|
783
|
+
hasOwnerMessage: (ref) => archive.hasOwnerMessage(ref),
|
|
784
|
+
}, room);
|
|
785
|
+
const deferred = () => surface.memberRecords.isBlocked(id) && !intakeRuntime.servesPrincipal(id);
|
|
786
|
+
return {
|
|
787
|
+
...direct,
|
|
788
|
+
deferIncoming: deferred,
|
|
789
|
+
acceptOwnerMessage: (input) => {
|
|
790
|
+
if (stopping)
|
|
791
|
+
throw new Error('Runtime is stopping');
|
|
792
|
+
if (!deferred())
|
|
793
|
+
return direct.acceptOwnerMessage(input);
|
|
794
|
+
executionAccess(id);
|
|
795
|
+
// New work received after retirement is durable, but creates no personal projection
|
|
796
|
+
// while an idempotent erasure is finishing its already exported snapshot.
|
|
797
|
+
const rowId = intakeRuntime.mailbox.enqueue({
|
|
798
|
+
id: input.id,
|
|
799
|
+
kind: 'owner_message',
|
|
800
|
+
principalId: id,
|
|
801
|
+
channelKey: input.channelKey,
|
|
802
|
+
occurredAt: input.occurredAt,
|
|
803
|
+
refs: [{ refId: input.id, observationRef: null }],
|
|
804
|
+
replyTo: input.replyTo,
|
|
805
|
+
payload: {
|
|
806
|
+
text: input.text,
|
|
807
|
+
deferredMemberMessage: true,
|
|
808
|
+
// A room message waits with its room, so it runs in that room session.
|
|
809
|
+
...(room === undefined ? {} : { roomChannel: room }),
|
|
810
|
+
...(input.payload === undefined ? {} : { input: input.payload }),
|
|
811
|
+
...(input.senderPrincipalId === undefined
|
|
812
|
+
? {}
|
|
813
|
+
: { senderPrincipalId: input.senderPrincipalId }),
|
|
814
|
+
},
|
|
815
|
+
});
|
|
816
|
+
return {
|
|
817
|
+
inputId: rowId === null ? null : String(rowId),
|
|
818
|
+
state: rowId === null ? 'duplicate' : 'accepted',
|
|
819
|
+
};
|
|
820
|
+
},
|
|
821
|
+
};
|
|
822
|
+
};
|
|
514
823
|
for (const [id, member] of members)
|
|
515
824
|
memberIntakes.set(id, createMemberIntake(id, member));
|
|
516
825
|
const retireMember = async (id, setAside = true) => {
|
|
517
826
|
if (memberRoot === undefined)
|
|
518
827
|
throw new Error('Member lifecycle requires member_root');
|
|
828
|
+
surface.releasePrincipal(id);
|
|
519
829
|
intakeRuntime.unservePrincipal(id);
|
|
520
830
|
const member = members.get(id);
|
|
521
831
|
if (member) {
|
|
@@ -565,7 +875,53 @@ async function createOwnerRuntime(options) {
|
|
|
565
875
|
memberIntakes.set(principalId, memberIntake);
|
|
566
876
|
return memberIntake;
|
|
567
877
|
};
|
|
878
|
+
// Gateway DMs and personal jobs serve active unserved members through the directory lane. A
|
|
879
|
+
// served intake needs no directory work; P9 restores its own known paths on the member chain.
|
|
880
|
+
const serveMemberOnLane = (id) => {
|
|
881
|
+
executionAccess(id);
|
|
882
|
+
if (stopping)
|
|
883
|
+
return Promise.reject(new Error('Cannot serve a member after runtime stop'));
|
|
884
|
+
if (surface.memberRecords.isBlocked(id) && !intakeRuntime.servesPrincipal(id))
|
|
885
|
+
return Promise.resolve(createMemberIntake(id));
|
|
886
|
+
if (intakeRuntime.servesPrincipal(id))
|
|
887
|
+
return Promise.resolve(memberIntakes.get(id));
|
|
888
|
+
return sessions.exclusive(async () => {
|
|
889
|
+
if (stopping)
|
|
890
|
+
throw new Error('Cannot serve a member after runtime stop');
|
|
891
|
+
if (surface.memberRecords.isBlocked(id) && !intakeRuntime.servesPrincipal(id))
|
|
892
|
+
return createMemberIntake(id);
|
|
893
|
+
return serveMember(id);
|
|
894
|
+
});
|
|
895
|
+
};
|
|
896
|
+
const personalJobs = (0, personal_jobs_js_1.createPersonalJobScheduler)({
|
|
897
|
+
adapter: database.adapter,
|
|
898
|
+
store: surface.personalJobs,
|
|
899
|
+
mailbox: intakeRuntime.mailbox,
|
|
900
|
+
intake: serveMemberOnLane,
|
|
901
|
+
blocked: surface.memberRecords.isBlocked,
|
|
902
|
+
onError: (error) => options.onPersonalJobError?.(error),
|
|
903
|
+
});
|
|
904
|
+
let schedulerStop;
|
|
905
|
+
const beginStop = () => {
|
|
906
|
+
if (stopping)
|
|
907
|
+
return;
|
|
908
|
+
stopping = true;
|
|
909
|
+
schedulerStop = personalJobs.stop();
|
|
910
|
+
recordOrders.stop();
|
|
911
|
+
surface.memberRecords.stopRecovery();
|
|
912
|
+
intakeRuntime.beginStop();
|
|
913
|
+
};
|
|
914
|
+
let drainPromise;
|
|
915
|
+
const drain = () => (drainPromise ??= (async () => {
|
|
916
|
+
beginStop();
|
|
917
|
+
await intakeRuntime.stop();
|
|
918
|
+
await schedulerStop;
|
|
919
|
+
await surface.memberRecords.idle();
|
|
920
|
+
await sessions.idle();
|
|
921
|
+
})());
|
|
922
|
+
surface.memberRecords.startRecovery();
|
|
568
923
|
return {
|
|
924
|
+
personalJobs,
|
|
569
925
|
runtime: intakeRuntime,
|
|
570
926
|
database,
|
|
571
927
|
knowledge,
|
|
@@ -575,14 +931,31 @@ async function createOwnerRuntime(options) {
|
|
|
575
931
|
wikiRoot,
|
|
576
932
|
intake,
|
|
577
933
|
acceptSourceDelta: intake.acceptSourceDelta,
|
|
578
|
-
serveMember:
|
|
934
|
+
serveMember: serveMemberOnLane,
|
|
935
|
+
memberIntake: (id, room) => {
|
|
579
936
|
// Erasure owns restoration once it unserves the member. Intake callers must not
|
|
580
937
|
// recreate a writer while the host takes and delivers that member's snapshot.
|
|
581
938
|
if (surface.memberRecords.isBlocked(id) && !intakeRuntime.servesPrincipal(id))
|
|
939
|
+
return createMemberIntake(id, undefined, room);
|
|
940
|
+
if (stopped)
|
|
941
|
+
throw new Error('Cannot obtain member intake after runtime stop');
|
|
942
|
+
executionAccess(id);
|
|
943
|
+
const intake = memberIntakes.get(id);
|
|
944
|
+
if (!intake || !intakeRuntime.servesPrincipal(id))
|
|
582
945
|
throw new Error(`Member session is not served: ${id}`);
|
|
583
|
-
|
|
946
|
+
if (room === undefined)
|
|
947
|
+
return intake;
|
|
948
|
+
const session = members.get(id).roomSession(room);
|
|
949
|
+
return (0, stimulus_delivery_js_1.createStimulusIntake)(intakeRuntime, id, {
|
|
950
|
+
saveOwnerMessage: (input) => {
|
|
951
|
+
executionAccess(id, session.native.sessionKey);
|
|
952
|
+
return session.chat.saveOwnerMessage(input);
|
|
953
|
+
},
|
|
954
|
+
saveReply: (input) => session.chat.saveReply(input),
|
|
955
|
+
hasOwnerMessage: (ref) => session.chat.hasOwnerMessage(ref),
|
|
956
|
+
}, room);
|
|
584
957
|
},
|
|
585
|
-
principalPaths: (principalId) => {
|
|
958
|
+
principalPaths: (principalId, room) => {
|
|
586
959
|
if (principalId === options.ownerPrincipalId) {
|
|
587
960
|
const downloadsDir = options.attachmentPorts?.downloadsDir;
|
|
588
961
|
if (!downloadsDir?.trim())
|
|
@@ -594,40 +967,53 @@ async function createOwnerRuntime(options) {
|
|
|
594
967
|
}
|
|
595
968
|
executionAccess(principalId);
|
|
596
969
|
sessions.get(principalId);
|
|
597
|
-
|
|
970
|
+
const member = members.get(principalId);
|
|
971
|
+
return room === undefined ? member.paths : member.roomSession(room).paths;
|
|
598
972
|
},
|
|
599
|
-
completeMemberEnrollment: (selection) =>
|
|
600
|
-
if (
|
|
601
|
-
|
|
973
|
+
completeMemberEnrollment: (selection) => {
|
|
974
|
+
if (stopping)
|
|
975
|
+
return Promise.reject(new Error('Cannot enroll a member after runtime stop'));
|
|
602
976
|
return surface.completeMemberEnrollment(selection);
|
|
603
|
-
}
|
|
977
|
+
},
|
|
604
978
|
recordHostExchange: ({ message, reply }) => {
|
|
605
979
|
const person = message.id.startsWith('telegram:')
|
|
606
980
|
? principals.resolveByExternal('telegram', 'private', message.channelKey)
|
|
607
981
|
: null;
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
}
|
|
615
|
-
|
|
982
|
+
if (person?.kind !== 'member') {
|
|
983
|
+
chat.saveOwnerMessage({
|
|
984
|
+
...message,
|
|
985
|
+
...(person ? { senderPrincipalId: person.principalId } : {}),
|
|
986
|
+
});
|
|
987
|
+
chat.saveReply({ ...reply, messageRef: message.id, author: 'host' });
|
|
988
|
+
}
|
|
989
|
+
else {
|
|
990
|
+
// A member being erased gets its personal copy after the erasure, never inside it.
|
|
991
|
+
if (!surface.memberRecords.deferWhileErasing(person.principalId, { message, reply }))
|
|
992
|
+
writeMemberHostExchange(person.principalId, { message, reply });
|
|
993
|
+
const ref = `host:management:${message.id}`;
|
|
994
|
+
chat.saveOwnerMessage({
|
|
995
|
+
...message,
|
|
996
|
+
id: ref,
|
|
997
|
+
channelKey: 'management',
|
|
998
|
+
senderPrincipalId: person.principalId,
|
|
999
|
+
});
|
|
1000
|
+
chat.saveReply({ ...reply, messageRef: ref, author: 'host' });
|
|
1001
|
+
}
|
|
616
1002
|
},
|
|
617
1003
|
stop: () => {
|
|
618
1004
|
if (stopPromise)
|
|
619
1005
|
return stopPromise;
|
|
620
|
-
|
|
1006
|
+
beginStop();
|
|
621
1007
|
stopPromise = (async () => {
|
|
622
|
-
await
|
|
1008
|
+
await drain();
|
|
623
1009
|
stopped = true;
|
|
624
|
-
recordOrders.stop();
|
|
625
|
-
await intakeRuntime.stop();
|
|
626
1010
|
rawStore?.close();
|
|
627
1011
|
await database.close();
|
|
628
1012
|
})();
|
|
629
1013
|
return stopPromise;
|
|
630
1014
|
},
|
|
1015
|
+
beginStop,
|
|
1016
|
+
drain,
|
|
631
1017
|
};
|
|
632
1018
|
}
|
|
633
1019
|
catch (error) {
|