@otterware/openclaw-otter 0.2.2 → 0.2.3
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 +20 -11
- package/otter.js +32 -1
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -15,14 +15,16 @@ import { runDetachedWebhookWork } from "openclaw/plugin-sdk/webhook-request-guar
|
|
|
15
15
|
import { readWorkspaceFile } from "./files.js";
|
|
16
16
|
import {
|
|
17
17
|
accountOf,
|
|
18
|
-
chatOf,
|
|
19
18
|
chatTarget,
|
|
19
|
+
isNewChat,
|
|
20
20
|
isOwnerConfigured,
|
|
21
21
|
messageOf,
|
|
22
|
-
|
|
22
|
+
newChatKey,
|
|
23
|
+
otterTargetsNote,
|
|
23
24
|
OWNER,
|
|
24
25
|
pointerOf,
|
|
25
26
|
sessionOf,
|
|
27
|
+
targetOf,
|
|
26
28
|
tell,
|
|
27
29
|
} from "./otter.js";
|
|
28
30
|
|
|
@@ -61,13 +63,15 @@ export const otterChannel = createChatChannelPlugin({
|
|
|
61
63
|
messaging: {
|
|
62
64
|
targetResolver: {
|
|
63
65
|
looksLikeId: () => true,
|
|
64
|
-
hint:
|
|
66
|
+
hint: '"chat:<session key>" for one of Otter\'s chats, "new" for a new chat, or "inbox"',
|
|
65
67
|
},
|
|
66
|
-
// A delivery to a chat is kept in its session;
|
|
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); any other, in the inbox.
|
|
67
70
|
resolveOutboundSessionRoute: ({ agentId, target }) => {
|
|
68
|
-
const
|
|
69
|
-
const sessionKey =
|
|
70
|
-
|
|
71
|
+
const to = targetOf(target);
|
|
72
|
+
const sessionKey = isNewChat(to)
|
|
73
|
+
? newChatKey(agentId, randomUUID())
|
|
74
|
+
: sessionOf(target, agentId);
|
|
71
75
|
return {
|
|
72
76
|
sessionKey,
|
|
73
77
|
baseSessionKey: sessionKey,
|
|
@@ -82,10 +86,7 @@ export const otterChannel = createChatChannelPlugin({
|
|
|
82
86
|
},
|
|
83
87
|
outbound: {
|
|
84
88
|
deliveryMode: "direct",
|
|
85
|
-
resolveTarget: (params) => {
|
|
86
|
-
const chat = chatOf(params?.to);
|
|
87
|
-
return { ok: true, to: chat ? chatTarget(chat) : OTTER_TARGET };
|
|
88
|
-
},
|
|
89
|
+
resolveTarget: (params) => ({ ok: true, to: targetOf(params?.to) }),
|
|
89
90
|
sendText: (ctx) => deliver(ctx),
|
|
90
91
|
sendPayload: (ctx) => deliver(ctx, ctx.payload),
|
|
91
92
|
},
|
|
@@ -155,6 +156,14 @@ export default defineChannelPluginEntry({
|
|
|
155
156
|
description: "Send what your agents want you to see to Otter.",
|
|
156
157
|
plugin: otterChannel,
|
|
157
158
|
registerFull(api) {
|
|
159
|
+
// What Otter's targets are, for a turn in an Otter chat: the Codex harness leaves channels'
|
|
160
|
+
// message-tool hints out of its instructions, but every harness runs this hook.
|
|
161
|
+
api.on("before_prompt_build", (_event, ctx) =>
|
|
162
|
+
ctx?.channel === "otter" || ctx?.messageProvider === "otter"
|
|
163
|
+
? { appendSystemContext: otterTargetsNote(ctx.sessionKey) }
|
|
164
|
+
: undefined,
|
|
165
|
+
);
|
|
166
|
+
|
|
158
167
|
// What you send in an Otter chat, as the gateway's owner: `chat.send`'s run, with the authority
|
|
159
168
|
// a configured channel owner has (an automation another chat made, for one).
|
|
160
169
|
api.registerGatewayMethod(
|
package/otter.js
CHANGED
|
@@ -12,6 +12,36 @@ export const OWNER_ENTRY = `otter:${OWNER}`;
|
|
|
12
12
|
/** A chat in Otter is a target of its own: what's sent to it is kept in its session. */
|
|
13
13
|
export const chatTarget = (sessionKey) => `chat:${sessionKey}`;
|
|
14
14
|
|
|
15
|
+
/** 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";
|
|
17
|
+
|
|
18
|
+
export const isNewChat = (to) =>
|
|
19
|
+
typeof to === "string" && to.trim().replace(/^otter:/, "") === NEW_CHAT_TARGET;
|
|
20
|
+
|
|
21
|
+
/** A target as the channel keeps it: one of Otter's chats, a new one each time, or the inbox. */
|
|
22
|
+
export function targetOf(to) {
|
|
23
|
+
const chat = chatOf(to);
|
|
24
|
+
if (chat) return chatTarget(chat);
|
|
25
|
+
return isNewChat(to) ? NEW_CHAT_TARGET : OTTER_TARGET;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** A session for a new chat, an Otter chat like one you start. */
|
|
29
|
+
export const newChatKey = (agentId, id) => `agent:${agentId}:otter-mail:${id}`;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* What an agent in an Otter chat is told about sending to Otter (the message tool, an automation's
|
|
33
|
+
* delivery), with this chat's own target.
|
|
34
|
+
*/
|
|
35
|
+
export function otterTargetsNote(sessionKey) {
|
|
36
|
+
const here = agentOf(sessionKey) ? chatTarget(sessionKey) : "chat:<session key>";
|
|
37
|
+
return [
|
|
38
|
+
"Otter delivery targets (channel `otter`), like a thread or a new post in Slack:",
|
|
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.',
|
|
42
|
+
].join("\n");
|
|
43
|
+
}
|
|
44
|
+
|
|
15
45
|
export const agentOf = (sessionKey) =>
|
|
16
46
|
typeof sessionKey === "string" && sessionKey.startsWith("agent:")
|
|
17
47
|
? sessionKey.split(":")[1]
|
|
@@ -41,7 +71,8 @@ export function pointerOf({ sessionKey, to, payload, at = Date.now() }) {
|
|
|
41
71
|
const approval = data.execApproval;
|
|
42
72
|
const question = data.askUser;
|
|
43
73
|
const session = approval?.sessionKey ?? sessionKey;
|
|
44
|
-
|
|
74
|
+
// A new chat's session is the one its delivery was routed to.
|
|
75
|
+
const chat = chatOf(to) ?? (isNewChat(to) && agentOf(sessionKey) ? sessionKey : undefined);
|
|
45
76
|
const agentId = approval?.agentId ?? agentOf(chat ?? session) ?? "main";
|
|
46
77
|
return {
|
|
47
78
|
kind: approval ? "approval" : question ? "question" : "message",
|
package/package.json
CHANGED