@clwnt/clawnet 0.7.13 → 0.7.14

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.13",
3
+ "version": "0.7.14",
4
4
  "type": "module",
5
5
  "description": "ClawNet integration for OpenClaw — poll inbox, route messages to hooks",
6
6
  "files": [
package/src/service.ts CHANGED
@@ -407,8 +407,9 @@ export function createClawnetService(params: { api: any; cfg: ClawnetConfig }) {
407
407
  const notifyCount = checkData.notify_count ?? (checkData.count + a2aDmCount + sentTaskUpdates);
408
408
 
409
409
  if (checkData.count === 0) {
410
- // Email inbox clear — release any delivery lock (agent finished processing)
411
- deliveryLock.delete(account.id);
410
+ // Email inbox clear — only release delivery lock if nothing else needs notification,
411
+ // otherwise tasks/sent-task-updates still need the lock for throttling.
412
+ if (notifyCount === 0) deliveryLock.delete(account.id);
412
413
  return { a2aDmCount, sentTaskUpdates, notifyCount };
413
414
  }
414
415
 
@@ -428,8 +429,8 @@ export function createClawnetService(params: { api: any; cfg: ClawnetConfig }) {
428
429
  state.lastInboxNonEmptyAt = new Date();
429
430
  api.logger.info(`[clawnet] ${account.id}: ${checkData.count} message(s) waiting (${notifyCount} to notify)`);
430
431
 
431
- // Fetch full messages
432
- const inboxRes = await fetch(`${cfg.baseUrl}/inbox`, { headers });
432
+ // Fetch full messages — request max limit so mark-notified covers everything
433
+ const inboxRes = await fetch(`${cfg.baseUrl}/inbox?limit=200`, { headers });
433
434
  if (!inboxRes.ok) {
434
435
  throw new Error(`/inbox returned ${inboxRes.status}`);
435
436
  }
@@ -449,6 +450,22 @@ export function createClawnetService(params: { api: any; cfg: ClawnetConfig }) {
449
450
 
450
451
  state.counters.messagesSeen += normalized.length;
451
452
 
453
+ // Mark ALL fetched emails as notified up front — the delivery batch is capped
454
+ // at maxBatchSize, but the nag timer should reset for every message the agent
455
+ // is being alerted about, not just the ones in the batch.
456
+ const allEmailIds = normalized.map((m) => m.id);
457
+ if (allEmailIds.length > 0) {
458
+ try {
459
+ await fetch(`${cfg.baseUrl}/inbox/mark-notified`, {
460
+ method: "POST",
461
+ headers,
462
+ body: JSON.stringify({ message_ids: allEmailIds }),
463
+ });
464
+ } catch {
465
+ // Non-fatal — deliverBatch will still mark its batch after delivery
466
+ }
467
+ }
468
+
452
469
  // Add to pending (dedup by ID) and schedule debounced flush
453
470
  const existing = pendingMessages.get(account.id) ?? [];
454
471
  const existingIds = new Set(existing.map((m) => m.id));
@@ -477,7 +494,7 @@ export function createClawnetService(params: { api: any; cfg: ClawnetConfig }) {
477
494
  jsonrpc: "2.0",
478
495
  id: `poll-${Date.now()}`,
479
496
  method: "tasks/list",
480
- params: { status: "submitted", limit: 50 },
497
+ params: { status: "submitted", limit: 100 },
481
498
  };
482
499
  const res = await fetch(`${cfg.baseUrl}/a2a`, {
483
500
  method: "POST",
@@ -515,6 +532,24 @@ export function createClawnetService(params: { api: any; cfg: ClawnetConfig }) {
515
532
  });
516
533
 
517
534
  state.counters.messagesSeen += messages.length;
535
+
536
+ // Mark all fetched tasks as notified up front (same rationale as emails)
537
+ const allTaskIds = messages.map((m) => m.id);
538
+ if (allTaskIds.length > 0) {
539
+ try {
540
+ await fetch(`${cfg.baseUrl}/inbox/mark-notified`, {
541
+ method: "POST",
542
+ headers: {
543
+ Authorization: `Bearer ${resolvedToken}`,
544
+ "Content-Type": "application/json",
545
+ },
546
+ body: JSON.stringify({ task_ids: allTaskIds }),
547
+ });
548
+ } catch {
549
+ // Non-fatal
550
+ }
551
+ }
552
+
518
553
  const existing = pendingMessages.get(account.id) ?? [];
519
554
  const existingIds = new Set(existing.map((m) => m.id));
520
555
  const freshTasks = messages.filter((m) => !existingIds.has(m.id));
@@ -535,7 +570,7 @@ export function createClawnetService(params: { api: any; cfg: ClawnetConfig }) {
535
570
  jsonrpc: "2.0",
536
571
  id: `sent-poll-${Date.now()}`,
537
572
  method: "tasks/list",
538
- params: { role: "sender", status: "input-required,completed,failed", limit: 50 },
573
+ params: { role: "sender", status: "input-required,completed,failed", limit: 100 },
539
574
  };
540
575
  const res = await fetch(`${cfg.baseUrl}/a2a`, {
541
576
  method: "POST",
@@ -570,6 +605,24 @@ export function createClawnetService(params: { api: any; cfg: ClawnetConfig }) {
570
605
  });
571
606
 
572
607
  state.counters.messagesSeen += messages.length;
608
+
609
+ // Mark all fetched sent-task updates as notified up front (same rationale as emails)
610
+ const allSentTaskIds = messages.map((m) => m.id);
611
+ if (allSentTaskIds.length > 0) {
612
+ try {
613
+ await fetch(`${cfg.baseUrl}/inbox/mark-notified`, {
614
+ method: "POST",
615
+ headers: {
616
+ Authorization: `Bearer ${resolvedToken}`,
617
+ "Content-Type": "application/json",
618
+ },
619
+ body: JSON.stringify({ task_ids: allSentTaskIds }),
620
+ });
621
+ } catch {
622
+ // Non-fatal
623
+ }
624
+ }
625
+
573
626
  const existing = pendingMessages.get(account.id) ?? [];
574
627
  const existingIds = new Set(existing.map((m) => m.id));
575
628
  const freshUpdates = messages.filter((m) => !existingIds.has(m.id));
package/src/tools.ts CHANGED
@@ -468,6 +468,24 @@ export function registerTools(api: any) {
468
468
  },
469
469
  }), { optional: true });
470
470
 
471
+ api.registerTool((ctx: { agentId?: string; sessionKey?: string }) => ({
472
+ name: "clawnet_email_bulk_status",
473
+ description: toolDesc("clawnet_email_bulk_status", "Set the status of multiple emails at once. Use to archive, read, or snooze many emails in one call instead of one at a time."),
474
+ parameters: {
475
+ type: "object",
476
+ properties: {
477
+ message_ids: { type: "array", items: { type: "string" }, description: "Array of message IDs (e.g. ['msg_abc123', 'msg_def456'])" },
478
+ status: { type: "string", enum: ["archived", "read", "snoozed", "new"], description: "New status to apply to all messages" },
479
+ },
480
+ required: ["message_ids", "status"],
481
+ },
482
+ async execute(_id: string, params: { message_ids: string[]; status: string }) {
483
+ const cfg = loadFreshConfig(api);
484
+ const result = await apiCall(cfg, "PATCH", `/messages/bulk/status`, { message_ids: params.message_ids, status: params.status }, ctx?.agentId, ctx?.sessionKey);
485
+ return textResult(result.data);
486
+ },
487
+ }), { optional: true });
488
+
471
489
  api.registerTool((ctx: { agentId?: string; sessionKey?: string }) => ({
472
490
  name: "clawnet_inbox_session",
473
491
  description: toolDesc("clawnet_inbox_session", "Start an interactive inbox session. Returns your emails with assigned numbers and a triage protocol. IMPORTANT: After calling this tool, also call clawnet_task_inbox to get pending agent tasks — present both emails and tasks together to your human."),