@2en/clawly-plugins 1.7.1 → 1.8.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/channel.ts CHANGED
@@ -1,39 +1,12 @@
1
1
  /**
2
- * Clawly cron delivery channel — registers a minimal channel plugin so that
3
- * cron jobs using `delivery: { channel: "clawly-cron" }` have a valid target.
2
+ * Clawly cron delivery channel — minimal no-op channel registration.
4
3
  *
5
- * When a cron job delivers text, the channel routes it through the agent via
6
- * `openclaw agent --message` (LLM round-trip) so the response appears as a
7
- * natural assistant message, then sends a push notification if the mobile
8
- * client is offline.
4
+ * With delivery.mode = "none", OpenClaw does not call sendText. This
5
+ * registration exists so that any existing cron jobs referencing
6
+ * channel "clawly-cron" still have a valid target and don't error out.
9
7
  */
10
8
 
11
- import {$} from 'zx'
12
9
  import type {PluginApi} from './index'
13
- import {sendPushNotification} from './gateway/notification'
14
- import {isClientOnline} from './gateway/presence'
15
-
16
- $.verbose = false
17
-
18
- async function deliverAndNotify(text: string, api: PluginApi): Promise<void> {
19
- try {
20
- const config = api.pluginConfig as Record<string, unknown> | undefined
21
- const agentId = typeof config?.agentId === 'string' ? config.agentId : 'clawly'
22
-
23
- await $`openclaw agent --agent ${agentId} --message ${text}`
24
- api.logger.info(`clawly-cron: delivered message (${text.length} chars) to agent ${agentId}`)
25
-
26
- // Push notification if client is offline
27
- const online = await isClientOnline()
28
- if (!online) {
29
- const pushSent = await sendPushNotification({body: text}, api)
30
- api.logger.info(`clawly-cron: push notification sent=${pushSent}`)
31
- }
32
- } catch (err) {
33
- const msg = err instanceof Error ? err.message : String(err)
34
- api.logger.error(`clawly-cron: failed to deliver message — ${msg}`)
35
- }
36
- }
37
10
 
