@occum-net/occumclaw 0.4.0 → 0.5.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/index.ts +75 -44
- package/openclaw.plugin.json +33 -1
- package/package.json +1 -1
package/index.ts
CHANGED
|
@@ -8,12 +8,8 @@
|
|
|
8
8
|
* occum.net.
|
|
9
9
|
*
|
|
10
10
|
* Configuration (set via `openclaw config set`):
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* Legacy config path (auto-migrated):
|
|
15
|
-
* plugins.entries.occumclaw.config.agentToken
|
|
16
|
-
* plugins.entries.occumclaw.config.apiUrl
|
|
11
|
+
* plugins.entries.occumclaw.config.agentToken — Agent bearer token (occ_...)
|
|
12
|
+
* plugins.entries.occumclaw.config.apiUrl — API base URL (default: https://api.occum.net)
|
|
17
13
|
*/
|
|
18
14
|
|
|
19
15
|
import { OccumWsClient, type OccumEvent, type AuthOkData } from "./ws-client";
|
|
@@ -69,12 +65,14 @@ interface MsgContext {
|
|
|
69
65
|
BodyForAgent?: string;
|
|
70
66
|
CommandBody?: string;
|
|
71
67
|
From?: string;
|
|
72
|
-
|
|
68
|
+
OriginatingChannel?: string;
|
|
69
|
+
OriginatingTo?: string;
|
|
70
|
+
Surface?: string;
|
|
73
71
|
SessionKey?: string;
|
|
74
72
|
AccountId?: string;
|
|
75
73
|
Provider?: string;
|
|
76
74
|
ChatType?: string;
|
|
77
|
-
|
|
75
|
+
GroupChannel?: string;
|
|
78
76
|
SenderId?: string;
|
|
79
77
|
SenderName?: string;
|
|
80
78
|
SenderUsername?: string;
|
|
@@ -155,8 +153,7 @@ interface OpenClawPluginApi {
|
|
|
155
153
|
// 5. undefined → fall back to global (tools.allow / tools.profile)
|
|
156
154
|
//
|
|
157
155
|
// Config path:
|
|
158
|
-
//
|
|
159
|
-
// channels.occum.accounts.<accountId>.groups.<channelId>.toolsBySender
|
|
156
|
+
// plugins.entries.occumclaw.config.groups.<channelId>.tools
|
|
160
157
|
//
|
|
161
158
|
// Per-sender key format:
|
|
162
159
|
// "id:<senderId>" — match by user/agent ID
|
|
@@ -263,15 +260,16 @@ function resolveToolsBySender(
|
|
|
263
260
|
/**
|
|
264
261
|
* Resolve the effective tool policy for an inbound occum.net message.
|
|
265
262
|
*
|
|
266
|
-
* Looks up config
|
|
267
|
-
*
|
|
263
|
+
* Looks up groups config from:
|
|
264
|
+
* plugins.entries.occumclaw.config.groups
|
|
265
|
+
*
|
|
266
|
+
* Then resolves per-channel and per-sender overrides with wildcard fallback.
|
|
268
267
|
*/
|
|
269
268
|
function resolveOccumToolPolicy(params: ChannelGroupContext): GroupToolPolicyConfig | undefined {
|
|
270
269
|
const { cfg, groupId, accountId, senderId, senderName, senderUsername, senderE164 } = params;
|
|
271
270
|
|
|
272
|
-
const acctId = accountId ?? "default";
|
|
273
271
|
const groups: Record<string, OccumGroupConfig> | undefined =
|
|
274
|
-
cfg?.
|
|
272
|
+
cfg?.plugins?.entries?.occumclaw?.config?.groups;
|
|
275
273
|
|
|
276
274
|
if (!groups || typeof groups !== "object") return undefined;
|
|
277
275
|
|
|
@@ -308,22 +306,8 @@ function resolveApiUrl(raw?: string): string {
|
|
|
308
306
|
return (raw ?? "https://api.occum.net").replace(/\/+$/, "") + "/v1";
|
|
309
307
|
}
|
|
310
308
|
|
|
311
|
-
/**
|
|
312
|
-
|
|
313
|
-
* Supports both channel-style config and legacy plugin config.
|
|
314
|
-
*/
|
|
315
|
-
function resolveAccount(cfg: any, accountId?: string | null): OccumAccount {
|
|
316
|
-
// Channel-style: channels.occum.accounts.<accountId>
|
|
317
|
-
const acctId = accountId ?? "default";
|
|
318
|
-
const channelAcct = cfg?.channels?.occum?.accounts?.[acctId];
|
|
319
|
-
if (channelAcct?.agentToken) {
|
|
320
|
-
return {
|
|
321
|
-
agentToken: channelAcct.agentToken,
|
|
322
|
-
apiUrl: resolveApiUrl(channelAcct.apiUrl),
|
|
323
|
-
};
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
// Plugin config: plugins.entries.occumclaw.config
|
|
309
|
+
/** Resolve account config from plugins.entries.occumclaw.config. */
|
|
310
|
+
function resolveAccount(cfg: any, _accountId?: string | null): OccumAccount {
|
|
327
311
|
const pluginCfg = cfg?.plugins?.entries?.occumclaw?.config;
|
|
328
312
|
if (pluginCfg?.agentToken) {
|
|
329
313
|
return {
|
|
@@ -336,12 +320,6 @@ function resolveAccount(cfg: any, accountId?: string | null): OccumAccount {
|
|
|
336
320
|
}
|
|
337
321
|
|
|
338
322
|
function listAccountIds(cfg: any): string[] {
|
|
339
|
-
// Channel-style accounts
|
|
340
|
-
const channelAccounts = cfg?.channels?.occum?.accounts;
|
|
341
|
-
if (channelAccounts && typeof channelAccounts === "object") {
|
|
342
|
-
return Object.keys(channelAccounts);
|
|
343
|
-
}
|
|
344
|
-
// Plugin config: single implicit account
|
|
345
323
|
const pluginCfg = cfg?.plugins?.entries?.occumclaw?.config;
|
|
346
324
|
if (pluginCfg?.agentToken) {
|
|
347
325
|
return ["default"];
|
|
@@ -352,6 +330,10 @@ function listAccountIds(cfg: any): string[] {
|
|
|
352
330
|
// ─── Channel Plugin ─────────────────────────────────────────────────────────
|
|
353
331
|
|
|
354
332
|
function createOccumChannelPlugin(logger: PluginLogger) {
|
|
333
|
+
// Shared state: set by startAccount, read by resolveTarget/sendText.
|
|
334
|
+
// All outbound delivery routes to the agent's control channel.
|
|
335
|
+
let controlChannelId: string | null = null;
|
|
336
|
+
|
|
355
337
|
return {
|
|
356
338
|
id: "occum" as const,
|
|
357
339
|
|
|
@@ -381,6 +363,22 @@ function createOccumChannelPlugin(logger: PluginLogger) {
|
|
|
381
363
|
outbound: {
|
|
382
364
|
deliveryMode: "direct" as const,
|
|
383
365
|
|
|
366
|
+
resolveTarget(params: {
|
|
367
|
+
cfg?: any;
|
|
368
|
+
to?: string;
|
|
369
|
+
allowFrom?: string[];
|
|
370
|
+
accountId?: string | null;
|
|
371
|
+
mode?: string;
|
|
372
|
+
}): { ok: true; to: string } | { ok: false; error: Error } {
|
|
373
|
+
// All outbound delivery routes to the control channel.
|
|
374
|
+
// OpenClaw may pass a user ID (e.g. from "channel": "last" cron
|
|
375
|
+
// delivery) — we resolve it to the control channel regardless.
|
|
376
|
+
if (!controlChannelId) {
|
|
377
|
+
return { ok: false, error: new Error("Occum.net agent not connected yet (no control channel)") };
|
|
378
|
+
}
|
|
379
|
+
return { ok: true, to: controlChannelId };
|
|
380
|
+
},
|
|
381
|
+
|
|
384
382
|
async sendText(ctx: {
|
|
385
383
|
cfg: any;
|
|
386
384
|
to: string;
|
|
@@ -390,8 +388,9 @@ function createOccumChannelPlugin(logger: PluginLogger) {
|
|
|
390
388
|
const account = resolveAccount(ctx.cfg, ctx.accountId);
|
|
391
389
|
const config: ToolConfig = { agentToken: account.agentToken, apiUrl: account.apiUrl };
|
|
392
390
|
|
|
393
|
-
// `to`
|
|
394
|
-
|
|
391
|
+
// Use control channel if `to` doesn't look like a channel ID
|
|
392
|
+
// (e.g. OpenClaw passed a user ID from "channel: last" resolution)
|
|
393
|
+
const channelId = controlChannelId ?? ctx.to;
|
|
395
394
|
|
|
396
395
|
const event = await apiRequest(config, `/channels/${channelId}/messages`, {
|
|
397
396
|
method: "POST",
|
|
@@ -426,11 +425,10 @@ function createOccumChannelPlugin(logger: PluginLogger) {
|
|
|
426
425
|
|
|
427
426
|
// Discover agent identity
|
|
428
427
|
let agentId: string | null = null;
|
|
429
|
-
let controlChannelId: string | null = null;
|
|
430
428
|
try {
|
|
431
429
|
const me = await apiRequest(config, "/agents/me");
|
|
432
430
|
agentId = me.id;
|
|
433
|
-
controlChannelId = me.controlChannelId;
|
|
431
|
+
controlChannelId = me.controlChannelId ?? null;
|
|
434
432
|
log?.info?.(`Agent identity: ${me.name} (${agentId}), control channel #${controlChannelId}`);
|
|
435
433
|
} catch (err) {
|
|
436
434
|
log?.warn?.(`Failed to discover agent identity: ${err}`);
|
|
@@ -540,12 +538,14 @@ function createOccumChannelPlugin(logger: PluginLogger) {
|
|
|
540
538
|
From: event.senderType === "user"
|
|
541
539
|
? `user:${event.senderUserId}`
|
|
542
540
|
: `agent:${event.senderAgentId}`,
|
|
543
|
-
|
|
541
|
+
OriginatingChannel: "occum",
|
|
542
|
+
OriginatingTo: replyChannelId,
|
|
543
|
+
Surface: "occum",
|
|
544
544
|
SessionKey: `occum-${ctx.accountId}-${event.channelId}`,
|
|
545
545
|
AccountId: ctx.accountId,
|
|
546
546
|
Provider: "occum",
|
|
547
547
|
ChatType: "direct",
|
|
548
|
-
|
|
548
|
+
GroupChannel: event.channelId,
|
|
549
549
|
SenderId: event.senderType === "user"
|
|
550
550
|
? String(event.senderUserId)
|
|
551
551
|
: String(event.senderAgentId),
|
|
@@ -577,15 +577,46 @@ function createOccumChannelPlugin(logger: PluginLogger) {
|
|
|
577
577
|
ctx: msgCtx,
|
|
578
578
|
cfg,
|
|
579
579
|
dispatcherOptions: {
|
|
580
|
-
deliver: async (payload: ReplyPayload) => {
|
|
580
|
+
deliver: async (payload: ReplyPayload, info: ReplyDispatchKindInfo) => {
|
|
581
581
|
if (!payload.text) return;
|
|
582
|
+
|
|
583
|
+
// Reasoning/thinking blocks → work update
|
|
584
|
+
if (payload.isReasoning) {
|
|
585
|
+
log?.info?.(`[occum] Work update (thinking, ${payload.text.length} chars)`);
|
|
586
|
+
apiRequest(config, `/channels/${event.channelId}/work/${sessionId}/update`, {
|
|
587
|
+
method: "POST",
|
|
588
|
+
body: JSON.stringify({ updateType: "thinking", content: payload.text }),
|
|
589
|
+
}).catch((err) => log?.warn?.(`[occum] Failed to post thinking update: ${err}`));
|
|
590
|
+
return;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
// Tool results and intermediate blocks → work updates
|
|
594
|
+
if (info.kind === "tool") {
|
|
595
|
+
log?.info?.(`[occum] Work update (tool, ${payload.text.length} chars)`);
|
|
596
|
+
apiRequest(config, `/channels/${event.channelId}/work/${sessionId}/update`, {
|
|
597
|
+
method: "POST",
|
|
598
|
+
body: JSON.stringify({ updateType: "tool_use", content: payload.text }),
|
|
599
|
+
}).catch((err) => log?.warn?.(`[occum] Failed to post tool update: ${err}`));
|
|
600
|
+
return;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
if (info.kind === "block") {
|
|
604
|
+
log?.info?.(`[occum] Work update (text, ${payload.text.length} chars)`);
|
|
605
|
+
apiRequest(config, `/channels/${event.channelId}/work/${sessionId}/update`, {
|
|
606
|
+
method: "POST",
|
|
607
|
+
body: JSON.stringify({ updateType: "text", content: payload.text }),
|
|
608
|
+
}).catch((err) => log?.warn?.(`[occum] Failed to post text update: ${err}`));
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
// Final reply → send as actual channel message
|
|
582
613
|
replyText += payload.text;
|
|
583
614
|
try {
|
|
584
615
|
await apiRequest(config, `/channels/${event.channelId}/messages`, {
|
|
585
616
|
method: "POST",
|
|
586
617
|
body: JSON.stringify({ text: payload.text }),
|
|
587
618
|
});
|
|
588
|
-
log?.info?.(`[occum]
|
|
619
|
+
log?.info?.(`[occum] Final reply sent to #${event.channelId} (${payload.text.length} chars)`);
|
|
589
620
|
} catch (err) {
|
|
590
621
|
log?.error?.(`[occum] Failed to send reply: ${err}`);
|
|
591
622
|
}
|
package/openclaw.plugin.json
CHANGED
|
@@ -9,7 +9,39 @@
|
|
|
9
9
|
"additionalProperties": false,
|
|
10
10
|
"properties": {
|
|
11
11
|
"agentToken": { "type": "string" },
|
|
12
|
-
"apiUrl": { "type": "string", "default": "https://api.occum.net" }
|
|
12
|
+
"apiUrl": { "type": "string", "default": "https://api.occum.net" },
|
|
13
|
+
"groups": {
|
|
14
|
+
"type": "object",
|
|
15
|
+
"description": "Per-channel and per-sender tool policy overrides. Keys are channel IDs or \"*\" for wildcard.",
|
|
16
|
+
"additionalProperties": {
|
|
17
|
+
"type": "object",
|
|
18
|
+
"properties": {
|
|
19
|
+
"tools": {
|
|
20
|
+
"type": "object",
|
|
21
|
+
"properties": {
|
|
22
|
+
"allow": { "type": "array", "items": { "type": "string" } },
|
|
23
|
+
"alsoAllow": { "type": "array", "items": { "type": "string" } },
|
|
24
|
+
"deny": { "type": "array", "items": { "type": "string" } }
|
|
25
|
+
},
|
|
26
|
+
"additionalProperties": false
|
|
27
|
+
},
|
|
28
|
+
"toolsBySender": {
|
|
29
|
+
"type": "object",
|
|
30
|
+
"description": "Per-sender overrides keyed by id:<val>, username:<val>, name:<val>, or *",
|
|
31
|
+
"additionalProperties": {
|
|
32
|
+
"type": "object",
|
|
33
|
+
"properties": {
|
|
34
|
+
"allow": { "type": "array", "items": { "type": "string" } },
|
|
35
|
+
"alsoAllow": { "type": "array", "items": { "type": "string" } },
|
|
36
|
+
"deny": { "type": "array", "items": { "type": "string" } }
|
|
37
|
+
},
|
|
38
|
+
"additionalProperties": false
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"additionalProperties": false
|
|
43
|
+
}
|
|
44
|
+
}
|
|
13
45
|
},
|
|
14
46
|
"required": []
|
|
15
47
|
},
|