dashclaw 1.8.0 → 1.8.2

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.
Files changed (3) hide show
  1. package/README.md +675 -1
  2. package/dashclaw.js +394 -12
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Full reference for the DashClaw SDK (Node.js). For Python, see the [Python SDK docs](../sdk-python/README.md).
4
4
 
5
- Install, configure, and instrument your AI agents with 78+ methods across action recording, behavior guard, context management, session handoffs, security scanning, policy testing, compliance, task routing, and more.
5
+ Install, configure, and instrument your AI agents with 95+ methods across 21+ categories including action recording, behavior guard, context management, session handoffs, security scanning, agent messaging, agent pairing, identity binding, organization management, webhooks, policy testing, compliance, task routing, and more.
6
6
 
7
7
  ---
8
8
 
@@ -239,6 +239,54 @@ Stream actions live to the dashboard as they happen.
239
239
 
240
240
  ---
241
241
 
242
+ ## Real-Time Events
243
+
244
+ Subscribe to server-sent events (SSE) for instant push notifications. Eliminates polling for approvals, policy changes, and task assignments.
245
+
246
+ ### claw.events()
247
+
248
+ Open a persistent SSE connection. Returns a chainable handle with `.on(event, callback)` and `.close()`.
249
+
250
+ **Supported events:** `action.created`, `action.updated`, `message.created`, `policy.updated`, `task.assigned`, `task.completed`
251
+
252
+ ```javascript
253
+ const stream = claw.events();
254
+
255
+ stream
256
+ .on('action.created', (data) => console.log('New action:', data.action_id))
257
+ .on('action.updated', (data) => {
258
+ if (data.status === 'running') console.log('Approved:', data.action_id);
259
+ })
260
+ .on('policy.updated', (data) => console.log('Policy changed:', data.change_type))
261
+ .on('task.assigned', (data) => console.log('Task routed:', data.task?.title))
262
+ .on('task.completed', (data) => console.log('Task done:', data.task?.task_id))
263
+ .on('error', (err) => console.error('Stream error:', err));
264
+
265
+ // When done:
266
+ stream.close();
267
+ ```
268
+
269
+ ### claw.waitForApproval(actionId, { useEvents: true })
270
+
271
+ SSE-powered approval waiting — resolves instantly when the operator approves/denies instead of polling every 5 seconds.
272
+
273
+ ```javascript
274
+ // SSE mode (instant, recommended)
275
+ const { action } = await claw.waitForApproval('act_abc', { useEvents: true });
276
+
277
+ // Polling mode (default, backward-compatible)
278
+ const { action } = await claw.waitForApproval('act_abc');
279
+ ```
280
+
281
+ | Parameter | Type | Default | Description |
282
+ |-----------|------|---------|-------------|
283
+ | actionId | string | — | Action ID to watch |
284
+ | options.timeout | number | 300000 | Max wait time (ms) |
285
+ | options.interval | number | 5000 | Poll interval (polling mode only) |
286
+ | options.useEvents | boolean | false | Use SSE instead of polling |
287
+
288
+ ---
289
+
242
290
  ## Token & Cost Analytics
243
291
 
244
292
  Track token usage and estimated costs for every action. DashClaw automatically aggregates these into "Cost per Goal" metrics.
@@ -412,6 +460,20 @@ Retrieve recent guard evaluation decisions for audit and review.
412
460
 
413
461
  Push data from your agent directly to the DashClaw dashboard. All methods auto-attach the agent's agentId.
414
462
 
463
+ ### claw.reportTokenUsage(usage)
464
+ Report a token usage snapshot for this agent.
465
+
466
+ **Parameters:**
467
+ | Parameter | Type | Required | Description |
468
+ |-----------|------|----------|-------------|
469
+ | tokens_in | number | Yes | Input tokens consumed |
470
+ | tokens_out | number | Yes | Output tokens generated |
471
+ | context_used | number | No | Context window tokens used |
472
+ | context_max | number | No | Context window max capacity |
473
+ | model | string | No | Model name |
474
+
475
+ **Returns:** `Promise<{snapshot: Object}>`
476
+
415
477
  ### claw.recordDecision(entry)
416
478
  Record a decision for the learning database. Track what your agent decides and why.
417
479
 
@@ -543,6 +605,47 @@ Report active connections/integrations for this agent.
543
605
 
544
606
  **Returns:** `Promise<{ connections: Object[], created: number }>`
545
607
 
608
+ ### claw.createCalendarEvent(event)
609
+ Create a calendar event.
610
+
611
+ **Parameters:**
612
+ | Parameter | Type | Required | Description |
613
+ |-----------|------|----------|-------------|
614
+ | summary | string | Yes | Event title/summary |
615
+ | start_time | string | Yes | Start time (ISO string) |
616
+ | end_time | string | No | End time (ISO string) |
617
+ | location | string | No | Event location |
618
+ | description | string | No | Event description |
619
+
620
+ **Returns:** `Promise<{event: Object}>`
621
+
622
+ ### claw.recordIdea(idea)
623
+ Record an idea or inspiration for later review.
624
+
625
+ **Parameters:**
626
+ | Parameter | Type | Required | Description |
627
+ |-----------|------|----------|-------------|
628
+ | title | string | Yes | Idea title |
629
+ | description | string | No | Detailed description |
630
+ | category | string | No | Category |
631
+ | score | number | No | Priority/quality score 0-100 |
632
+ | status | string | No | "pending", "in_progress", "shipped", "rejected" |
633
+ | source | string | No | Where this idea came from |
634
+
635
+ **Returns:** `Promise<{idea: Object}>`
636
+
637
+ ### claw.reportMemoryHealth(report)
638
+ Report a memory health snapshot with entities and topics.
639
+
640
+ **Parameters:**
641
+ | Parameter | Type | Required | Description |
642
+ |-----------|------|----------|-------------|
643
+ | health | Object | Yes | Health metrics (must include `score` 0-100) |
644
+ | entities | Object[] | No | Key entities found in memory |
645
+ | topics | Object[] | No | Topics/themes found in memory |
646
+
647
+ **Returns:** `Promise<{snapshot: Object, entities_count: number, topics_count: number}>`
648
+
546
649
  ---
547
650
 
548
651
  ## Session Handoffs
@@ -573,6 +676,20 @@ Get handoffs for this agent with optional date and limit filters.
573
676
 
574
677
  **Returns:** `Promise<{handoffs: Object[], total: number}>`
575
678
 
679
+ ### claw.getLatestHandoff()
680
+ Get the most recent handoff for this agent. Useful for resuming context at the start of a new session.
681
+
682
+ **Returns:** `Promise<{handoff: Object|null}>`
683
+
684
+ **Example:**
685
+ ```javascript
686
+ const { handoff } = await claw.getLatestHandoff();
687
+ if (handoff) {
688
+ console.log('Last session:', handoff.summary);
689
+ console.log('Next priorities:', handoff.next_priorities);
690
+ }
691
+ ```
692
+
576
693
  ---
