@otterware/openclaw-otter 0.2.3 → 0.2.4
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/index.js +13 -8
- package/otter.js +14 -18
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -15,6 +15,7 @@ import { runDetachedWebhookWork } from "openclaw/plugin-sdk/webhook-request-guar
|
|
|
15
15
|
import { readWorkspaceFile } from "./files.js";
|
|
16
16
|
import {
|
|
17
17
|
accountOf,
|
|
18
|
+
chatOf,
|
|
18
19
|
chatTarget,
|
|
19
20
|
isNewChat,
|
|
20
21
|
isOwnerConfigured,
|
|
@@ -23,8 +24,8 @@ import {
|
|
|
23
24
|
otterTargetsNote,
|
|
24
25
|
OWNER,
|
|
25
26
|
pointerOf,
|
|
26
|
-
sessionOf,
|
|
27
27
|
targetOf,
|
|
28
|
+
TARGETS_HINT,
|
|
28
29
|
tell,
|
|
29
30
|
} from "./otter.js";
|
|
30
31
|
|
|
@@ -63,15 +64,14 @@ export const otterChannel = createChatChannelPlugin({
|
|
|
63
64
|
messaging: {
|
|
64
65
|
targetResolver: {
|
|
65
66
|
looksLikeId: () => true,
|
|
66
|
-
hint:
|
|
67
|
+
hint: TARGETS_HINT,
|
|
67
68
|
},
|
|
68
|
-
// A delivery to a chat is kept in its session; to "new", in a chat it starts (the gateway
|
|
69
|
-
// routes each delivery once, and sends it to that session)
|
|
69
|
+
// A delivery to a chat is kept in its session; to "chat:new", in a chat it starts (the gateway
|
|
70
|
+
// routes each delivery once, and sends it to that session).
|
|
70
71
|
resolveOutboundSessionRoute: ({ agentId, target }) => {
|
|
71
72
|
const to = targetOf(target);
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
: sessionOf(target, agentId);
|
|
73
|
+
if (!to) return null;
|
|
74
|
+
const sessionKey = isNewChat(to) ? newChatKey(agentId, randomUUID()) : chatOf(to);
|
|
75
75
|
return {
|
|
76
76
|
sessionKey,
|
|
77
77
|
baseSessionKey: sessionKey,
|
|
@@ -86,7 +86,12 @@ export const otterChannel = createChatChannelPlugin({
|
|
|
86
86
|
},
|
|
87
87
|
outbound: {
|
|
88
88
|
deliveryMode: "direct",
|
|
89
|
-
resolveTarget: (params) =>
|
|
89
|
+
resolveTarget: (params) => {
|
|
90
|
+
const to = targetOf(params?.to);
|
|
91
|
+
return to
|
|
92
|
+
? { ok: true, to }
|
|
93
|
+
: { ok: false, error: new Error(`Otter's targets are ${TARGETS_HINT}.`) };
|
|
94
|
+
},
|
|
90
95
|
sendText: (ctx) => deliver(ctx),
|
|
91
96
|
sendPayload: (ctx) => deliver(ctx, ctx.payload),
|
|
92
97
|
},
|
package/otter.js
CHANGED
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
// What the plugin does, apart from OpenClaw's SDK, so Otter's tests can run it.
|
|
2
2
|
|
|
3
|
-
/** Where an agent's deliveries to Otter are kept: one session per agent, read by Otter's apps. */
|
|
4
|
-
export const inboxKey = (agentId) => `agent:${agentId}:otter:inbox`;
|
|
5
|
-
|
|
6
|
-
export const OTTER_TARGET = "inbox";
|
|
7
|
-
|
|
8
3
|
/** Who sends what you type in Otter: the gateway's owner once `commands.ownerAllowFrom` lists `otter:owner`. */
|
|
9
4
|
export const OWNER = "owner";
|
|
10
5
|
export const OWNER_ENTRY = `otter:${OWNER}`;
|
|
@@ -13,18 +8,23 @@ export const OWNER_ENTRY = `otter:${OWNER}`;
|
|
|
13
8
|
export const chatTarget = (sessionKey) => `chat:${sessionKey}`;
|
|
14
9
|
|
|
15
10
|
/** Like posting to a Slack channel rather than in a thread: each delivery starts a chat of its own. */
|
|
16
|
-
export const NEW_CHAT_TARGET = "new";
|
|
11
|
+
export const NEW_CHAT_TARGET = "chat:new";
|
|
12
|
+
|
|
13
|
+
const bare = (to) => (typeof to === "string" ? to.trim().replace(/^otter:/, "") : "");
|
|
17
14
|
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
/** What's sent to the gateway's owner (`otter:owner`) starts a chat, as it names none. */
|
|
16
|
+
export const isNewChat = (to) => bare(to) === NEW_CHAT_TARGET || bare(to) === OWNER;
|
|
20
17
|
|
|
21
|
-
/** A target as the channel keeps it: one of Otter's chats, a new one each time
|
|
18
|
+
/** A target as the channel keeps it: one of Otter's chats, or a new one each time; else none. */
|
|
22
19
|
export function targetOf(to) {
|
|
23
20
|
const chat = chatOf(to);
|
|
24
21
|
if (chat) return chatTarget(chat);
|
|
25
|
-
return isNewChat(to) ? NEW_CHAT_TARGET :
|
|
22
|
+
return isNewChat(to) ? NEW_CHAT_TARGET : undefined;
|
|
26
23
|
}
|
|
27
24
|
|
|
25
|
+
export const TARGETS_HINT =
|
|
26
|
+
'"chat:<session key>" for one of Otter\'s chats, or "chat:new" for a new one';
|
|
27
|
+
|
|
28
28
|
/** A session for a new chat, an Otter chat like one you start. */
|
|
29
29
|
export const newChatKey = (agentId, id) => `agent:${agentId}:otter-mail:${id}`;
|
|
30
30
|
|
|
@@ -35,10 +35,9 @@ export const newChatKey = (agentId, id) => `agent:${agentId}:otter-mail:${id}`;
|
|
|
35
35
|
export function otterTargetsNote(sessionKey) {
|
|
36
36
|
const here = agentOf(sessionKey) ? chatTarget(sessionKey) : "chat:<session key>";
|
|
37
37
|
return [
|
|
38
|
-
"Otter delivery targets (channel `otter`), like a thread or a new post in Slack:",
|
|
38
|
+
"Otter delivery targets (channel `otter`), like replying in a thread or a new post in Slack:",
|
|
39
39
|
`- "${here}": this chat. Any Otter chat is "chat:<its session key>".`,
|
|
40
|
-
'- "new": a new Otter chat for each delivery (an automation that should post a fresh chat every run, such as a morning brief).',
|
|
41
|
-
'- "inbox": the agent\'s single Otter inbox chat.',
|
|
40
|
+
'- "chat:new": a new Otter chat for each delivery (an automation that should post a fresh chat every run, such as a morning brief or an alert).',
|
|
42
41
|
].join("\n");
|
|
43
42
|
}
|
|
44
43
|
|
|
@@ -49,15 +48,12 @@ export const agentOf = (sessionKey) =>
|
|
|
49
48
|
|
|
50
49
|
/** The chat a target names, if it names one. */
|
|
51
50
|
export function chatOf(to) {
|
|
52
|
-
const target =
|
|
51
|
+
const target = bare(to);
|
|
53
52
|
if (!target.startsWith("chat:")) return undefined;
|
|
54
53
|
const sessionKey = target.slice("chat:".length);
|
|
55
54
|
return agentOf(sessionKey) ? sessionKey : undefined;
|
|
56
55
|
}
|
|
57
56
|
|
|
58
|
-
/** The session a delivery to this target is kept in. */
|
|
59
|
-
export const sessionOf = (to, agentId) => chatOf(to) ?? inboxKey(agentId);
|
|
60
|
-
|
|
61
57
|
export const isOwnerConfigured = (cfg) =>
|
|
62
58
|
Array.isArray(cfg?.commands?.ownerAllowFrom) &&
|
|
63
59
|
cfg.commands.ownerAllowFrom.some((entry) => String(entry).trim() === OWNER_ENTRY);
|
|
@@ -77,7 +73,7 @@ export function pointerOf({ sessionKey, to, payload, at = Date.now() }) {
|
|
|
77
73
|
return {
|
|
78
74
|
kind: approval ? "approval" : question ? "question" : "message",
|
|
79
75
|
agentId,
|
|
80
|
-
sessionKey: approval || question ? (session ?? null) : (chat ??
|
|
76
|
+
sessionKey: approval || question ? (session ?? null) : (chat ?? session ?? null),
|
|
81
77
|
...(approval && {
|
|
82
78
|
approvalId: String(approval.approvalId),
|
|
83
79
|
approvalKind: approval.approvalKind === "plugin" ? "plugin" : "exec",
|
package/package.json
CHANGED