dashclaw 1.8.1 → 1.8.3

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 (4) hide show
  1. package/README.md +125 -8
  2. package/dashclaw.js +109 -36
  3. package/index.cjs +1 -1
  4. package/package.json +3 -3
package/README.md CHANGED
@@ -1,8 +1,10 @@
1
- # DashClaw SDK Reference (Full)
1
+ # DashClaw SDK: Agent Decision Infrastructure
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 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.
5
+ DashClaw treats every agent action as a governed decision. The SDK provides decision recording, policy enforcement, assumption tracking, and compliance mapping. It proves what your agents decided and why.
6
+
7
+ Install, configure, and govern 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
8
 
7
9
  ---
8
10
 
@@ -111,11 +113,93 @@ try {
111
113
  }
112
114
  ```
113
115
 
116
+ ### Compliance & Governance Patterns
117
+
118
+ DashClaw's guard + action recording pipeline maps directly to compliance controls.
119
+
120
+ **SOC 2 CC6.1: Logical Access Controls**
121
+ ```javascript
122
+ // Before any high-risk operation, enforce policy
123
+ const guardResult = await claw.guard({
124
+ action_type: 'database_write',
125
+ risk_score: 85,
126
+ systems_touched: ['production_db'],
127
+ reversible: false,
128
+ declared_goal: 'Drop legacy user table'
129
+ });
130
+
131
+ if (guardResult.decision === 'block') {
132
+ // SOC 2 control satisfied: unauthorized action prevented
133
+ console.log('Policy blocked:', guardResult.reasons);
134
+ return;
135
+ }
136
+
137
+ // Decision is governed. Record with full lineage
138
+ const { action_id } = await claw.createAction({
139
+ action_type: 'database_write',
140
+ declared_goal: 'Drop legacy user table',
141
+ risk_score: 85,
142
+ reversible: false,
143
+ authorization_scope: 'admin-approved'
144
+ });
145
+
146
+ // Register the assumption this decision relies on
147
+ await claw.registerAssumption({
148
+ action_id,
149
+ assumption: 'Legacy table has zero active references',
150
+ basis: 'Schema dependency scan completed 2h ago'
151
+ });
152
+ ```
153
+
154
+ **EU AI Act Article 14: Human Oversight**
155
+ ```javascript
156
+ // require_approval forces human-in-the-loop
157
+ const result = await claw.guard({
158
+ action_type: 'customer_communication',
159
+ risk_score: 60,
160
+ declared_goal: 'Send pricing update to 500 customers'
161
+ });
162
+
163
+ if (result.decision === 'require_approval') {
164
+ // Create action in pending state, wait for human approval
165
+ const { action_id } = await claw.createAction({
166
+ action_type: 'customer_communication',
167
+ declared_goal: 'Send pricing update to 500 customers',
168
+ status: 'pending'
169
+ });
170
+ // Approval queue at /approvals shows this to operators
171
+ }
172
+ ```
173
+
174
+ **ISO 42001: AI Decision Accountability**
175
+ ```javascript
176
+ // Full decision lineage: guard → action → assumptions → outcome
177
+ const { action_id } = await claw.createAction({
178
+ action_type: 'data_processing',
179
+ declared_goal: 'Rebuild customer segmentation model',
180
+ risk_score: 45,
181
+ systems_touched: ['ml-pipeline', 'customer-db']
182
+ });
183
+
184
+ await claw.registerAssumption({
185
+ action_id,
186
+ assumption: 'Customer data is current as of today',
187
+ basis: 'CRM sync completed at 09:00 UTC'
188
+ });
189
+
190
+ // Later: validate or invalidate assumptions
191
+ await claw.validateAssumption(assumptionId, true);
192
+
193
+ // Decision integrity signals auto-detect when assumptions drift
194
+ const signals = await claw.getSignals();
195
+ // → Returns 'assumption_drift' if too many invalidated
196
+ ```
197
+
114
198
  ---
115
199
 
116
200
  ## Action Recording
117
201
 
118
- Create, update, and query action records. Every agent action gets a full audit trail.
202
+ Create, update, and query action records. Every agent action is a governed decision with a full audit trail capturing intent, reasoning, and outcome for compliance and review.
119
203
 
120
204
  ### claw.createAction(action)
121
205
  Create a new action record. The agent's agentId, agentName, and swarmId are automatically attached.
@@ -268,7 +352,7 @@ stream.close();
268
352
 
269
353
  ### claw.waitForApproval(actionId, { useEvents: true })
270
354
 
271
- SSE-powered approval waiting resolves instantly when the operator approves/denies instead of polling every 5 seconds.
355
+ SSE-powered approval waiting. Resolves instantly when the operator approves/denies instead of polling every 5 seconds.
272
356
 
273
357
  ```javascript