577
694
 
578
695
  ## Context Manager
@@ -603,9 +720,63 @@ Create a context thread for tracking a topic across multiple entries.
603
720
 
604
721
  **Returns:** `Promise<{thread: Object, thread_id: string}>`
605
722
 
723
+ ### claw.getKeyPoints(filters?)
724
+ Get key points with optional filters.
725
+
726
+ **Parameters:**
727
+ | Parameter | Type | Required | Description |
728
+ |-----------|------|----------|-------------|
729
+ | category | string | No | Filter by category: decision, task, insight, question, general |
730
+ | session_date | string | No | Filter by date (YYYY-MM-DD) |
731
+ | limit | number | No | Max results |
732
+
733
+ **Returns:** `Promise<{points: Object[], total: number}>`
734
+
606
735
  ### claw.addThreadEntry(threadId, content, entryType?)
607
736
  Add an entry to an existing thread.
608
737
 
738
+ **Parameters:**
739
+ | Parameter | Type | Required | Description |
740
+ |-----------|------|----------|-------------|
741
+ | threadId | string | Yes | The thread ID |
742
+ | content | string | Yes | Entry content |
743
+ | entryType | string | No | Entry type (default: "note") |
744
+
745
+ **Returns:** `Promise<{entry: Object, entry_id: string}>`
746
+
747
+ ### claw.closeThread(threadId, summary?)
748
+ Close a context thread with an optional final summary.
749
+
750
+ **Parameters:**
751
+ | Parameter | Type | Required | Description |
752
+ |-----------|------|----------|-------------|
753
+ | threadId | string | Yes | The thread ID to close |
754
+ | summary | string | No | Final summary for the thread |
755
+
756
+ **Returns:** `Promise<{thread: Object}>`
757
+
758
+ ### claw.getThreads(filters?)
759
+ Get context threads with optional filters.
760
+
761
+ **Parameters:**
762
+ | Parameter | Type | Required | Description |
763
+ |-----------|------|----------|-------------|
764
+ | status | string | No | Filter by status: active, closed |
765
+ | limit | number | No | Max results |
766
+
767
+ **Returns:** `Promise<{threads: Object[], total: number}>`
768
+
769
+ ### claw.getContextSummary()
770
+ Get a combined context summary containing today's key points and all active threads. Convenience method that calls `getKeyPoints()` and `getThreads()` in parallel.
771
+
772
+ **Returns:** `Promise<{points: Object[], threads: Object[]}>`
773
+
774
+ **Example:**
775
+ ```javascript
776
+ const { points, threads } = await claw.getContextSummary();
777
+ console.log(`${points.length} key points today, ${threads.length} active threads`);
778
+ ```
779
+
609
780
  ---
610
781
 
611
782
  ## Automation Snippets
@@ -702,6 +873,144 @@ await claw.deleteSnippet('sn_abc123');
702
873
 
703
874
  ---
704
875
 
