@brantrusnak/openclaw-omadeus 1.0.4 → 1.0.6
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/dist/src/api/message.api.js +13 -2
- package/dist/src/channel.js +38 -11
- package/dist/src/config.js +9 -4
- package/dist/src/defaults.js +30 -4
- package/dist/src/inbound-policy.js +11 -14
- package/dist/src/message-handler.js +14 -2
- package/dist/src/onboarding.js +85 -56
- package/dist/src/outbound.js +10 -1
- package/dist/src/sent-message-tracker.js +116 -0
- package/dist/src/setup-core.js +4 -4
- package/openclaw.plugin.json +22 -11
- package/package.json +1 -1
- package/src/api/message.api.ts +4 -2
- package/src/channel.ts +43 -7
- package/src/config.ts +14 -4
- package/src/defaults.ts +44 -2
- package/src/inbound-policy.ts +24 -13
- package/src/message-handler.ts +35 -7
- package/src/onboarding.ts +136 -60
- package/src/outbound.ts +14 -2
- package/src/sent-message-tracker.ts +155 -0
- package/src/setup-core.ts +4 -4
- package/src/types.ts +6 -2
|
@@ -16,7 +16,7 @@ async function sendRoomMessage(opts, params) {
|
|
|
16
16
|
method: "SEND",
|
|
17
17
|
body: JSON.stringify({
|
|
18
18
|
body: params.body,
|
|
19
|
-
temporaryId: generateTemporaryId(),
|
|
19
|
+
temporaryId: params.temporaryId ?? generateTemporaryId(),
|
|
20
20
|
links: "[]"
|
|
21
21
|
})
|
|
22
22
|
});
|
|
@@ -38,6 +38,17 @@ async function sendRoomMessage(opts, params) {
|
|
|
38
38
|
};
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
|
+
async function seeMessage(opts, params) {
|
|
42
|
+
const res = await jaguarFetch(opts, `/messages/${params.messageId}`, {
|
|
43
|
+
method: "SEE",
|
|
44
|
+
body: "{}"
|
|
45
|
+
});
|
|
46
|
+
if (!res.ok) {
|
|
47
|
+
const text = await res.text().catch(() => "");
|
|
48
|
+
throw new Error(`Omadeus see message failed (${res.status}): ${text.slice(0, 200)}`);
|
|
49
|
+
}
|
|
50
|
+
return await res.json();
|
|
51
|
+
}
|
|
41
52
|
async function editMessage(opts, params) {
|
|
42
53
|
const res = await jaguarFetch(opts, `/messages/${params.messageId}`, {
|
|
43
54
|
method: "EDIT",
|
|
@@ -73,4 +84,4 @@ async function addMessageReaction(opts, params) {
|
|
|
73
84
|
return readJsonOrEmpty(res);
|
|
74
85
|
}
|
|
75
86
|
//#endregion
|
|
76
|
-
export { addMessageReaction, deleteMessage, editMessage, sendRoomMessage };
|
|
87
|
+
export { addMessageReaction, deleteMessage, editMessage, seeMessage, sendRoomMessage };
|
package/dist/src/channel.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { DEFAULT_ACCOUNT_ID, missingTargetError } from "../runtime-api.js";
|
|
2
2
|
import { ALLOWED_OMADEUS_REACTION_EMOJI_LIST, isAllowedOmadeusReactionEmoji } from "./allowed-reaction-emojis.js";
|
|
3
|
+
import { generateTemporaryId } from "./utils/http.util.js";
|
|
3
4
|
import { createNugget, resolveTaskRoomIdByNumber } from "./api/nugget.api.js";
|
|
4
5
|
import { addMessageReaction, deleteMessage, editMessage } from "./api/message.api.js";
|
|
5
6
|
import { getOmadeusChannelConfig, listOmadeusAccountIds, resolveDefaultOmadeusAccountId, resolveOmadeusAccount } from "./config.js";
|
|
@@ -8,6 +9,7 @@ import { parseTaskChannelTargetIntent } from "./nugget-lookup.js";
|
|
|
8
9
|
import { sendOmadeusMessage } from "./outbound.js";
|
|
9
10
|
import { getOmadeusRuntime } from "./runtime.js";
|
|
10
11
|
import { createOmadeusMessageHandler } from "./message-handler.js";
|
|
12
|
+
import { SentMessageTracker } from "./sent-message-tracker.js";
|
|
11
13
|
import { omadeusSetupAdapter } from "./setup-core.js";
|
|
12
14
|
import { omadeusSetupWizard } from "./onboarding.js";
|
|
13
15
|
import "./setup-surface.js";
|
|
@@ -23,14 +25,16 @@ const CHANNEL_ID = "omadeus";
|
|
|
23
25
|
const gatewayState = {
|
|
24
26
|
tokenManager: null,
|
|
25
27
|
dolphin: null,
|
|
26
|
-
jaguar: null
|
|
28
|
+
jaguar: null,
|
|
29
|
+
sentTracker: null
|
|
27
30
|
};
|
|
28
31
|
const isUnconfigured = (account) => account.credentialSource === "none";
|
|
29
32
|
let lastPersistedToken = null;
|
|
30
|
-
async function persistSessionToken(token) {
|
|
33
|
+
async function persistSessionToken(token, environment) {
|
|
31
34
|
if (lastPersistedToken === token) return;
|
|
32
35
|
const runtime = getOmadeusRuntime();
|
|
33
|
-
|
|
36
|
+
const section = getOmadeusChannelConfig(runtime.config.current()) ?? {};
|
|
37
|
+
if (section.sessionToken === token && section.sessionTokenEnvironment === environment) {
|
|
34
38
|
lastPersistedToken = token;
|
|
35
39
|
return;
|
|
36
40
|
}
|
|
@@ -41,7 +45,8 @@ async function persistSessionToken(token) {
|
|
|
41
45
|
...draft.channels ?? {},
|
|
42
46
|
omadeus: {
|
|
43
47
|
...getOmadeusChannelConfig(draft) ?? {},
|
|
44
|
-
sessionToken: token
|
|
48
|
+
sessionToken: token,
|
|
49
|
+
sessionTokenEnvironment: environment
|
|
45
50
|
}
|
|
46
51
|
};
|
|
47
52
|
}
|
|
@@ -82,12 +87,12 @@ const omadeusConfigAdapter = createTopLevelChannelConfigAdapter({
|
|
|
82
87
|
defaultAccountId: resolveDefaultOmadeusAccountId,
|
|
83
88
|
deleteMode: "clear-fields",
|
|
84
89
|
clearBaseFields: [
|
|
85
|
-
"
|
|
86
|
-
"maestroUrl",
|
|
90
|
+
"environment",
|
|
87
91
|
"email",
|
|
88
92
|
"password",
|
|
89
93
|
"organizationId",
|
|
90
94
|
"sessionToken",
|
|
95
|
+
"sessionTokenEnvironment",
|
|
91
96
|
"inbound"
|
|
92
97
|
],
|
|
93
98
|
resolveAllowFrom: () => [],
|
|
@@ -249,10 +254,17 @@ const omadeusPlugin = {
|
|
|
249
254
|
if (messageId == null) return actionError("Omadeus edit requires `messageId` (Jaguar message id) or current inbound MessageSid.", "Missing messageId for edit.");
|
|
250
255
|
if (!body) return actionError("Omadeus edit requires new text in `message`, `text`, or `content`.", "Missing body for edit.");
|
|
251
256
|
try {
|
|
252
|
-
|
|
253
|
-
|
|
257
|
+
const temporaryId = generateTemporaryId();
|
|
258
|
+
gatewayState.sentTracker?.trackOutbound({
|
|
259
|
+
temporaryId,
|
|
254
260
|
body
|
|
255
261
|
});
|
|
262
|
+
const edited = await editMessage(apiOpts(), {
|
|
263
|
+
messageId,
|
|
264
|
+
body,
|
|
265
|
+
temporaryId
|
|
266
|
+
});
|
|
267
|
+
if (typeof edited?.id === "number") gatewayState.sentTracker?.trackId(edited.id);
|
|
256
268
|
} catch (err) {
|
|
257
269
|
return actionError(err instanceof Error ? err.message : String(err));
|
|
258
270
|
}
|
|
@@ -374,7 +386,8 @@ const omadeusPlugin = {
|
|
|
374
386
|
maestroUrl: resolveOmadeusAccount({ cfg }).maestroUrl,
|
|
375
387
|
tokenManager: gatewayState.tokenManager
|
|
376
388
|
},
|
|
377
|
-
jaguarSocket: gatewayState.jaguar
|
|
389
|
+
jaguarSocket: gatewayState.jaguar,
|
|
390
|
+
sentTracker: gatewayState.sentTracker ?? void 0
|
|
378
391
|
}, {
|
|
379
392
|
to,
|
|
380
393
|
text
|
|
@@ -473,7 +486,7 @@ const omadeusPlugin = {
|
|
|
473
486
|
initialToken: account.sessionToken,
|
|
474
487
|
onRefresh: (token) => {
|
|
475
488
|
log.info("[omadeus] token refreshed");
|
|
476
|
-
persistSessionToken(token).catch((err) => log.warn(`[omadeus] failed to persist session token: ${String(err)}`));
|
|
489
|
+
persistSessionToken(token, account.environment).catch((err) => log.warn(`[omadeus] failed to persist session token: ${String(err)}`));
|
|
477
490
|
},
|
|
478
491
|
onError: (err) => {
|
|
479
492
|
log.error(`[omadeus] token refresh failed: ${err.message}`);
|
|
@@ -498,12 +511,15 @@ const omadeusPlugin = {
|
|
|
498
511
|
tokenManager.startAutoRefresh();
|
|
499
512
|
gatewayState.tokenManager = tokenManager;
|
|
500
513
|
const selfReferenceId = tokenManager.getPayload().referenceId;
|
|
514
|
+
const sentTracker = new SentMessageTracker();
|
|
515
|
+
gatewayState.sentTracker = sentTracker;
|
|
501
516
|
const outboundDeps = {
|
|
502
517
|
apiOpts: {
|
|
503
518
|
maestroUrl: account.maestroUrl,
|
|
504
519
|
tokenManager
|
|
505
520
|
},
|
|
506
|
-
jaguarSocket: null
|
|
521
|
+
jaguarSocket: null,
|
|
522
|
+
sentTracker
|
|
507
523
|
};
|
|
508
524
|
const handleMessage = createOmadeusMessageHandler({
|
|
509
525
|
cfg,
|
|
@@ -519,6 +535,16 @@ const omadeusPlugin = {
|
|
|
519
535
|
onMessage: (msg) => {
|
|
520
536
|
const label = msg.subscribableKind === "direct" ? `DM from ${msg.senderReferenceId}` : `${msg.subscribableKind}/${msg.roomName ?? msg.roomId} from ${msg.senderReferenceId}`;
|
|
521
537
|
log.info(`[jaguar] ${label}: ${msg.body.slice(0, 80)}`);
|
|
538
|
+
if (sentTracker.isEcho({
|
|
539
|
+
id: msg.id,
|
|
540
|
+
temporaryId: msg.temporaryId,
|
|
541
|
+
body: msg.body,
|
|
542
|
+
roomId: msg.roomId,
|
|
543
|
+
fromSelf: msg.senderReferenceId === selfReferenceId
|
|
544
|
+
})) {
|
|
545
|
+
log.debug?.(`[jaguar] suppressed self-echo id=${msg.id}`);
|
|
546
|
+
return;
|
|
547
|
+
}
|
|
522
548
|
const inbound = parseJaguarMessage(msg, { selfReferenceId }, log);
|
|
523
549
|
if (inbound) {
|
|
524
550
|
log.info(`[jaguar] inbound: ${inbound.subscribableKind} room=${inbound.roomId} from=${inbound.from} mention=${inbound.isMention}`);
|
|
@@ -605,6 +631,7 @@ const omadeusPlugin = {
|
|
|
605
631
|
gatewayState.tokenManager = null;
|
|
606
632
|
gatewayState.jaguar = null;
|
|
607
633
|
gatewayState.dolphin = null;
|
|
634
|
+
gatewayState.sentTracker = null;
|
|
608
635
|
lastPersistedToken = null;
|
|
609
636
|
ctx.setStatus({
|
|
610
637
|
accountId: account.accountId,
|
package/dist/src/config.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DEFAULT_ACCOUNT_ID } from "../runtime-api.js";
|
|
2
|
-
import "./defaults.js";
|
|
2
|
+
import { getOmadeusEnvironmentUrls, resolveOmadeusEnvironment } from "./defaults.js";
|
|
3
3
|
//#region src/config.ts
|
|
4
4
|
function getOmadeusChannelConfig(cfg) {
|
|
5
5
|
return cfg.channels?.["omadeus"];
|
|
@@ -14,11 +14,15 @@ function resolveDefaultOmadeusAccountId(_cfg) {
|
|
|
14
14
|
function resolveOmadeusAccount(params) {
|
|
15
15
|
const { cfg } = params;
|
|
16
16
|
const section = getOmadeusChannelConfig(cfg) ?? {};
|
|
17
|
+
const environment = resolveOmadeusEnvironment(section.environment);
|
|
18
|
+
const { casUrl, maestroUrl } = getOmadeusEnvironmentUrls(environment);
|
|
17
19
|
const envCredentials = resolveOmadeusEnvCredentials();
|
|
18
20
|
const email = section.email?.trim() || envCredentials?.email || "";
|
|
19
21
|
const password = section.password?.trim() || envCredentials?.password || "";
|
|
20
22
|
const orgId = section.organizationId ?? envCredentials?.organizationId;
|
|
21
|
-
const
|
|
23
|
+
const rawSessionToken = section.sessionToken?.trim() ?? "";
|
|
24
|
+
const sessionTokenEnvironment = resolveOmadeusEnvironment(section.sessionTokenEnvironment);
|
|
25
|
+
const sessionToken = Boolean(rawSessionToken) && sessionTokenEnvironment === environment ? rawSessionToken : "";
|
|
22
26
|
const hasCredentials = Boolean(email && password && orgId);
|
|
23
27
|
const hasSessionToken = Boolean(sessionToken);
|
|
24
28
|
const credentialSource = Boolean(section.email?.trim() && section.password?.trim() && section.organizationId) ? "config" : hasCredentials ? "env" : hasSessionToken ? "session" : "none";
|
|
@@ -27,8 +31,9 @@ function resolveOmadeusAccount(params) {
|
|
|
27
31
|
name: "Omadeus",
|
|
28
32
|
enabled: section.enabled !== false,
|
|
29
33
|
config: section,
|
|
30
|
-
|
|
31
|
-
|
|
34
|
+
environment,
|
|
35
|
+
casUrl,
|
|
36
|
+
maestroUrl,
|
|
32
37
|
email,
|
|
33
38
|
password,
|
|
34
39
|
organizationId: orgId ?? 0,
|
package/dist/src/defaults.js
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
const OMADEUS_ENVIRONMENTS = {
|
|
2
|
+
production: {
|
|
3
|
+
label: "Production",
|
|
4
|
+
casUrl: "https://xas.xeba.tech",
|
|
5
|
+
maestroUrl: "https://maestro.xeba.tech"
|
|
6
|
+
},
|
|
7
|
+
staging: {
|
|
8
|
+
label: "Staging",
|
|
9
|
+
casUrl: "https://staging-xas.xeba.tech",
|
|
10
|
+
maestroUrl: "https://staging.xeba.tech"
|
|
11
|
+
},
|
|
12
|
+
dev: {
|
|
13
|
+
label: "Dev",
|
|
14
|
+
casUrl: "https://dev1-cas.rouztech.com",
|
|
15
|
+
maestroUrl: "https://dev1-maestro.rouztech.com"
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
const OMADEUS_ENVIRONMENT_SET = new Set(Object.keys(OMADEUS_ENVIRONMENTS));
|
|
19
|
+
function resolveOmadeusEnvironment(value) {
|
|
20
|
+
if (typeof value === "string" && OMADEUS_ENVIRONMENT_SET.has(value)) return value;
|
|
21
|
+
return "dev";
|
|
22
|
+
}
|
|
23
|
+
function getOmadeusEnvironmentUrls(env) {
|
|
24
|
+
const config = OMADEUS_ENVIRONMENTS[env];
|
|
25
|
+
return {
|
|
26
|
+
casUrl: config.casUrl,
|
|
27
|
+
maestroUrl: config.maestroUrl
|
|
28
|
+
};
|
|
29
|
+
}
|
|
4
30
|
//#endregion
|
|
5
|
-
export {
|
|
31
|
+
export { OMADEUS_ENVIRONMENTS, getOmadeusEnvironmentUrls, resolveOmadeusEnvironment };
|
|
@@ -42,7 +42,8 @@ function surfaceForKind(kind) {
|
|
|
42
42
|
if (kind === "channel") return "channel";
|
|
43
43
|
return "entity";
|
|
44
44
|
}
|
|
45
|
-
function senderAllowed(allowed, fromReferenceId) {
|
|
45
|
+
function senderAllowed(allowed, fromReferenceId, selfReferenceId) {
|
|
46
|
+
if (fromReferenceId === selfReferenceId) return true;
|
|
46
47
|
if (!allowed || allowed.length === 0) return true;
|
|
47
48
|
return allowed.includes(fromReferenceId);
|
|
48
49
|
}
|
|
@@ -85,18 +86,14 @@ function mentionRequired(params) {
|
|
|
85
86
|
}
|
|
86
87
|
/**
|
|
87
88
|
* Evaluate whether a normalized Jaguar inbound should be dispatched to OpenClaw.
|
|
88
|
-
*
|
|
89
|
+
*
|
|
90
|
+
* The logged-in user (`selfReferenceId`) is always treated as an allowed sender
|
|
91
|
+
* so they can message their own OpenClaw even if the stored allowlist predates
|
|
92
|
+
* them. Self-authored *echoes* (the reply loop) are filtered earlier, at socket
|
|
93
|
+
* ingestion, by the {@link SentMessageTracker} — not here.
|
|
89
94
|
*/
|
|
90
95
|
function evaluateOmadeusInboundPolicy(params) {
|
|
91
96
|
const { inbound, omadeusCfg, selfReferenceId } = params;
|
|
92
|
-
if (inbound.fromReferenceId === selfReferenceId) return {
|
|
93
|
-
allow: false,
|
|
94
|
-
reason: "self_message",
|
|
95
|
-
details: {
|
|
96
|
-
fromReferenceId: inbound.fromReferenceId,
|
|
97
|
-
selfReferenceId
|
|
98
|
-
}
|
|
99
|
-
};
|
|
100
97
|
const policy = mergePolicy(omadeusCfg);
|
|
101
98
|
const surface = surfaceForKind(inbound.subscribableKind);
|
|
102
99
|
if (surface === "direct") {
|
|
@@ -105,7 +102,7 @@ function evaluateOmadeusInboundPolicy(params) {
|
|
|
105
102
|
reason: "direct_disabled",
|
|
106
103
|
details: { surface }
|
|
107
104
|
};
|
|
108
|
-
if (!senderAllowed(policy.direct.allowedSenderReferenceIds, inbound.fromReferenceId)) return {
|
|
105
|
+
if (!senderAllowed(policy.direct.allowedSenderReferenceIds, inbound.fromReferenceId, selfReferenceId)) return {
|
|
109
106
|
allow: false,
|
|
110
107
|
reason: "direct_sender_not_allowed",
|
|
111
108
|
details: { fromReferenceId: inbound.fromReferenceId }
|
|
@@ -128,7 +125,7 @@ function evaluateOmadeusInboundPolicy(params) {
|
|
|
128
125
|
reason: "channels_disabled",
|
|
129
126
|
details: { surface }
|
|
130
127
|
};
|
|
131
|
-
if (!senderAllowed(policy.channels.allowedSenderReferenceIds, inbound.fromReferenceId)) return {
|
|
128
|
+
if (!senderAllowed(policy.channels.allowedSenderReferenceIds, inbound.fromReferenceId, selfReferenceId)) return {
|
|
132
129
|
allow: false,
|
|
133
130
|
reason: "channel_sender_not_allowed",
|
|
134
131
|
details: { fromReferenceId: inbound.fromReferenceId }
|
|
@@ -139,7 +136,7 @@ function evaluateOmadeusInboundPolicy(params) {
|
|
|
139
136
|
allowedRoomIds: policy.channels.allowedRoomIds,
|
|
140
137
|
allowedChannelViewIds: policy.channels.allowedChannelViewIds
|
|
141
138
|
});
|
|
142
|
-
const senderInList = !policy.channels.allowedSenderReferenceIds || policy.channels.allowedSenderReferenceIds.length === 0 || policy.channels.allowedSenderReferenceIds.includes(inbound.fromReferenceId);
|
|
139
|
+
const senderInList = inbound.fromReferenceId === selfReferenceId || !policy.channels.allowedSenderReferenceIds || policy.channels.allowedSenderReferenceIds.length === 0 || policy.channels.allowedSenderReferenceIds.includes(inbound.fromReferenceId);
|
|
143
140
|
const inAllowlist = rv.geoInAllowlist && senderInList;
|
|
144
141
|
const channelMention = policy.channels.requireMention ?? DEFAULT_INBOUND_POLICY.channels.requireMention;
|
|
145
142
|
if (mentionRequired({
|
|
@@ -170,7 +167,7 @@ function evaluateOmadeusInboundPolicy(params) {
|
|
|
170
167
|
allowedKinds: policy.entities.allowedKinds
|
|
171
168
|
}
|
|
172
169
|
};
|
|
173
|
-
if (!senderAllowed(policy.entities.allowedSenderReferenceIds, inbound.fromReferenceId)) return {
|
|
170
|
+
if (!senderAllowed(policy.entities.allowedSenderReferenceIds, inbound.fromReferenceId, selfReferenceId)) return {
|
|
174
171
|
allow: false,
|
|
175
172
|
reason: "entity_sender_not_allowed",
|
|
176
173
|
details: { fromReferenceId: inbound.fromReferenceId }
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { logInboundDrop, resolveControlCommandGate } from "../runtime-api.js";
|
|
2
2
|
import { createNugget, findNuggetByTaskChannelRoom, resolveTaskChannelRoomId, searchNuggetByNumber } from "./api/nugget.api.js";
|
|
3
|
+
import { seeMessage } from "./api/message.api.js";
|
|
3
4
|
import { getOmadeusChannelConfig } from "./config.js";
|
|
4
5
|
import { appendNuggetContextForTaskOrNuggetRoom, appendNuggetLookupContextForAgent, parseChannelTaskCreateIntent, parseNuggetLookupIntent, parseRecurringScheduleIntent } from "./nugget-lookup.js";
|
|
5
6
|
import { getOmadeusRuntime } from "./runtime.js";
|
|
@@ -36,7 +37,17 @@ function createOmadeusMessageHandler(deps) {
|
|
|
36
37
|
cfg,
|
|
37
38
|
channel: "omadeus"
|
|
38
39
|
});
|
|
39
|
-
|
|
40
|
+
/** Mark inbound messages as seen in Omadeus (fire-and-forget). */
|
|
41
|
+
const markMessagesSeen = (messageIds) => {
|
|
42
|
+
for (const messageId of messageIds) {
|
|
43
|
+
if (!Number.isFinite(messageId)) continue;
|
|
44
|
+
log.info(`omadeus: marking message ${messageId} seen`);
|
|
45
|
+
seeMessage(outboundDeps.apiOpts, { messageId }).then(() => log.debug?.(`omadeus: marked message ${messageId} seen`)).catch((err) => {
|
|
46
|
+
log.warn(`omadeus: failed to mark message ${messageId} seen: ${err instanceof Error ? err.message : String(err)}`);
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
const handleMessageNow = async (inbound, ackMessageIds = [inbound.messageId]) => {
|
|
40
51
|
const isDirectMessage = inbound.subscribableKind === "direct";
|
|
41
52
|
const senderId = String(inbound.fromReferenceId);
|
|
42
53
|
const senderName = inbound.from;
|
|
@@ -77,6 +88,7 @@ function createOmadeusMessageHandler(deps) {
|
|
|
77
88
|
});
|
|
78
89
|
return;
|
|
79
90
|
}
|
|
91
|
+
if (inbound.fromReferenceId !== selfReferenceId) markMessagesSeen(ackMessageIds);
|
|
80
92
|
let bodyForAgent = rawBody;
|
|
81
93
|
const createIntent = parseChannelTaskCreateIntent(rawBody);
|
|
82
94
|
if (createIntent) try {
|
|
@@ -248,7 +260,7 @@ function createOmadeusMessageHandler(deps) {
|
|
|
248
260
|
...last,
|
|
249
261
|
content: combinedContent,
|
|
250
262
|
isMention: entries.some((e) => e.isMention)
|
|
251
|
-
});
|
|
263
|
+
}, entries.map((e) => e.messageId));
|
|
252
264
|
},
|
|
253
265
|
onError: (err) => {
|
|
254
266
|
runtime.error?.(`omadeus debounce flush failed: ${String(err)}`);
|
package/dist/src/onboarding.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { OMADEUS_ENVIRONMENTS, getOmadeusEnvironmentUrls, resolveOmadeusEnvironment } from "./defaults.js";
|
|
2
2
|
import { getOmadeusChannelConfig, resolveOmadeusAccount } from "./config.js";
|
|
3
3
|
import { listOrganizationMembers, listOrganizations } from "./api/auth.api.js";
|
|
4
4
|
import { formatMemberLabel } from "./member-resolve.js";
|
|
@@ -19,17 +19,26 @@ function formatAuthError(err) {
|
|
|
19
19
|
} else if (typeof cause === "string" && cause.trim()) parts.push(cause);
|
|
20
20
|
return parts.join(" — ");
|
|
21
21
|
}
|
|
22
|
-
async function noteOmadeusAuthHelp(prompter) {
|
|
22
|
+
async function noteOmadeusAuthHelp(prompter, environment) {
|
|
23
|
+
const envLabel = OMADEUS_ENVIRONMENTS[environment].label;
|
|
23
24
|
await prompter.note([
|
|
24
|
-
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
" - Organization ID (we can look it up for you)",
|
|
28
|
-
`CAS URL: ${OMADEUS_CAS_URL}`,
|
|
29
|
-
`Maestro URL: ${OMADEUS_MAESTRO_URL}`,
|
|
30
|
-
"Env vars supported: OMADEUS_EMAIL, OMADEUS_PASSWORD, OMADEUS_ORGANIZATION_ID."
|
|
25
|
+
`Connect OpenClaw to Omadeus (${envLabel}).`,
|
|
26
|
+
"",
|
|
27
|
+
"We'll ask for your email and password, then show the organizations on your account so you can pick one."
|
|
31
28
|
].join("\n"), "Omadeus setup");
|
|
32
29
|
}
|
|
30
|
+
async function promptEnvironment(prompter, existing) {
|
|
31
|
+
const initial = existing ?? "dev";
|
|
32
|
+
return resolveOmadeusEnvironment(await prompter.select({
|
|
33
|
+
message: "Select Omadeus environment",
|
|
34
|
+
options: Object.keys(OMADEUS_ENVIRONMENTS).map((env) => ({
|
|
35
|
+
value: env,
|
|
36
|
+
label: OMADEUS_ENVIRONMENTS[env].label,
|
|
37
|
+
hint: getOmadeusEnvironmentUrls(env).maestroUrl
|
|
38
|
+
})),
|
|
39
|
+
initialValue: initial
|
|
40
|
+
}));
|
|
41
|
+
}
|
|
33
42
|
async function promptOrganizationId(params) {
|
|
34
43
|
const { prompter, maestroUrl, email, existing } = params;
|
|
35
44
|
try {
|
|
@@ -143,28 +152,43 @@ async function promptCredentials(prompter, existing) {
|
|
|
143
152
|
})).trim()
|
|
144
153
|
};
|
|
145
154
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
155
|
+
/**
|
|
156
|
+
* Prompt for the set of users allowed to message this OpenClaw instance.
|
|
157
|
+
*
|
|
158
|
+
* This single allowlist governs direct messages, channels, and entity rooms.
|
|
159
|
+
* There is no "all users" option: only whitelisted members may message
|
|
160
|
+
* OpenClaw. The logged-in user is always added
|
|
161
|
+
* to the allowlist (and is excluded from the selectable list by the caller) so
|
|
162
|
+
* they can interact with their own OpenClaw. Self-authored echoes (the reply
|
|
163
|
+
* loop) are filtered earlier, at socket ingestion, by the SentMessageTracker —
|
|
164
|
+
* not by the inbound policy — so allowing yourself here cannot cause a loop.
|
|
165
|
+
*/
|
|
166
|
+
async function promptMessagingAllowlist(params) {
|
|
167
|
+
const { prompter, members, selfReferenceId, existingReferenceIds } = params;
|
|
168
|
+
let selected = [];
|
|
169
|
+
if (members.length === 0) await prompter.note("No other organization members found. Only you will be able to message OpenClaw.", "Omadeus messaging allowlist");
|
|
170
|
+
else {
|
|
171
|
+
const memberReferenceIds = new Set(members.map((member) => member.referenceId));
|
|
172
|
+
const initialValues = (existingReferenceIds ?? []).filter((id) => id !== selfReferenceId && memberReferenceIds.has(id)).map(String);
|
|
173
|
+
selected = readReferenceIds(await promptMultiSelect({
|
|
174
|
+
prompter,
|
|
175
|
+
message: "Which users do you want to be able to message this OpenClaw instance? (You are always allowed.)",
|
|
176
|
+
options: memberOptions(members),
|
|
177
|
+
initialValues
|
|
178
|
+
}));
|
|
179
|
+
}
|
|
180
|
+
return Array.from(new Set([selfReferenceId, ...selected]));
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Ask whether an @mention is required to trigger OpenClaw in a given surface
|
|
184
|
+
* (channels or entity rooms). DMs never use this — you can't @mention in a DM.
|
|
185
|
+
*/
|
|
186
|
+
async function promptRequireMention(params) {
|
|
187
|
+
if (params.existing === "outsideAllowlist") return "outsideAllowlist";
|
|
188
|
+
return await params.prompter.confirm({
|
|
189
|
+
message: `Require an @mention to trigger OpenClaw in ${params.surfaceLabel}?`,
|
|
190
|
+
initialValue: params.existing ? params.existing !== "never" : true
|
|
191
|
+
}) ? "always" : "never";
|
|
168
192
|
}
|
|
169
193
|
async function promptEntityKindSelection(params) {
|
|
170
194
|
const selected = await promptMultiSelect({
|
|
@@ -208,11 +232,11 @@ const omadeusSetupWizard = {
|
|
|
208
232
|
const account = resolveOmadeusAccount({ cfg });
|
|
209
233
|
const section = getOmadeusChannelConfig(cfg) ?? {};
|
|
210
234
|
let next = cfg;
|
|
211
|
-
|
|
235
|
+
const environment = await promptEnvironment(prompter, section.environment ? resolveOmadeusEnvironment(section.environment) : void 0);
|
|
236
|
+
const { casUrl, maestroUrl } = getOmadeusEnvironmentUrls(environment);
|
|
237
|
+
if (account.credentialSource === "none") await noteOmadeusAuthHelp(prompter, environment);
|
|
212
238
|
const envEmail = process.env.OMADEUS_EMAIL?.trim();
|
|
213
239
|
const envPassword = process.env.OMADEUS_PASSWORD?.trim();
|
|
214
|
-
const casUrl = OMADEUS_CAS_URL;
|
|
215
|
-
const maestroUrl = OMADEUS_MAESTRO_URL;
|
|
216
240
|
let { email, password } = await promptCredentials(prompter, {
|
|
217
241
|
email: section.email ?? envEmail,
|
|
218
242
|
password: section.password ?? envPassword
|
|
@@ -260,11 +284,15 @@ const omadeusSetupWizard = {
|
|
|
260
284
|
excludeReferenceIds: [selfReferenceId]
|
|
261
285
|
});
|
|
262
286
|
const existingInbound = section.inbound;
|
|
263
|
-
const
|
|
287
|
+
const allowedUserReferenceIds = await promptMessagingAllowlist({
|
|
264
288
|
prompter,
|
|
265
|
-
message: "Which users can DM OpenClaw directly?",
|
|
266
289
|
members,
|
|
267
|
-
|
|
290
|
+
selfReferenceId,
|
|
291
|
+
existingReferenceIds: Array.from(new Set([
|
|
292
|
+
...existingInbound?.direct?.allowedSenderReferenceIds ?? [],
|
|
293
|
+
...existingInbound?.channels?.allowedSenderReferenceIds ?? [],
|
|
294
|
+
...existingInbound?.entities?.allowedSenderReferenceIds ?? []
|
|
295
|
+
]))
|
|
268
296
|
});
|
|
269
297
|
const selectedChannels = await promptChannelSelection({
|
|
270
298
|
prompter,
|
|
@@ -273,33 +301,34 @@ const omadeusSetupWizard = {
|
|
|
273
301
|
memberReferenceId: selfReferenceId,
|
|
274
302
|
existingChannelViewIds: existingInbound?.channels?.allowedChannelViewIds
|
|
275
303
|
});
|
|
276
|
-
const channelSenderIds = selectedChannels.length > 0 ?
|
|
304
|
+
const channelSenderIds = selectedChannels.length > 0 ? allowedUserReferenceIds : void 0;
|
|
305
|
+
const channelRequireMention = selectedChannels.length > 0 ? await promptRequireMention({
|
|
277
306
|
prompter,
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
}) : void 0;
|
|
307
|
+
surfaceLabel: "allowed channels",
|
|
308
|
+
existing: existingInbound?.channels?.requireMention
|
|
309
|
+
}) : "never";
|
|
282
310
|
const entityKinds = await promptEntityKindSelection({
|
|
283
311
|
prompter,
|
|
284
312
|
existingKinds: existingInbound?.entities?.allowedKinds
|
|
285
313
|
});
|
|
286
|
-
const entitySenderIds = entityKinds.length > 0 ?
|
|
314
|
+
const entitySenderIds = entityKinds.length > 0 ? allowedUserReferenceIds : void 0;
|
|
315
|
+
const entityRequireMention = entityKinds.length > 0 ? await promptRequireMention({
|
|
287
316
|
prompter,
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
}) : void 0;
|
|
317
|
+
surfaceLabel: "entity rooms",
|
|
318
|
+
existing: existingInbound?.entities?.requireMention
|
|
319
|
+
}) : "never";
|
|
292
320
|
const channelRoomIds = selectedChannels.flatMap((selectedChannel) => [selectedChannel.publicRoomId, selectedChannel.privateRoomId]).filter((id) => typeof id === "number");
|
|
293
321
|
const channelViewIds = selectedChannels.map((selectedChannel) => selectedChannel.id);
|
|
294
322
|
const channelTitles = selectedChannels.map((selectedChannel) => selectedChannel.title || `Channel ${selectedChannel.id}`).join(", ");
|
|
295
323
|
const senderSummary = (ids) => ids && ids.length > 0 ? ids.join(", ") : "all users";
|
|
296
|
-
const
|
|
297
|
-
const channelSummary = selectedChannels.length > 0 ? `- Channels "${channelTitles}": rooms ${channelRoomIds.join(", ") || "(no room ids)"} from ${senderSummary(channelSenderIds)};
|
|
324
|
+
const mentionSummary = (require) => require === "never" ? "no @mention required" : require === "outsideAllowlist" ? "@mention required outside the allowlist" : "@mention required";
|
|
325
|
+
const channelSummary = selectedChannels.length > 0 ? `- Channels "${channelTitles}": rooms ${channelRoomIds.join(", ") || "(no room ids)"} from ${senderSummary(channelSenderIds)}; ${mentionSummary(channelRequireMention)}.` : "- Channels: disabled (none selected).";
|
|
326
|
+
const entitySummary = entityKinds.length > 0 ? `- Entity rooms (${entityKinds.join(", ")}): ${senderSummary(entitySenderIds)}; ${mentionSummary(entityRequireMention)}.` : "- Entity rooms: disabled (no room types selected).";
|
|
298
327
|
await prompter.note([
|
|
299
328
|
`Inbound policy (Jaguar chat):`,
|
|
300
|
-
`- Direct messages: enabled for ${senderSummary(
|
|
329
|
+
`- Direct messages: enabled for ${senderSummary(allowedUserReferenceIds)}.`,
|
|
301
330
|
channelSummary,
|
|
302
|
-
|
|
331
|
+
entitySummary
|
|
303
332
|
].join("\n"), "Omadeus inbound policy");
|
|
304
333
|
next = {
|
|
305
334
|
...next,
|
|
@@ -307,17 +336,17 @@ const omadeusSetupWizard = {
|
|
|
307
336
|
...next.channels,
|
|
308
337
|
omadeus: {
|
|
309
338
|
enabled: true,
|
|
310
|
-
|
|
311
|
-
maestroUrl,
|
|
339
|
+
environment,
|
|
312
340
|
email,
|
|
313
341
|
password,
|
|
314
342
|
organizationId,
|
|
315
343
|
sessionToken,
|
|
344
|
+
sessionTokenEnvironment: environment,
|
|
316
345
|
inbound: {
|
|
317
346
|
version: 1,
|
|
318
347
|
direct: {
|
|
319
348
|
enabled: true,
|
|
320
|
-
|
|
349
|
+
allowedSenderReferenceIds: allowedUserReferenceIds,
|
|
321
350
|
requireMention: "never"
|
|
322
351
|
},
|
|
323
352
|
channels: {
|
|
@@ -325,13 +354,13 @@ const omadeusSetupWizard = {
|
|
|
325
354
|
allowedRoomIds: channelRoomIds,
|
|
326
355
|
allowedChannelViewIds: channelViewIds,
|
|
327
356
|
...channelSenderIds ? { allowedSenderReferenceIds: channelSenderIds } : {},
|
|
328
|
-
requireMention:
|
|
357
|
+
requireMention: channelRequireMention
|
|
329
358
|
},
|
|
330
359
|
entities: {
|
|
331
360
|
enabled: entityKinds.length > 0,
|
|
332
361
|
allowedKinds: entityKinds,
|
|
333
362
|
...entitySenderIds ? { allowedSenderReferenceIds: entitySenderIds } : {},
|
|
334
|
-
requireMention:
|
|
363
|
+
requireMention: entityRequireMention
|
|
335
364
|
}
|
|
336
365
|
}
|
|
337
366
|
}
|
package/dist/src/outbound.js
CHANGED
|
@@ -1,12 +1,21 @@
|
|
|
1
|
+
import { generateTemporaryId } from "./utils/http.util.js";
|
|
1
2
|
import { sendRoomMessage } from "./api/message.api.js";
|
|
2
3
|
//#region src/outbound.ts
|
|
3
4
|
async function sendOmadeusMessage(deps, params) {
|
|
4
5
|
const { to, text } = params;
|
|
6
|
+
const temporaryId = generateTemporaryId();
|
|
7
|
+
deps.sentTracker?.trackOutbound({
|
|
8
|
+
temporaryId,
|
|
9
|
+
body: text,
|
|
10
|
+
roomId: to
|
|
11
|
+
});
|
|
5
12
|
const result = await sendRoomMessage(deps.apiOpts, {
|
|
6
13
|
roomId: to,
|
|
7
|
-
body: text
|
|
14
|
+
body: text,
|
|
15
|
+
temporaryId
|
|
8
16
|
});
|
|
9
17
|
if (!result.ok) throw new Error(`Omadeus send failed: ${result.error}`);
|
|
18
|
+
if (typeof result.message?.id === "number") deps.sentTracker?.trackId(result.message.id);
|
|
10
19
|
return {
|
|
11
20
|
channel: "omadeus",
|
|
12
21
|
messageId: String(result.message?.id ?? ""),
|