274
358
  // SSE mode (instant, recommended)
@@ -280,7 +364,7 @@ const { action } = await claw.waitForApproval('act_abc');
280
364
 
281
365
  | Parameter | Type | Default | Description |
282
366
  |-----------|------|---------|-------------|
283
- | actionId | string | | Action ID to watch |
367
+ | actionId | string | Yes | Action ID to watch |
284
368
  | options.timeout | number | 300000 | Max wait time (ms) |
285
369
  | options.interval | number | 5000 | Poll interval (polling mode only) |
286
370
  | options.useEvents | boolean | false | Use SSE instead of polling |
@@ -425,7 +509,7 @@ Get current risk signals across all agents. Returns 7 signal types: autonomy_spi
425
509
 
426
510
  ## Behavior Guard
427
511
 
428
- Check org-level policies before executing risky actions. Returns allow, warn, block, or require_approval based on configured guard policies.
512
+ Guard is the heart of DashClaw. Every action can be checked against policies before execution. Returns allow, warn, block, or require_approval based on configured guard policies.
429
513
 
430
514
  ### claw.guard(context, options?)
431
515
  Evaluate guard policies for a proposed action. Call this before risky operations to get a go/no-go decision. The agent_id is auto-attached from the SDK constructor.
@@ -1026,6 +1110,7 @@ Send a message to another agent or broadcast.
1026
1110
  | threadId | string | No | Thread ID to attach to |
1027
1111
  | urgent | boolean | No | Mark as urgent |
1028
1112
  | docRef | string | No | Reference to a shared doc ID |
1113
+ | attachments | Array<{filename, mime_type, data}> | No | File attachments (base64, max 3, max 5MB each) |
1029
1114
 
1030
1115
  **Returns:** `Promise<{message: Object, message_id: string}>`
1031
1116
 
@@ -1119,6 +1204,38 @@ Create or update a shared workspace document. Upserts by name.
1119
1204
 
1120
1205
  **Returns:** `Promise<{doc: Object, doc_id: string}>`
1121
1206
 
1207
+ ### claw.getAttachmentUrl(attachmentId)
1208
+
1209
+ Get a URL to download an attachment.
1210
+
1211
+ | Parameter | Type | Description |
1212
+ |---|---|---|
1213
+ | `attachmentId` | `string` | Attachment ID (`att_*`) |
1214
+
1215
+ **Returns:** `string`: URL to fetch the attachment binary
1216
+
1217
+ ---
1218
+
1219
+ ### claw.getAttachment(attachmentId)
1220
+
1221
+ Download an attachment as a Buffer.
1222
+
1223
+ | Parameter | Type | Description |
1224
+ |---|---|---|
1225
+ | `attachmentId` | `string` | Attachment ID (`att_*`) |
1226
+
1227
+ **Returns:** `Promise<{ data: Buffer, filename: string, mimeType: string }>`
1228
+
1229
+ ```js
1230
+ const inbox = await claw.getInbox();
1231
+ for (const msg of inbox.messages) {
1232
+ for (const att of msg.attachments || []) {
1233
+ const { data, filename } = await claw.getAttachment(att.id);
1234
+ fs.writeFileSync(filename, data);
1235
+ }
1236
+ }
1237
+ ```
1238
+
1122
1239
  ---
1123
1240
 
1124
1241
  ## Agent Pairing
@@ -1346,7 +1463,7 @@ Run guardrails tests against all active policies. Returns pass/fail results per
1346
1463
  const report = await claw.testPolicies();
1347
1464
  console.log(`${report.passed}/${report.total} policies passed`);
1348
1465
  for (const r of report.results.filter(r => !r.passed)) {
1349
- console.log(`FAIL: ${r.policy} ${r.reason}`);
1466
+ console.log(`FAIL: ${r.policy}: ${r.reason}`);
1350
1467
  }