876
+ ## User Preferences
877
+
878
+ Track user observations, learned preferences, mood/energy, and approach effectiveness across sessions.
879
+
880
+ ### claw.logObservation(obs)
881
+ Log a user observation (what you noticed about the user's behavior or preferences).
882
+
883
+ **Parameters:**
884
+ | Parameter | Type | Required | Description |
885
+ |-----------|------|----------|-------------|
886
+ | observation | string | Yes | The observation text |
887
+ | category | string | No | Category tag |
888
+ | importance | number | No | Importance 1-10 |
889
+
890
+ **Returns:** `Promise<{observation: Object, observation_id: string}>`
891
+
892
+ **Example:**
893
+ ```javascript
894
+ await claw.logObservation({
895
+ observation: 'User prefers concise responses over detailed explanations',
896
+ category: 'communication',
897
+ importance: 8,
898
+ });
899
+ ```
900
+
901
+ ### claw.setPreference(pref)
902
+ Set a learned user preference. Use this to record patterns you detect about how the user likes to work.
903
+
904
+ **Parameters:**
905
+ | Parameter | Type | Required | Description |
906
+ |-----------|------|----------|-------------|
907
+ | preference | string | Yes | The preference description |
908
+ | category | string | No | Category tag |
909
+ | confidence | number | No | Confidence 0-100 |
910
+
911
+ **Returns:** `Promise<{preference: Object, preference_id: string}>`
912
+
913
+ ### claw.logMood(entry)
914
+ Log user mood/energy for a session. Helps track patterns in productivity and satisfaction.
915
+
916
+ **Parameters:**
917
+ | Parameter | Type | Required | Description |
918
+ |-----------|------|----------|-------------|
919
+ | mood | string | Yes | Mood description (e.g., "focused", "frustrated") |
920
+ | energy | string | No | Energy level (e.g., "high", "low") |
921
+ | notes | string | No | Additional notes |
922
+
923
+ **Returns:** `Promise<{mood: Object, mood_id: string}>`
924
+
925
+ ### claw.trackApproach(entry)
926
+ Track an approach and whether it succeeded or failed. Builds a knowledge base of what works.
927
+
928
+ **Parameters:**
929
+ | Parameter | Type | Required | Description |
930
+ |-----------|------|----------|-------------|
931
+ | approach | string | Yes | The approach description |
932
+ | context | string | No | Context for when to use this approach |
933
+ | success | boolean | No | true = worked, false = failed, undefined = just recording |
934
+
935
+ **Returns:** `Promise<{approach: Object, approach_id: string}>`
936
+
937
+ ### claw.getPreferenceSummary()
938
+ Get a summary of all user preference data including observations, preferences, moods, and approaches.
939
+
940
+ **Returns:** `Promise<{summary: Object}>`
941
+
942
+ ### claw.getApproaches(filters?)
943
+ Get tracked approaches with success/fail counts.
944
+
945
+ **Parameters:**
946
+ | Parameter | Type | Required | Description |
947
+ |-----------|------|----------|-------------|
948
+ | limit | number | No | Max results |
949
+
950
+ **Returns:** `Promise<{approaches: Object[], total: number}>`
951
+
952
+ ---
953
+
954
+ ## Daily Digest
955
+
956
+ ### claw.getDailyDigest(date?)
957
+ Get a daily activity digest aggregated from all data sources (actions, decisions, handoffs, context, etc.).
958
+
959
+ **Parameters:**
960
+ | Parameter | Type | Required | Description |
961
+ |-----------|------|----------|-------------|
962
+ | date | string | No | Date string YYYY-MM-DD (defaults to today) |
963
+
964
+ **Returns:** `Promise<{date: string, digest: Object, summary: Object}>`
965
+
966
+ **Example:**
967
+ ```javascript
968
+ const { digest, summary } = await claw.getDailyDigest('2025-01-15');
969
+ console.log(`Actions: ${summary.actions_count}, Decisions: ${summary.decisions_count}`);
970
+ ```
971
+
972
+ ---
973
+
974
+ ## Security Scanning
975
+
976
+ Scan text for sensitive data before sending it anywhere. The scanner detects API keys, tokens, PII, and other secrets.
977
+
978
+ ### claw.scanContent(text, destination?)
979
+ Scan text for sensitive data. Returns findings and redacted text. Does NOT store the original content.
980
+
981
+ **Parameters:**
982
+ | Parameter | Type | Required | Description |
983
+ |-----------|------|----------|-------------|
984
+ | text | string | Yes | Text to scan |
985
+ | destination | string | No | Where this text is headed (for context) |
986
+
987
+ **Returns:** `Promise<{clean: boolean, findings_count: number, findings: Object[], redacted_text: string}>`
988
+
989
+ **Example:**
990
+ ```javascript
991
+ const result = await claw.scanContent(
992
+ 'Deploy with key sk-abc123xyz to production',
993
+ 'slack'
994
+ );
995
+ if (!result.clean) {
996
+ console.log(`Found ${result.findings_count} issues`);
997
+ console.log('Safe version:', result.redacted_text);
998
+ }
999
+ ```
1000
+
1001
+ ### claw.reportSecurityFinding(text, destination?)
1002
+ Scan text and store finding metadata for audit trails. The original content is never stored, only the finding metadata.
1003
+
1004
+ **Parameters:**
1005
+ | Parameter | Type | Required | Description |
1006
+ |-----------|------|----------|-------------|
1007
+ | text | string | Yes | Text to scan |
1008
+ | destination | string | No | Where this text is headed |
1009
+
1010
+ **Returns:** `Promise<{clean: boolean, findings_count: number, findings: Object[], redacted_text: string}>`
1011
+
1012
+ ---
1013
+
705
1014
  ## Agent Messaging
706
1015
 
707
1016
  ### claw.sendMessage(params)
@@ -717,12 +1026,334 @@ Send a message to another agent or broadcast.
717
1026
  | threadId | string | No | Thread ID to attach to |
718
1027
  | urgent | boolean | No | Mark as urgent |
719
1028
  | docRef | string | No | Reference to a shared doc ID |
1029
+ | attachments | Array<{filename, mime_type, data}> | No | File attachments (base64, max 3, max 5MB each) |
720
1030
 
721
1031
  **Returns:** `Promise<{message: Object, message_id: string}>`
722
1032
 
1033
+ ### claw.getInbox(params?)
1034
+ Get inbox messages for this agent.
1035
+
1036
+ **Parameters:**
1037
+ | Parameter | Type | Required | Description |
1038
+ |-----------|------|----------|-------------|
1039
+ | type | string | No | Filter by message type |
1040
+ | unread | boolean | No | Only unread messages |
1041
+ | threadId | string | No | Filter by thread |
1042
+ | limit | number | No | Max messages to return (default: 50) |
1043
+
1044
+ **Returns:** `Promise<{messages: Object[], total: number, unread_count: number}>`
1045
+
1046
+ ### claw.markRead(messageIds)
1047
+ Mark messages as read.
1048
+
1049
+ **Parameters:**
1050
+ | Parameter | Type | Required | Description |
1051
+ |-----------|------|----------|-------------|
1052
+ | messageIds | string[] | Yes | Array of message IDs to mark read |
1053
+
1054
+ **Returns:** `Promise<{updated: number}>`
1055
+
1056
+ ### claw.archiveMessages(messageIds)
1057
+ Archive messages.
1058
+
1059
+ **Parameters:**
1060
+ | Parameter | Type | Required | Description |
1061
+ |-----------|------|----------|-------------|
1062
+ | messageIds | string[] | Yes | Array of message IDs to archive |
1063
+
1064
+ **Returns:** `Promise<{updated: number}>`
1065
+
1066
+ ### claw.broadcast(params)
1067
+ Broadcast a message to all agents in the organization.
1068
+
1069
+ **Parameters:**
1070
+ | Parameter | Type | Required | Description |
1071
+ |-----------|------|----------|-------------|
1072
+ | type | string | No | Message type (default: "info") |
1073
+ | subject | string | No | Subject line |
1074
+ | body | string | Yes | Message body |
1075
+ | threadId | string | No | Thread ID |
1076
+
1077
+ **Returns:** `Promise<{message: Object, message_id: string}>`
1078
+
1079
+ ### claw.createMessageThread(params)
1080
+ Create a new message thread for multi-turn conversations between agents.
1081
+
1082
+ **Parameters:**
1083
+ | Parameter | Type | Required | Description |
1084
+ |-----------|------|----------|-------------|
1085
+ | name | string | Yes | Thread name |
1086
+ | participants | string[] | No | Agent IDs (null = open to all) |
1087
+
1088
+ **Returns:** `Promise<{thread: Object, thread_id: string}>`
1089
+
1090
+ ### claw.getMessageThreads(params?)
1091
+ List message threads.
1092
+
1093
+ **Parameters:**
1094
+ | Parameter | Type | Required | Description |
1095
+ |-----------|------|----------|-------------|
1096
+ | status | string | No | Filter by status: open, resolved, archived |
1097
+ | limit | number | No | Max threads to return (default: 20) |
1098
+
1099
+ **Returns:** `Promise<{threads: Object[], total: number}>`
1100
+
1101
+ ### claw.resolveMessageThread(threadId, summary?)
1102
+ Resolve (close) a message thread with an optional summary.
1103
+
1104
+ **Parameters:**
1105
+ | Parameter | Type | Required | Description |
1106
+ |-----------|------|----------|-------------|
1107
+ | threadId | string | Yes | Thread ID to resolve |
1108
+ | summary | string | No | Resolution summary |
1109
+
1110
+ **Returns:** `Promise<{thread: Object}>`
1111
+
723
1112
  ### claw.saveSharedDoc(params)
724
1113
  Create or update a shared workspace document. Upserts by name.
725
1114
 
1115
+ **Parameters:**
1116
+ | Parameter | Type | Required | Description |
1117
+ |-----------|------|----------|-------------|
1118
+ | name | string | Yes | Document name (unique per org) |
1119
+ | content | string | Yes | Document content |
1120
+
1121
+ **Returns:** `Promise<{doc: Object, doc_id: string}>`
1122
+
1123
+ ### claw.getAttachmentUrl(attachmentId)
1124
+
1125
+ Get a URL to download an attachment.
1126
+
1127
+ | Parameter | Type | Description |
1128
+ |---|---|---|
1129
+ | `attachmentId` | `string` | Attachment ID (`att_*`) |
1130
+
1131
+ **Returns:** `string` — URL to fetch the attachment binary
1132
+
1133
+ ---
1134
+
1135
+ ### claw.getAttachment(attachmentId)
1136
+
1137
+ Download an attachment as a Buffer.
1138
+
1139
+ | Parameter | Type | Description |
1140
+ |---|---|---|
1141
+ | `attachmentId` | `string` | Attachment ID (`att_*`) |
1142
+
1143
+ **Returns:** `Promise<{ data: Buffer, filename: string, mimeType: string }>`
1144
+
1145
+ ```js
1146
+ const inbox = await claw.getInbox();
1147
+ for (const msg of inbox.messages) {
1148
+ for (const att of msg.attachments || []) {
1149
+ const { data, filename } = await claw.getAttachment(att.id);
1150
+ fs.writeFileSync(filename, data);
1151
+ }
1152
+ }
1153
+ ```
1154
+
1155
+ ---
1156
+
1157
+ ## Agent Pairing
1158
+
1159
+ Pair agents with user accounts via public key registration and approval flow.
1160
+
1161
+ ### claw.createPairing(options)
1162
+ Create an agent pairing request. Returns a link the user can click to approve the pairing.
1163
+
1164
+ **Parameters:**
1165
+ | Parameter | Type | Required | Description |
1166
+ |-----------|------|----------|-------------|
1167
+ | publicKeyPem | string | Yes | PEM public key (SPKI format) to register for this agent |
1168
+ | algorithm | string | No | Signing algorithm (default: "RSASSA-PKCS1-v1_5") |
1169
+ | agentName | string | No | Agent name override |
1170
+
1171
+ **Returns:** `Promise<{pairing: Object, pairing_url: string}>`
1172
+
1173
+ ### claw.createPairingFromPrivateJwk(privateJwk, options?)
1174
+ Convenience method that derives the public PEM from a private JWK and creates a pairing request.
1175
+
1176
+ **Parameters:**
1177
+ | Parameter | Type | Required | Description |
1178
+ |-----------|------|----------|-------------|
1179
+ | privateJwk | Object | Yes | Private key in JWK format |
1180
+ | options.agentName | string | No | Agent name override |
1181
+
1182
+ **Returns:** `Promise<{pairing: Object, pairing_url: string}>`
1183
+
1184
+ ### claw.waitForPairing(pairingId, options?)
1185
+ Poll a pairing request until it is approved or expired.
1186
+
1187
+ **Parameters:**
1188
+ | Parameter | Type | Required | Description |
1189
+ |-----------|------|----------|-------------|
1190
+ | pairingId | string | Yes | The pairing ID to poll |
1191
+ | options.timeout | number | No | Max wait time in ms (default: 300000 / 5 min) |
1192
+ | options.interval | number | No | Poll interval in ms (default: 2000) |
1193
+
1194
+ **Returns:** `Promise<Object>` (the approved pairing object)
1195
+
1196
+ **Throws:** `Error` if pairing expires or times out.
1197
+
1198
+ **Example:**
1199
+ ```javascript
1200
+ const { pairing, pairing_url } = await claw.createPairing({
1201
+ publicKeyPem: myPublicKeyPem,
1202
+ });
1203
+ console.log('Approve pairing at:', pairing_url);
1204
+
1205
+ const approved = await claw.waitForPairing(pairing.id);
1206
+ console.log('Pairing approved!', approved.status);
1207
+ ```
1208
+
1209
+ ---
1210
+
1211
+ ## Identity Binding
1212
+
1213
+ Register and manage agent public keys for cryptographic identity verification.
1214
+
1215
+ ### claw.registerIdentity(identity)
1216
+ Register or update an agent's public key for identity verification. Requires admin API key.
1217
+
1218
+ **Parameters:**
1219
+ | Parameter | Type | Required | Description |
1220
+ |-----------|------|----------|-------------|
1221
+ | agent_id | string | Yes | Agent ID to register |
1222
+ | public_key | string | Yes | PEM public key (SPKI format) |
1223
+ | algorithm | string | No | Signing algorithm (default: "RSASSA-PKCS1-v1_5") |
1224
+
1225
+ **Returns:** `Promise<{identity: Object}>`
1226
+
1227
+ ### claw.getIdentities()
1228
+ List all registered agent identities for this organization.
1229
+
1230
+ **Returns:** `Promise<{identities: Object[]}>`
1231
+
1232
+ ---
1233
+
1234
+ ## Organization Management
1235
+
1236
+ Manage organizations and API keys. All methods require admin API key.
1237
+
1238
+ ### claw.getOrg()
1239
+ Get the current organization's details.
1240
+
1241
+ **Returns:** `Promise<{organizations: Object[]}>`
1242
+
1243
+ ### claw.createOrg(org)
1244
+ Create a new organization with an initial admin API key.
1245
+
1246
+ **Parameters:**
1247
+ | Parameter | Type | Required | Description |
1248
+ |-----------|------|----------|-------------|
1249
+ | name | string | Yes | Organization name |
1250
+ | slug | string | Yes | URL-safe slug (lowercase alphanumeric + hyphens) |
1251
+
1252
+ **Returns:** `Promise<{organization: Object, api_key: Object}>`
1253
+
1254
+ ### claw.getOrgById(orgId)
1255
+ Get organization details by ID.
1256
+
1257
+ **Parameters:**
1258
+ | Parameter | Type | Required | Description |
1259
+ |-----------|------|----------|-------------|
1260
+ | orgId | string | Yes | Organization ID |
1261
+
1262
+ **Returns:** `Promise<{organization: Object}>`
1263
+
1264
+ ### claw.updateOrg(orgId, updates)
1265
+ Update organization details.
1266
+
1267
+ **Parameters:**
1268
+ | Parameter | Type | Required | Description |
1269
+ |-----------|------|----------|-------------|
1270
+ | orgId | string | Yes | Organization ID |
1271
+ | updates | Object | Yes | Fields to update (name, slug) |
1272
+
1273
+ **Returns:** `Promise<{organization: Object}>`
1274
+
1275
+ ### claw.getOrgKeys(orgId)
1276
+ List API keys for an organization.
1277
+
1278
+ **Parameters:**
1279
+ | Parameter | Type | Required | Description |
1280
+ |-----------|------|----------|-------------|
1281
+ | orgId | string | Yes | Organization ID |
1282
+
1283
+ **Returns:** `Promise<{keys: Object[]}>`
1284
+
1285
+ ---
1286
+
1287
+ ## Activity Logs
1288
+
1289
+ ### claw.getActivityLogs(filters?)
1290
+ Get activity/audit logs for the organization.
1291
+
1292
+ **Parameters:**
1293
+ | Parameter | Type | Required | Description |
1294
+ |-----------|------|----------|-------------|
1295
+ | action | string | No | Filter by action type |
1296
+ | actor_id | string | No | Filter by actor |
1297
+ | resource_type | string | No | Filter by resource type |
1298
+ | before | string | No | Before timestamp (ISO string) |
1299
+ | after | string | No | After timestamp (ISO string) |
1300
+ | limit | number | No | Max results (default: 50, max: 200) |
1301
+ | offset | number | No | Pagination offset |
1302
+
1303
+ **Returns:** `Promise<{logs: Object[], stats: Object, pagination: Object}>`
1304
+
1305
+ ---
1306
+
1307
+ ## Webhooks
1308
+
1309
+ Subscribe to DashClaw events and receive real-time notifications.
1310
+
1311
+ ### claw.getWebhooks()
1312
+ List all webhooks for this organization.
1313
+
1314
+ **Returns:** `Promise<{webhooks: Object[]}>`
1315
+
1316
+ ### claw.createWebhook(webhook)
1317
+ Create a new webhook subscription.
1318
+
1319
+ **Parameters:**
1320
+ | Parameter | Type | Required | Description |
1321
+ |-----------|------|----------|-------------|
1322
+ | url | string | Yes | Webhook endpoint URL |
1323
+ | events | string[] | No | Event types to subscribe to |
1324
+
1325
+ **Returns:** `Promise<{webhook: Object}>`
1326
+
1327
+ ### claw.deleteWebhook(webhookId)
1328
+ Delete a webhook.
1329
+
1330
+ **Parameters:**
1331
+ | Parameter | Type | Required | Description |
1332
+ |-----------|------|----------|-------------|
1333
+ | webhookId | string | Yes | Webhook ID |
1334
+
1335
+ **Returns:** `Promise<{deleted: boolean}>`
1336
+
1337
+ ### claw.testWebhook(webhookId)
1338
+ Send a test event to a webhook to verify connectivity.
1339
+
1340
+ **Parameters:**
1341
+ | Parameter | Type | Required | Description |
1342
+ |-----------|------|----------|-------------|
1343
+ | webhookId | string | Yes | Webhook ID |
1344
+
1345
+ **Returns:** `Promise<{delivery: Object}>`
1346
+
1347
+ ### claw.getWebhookDeliveries(webhookId)
1348
+ Get delivery history for a webhook.
1349
+
1350
+ **Parameters:**
1351
+ | Parameter | Type | Required | Description |
1352
+ |-----------|------|----------|-------------|
1353
+ | webhookId | string | Yes | Webhook ID |
1354
+
1355
+ **Returns:** `Promise<{deliveries: Object[]}>`
1356
+
726
1357
  ---
727
1358
 
728
1359
  ## Bulk Sync
@@ -959,6 +1590,49 @@ Get routing system health status and diagnostics.
959
1590
 
960
1591
  ---
961
1592
 
1593
+ ## Agent Schedules
1594
+
1595
+ Define recurring tasks and cron-based schedules for agents.
1596
+
1597
+ ### claw.listAgentSchedules(filters?)
1598
+ List agent schedules, optionally filtered by agent.
1599
+
1600
+ ```javascript
1601
+ const { schedules } = await claw.listAgentSchedules({ agent_id: 'forge' });
1602
+ ```
1603
+
1604
+ **Parameters:**
1605
+ | Parameter | Type | Required | Description |
1606
+ |-----------|------|----------|-------------|
1607
+ | filters.agent_id | string | No | Filter by agent ID |
1608
+
1609
+ **Returns:** `Promise<{ schedules: Object[] }>`
1610
+
1611
+ ### claw.createAgentSchedule(schedule)
1612
+ Create a new agent schedule entry.
1613
+
1614
+ ```javascript
1615
+ const { schedule } = await claw.createAgentSchedule({
1616
+ agent_id: 'forge',
1617
+ name: 'Build projects',
1618
+ cron_expression: '0 */6 * * *',
1619
+ description: 'Check for pending builds every 6 hours'
1620
+ });
1621
+ ```
1622
+
1623
+ **Parameters:**
1624
+ | Parameter | Type | Required | Description |
1625
+ |-----------|------|----------|-------------|
1626
+ | schedule.agent_id | string | Yes | Agent this schedule belongs to |
1627
+ | schedule.name | string | Yes | Schedule name |
1628
+ | schedule.cron_expression | string | Yes | Cron expression (e.g. `0 */6 * * *`) |
1629
+ | schedule.description | string | No | Human-readable description |
1630
+ | schedule.enabled | boolean | No | Whether schedule is active (default: true) |
1631
+
1632
+ **Returns:** `Promise<{ schedule: Object }>`
1633
+
1634
+ ---
1635
+
962
1636
  ## Error Handling
963
1637
 
964
1638
  All SDK methods throw on non-2xx responses. Errors include `status` (HTTP code) and `details` (when available).
package/dashclaw.js CHANGED
@@ -3,7 +3,7 @@
3
3
  * Full-featured agent toolkit for the DashClaw platform.
4
4
  * Zero-dependency ESM SDK — requires Node 18+ (native fetch).
5
5
  *
6
- * 78+ methods across 16+ categories:
6
+ * 96+ methods across 22+ categories:
7
7
  * - Action Recording (7)
8
8
  * - Loops & Assumptions (7)
9
9
  * - Signals (1)
@@ -16,10 +16,16 @@
16
16
  * - Security Scanning (2)
17
17
  * - Agent Messaging (9)
18
18
  * - Behavior Guard (2)
19
+ * - Agent Pairing (3)
20
+ * - Identity Binding (2)
21
+ * - Organization Management (5)
22
+ * - Activity Logs (1)
23
+ * - Webhooks (5)
19
24
  * - Bulk Sync (1)
20
25
  * - Policy Testing (3)
21
26
  * - Compliance Engine (5)
22
27
  * - Task Routing (10)
28
+ * - Real-Time Events (1)
23
29
  */
24
30
 
25
31
  class DashClaw {
@@ -65,6 +71,9 @@ class DashClaw {
65
71
  }
66
72
 
67
73
  this.baseUrl = baseUrl.replace(/\/$/, '');
74
+ if (!this.baseUrl.startsWith('https://') && !this.baseUrl.includes('localhost') && !this.baseUrl.includes('127.0.0.1')) {
75
+ console.warn('[DashClaw] WARNING: baseUrl does not use HTTPS. API keys will be sent in plaintext. Use HTTPS in production.');
76
+ }
68
77
  this.apiKey = apiKey;
69
78
  this.agentId = agentId;
70
79
  this.agentName = agentName || null;
@@ -464,32 +473,210 @@ class DashClaw {
464
473
 
465
474
  /**
466
475
  * Poll for human approval of a pending action.
467
- * @param {string} actionId
476
+ * @param {string} actionId
468
477
  * @param {Object} [options]
469
478
  * @param {number} [options.timeout=300000] - Max wait time (5 min)
470
479
  * @param {number} [options.interval=5000] - Poll interval
480
+ * @param {boolean} [options.useEvents=false] - Use SSE stream instead of polling
471
481
  */
472
- async waitForApproval(actionId, { timeout = 300000, interval = 5000 } = {}) {
482
+ async waitForApproval(actionId, { timeout = 300000, interval = 5000, useEvents = false } = {}) {
483
+ if (!useEvents) {
484
+ return this._waitForApprovalPolling(actionId, timeout, interval);
485
+ }
486
+
487
+ return new Promise((resolve, reject) => {
488
+ const stream = this.events();
489
+ const timeoutId = setTimeout(() => {
490
+ stream.close();
491
+ reject(new Error(`Timed out waiting for approval of action ${actionId}`));
492
+ }, timeout);
493
+
494
+ stream.on('action.updated', (data) => {
495
+ if (data.action_id !== actionId) return;
496
+ if (data.status === 'running') {
497
+ clearTimeout(timeoutId);
498
+ stream.close();
499
+ resolve({ action: data, action_id: actionId });
500
+ } else if (data.status === 'failed' || data.status === 'cancelled') {
501
+ clearTimeout(timeoutId);
502
+ stream.close();
503
+ reject(new ApprovalDeniedError(data.error_message || 'Operator denied the action.'));
504
+ }
505
+ });
506
+
507
+ stream.on('error', (err) => {
508
+ clearTimeout(timeoutId);
509
+ stream.close();
510
+ reject(err);
511
+ });
512
+ });
513
+ }
514
+
515
+ /** @private Polling-based waitForApproval implementation. */
516
+ async _waitForApprovalPolling(actionId, timeout, interval) {
473
517
  const startTime = Date.now();
474
-
518
+
475
519
  while (Date.now() - startTime < timeout) {
476
520
  const { action } = await this.getAction(actionId);
477
-
521
+
478
522
  if (action.status === 'running') {
479
523
  console.log(`[DashClaw] Action ${actionId} approved by operator.`);
480
524
  return { action, action_id: actionId };
481
525
  }
482
-
526
+
483
527
  if (action.status === 'failed' || action.status === 'cancelled') {
484
528
  throw new ApprovalDeniedError(action.error_message || 'Operator denied the action.');
485
529
  }
486
-
530
+
487
531
  await new Promise(r => setTimeout(r, interval));
488
532
  }
489
-
533
+
490
534
  throw new Error(`[DashClaw] Timed out waiting for approval of action ${actionId}`);
491
535
  }
492
536
 
537
+ // ══════════════════════════════════════════════
538
+ // Real-Time Events (1 method)
539
+ // ══════════════════════════════════════════════
540
+
541
+ /**
542
+ * Subscribe to real-time SSE events from the DashClaw server.
543
+ * Uses fetch-based SSE parsing for Node 18+ compatibility (no native EventSource required).
544
+ *
545
+ * @param {Object} [options]
546
+ * @param {boolean} [options.reconnect=true] - Auto-reconnect on disconnect (resumes from last event ID)
547
+ * @param {number} [options.maxRetries=Infinity] - Max reconnection attempts before giving up
548
+ * @param {number} [options.retryInterval=3000] - Milliseconds between reconnection attempts
549
+ * @returns {{ on(eventType: string, callback: Function): this, close(): void, _promise: Promise<void> }}
550
+ *
551
+ * @example
552
+ * const stream = client.events();
553
+ * stream
554
+ * .on('action.created', (data) => console.log('New action:', data))
555
+ * .on('action.updated', (data) => console.log('Action updated:', data))
556
+ * .on('policy.updated', (data) => console.log('Policy changed:', data))
557
+ * .on('task.assigned', (data) => console.log('Task assigned:', data))
558
+ * .on('task.completed', (data) => console.log('Task done:', data))
559
+ * .on('reconnecting', ({ attempt }) => console.log(`Reconnecting #${attempt}...`))
560
+ * .on('error', (err) => console.error('Stream error:', err));
561
+ *
562
+ * // Later:
563
+ * stream.close();
564
+ */
565
+ events({ reconnect = true, maxRetries = Infinity, retryInterval = 3000 } = {}) {
566
+ const url = `${this.baseUrl}/api/stream`;
567
+ const apiKey = this.apiKey;
568
+
569
+ const handlers = new Map();
570
+ let closed = false;
571
+ let controller = null;
572
+ let lastEventId = null;
573
+ let retryCount = 0;
574
+
575
+ const emit = (eventType, data) => {
576
+ const cbs = handlers.get(eventType) || [];
577
+ for (const cb of cbs) {
578
+ try { cb(data); } catch { /* ignore handler errors */ }
579
+ }
580
+ };
581
+
582
+ const connect = async () => {
583
+ controller = new AbortController();
584
+ const headers = { 'x-api-key': apiKey };
585
+ if (lastEventId) headers['last-event-id'] = lastEventId;
586
+
587
+ const res = await fetch(url, {
588
+ headers,
589
+ signal: controller.signal,
590
+ });
591
+
592
+ if (!res.ok) {
593
+ throw new Error(`SSE connection failed: ${res.status} ${res.statusText}`);
594
+ }
595
+
596
+ retryCount = 0; // Reset on successful connection
597
+
598
+ const reader = res.body.getReader();
599
+ const decoder = new TextDecoder();
600
+ let buffer = '';
601
+ // Persist across reads so frames split across chunks are handled correctly
602
+ let currentEvent = null;
603
+ let currentData = '';
604
+
605
+ while (!closed) {
606
+ const { done, value } = await reader.read();
607
+ if (done) break;
608
+ buffer += decoder.decode(value, { stream: true });
609
+
610
+ // Parse SSE frames from buffer
611
+ const lines = buffer.split('\n');
612
+ buffer = lines.pop(); // Keep incomplete line in buffer
613
+
614
+ for (const line of lines) {
615
+ if (line.startsWith('id: ')) {
616
+ lastEventId = line.slice(4).trim();
617
+ } else if (line.startsWith('event: ')) {
618
+ currentEvent = line.slice(7).trim();
619
+ } else if (line.startsWith('data: ')) {
620
+ currentData += line.slice(6);
621
+ } else if (line.startsWith(':')) {
622
+ // SSE comment (keepalive heartbeat) — ignore
623
+ } else if (line === '' && currentEvent) {
624
+ // End of SSE frame — dispatch
625
+ if (currentData) {
626
+ try {
627
+ const parsed = JSON.parse(currentData);
628
+ emit(currentEvent, parsed);
629
+ } catch { /* ignore parse errors */ }
630
+ }
631
+ currentEvent = null;
632
+ currentData = '';
633
+ } else if (line === '') {
634
+ // Blank line without a pending event — reset partial state
635
+ currentEvent = null;
636
+ currentData = '';
637
+ }
638
+ }
639
+ }
640
+ };
641
+
642
+ const connectLoop = async () => {
643
+ while (!closed) {
644
+ try {
645
+ await connect();
646
+ } catch (err) {
647
+ if (closed) return;
648
+ emit('error', err);
649
+ }
650
+ // Stream ended (server closed, network drop, etc.)
651
+ if (closed) return;
652
+ if (!reconnect || retryCount >= maxRetries) {
653
+ emit('error', new Error('SSE stream ended'));
654
+ return;
655
+ }
656
+ retryCount++;
657
+ emit('reconnecting', { attempt: retryCount, maxRetries });
658
+ await new Promise((r) => setTimeout(r, retryInterval));
659
+ }
660
+ };
661
+
662
+ const connectionPromise = connectLoop();
663
+
664
+ const handle = {
665
+ on(eventType, callback) {
666
+ if (!handlers.has(eventType)) handlers.set(eventType, []);
667
+ handlers.get(eventType).push(callback);
668
+ return handle;
669
+ },
670
+ close() {
671
+ closed = true;
672
+ if (controller) controller.abort();
673
+ },
674
+ _promise: connectionPromise,
675
+ };
676
+
677
+ return handle;
678
+ }
679
+
493
680
  /**
494
681
  * Update the outcome of an existing action.
495
682
  * @param {string} actionId - The action_id to update
@@ -1346,7 +1533,7 @@ class DashClaw {
1346
1533
  }
1347
1534
 
1348
1535
  // ══════════════════════════════════════════════
1349
- // Category 11: Agent Messaging (9 methods)
1536
+ // Category 11: Agent Messaging (11 methods)
1350
1537
  // ══════════════════════════════════════════════
1351
1538
 
1352
1539
  /**
@@ -1359,10 +1546,11 @@ class DashClaw {
1359
1546
  * @param {string} [params.threadId] - Thread ID to attach message to
1360
1547
  * @param {boolean} [params.urgent=false] - Mark as urgent
1361
1548
  * @param {string} [params.docRef] - Reference to a shared doc ID
1549
+ * @param {Array<{filename: string, mime_type: string, data: string}>} [params.attachments] - File attachments (base64 data, max 3, max 5MB each)
1362
1550
  * @returns {Promise<{message: Object, message_id: string}>}
1363
1551
  */
1364
- async sendMessage({ to, type, subject, body, threadId, urgent, docRef }) {
1365
- return this._request('/api/messages', 'POST', {
1552
+ async sendMessage({ to, type, subject, body, threadId, urgent, docRef, attachments }) {
1553
+ const payload = {
1366
1554
  from_agent_id: this.agentId,
1367
1555
  to_agent_id: to || null,
1368
1556
  message_type: type || 'info',
@@ -1371,7 +1559,9 @@ class DashClaw {
1371
1559
  thread_id: threadId,
1372
1560
  urgent,
1373
1561
  doc_ref: docRef,
1374
- });
1562
+ };
1563
+ if (attachments?.length) payload.attachments = attachments;
1564
+ return this._request('/api/messages', 'POST', payload);
1375
1565
  }
1376
1566
 
1377
1567
  /**
@@ -1493,6 +1683,39 @@ class DashClaw {
1493
1683
  });
1494
1684
  }
1495
1685
 
1686
+ /**
1687
+ * Get an attachment's download URL or fetch its binary data.
1688
+ * @param {string} attachmentId - Attachment ID (att_*)
1689
+ * @returns {string} URL to fetch the attachment
1690
+ */
1691
+ getAttachmentUrl(attachmentId) {
1692
+ return `${this.baseUrl}/api/messages/attachments?id=${encodeURIComponent(attachmentId)}`;
1693
+ }
1694
+
1695
+ /**
1696
+ * Download an attachment as a Buffer.
1697
+ * @param {string} attachmentId - Attachment ID (att_*)
1698
+ * @returns {Promise<{data: Buffer, filename: string, mimeType: string}>}
1699
+ */
1700
+ async getAttachment(attachmentId) {
1701
+ const url = this.getAttachmentUrl(attachmentId);
1702
+ const res = await fetch(url, {
1703
+ headers: { 'x-api-key': this.apiKey },
1704
+ });
1705
+ if (!res.ok) {
1706
+ const err = await res.json().catch(() => ({}));
1707
+ throw new Error(err.error || `Attachment fetch failed: ${res.status}`);
1708
+ }
1709
+ const data = Buffer.from(await res.arrayBuffer());
1710
+ const cd = res.headers.get('content-disposition') || '';
1711
+ const match = cd.match(/filename="(.+?)"/);
1712
+ return {
1713
+ data,
1714
+ filename: match ? match[1] : attachmentId,
1715
+ mimeType: res.headers.get('content-type') || 'application/octet-stream',
1716
+ };
1717
+ }
1718
+
1496
1719
  // ══════════════════════════════════════════════
1497
1720
  // Category 13: Behavior Guard (2 methods)
1498
1721
  // ══════════════════════════════════════════════
@@ -1747,6 +1970,165 @@ class DashClaw {
1747
1970
  return this._request('/api/routing/health', 'GET');
1748
1971
  }
1749
1972
 
1973
+ // ══════════════════════════════════════════════════════════
1974
+ // Agent Pairing (3 methods)
1975
+ // ══════════════════════════════════════════════════════════
1976
+
1977
+ // createPairing, createPairingFromPrivateJwk, waitForPairing
1978
+ // (defined near the top of the class)
1979
+
1980
+ // ══════════════════════════════════════════════════════════
1981
+ // Identity Binding (2 methods)
1982
+ // ══════════════════════════════════════════════════════════
1983
+
1984
+ /**
1985
+ * Register or update an agent's public key for identity verification.
1986
+ * Requires admin API key.
1987
+ * @param {Object} identity
1988
+ * @param {string} identity.agent_id - Agent ID to register
1989
+ * @param {string} identity.public_key - PEM public key (SPKI format)
1990
+ * @param {string} [identity.algorithm='RSASSA-PKCS1-v1_5'] - Signing algorithm
1991
+ * @returns {Promise<{identity: Object}>}
1992
+ */
1993
+ async registerIdentity(identity) {
1994
+ return this._request('/api/identities', 'POST', identity);
1995
+ }
1996
+
1997
+ /**
1998
+ * List all registered agent identities for this org.
1999
+ * @returns {Promise<{identities: Object[]}>}
2000
+ */
2001
+ async getIdentities() {
2002
+ return this._request('/api/identities', 'GET');
2003
+ }
2004
+
2005
+ // ══════════════════════════════════════════════════════════
2006
+ // Organization Management (5 methods)
2007
+ // ══════════════════════════════════════════════════════════
2008
+
2009
+ /**
2010
+ * Get the current organization's details. Requires admin API key.
2011
+ * @returns {Promise<{organizations: Object[]}>}
2012
+ */
2013
+ async getOrg() {
2014
+ return this._request('/api/orgs', 'GET');
2015
+ }
2016
+
2017
+ /**
2018
+ * Create a new organization with an initial admin API key. Requires admin API key.
2019
+ * @param {Object} org
2020
+ * @param {string} org.name - Organization name
2021
+ * @param {string} org.slug - URL-safe slug (lowercase alphanumeric + hyphens)
2022
+ * @returns {Promise<{organization: Object, api_key: Object}>}
2023
+ */
2024
+ async createOrg(org) {
2025
+ return this._request('/api/orgs', 'POST', org);
2026
+ }
2027
+
2028
+ /**
2029
+ * Get organization details by ID. Requires admin API key.
2030
+ * @param {string} orgId - Organization ID
2031
+ * @returns {Promise<{organization: Object}>}
2032
+ */
2033
+ async getOrgById(orgId) {
2034
+ return this._request(`/api/orgs/${encodeURIComponent(orgId)}`, 'GET');
2035
+ }
2036
+
2037
+ /**
2038
+ * Update organization details. Requires admin API key.
2039
+ * @param {string} orgId - Organization ID
2040
+ * @param {Object} updates - Fields to update (name, slug)
2041
+ * @returns {Promise<{organization: Object}>}
2042
+ */
2043
+ async updateOrg(orgId, updates) {
2044
+ return this._request(`/api/orgs/${encodeURIComponent(orgId)}`, 'PATCH', updates);
2045
+ }
2046
+
2047
+ /**
2048
+ * List API keys for an organization. Requires admin API key.
2049
+ * @param {string} orgId - Organization ID
2050
+ * @returns {Promise<{keys: Object[]}>}
2051
+ */
2052
+ async getOrgKeys(orgId) {
2053
+ return this._request(`/api/orgs/${encodeURIComponent(orgId)}/keys`, 'GET');
2054
+ }
2055
+
2056
+ // ══════════════════════════════════════════════════════════
2057
+ // Activity Logs (1 method)
2058
+ // ══════════════════════════════════════════════════════════
2059
+
2060
+ /**
2061
+ * Get activity/audit logs for the organization.
2062
+ * @param {Object} [filters]
2063
+ * @param {string} [filters.action] - Filter by action type
2064
+ * @param {string} [filters.actor_id] - Filter by actor
2065
+ * @param {string} [filters.resource_type] - Filter by resource type
2066
+ * @param {string} [filters.before] - Before timestamp (ISO string)
2067
+ * @param {string} [filters.after] - After timestamp (ISO string)
2068
+ * @param {number} [filters.limit=50] - Max results (max 200)
2069
+ * @param {number} [filters.offset=0] - Pagination offset
2070
+ * @returns {Promise<{logs: Object[], stats: Object, pagination: Object}>}
2071
+ */
2072
+ async getActivityLogs(filters = {}) {
2073
+ const params = new URLSearchParams();
2074
+ for (const [key, value] of Object.entries(filters)) {
2075
+ if (value !== undefined && value !== null && value !== '') {
2076
+ params.set(key, String(value));
2077
+ }
2078
+ }
2079
+ return this._request(`/api/activity?${params}`, 'GET');
2080
+ }
2081
+
2082
+ // ══════════════════════════════════════════════════════════
2083
+ // Webhooks (5 methods)
2084
+ // ══════════════════════════════════════════════════════════
2085
+
2086
+ /**
2087
+ * List all webhooks for this org.
2088
+ * @returns {Promise<{webhooks: Object[]}>}
2089
+ */
2090
+ async getWebhooks() {
2091
+ return this._request('/api/webhooks', 'GET');
2092
+ }
2093
+
2094
+ /**
2095
+ * Create a new webhook subscription.
2096
+ * @param {Object} webhook
2097
+ * @param {string} webhook.url - Webhook endpoint URL
2098
+ * @param {string[]} [webhook.events] - Event types to subscribe to
2099
+ * @returns {Promise<{webhook: Object}>}
2100
+ */
2101
+ async createWebhook(webhook) {
2102
+ return this._request('/api/webhooks', 'POST', webhook);
2103
+ }
2104
+
2105
+ /**
2106
+ * Delete a webhook.
2107
+ * @param {string} webhookId - Webhook ID
2108
+ * @returns {Promise<{deleted: boolean}>}
2109
+ */
2110
+ async deleteWebhook(webhookId) {
2111
+ return this._request(`/api/webhooks?id=${encodeURIComponent(webhookId)}`, 'DELETE');
2112
+ }
2113
+
2114
+ /**
2115
+ * Send a test event to a webhook.
2116
+ * @param {string} webhookId - Webhook ID
2117
+ * @returns {Promise<{delivery: Object}>}
2118
+ */
2119
+ async testWebhook(webhookId) {
2120
+ return this._request(`/api/webhooks/${encodeURIComponent(webhookId)}/test`, 'POST');
2121
+ }
2122
+
2123
+ /**
2124
+ * Get delivery history for a webhook.
2125
+ * @param {string} webhookId - Webhook ID
2126
+ * @returns {Promise<{deliveries: Object[]}>}
2127
+ */
2128
+ async getWebhookDeliveries(webhookId) {
2129
+ return this._request(`/api/webhooks/${encodeURIComponent(webhookId)}/deliveries`, 'GET');
2130
+ }
2131
+
1750
2132
  // ─── Bulk Sync ────────────────────────────────────────────
1751
2133
 
1752
2134
  /**
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dashclaw",
3
- "version": "1.8.0",
4
- "description": "Full-featured agent toolkit for the DashClaw platform. 78+ methods for action recording, context management, session handoffs, security scanning, behavior guard, compliance, task routing, bulk sync, and more.",
3
+ "version": "1.8.2",
4
+ "description": "Full-featured agent toolkit for the DashClaw platform. 96+ methods across 22+ categories for action recording, context management, session handoffs, security scanning, behavior guard, compliance, task routing, identity binding, organization management, webhooks, bulk sync, and more.",
5
5
  "type": "module",
6
6
  "publishConfig": {
7
7
  "access": "public"