38
11
  export function registerClawlyCronChannel(api: PluginApi) {
39
12
  const channelRegistration = {
@@ -44,7 +17,7 @@ export function registerClawlyCronChannel(api: PluginApi) {
44
17
  label: 'Clawly Cron',
45
18
  selectionLabel: 'Clawly Cron (webchat)',
46
19
  docsPath: '',
47
- blurb: 'Webchat-only cron delivery channel',
20
+ blurb: 'No-op cron delivery channel (delivery.mode = none)',
48
21
  },
49
22
  capabilities: {chatTypes: ['dm'] as const},
50
23
  config: {
@@ -68,18 +41,10 @@ export function registerClawlyCronChannel(api: PluginApi) {
68
41
  },
69
42
  outbound: {
70
43
  deliveryMode: 'direct' as const,
71
- sendText: async (...args: unknown[]) => {
72
- const firstArg = args[0] as Record<string, unknown> | undefined
73
- const text = typeof firstArg?.text === 'string' ? firstArg.text : ''
74
- api.logger.info(`clawly-cron sendText: text=${text.length} chars`)
75
- if (text) {
76
- // Fire-and-forget — delivery system may not await sendText
77
- deliverAndNotify(text, api).catch(() => {})
78
- }
44
+ sendText: async () => {
79
45
  return {channel: 'clawly-cron', messageId: crypto.randomUUID()}
80
46
  },
81
- sendMedia: async (...args: unknown[]) => {
82
- api.logger.info(`clawly-cron sendMedia: ${JSON.stringify(args)}`)
47
+ sendMedia: async () => {
83
48
  return {channel: 'clawly-cron', messageId: crypto.randomUUID()}
84
49
  },
85
50
  },
@@ -87,5 +52,5 @@ export function registerClawlyCronChannel(api: PluginApi) {
87
52
  }
88
53
 
89
54
  api.registerChannel(channelRegistration)
90
- api.logger.info('channel: registered clawly-cron delivery channel')
55
+ api.logger.info('channel: registered clawly-cron delivery channel (no-op)')
91
56
  }
package/cron-hook.ts CHANGED
@@ -2,7 +2,8 @@
2
2
  * before_tool_call hook for cron (action=add) — ensures delivery fields are
3
3
  * always set correctly, even when the LLM omits them.
4
4
  *
5
- * Forces: delivery.channel = "clawly-cron", delivery.to = "self", delivery.mode = "announce"
5
+ * Forces: delivery.mode = "none" (agent uses clawly_send_message/push tools)
6
+ * Appends: delivery instructions to payload.message
6
7
  * Patches: payload.kind "systemEvent" → "agentTurn"
7
8
  *
8
9
  * The cron tool name is "cron" (not "cron.create"). The LLM passes
@@ -18,21 +19,33 @@ function isRecord(v: unknown): v is UnknownRecord {
18
19
  return typeof v === 'object' && v !== null && !Array.isArray(v)
19
20
  }
20
21
 
22
+ const DELIVERY_SUFFIX = [
23
+ '',
24
+ '---',
25
+ 'DELIVERY INSTRUCTIONS (mandatory):',
26
+ 'When done, you MUST deliver your result to the user:',
27
+ '1. Call the clawly_send_message tool with a brief, natural summary of your result.',
28
+ '2. Call clawly_is_user_online to check if the user is online.',
29
+ '3. If offline, also call clawly_send_app_push with a short notification.',
30
+ ].join('\n')
31
+
21
32
  function patchJob(job: UnknownRecord): UnknownRecord {
22
33
  const patched: UnknownRecord = {...job}
23
34
 
24
- // Force delivery fields
25
- const delivery = isRecord(job.delivery) ? job.delivery : {}
26
- patched.delivery = {
27
- ...delivery,
28
- mode: 'announce',
29
- channel: 'clawly-cron',
30
- to: 'self',
35
+ // Force delivery.mode = "none" — agent reports via tools explicitly
36
+ patched.delivery = {mode: 'none'}
37
+
38
+ // Append delivery instructions to payload.message
39
+ if (isRecord(job.payload) && typeof job.payload.message === 'string') {
40
+ patched.payload = {
41
+ ...job.payload,
42
+ message: job.payload.message + DELIVERY_SUFFIX,
43
+ }
31
44
  }
32
45
 
33
46
  // Patch payload.kind: systemEvent → agentTurn
34
- if (isRecord(job.payload) && job.payload.kind === 'systemEvent') {
35
- patched.payload = {...job.payload, kind: 'agentTurn'}
47
+ if (isRecord(patched.payload) && (patched.payload as UnknownRecord).kind === 'systemEvent') {
48
+ patched.payload = {...(patched.payload as UnknownRecord), kind: 'agentTurn'}
36
49
  }
37
50
 
38
51
  return patched
package/index.ts CHANGED
@@ -15,6 +15,7 @@
15
15
  * Agent tools:
16
16
  * - clawly_is_user_online — check if user's device is connected
17
17
  * - clawly_send_app_push — send a push notification to user's device
18
+ * - clawly_send_message — send a message to user via main session agent
18
19
  *
19
20
  * Commands:
20
21
  * - /clawly_echo — echo text back without LLM
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@2en/clawly-plugins",
3
- "version": "1.7.1",
3
+ "version": "1.8.0",
4
4
  "module": "index.ts",
5
5
  "type": "module",
6
6
  "repository": {
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Agent tool: clawly_send_message — send a message to the main session
3
+ * agent from an isolated context (e.g. cron job).
4
+ *
5
+ * Runs `openclaw agent --agent <agent> --message <message>` which delivers
6
+ * the text as a natural assistant response in the user's chat.
7
+ */
8
+
9
+ import {$} from 'zx'
10
+ import type {PluginApi} from '../index'
11
+
12
+ $.verbose = false
13
+
14
+ const TOOL_NAME = 'clawly_send_message'
15
+
16
+ const parameters: Record<string, unknown> = {
17
+ type: 'object',
18
+ required: ['message'],
19
+ properties: {
20
+ message: {type: 'string', description: 'The message to send to the user'},
21
+ agent: {type: 'string', description: 'Target agent ID (default: "clawly")'},
22
+ },
23
+ }
24
+
25
+ export function registerSendMessageTool(api: PluginApi) {
26
+ api.registerTool({
27
+ name: TOOL_NAME,
28
+ description:
29
+ 'Send a message to the user via the main session agent. Use this from cron jobs or isolated sessions to deliver results to the user.',
30
+ parameters,
31
+ async execute(_toolCallId, params) {
32
+ const message = typeof params.message === 'string' ? params.message.trim() : ''
33
+ if (!message) {
34
+ return {content: [{type: 'text', text: JSON.stringify({error: 'message is required'})}]}
35
+ }
36
+
37
+ const agent = typeof params.agent === 'string' ? params.agent.trim() : 'clawly'
38
+
39
+ try {
40
+ await $`openclaw agent --agent ${agent} --message ${message}`
41
+ api.logger.info(
42
+ `${TOOL_NAME}: delivered message (${message.length} chars) to agent ${agent}`,
43
+ )
44
+ return {content: [{type: 'text', text: JSON.stringify({sent: true})}]}
45
+ } catch (err) {
46
+ const msg = err instanceof Error ? err.message : String(err)
47
+ api.logger.error(`${TOOL_NAME}: failed — ${msg}`)
48
+ return {content: [{type: 'text', text: JSON.stringify({sent: false, error: msg})}]}
49
+ }
50
+ },
51
+ })
52
+
53
+ api.logger.info(`tool: registered ${TOOL_NAME} agent tool`)
54
+ }
package/tools/index.ts CHANGED
@@ -1,8 +1,10 @@
1
1
  import type {PluginApi} from '../index'
2
2
  import {registerIsUserOnlineTool} from './clawly-is-user-online'
3
3
  import {registerSendAppPushTool} from './clawly-send-app-push'
4
+ import {registerSendMessageTool} from './clawly-send-message'
4
5
 
5
6
  export function registerTools(api: PluginApi) {
6
7
  registerIsUserOnlineTool(api)
7
8
  registerSendAppPushTool(api)
9
+ registerSendMessageTool(api)
8
10
  }