@clwnt/clawnet 0.7.8 → 0.7.10

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clwnt/clawnet",
3
- "version": "0.7.8",
3
+ "version": "0.7.10",
4
4
  "type": "module",
5
5
  "description": "ClawNet integration for OpenClaw — poll inbox, route messages to hooks",
6
6
  "files": [
package/src/cli.ts CHANGED
@@ -154,6 +154,10 @@ export function buildStatusText(api: any): string {
154
154
  lines.push("Polling: **PAUSED** (run /clawnet resume to restart)");
155
155
  }
156
156
  lines.push(`Poll interval: ${pluginCfg.pollEverySeconds ?? "?"}s`);
157
+ const notifyOn = pluginCfg.notifyOnNew ?? true;
158
+ const remindH = pluginCfg.remindAfterHours ?? null;
159
+ lines.push(`Notify on new: ${notifyOn ? "on" : "off"}`);
160
+ lines.push(`Reminder interval: ${remindH ? `${remindH}h` : "never"}`);
157
161
 
158
162
  const accounts: any[] = pluginCfg.accounts ?? [];
159
163
  const agentList: any[] = currentConfig?.agents?.list ?? [];
package/src/config.ts CHANGED
@@ -19,6 +19,8 @@ export interface ClawnetConfig {
19
19
  maxSnippetChars: number;
20
20
  setupVersion: number;
21
21
  paused: boolean;
22
+ notifyOnNew: boolean;
23
+ remindAfterHours: number | null;
22
24
  }
23
25
 
24
26
  const DEFAULTS: ClawnetConfig = {
@@ -32,6 +34,8 @@ const DEFAULTS: ClawnetConfig = {
32
34
  maxSnippetChars: 500,
33
35
  setupVersion: 0,
34
36
  paused: false,
37
+ notifyOnNew: true,
38
+ remindAfterHours: 4,
35
39
  };
36
40
 
37
41
  export function parseConfig(raw: Record<string, unknown>): ClawnetConfig {
@@ -67,6 +71,8 @@ export function parseConfig(raw: Record<string, unknown>): ClawnetConfig {
67
71
  deliveryMethod:
68
72
  raw.deliveryMethod === "agent" ? "agent" : DEFAULTS.deliveryMethod,
69
73
  paused: raw.paused === true,
74
+ notifyOnNew: raw.notifyOnNew !== false,
75
+ remindAfterHours: typeof raw.remindAfterHours === "number" ? raw.remindAfterHours : null,
70
76
  };
71
77
  }
72
78
 
package/src/service.ts CHANGED
@@ -73,7 +73,7 @@ async function reloadOnboardingMessage(): Promise<void> {
73
73
 
74
74
  const SKILL_UPDATE_INTERVAL_MS = 6 * 60 * 60 * 1000; // 6 hours
75
75
  const SKILL_FILES = ["skill.json", "api-reference.md", "inbox-handler.md", "capabilities.json", "hook-template.txt", "tool-descriptions.json", "onboarding-message.txt", "inbox-protocol.md"];
76
- export const PLUGIN_VERSION = "0.7.8"; // Reported to server via PATCH /me every 6h
76
+ export const PLUGIN_VERSION = "0.7.10"; // Reported to server via PATCH /me every 6h
77
77
 
78
78
  function loadFreshConfig(api: any): ClawnetConfig {
79
79
  const raw = api.runtime?.config?.loadConfig?.()?.plugins?.entries?.clawnet?.config ?? {};
@@ -177,7 +177,7 @@ export function createClawnetService(params: { api: any; cfg: ClawnetConfig }) {
177
177
  // Mark notified (non-fatal)
178
178
  if (emailIds.length > 0 || taskIds.length > 0) {
179
179
  try {
180
- await fetch(`${auth.baseUrl}/inbox/mark-notified`, {
180
+ const markRes = await fetch(`${auth.baseUrl}/inbox/mark-notified`, {
181
181
  method: "POST",
182
182
  headers: { Authorization: `Bearer ${auth.token}`, "Content-Type": "application/json" },
183
183
  body: JSON.stringify({
@@ -185,31 +185,18 @@ export function createClawnetService(params: { api: any; cfg: ClawnetConfig }) {
185
185
  ...(taskIds.length > 0 ? { task_ids: taskIds } : {}),
186
186
  }),
187
187
  });
188
- api.logger.debug?.(`[clawnet] ${accountId}: marked ${emailIds.length} message(s) + ${taskIds.length} task(s) notified`);
188
+ if (markRes.ok) {
189
+ const markData = await markRes.json().catch(() => ({})) as Record<string, unknown>;
190
+ api.logger.info(`[clawnet] ${accountId}: marked notified (${markData.marked_messages ?? 0} msgs, ${markData.marked_tasks ?? 0} tasks)`);
191
+ } else {
192
+ const errText = await markRes.text().catch(() => "");
193
+ api.logger.warn(`[clawnet] ${accountId}: mark-notified returned ${markRes.status}: ${errText}`);
194
+ }
189
195
  } catch (err: any) {
190
196
  api.logger.warn(`[clawnet] ${accountId}: mark-notified failed (non-fatal): ${err.message}`);
191
197
  }
192
198
  }
193
199
 
194
- // Mark incoming A2A tasks as 'working' (protocol semantics, separate from notification tracking)
195
- for (const msg of messages) {
196
- if (msg.type === "task" && msg.content.startsWith("[A2A Task ")) {
197
- try {
198
- await fetch(`${auth.baseUrl}/a2a`, {
199
- method: "POST",
200
- headers: { Authorization: `Bearer ${auth.token}`, "Content-Type": "application/json" },
201
- body: JSON.stringify({
202
- jsonrpc: "2.0",
203
- id: `ack-${msg.id}`,
204
- method: "tasks/respond",
205
- params: { id: msg.id, state: "working" },
206
- }),
207
- });
208
- } catch {
209
- // Non-fatal — task may get re-delivered next cycle
210
- }
211
- }
212
- }
213
200
  }
214
201
  } catch (err: any) {
215
202
  state.lastError = { message: err.message, at: new Date() };
@@ -389,8 +376,16 @@ export function createClawnetService(params: { api: any; cfg: ClawnetConfig }) {
389
376
  cfg.deliver.channel = pc.deliver_channel;
390
377
  changed = true;
391
378
  }
379
+ if (pc.notify_on_new !== undefined && pc.notify_on_new !== cfg.notifyOnNew) {
380
+ cfg.notifyOnNew = pc.notify_on_new;
381
+ changed = true;
382
+ }
383
+ if (pc.remind_after_hours !== undefined && pc.remind_after_hours !== cfg.remindAfterHours) {
384
+ cfg.remindAfterHours = pc.remind_after_hours;
385
+ changed = true;
386
+ }
392
387
  if (changed) {
393
- api.logger.info(`[clawnet] Config updated from server: poll=${cfg.pollEverySeconds}s debounce=${cfg.debounceSeconds}s batch=${cfg.maxBatchSize}`);
388
+ api.logger.info(`[clawnet] Config updated from server: poll=${cfg.pollEverySeconds}s debounce=${cfg.debounceSeconds}s batch=${cfg.maxBatchSize} notify=${cfg.notifyOnNew} remind=${cfg.remindAfterHours ?? "never"}`);
394
389
  }
395
390
  }
396
391
 
@@ -489,7 +484,7 @@ export function createClawnetService(params: { api: any; cfg: ClawnetConfig }) {
489
484
  api.logger.info(`[clawnet] ${account.id}: ${tasks.length} A2A task(s) to deliver`);
490
485
 
491
486
  // Convert A2A tasks to the message format for delivery
492
- // Working transition + mark-notified happen post-delivery in deliverBatch
487
+ // mark-notified happens post-delivery in deliverBatch
493
488
  const messages: InboxMessage[] = tasks.map((task) => {
494
489
  const history = task.history as Array<{ role: string; parts: Array<{ text?: string }> }> ?? [];
495
490
  const lastMsg = history[history.length - 1];
@@ -607,10 +602,10 @@ export function createClawnetService(params: { api: any; cfg: ClawnetConfig }) {
607
602
  let hadError = false;
608
603
  for (const account of enabledAccounts) {
609
604
  try {
610
- const { a2aDmCount, sentTaskUpdates } = await pollAccount(account);
605
+ const { a2aDmCount, sentTaskUpdates, notifyCount } = await pollAccount(account);
611
606
 
612
607
  // Also poll for A2A DMs if any pending
613
- if (a2aDmCount > 0) {
608
+ if (a2aDmCount > 0 && notifyCount > 0) {
614
609
  try {
615
610
  await pollAccountA2A(account, a2aDmCount);
616
611
  } catch (a2aErr: any) {
@@ -619,7 +614,7 @@ export function createClawnetService(params: { api: any; cfg: ClawnetConfig }) {
619
614
  }
620
615
 
621
616
  // Poll for sent task updates (tasks I sent that got a response)
622
- if (sentTaskUpdates > 0) {
617
+ if (sentTaskUpdates > 0 && notifyCount > 0) {
623
618
  try {
624
619
  await pollSentTaskUpdates(account);
625
620
  } catch (err: any) {
package/src/tools.ts CHANGED
@@ -203,6 +203,12 @@ const BUILTIN_OPERATIONS: CapabilityOp[] = [
203
203
  { operation: "profile.capabilities", method: "PATCH", path: "/me/capabilities", description: "Set agent capabilities list", params: {
204
204
  capabilities: { type: "array", description: "List of capability strings (replaces all)", required: true },
205
205
  }},
206
+ // Notification settings
207
+ { operation: "notifications.get", method: "GET", path: "/me/notifications", description: "Get notification preferences (notify_on_new, remind_after_hours)" },
208
+ { operation: "notifications.update", method: "PATCH", path: "/me/notifications", description: "Update notification preferences", params: {
209
+ notify_on_new: { type: "boolean", description: "Notify when new messages/tasks arrive (default: true)" },
210
+ remind_after_hours: { type: "number", description: "Re-notify about unresolved items every N hours (1-168), or null to disable reminders" },
211
+ }},
206
212
  // Contacts
207
213
  { operation: "contacts.list", method: "GET", path: "/contacts", description: "List your contacts", params: {
208
214
  type: { type: "string", description: "'email' or 'agent'" },