1351
1468
  ```
1352
1469
 
@@ -1392,7 +1509,7 @@ Map active policies to framework controls. Returns a control-by-control coverage
1392
1509
  const { controls, coverage_pct } = await claw.mapCompliance('soc2');
1393
1510
  console.log(`SOC 2 coverage: ${coverage_pct}%`);
1394
1511
  for (const ctrl of controls.filter(c => !c.covered)) {
1395
- console.log(`Gap: ${ctrl.id} ${ctrl.name}`);
1512
+ console.log(`Gap: ${ctrl.id}: ${ctrl.name}`);
1396
1513
  }
1397
1514
  ```
1398
1515
 
package/dashclaw.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * DashClaw SDK
3
3
  * Full-featured agent toolkit for the DashClaw platform.
4
- * Zero-dependency ESM SDK requires Node 18+ (native fetch).
4
+ * Zero-dependency ESM SDK. Requires Node 18+ (native fetch).
5
5
  *
6
6
  * 96+ methods across 22+ categories:
7
7
  * - Action Recording (7)
@@ -217,7 +217,7 @@ class DashClaw {
217
217
  try {
218
218
  decision = await this.guard(context);
219
219
  } catch (err) {
220
- // Guard API failure is fail-open log and proceed
220
+ // Guard API failure is fail-open: log and proceed
221
221
  console.warn(`[DashClaw] Guard check failed (proceeding): ${err.message}`);
222
222
  return;
223
223
  }
@@ -403,11 +403,11 @@ class DashClaw {
403
403
  }
404
404
 
405
405
  // ══════════════════════════════════════════════
406
- // Category 1: Action Recording (6 methods)
406
+ // Category 1: Decision Recording (6 methods)
407
407
  // ══════════════════════════════════════════════
408
408
 
409
409
  /**
410
- * Create a new action record.
410
+ * Record a governed decision. Every action is a decision with a full audit trail: goal, reasoning, assumptions, and policy compliance.
411
411
  * @param {Object} action
412
412
  * @param {string} action.action_type - One of: build, deploy, post, apply, security, message, api, calendar, research, review, fix, refactor, test, config, monitor, alert, cleanup, sync, migrate, other
413
413
  * @param {string} action.declared_goal - What this action aims to accomplish
@@ -542,6 +542,10 @@ class DashClaw {
542
542
  * Subscribe to real-time SSE events from the DashClaw server.
543
543
  * Uses fetch-based SSE parsing for Node 18+ compatibility (no native EventSource required).
544
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
545
549
  * @returns {{ on(eventType: string, callback: Function): this, close(): void, _promise: Promise<void> }}
546
550
  *
547
551
  * @example
@@ -552,23 +556,36 @@ class DashClaw {
552
556
  * .on('policy.updated', (data) => console.log('Policy changed:', data))
553
557
  * .on('task.assigned', (data) => console.log('Task assigned:', data))
554
558
  * .on('task.completed', (data) => console.log('Task done:', data))
559
+ * .on('reconnecting', ({ attempt }) => console.log(`Reconnecting #${attempt}...`))
555
560
  * .on('error', (err) => console.error('Stream error:', err));
556
561
  *
557
562
  * // Later:
558
563
  * stream.close();
559
564
  */
560
- events() {
565
+ events({ reconnect = true, maxRetries = Infinity, retryInterval = 3000 } = {}) {
561
566
  const url = `${this.baseUrl}/api/stream`;
562
567
  const apiKey = this.apiKey;
563
568
 
564
569
  const handlers = new Map();
565
570
  let closed = false;
566
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
+ };
567
581
 
568
582
  const connect = async () => {
569
583
  controller = new AbortController();
584
+ const headers = { 'x-api-key': apiKey };
585
+ if (lastEventId) headers['last-event-id'] = lastEventId;
586
+
570
587
  const res = await fetch(url, {
571
- headers: { 'x-api-key': apiKey },
588
+ headers,
572
589
  signal: controller.signal,
573
590
  });
574
591
 
@@ -576,9 +593,14 @@ class DashClaw {
576
593
  throw new Error(`SSE connection failed: ${res.status} ${res.statusText}`);
577
594
  }
578
595
 
596
+ retryCount = 0; // Reset on successful connection
597
+
579
598
  const reader = res.body.getReader();
580
599
  const decoder = new TextDecoder();
581
600
  let buffer = '';
601
+ // Persist across reads so frames split across chunks are handled correctly
602
+ let currentEvent = null;
603
+ let currentData = '';
582
604
 
583
605
  while (!closed) {
584
606
  const { done, value } = await reader.read();
@@ -589,39 +611,55 @@ class DashClaw {
589
611
  const lines = buffer.split('\n');
590
612
  buffer = lines.pop(); // Keep incomplete line in buffer
591
613
 
592
- let currentEvent = null;
593
- let currentData = '';
594
-
595
614
  for (const line of lines) {
596
- if (line.startsWith('event: ')) {
615
+ if (line.startsWith('id: ')) {
616
+ lastEventId = line.slice(4).trim();
617
+ } else if (line.startsWith('event: ')) {
597
618
  currentEvent = line.slice(7).trim();
598
619
  } else if (line.startsWith('data: ')) {
599
620
  currentData += line.slice(6);
621
+ } else if (line.startsWith(':')) {
622
+ // SSE comment (keepalive heartbeat). Ignore.
600
623
  } else if (line === '' && currentEvent) {
601
- // End of SSE frame — dispatch
602
- const eventHandlers = handlers.get(currentEvent) || [];
603
- if (eventHandlers.length > 0 && currentData) {
624
+ // End of SSE frame. Dispatch.
625
+ if (currentData) {
604
626
  try {
605
627
  const parsed = JSON.parse(currentData);
606
- for (const cb of eventHandlers) {
607
- try { cb(parsed); } catch { /* ignore handler errors */ }
608
- }
628
+ emit(currentEvent, parsed);
609
629
  } catch { /* ignore parse errors */ }
610
630
  }
611
631
  currentEvent = null;
612
632
  currentData = '';
633
+ } else if (line === '') {
634
+ // Blank line without a pending event. Reset partial state.
635
+ currentEvent = null;
636
+ currentData = '';
613
637
  }
614
638
  }
615
639
  }
616
640
  };
617
641
 
618
- const connectionPromise = connect().catch((err) => {
619
- if (closed) return; // Abort is expected on close
620
- const errorHandlers = handlers.get('error') || [];
621
- for (const cb of errorHandlers) {
622
- try { cb(err); } catch { /* ignore */ }
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));
623
659
  }
624
- });
660
+ };
661
+
662
+ const connectionPromise = connectLoop();
625
663
 
626
664
  const handle = {
627
665
  on(eventType, callback) {
@@ -728,11 +766,11 @@ class DashClaw {
728
766
  }
729
767
 
730
768
  // ══════════════════════════════════════════════
731
- // Category 2: Loops & Assumptions (7 methods)
769
+ // Category 2: Decision Integrity (Loops & Assumptions) (7 methods)
732
770
  // ══════════════════════════════════════════════
733
771
 
734
772
  /**
735
- * Register an open loop for an action.
773
+ * Register an unresolved dependency for a decision. Open loops track work that must be completed before the decision can be considered fully resolved.
736
774
  * @param {Object} loop
737
775
  * @param {string} loop.action_id - Parent action ID
738
776
  * @param {string} loop.loop_type - One of: followup, question, dependency, approval, review, handoff, other
@@ -779,7 +817,7 @@ class DashClaw {
779
817
  }
780
818
 
781
819
  /**
782
- * Register assumptions made during an action.
820
+ * Register assumptions underlying a decision. Assumptions are the decision basis. They must be validated or invalidated to maintain decision integrity.
783
821
  * @param {Object} assumption
784
822
  * @param {string} assumption.action_id - Parent action ID
785
823
  * @param {string} assumption.assumption - The assumption being made
@@ -835,11 +873,11 @@ class DashClaw {
835
873
  }
836
874
 
837
875
  // ══════════════════════════════════════════════
838
- // Category 3: Signals (1 method)
876
+ // Category 3: Decision Integrity Signals (1 method)
839
877
  // ══════════════════════════════════════════════
840
878
 
841
879
  /**
842
- * Get current risk signals.
880
+ * Get current decision integrity signals. Returns autonomy breaches, logic drift, and governance violations.
843
881
  * @returns {Promise<{signals: Object[], counts: {red: number, amber: number, total: number}}>}
844
882
  */
845
883
  async getSignals() {
@@ -1495,7 +1533,7 @@ class DashClaw {
1495
1533
  }
1496
1534
 
1497
1535
  // ══════════════════════════════════════════════
1498
- // Category 11: Agent Messaging (9 methods)
1536
+ // Category 11: Agent Messaging (11 methods)
1499
1537
  // ══════════════════════════════════════════════
1500
1538
 
1501
1539
  /**
@@ -1508,10 +1546,11 @@ class DashClaw {
1508
1546
  * @param {string} [params.threadId] - Thread ID to attach message to
1509
1547
  * @param {boolean} [params.urgent=false] - Mark as urgent
1510
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)
1511
1550
  * @returns {Promise<{message: Object, message_id: string}>}
1512
1551
  */
1513
- async sendMessage({ to, type, subject, body, threadId, urgent, docRef }) {
1514
- return this._request('/api/messages', 'POST', {
1552
+ async sendMessage({ to, type, subject, body, threadId, urgent, docRef, attachments }) {
1553
+ const payload = {
1515
1554
  from_agent_id: this.agentId,
1516
1555
  to_agent_id: to || null,
1517
1556
  message_type: type || 'info',
@@ -1520,7 +1559,9 @@ class DashClaw {
1520
1559
  thread_id: threadId,
1521
1560
  urgent,
1522
1561
  doc_ref: docRef,
1523
- });
1562
+ };
1563
+ if (attachments?.length) payload.attachments = attachments;
1564
+ return this._request('/api/messages', 'POST', payload);
1524
1565
  }
1525
1566
 
1526
1567
  /**
@@ -1628,7 +1669,7 @@ class DashClaw {
1628
1669
 
1629
1670
  /**
1630
1671
  * Create or update a shared workspace document.
1631
- * Upserts by (org_id, name) updates increment the version.
1672
+ * Upserts by (org_id, name). Updates increment the version.
1632
1673
  * @param {Object} params
1633
1674
  * @param {string} params.name - Document name (unique per org)
1634
1675
  * @param {string} params.content - Document content
@@ -1642,13 +1683,45 @@ class DashClaw {
1642
1683
  });
1643
1684
  }
1644
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
+
1645
1719
  // ══════════════════════════════════════════════
1646
- // Category 13: Behavior Guard (2 methods)
1720
+ // Category 13: Policy Enforcement (Guard) (2 methods)
1647
1721
  // ══════════════════════════════════════════════
1648
1722
 
1649
1723
  /**
1650
- * Check guard policies before executing a risky action.
1651
- * Returns allow/warn/block/require_approval.
1724
+ * Enforce policies before a decision executes. Guard is the heart of DashClaw. It intercepts intent and returns allow/warn/block/require_approval.
1652
1725
  * @param {Object} context
1653
1726
  * @param {string} context.action_type - Action type (required)
1654
1727
  * @param {number} [context.risk_score] - Risk score 0-100
@@ -2059,7 +2132,7 @@ class DashClaw {
2059
2132
 
2060
2133
  /**
2061
2134
  * Sync multiple data categories in a single request.
2062
- * Every key is optional only provided categories are processed.
2135
+ * Every key is optional. Only provided categories are processed.
2063
2136
  * @param {Object} state - Data to sync (connections, memory, goals, learning, content, inspiration, context_points, context_threads, handoffs, preferences, snippets)
2064
2137
  * @returns {Promise<{results: Object, total_synced: number, total_errors: number, duration_ms: number}>}
2065
2138
  */
package/index.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * DashClaw SDK CommonJS compatibility wrapper.
2
+ * DashClaw SDK: CommonJS compatibility wrapper.
3
3
  * For ESM: import { DashClaw } from 'dashclaw'
4
4
  * For CJS: const { DashClaw } = require('dashclaw')
5
5
  */
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dashclaw",
3
- "version": "1.8.1",
4
- "description": "Full-featured agent toolkit for the DashClaw platform. 95+ methods across 21+ categories for action recording, context management, session handoffs, security scanning, behavior guard, compliance, task routing, identity binding, organization management, webhooks, bulk sync, and more.",
3
+ "version": "1.8.3",
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"
@@ -22,7 +22,7 @@
22
22
  ],
23
23
  "keywords": [
24
24
  "ai-agent",
25
- "observability",
25
+ "decision-infrastructure",
26
26
  "agent-toolkit",
27
27
  "dashclaw",
28
28
  "dashclaw",