dashclaw 1.0.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/LICENSE +21 -0
- package/README.md +177 -0
- package/dashclaw.js +1089 -0
- package/index.cjs +66 -0
- package/package.json +40 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 OpenClaw
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# DashClaw
|
|
2
|
+
|
|
3
|
+
Full-featured agent toolkit for the [DashClaw](https://github.com/ucsandman/DashClaw) platform. Zero dependencies, requires Node 18+ (native fetch).
|
|
4
|
+
|
|
5
|
+
**57 methods** across 13 categories: action recording, context management, session handoffs, security scanning, agent messaging, behavior guard, user preferences, and more.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install dashclaw
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
```js
|
|
16
|
+
import { DashClaw } from 'dashclaw';
|
|
17
|
+
|
|
18
|
+
const claw = new DashClaw({
|
|
19
|
+
baseUrl: 'https://your-app.vercel.app',
|
|
20
|
+
apiKey: process.env.DASHCLAW_API_KEY,
|
|
21
|
+
agentId: 'my-agent',
|
|
22
|
+
agentName: 'My Agent',
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// Record an action
|
|
26
|
+
const { action_id } = await claw.createAction({
|
|
27
|
+
action_type: 'deploy',
|
|
28
|
+
declared_goal: 'Deploy auth service to production',
|
|
29
|
+
risk_score: 60,
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// ... do the work ...
|
|
33
|
+
|
|
34
|
+
await claw.updateOutcome(action_id, {
|
|
35
|
+
status: 'completed',
|
|
36
|
+
output_summary: 'Auth service deployed successfully',
|
|
37
|
+
});
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Migration from OpenClawAgent
|
|
41
|
+
|
|
42
|
+
The backward-compatible alias `OpenClawAgent` is preserved:
|
|
43
|
+
|
|
44
|
+
```js
|
|
45
|
+
// Old:
|
|
46
|
+
import { OpenClawAgent } from './openclaw-agent.js';
|
|
47
|
+
// New (both work):
|
|
48
|
+
import { DashClaw } from 'dashclaw';
|
|
49
|
+
import { OpenClawAgent } from 'dashclaw';
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## API Reference (54 methods)
|
|
53
|
+
|
|
54
|
+
### Action Recording (6)
|
|
55
|
+
|
|
56
|
+
| Method | Description |
|
|
57
|
+
|--------|-------------|
|
|
58
|
+
| `createAction(action)` | Record a new action |
|
|
59
|
+
| `updateOutcome(actionId, outcome)` | Update action result |
|
|
60
|
+
| `getActions(filters?)` | List actions with filters |
|
|
61
|
+
| `getAction(actionId)` | Get single action with loops + assumptions |
|
|
62
|
+
| `getActionTrace(actionId)` | Get root-cause trace |
|
|
63
|
+
| `track(actionDef, fn)` | Auto-tracked action wrapper |
|
|
64
|
+
|
|
65
|
+
### Loops & Assumptions (7)
|
|
66
|
+
|
|
67
|
+
| Method | Description |
|
|
68
|
+
|--------|-------------|
|
|
69
|
+
| `registerOpenLoop(loop)` | Register an unresolved item |
|
|
70
|
+
| `resolveOpenLoop(loopId, status, resolution)` | Close an open loop |
|
|
71
|
+
| `getOpenLoops(filters?)` | List open loops |
|
|
72
|
+
| `registerAssumption(assumption)` | Record an assumption |
|
|
73
|
+
| `getAssumption(assumptionId)` | Get single assumption |
|
|
74
|
+
| `validateAssumption(id, validated, reason?)` | Validate or invalidate |
|
|
75
|
+
| `getDriftReport(filters?)` | Get assumption drift scores |
|
|
76
|
+
|
|
77
|
+
### Signals (1)
|
|
78
|
+
|
|
79
|
+
| Method | Description |
|
|
80
|
+
|--------|-------------|
|
|
81
|
+
| `getSignals()` | Get computed risk signals |
|
|
82
|
+
|
|
83
|
+
### Dashboard Data (8)
|
|
84
|
+
|
|
85
|
+
| Method | Description |
|
|
86
|
+
|--------|-------------|
|
|
87
|
+
| `recordDecision(entry)` | Record a decision |
|
|
88
|
+
| `createGoal(goal)` | Create a goal |
|
|
89
|
+
| `recordContent(content)` | Record content creation |
|
|
90
|
+
| `recordInteraction(interaction)` | Record a relationship interaction |
|
|
91
|
+
| `reportConnections(connections)` | Report agent integrations |
|
|
92
|
+
| `createCalendarEvent(event)` | Create a calendar event |
|
|
93
|
+
| `recordIdea(idea)` | Record an idea/inspiration |
|
|
94
|
+
| `reportMemoryHealth(report)` | Report memory health snapshot |
|
|
95
|
+
|
|
96
|
+
### Session Handoffs (3)
|
|
97
|
+
|
|
98
|
+
| Method | Description |
|
|
99
|
+
|--------|-------------|
|
|
100
|
+
| `createHandoff(handoff)` | Create a session handoff document |
|
|
101
|
+
| `getHandoffs(filters?)` | Get handoffs for this agent |
|
|
102
|
+
| `getLatestHandoff()` | Get the most recent handoff |
|
|
103
|
+
|
|
104
|
+
### Context Manager (7)
|
|
105
|
+
|
|
106
|
+
| Method | Description |
|
|
107
|
+
|--------|-------------|
|
|
108
|
+
| `captureKeyPoint(point)` | Capture a key point |
|
|
109
|
+
| `getKeyPoints(filters?)` | Get key points |
|
|
110
|
+
| `createThread(thread)` | Create a context thread |
|
|
111
|
+
| `addThreadEntry(threadId, content)` | Add entry to a thread |
|
|
112
|
+
| `closeThread(threadId, summary?)` | Close a thread |
|
|
113
|
+
| `getThreads(filters?)` | Get threads |
|
|
114
|
+
| `getContextSummary()` | Today's points + active threads |
|
|
115
|
+
|
|
116
|
+
### Automation Snippets (4)
|
|
117
|
+
|
|
118
|
+
| Method | Description |
|
|
119
|
+
|--------|-------------|
|
|
120
|
+
| `saveSnippet(snippet)` | Save/update a code snippet |
|
|
121
|
+
| `getSnippets(filters?)` | Search and list snippets |
|
|
122
|
+
| `useSnippet(snippetId)` | Mark snippet as used |
|
|
123
|
+
| `deleteSnippet(snippetId)` | Delete a snippet |
|
|
124
|
+
|
|
125
|
+
### User Preferences (6)
|
|
126
|
+
|
|
127
|
+
| Method | Description |
|
|
128
|
+
|--------|-------------|
|
|
129
|
+
| `logObservation(obs)` | Log a user observation |
|
|
130
|
+
| `setPreference(pref)` | Set a learned preference |
|
|
131
|
+
| `logMood(entry)` | Log user mood/energy |
|
|
132
|
+
| `trackApproach(entry)` | Track approach success/failure |
|
|
133
|
+
| `getPreferenceSummary()` | Get preference summary |
|
|
134
|
+
| `getApproaches(filters?)` | Get tracked approaches |
|
|
135
|
+
|
|
136
|
+
### Daily Digest (1)
|
|
137
|
+
|
|
138
|
+
| Method | Description |
|
|
139
|
+
|--------|-------------|
|
|
140
|
+
| `getDailyDigest(date?)` | Aggregated daily summary |
|
|
141
|
+
|
|
142
|
+
### Security Scanning (2)
|
|
143
|
+
|
|
144
|
+
| Method | Description |
|
|
145
|
+
|--------|-------------|
|
|
146
|
+
| `scanContent(text, destination?)` | Scan text for sensitive data |
|
|
147
|
+
| `reportSecurityFinding(text, dest?)` | Scan + store finding metadata |
|
|
148
|
+
|
|
149
|
+
### Agent Messaging (9)
|
|
150
|
+
|
|
151
|
+
| Method | Description |
|
|
152
|
+
|--------|-------------|
|
|
153
|
+
| `sendMessage(params)` | Send message to agent or broadcast |
|
|
154
|
+
| `getInbox(filters?)` | Get inbox messages |
|
|
155
|
+
| `markRead(messageIds)` | Mark messages as read |
|
|
156
|
+
| `archiveMessages(messageIds)` | Archive messages |
|
|
157
|
+
| `broadcast(params)` | Send to all agents in org |
|
|
158
|
+
| `createMessageThread(params)` | Start a conversation thread |
|
|
159
|
+
| `getMessageThreads(filters?)` | List message threads |
|
|
160
|
+
| `resolveMessageThread(threadId, summary?)` | Close a thread |
|
|
161
|
+
| `saveSharedDoc(params)` | Create/update shared document |
|
|
162
|
+
|
|
163
|
+
## Authentication
|
|
164
|
+
|
|
165
|
+
The SDK sends your API key via the `x-api-key` header. The key determines which organization's data you access.
|
|
166
|
+
|
|
167
|
+
```js
|
|
168
|
+
const claw = new DashClaw({
|
|
169
|
+
baseUrl: 'https://your-deployment.vercel.app',
|
|
170
|
+
apiKey: process.env.DASHCLAW_API_KEY,
|
|
171
|
+
agentId: 'my-agent',
|
|
172
|
+
});
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## License
|
|
176
|
+
|
|
177
|
+
MIT
|
package/dashclaw.js
ADDED
|
@@ -0,0 +1,1089 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DashClaw SDK
|
|
3
|
+
* Full-featured agent toolkit for the DashClaw platform.
|
|
4
|
+
* Zero-dependency ESM SDK — requires Node 18+ (native fetch).
|
|
5
|
+
*
|
|
6
|
+
* 57 methods across 13 categories:
|
|
7
|
+
* - Action Recording (6)
|
|
8
|
+
* - Loops & Assumptions (7)
|
|
9
|
+
* - Signals (1)
|
|
10
|
+
* - Dashboard Data (8)
|
|
11
|
+
* - Session Handoffs (3)
|
|
12
|
+
* - Context Manager (7)
|
|
13
|
+
* - Automation Snippets (4)
|
|
14
|
+
* - User Preferences (6)
|
|
15
|
+
* - Daily Digest (1)
|
|
16
|
+
* - Security Scanning (2)
|
|
17
|
+
* - Agent Messaging (9)
|
|
18
|
+
* - Behavior Guard (2)
|
|
19
|
+
* - Bulk Sync (1)
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
class DashClaw {
|
|
23
|
+
/**
|
|
24
|
+
* @param {Object} options
|
|
25
|
+
* @param {string} options.baseUrl - DashClaw base URL (e.g. "https://your-app.vercel.app")
|
|
26
|
+
* @param {string} options.apiKey - API key for authentication (determines which org's data you access)
|
|
27
|
+
* @param {string} options.agentId - Unique identifier for this agent
|
|
28
|
+
* @param {string} [options.agentName] - Human-readable agent name
|
|
29
|
+
* @param {string} [options.swarmId] - Swarm/group identifier if part of a multi-agent system
|
|
30
|
+
* @param {string} [options.guardMode='off'] - Auto guard check before createAction: 'off' | 'warn' | 'enforce'
|
|
31
|
+
* @param {Function} [options.guardCallback] - Called with guard decision object when guardMode is active
|
|
32
|
+
*/
|
|
33
|
+
constructor({ baseUrl, apiKey, agentId, agentName, swarmId, guardMode, guardCallback }) {
|
|
34
|
+
if (!baseUrl) throw new Error('baseUrl is required');
|
|
35
|
+
if (!apiKey) throw new Error('apiKey is required');
|
|
36
|
+
if (!agentId) throw new Error('agentId is required');
|
|
37
|
+
|
|
38
|
+
const validModes = ['off', 'warn', 'enforce'];
|
|
39
|
+
if (guardMode && !validModes.includes(guardMode)) {
|
|
40
|
+
throw new Error(`guardMode must be one of: ${validModes.join(', ')}`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
this.baseUrl = baseUrl.replace(/\/$/, '');
|
|
44
|
+
this.apiKey = apiKey;
|
|
45
|
+
this.agentId = agentId;
|
|
46
|
+
this.agentName = agentName || null;
|
|
47
|
+
this.swarmId = swarmId || null;
|
|
48
|
+
this.guardMode = guardMode || 'off';
|
|
49
|
+
this.guardCallback = guardCallback || null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async _request(path, method, body) {
|
|
53
|
+
const url = `${this.baseUrl}${path}`;
|
|
54
|
+
const headers = {
|
|
55
|
+
'Content-Type': 'application/json',
|
|
56
|
+
'x-api-key': this.apiKey
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const res = await fetch(url, {
|
|
60
|
+
method,
|
|
61
|
+
headers,
|
|
62
|
+
body: body ? JSON.stringify(body) : undefined
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const data = await res.json();
|
|
66
|
+
|
|
67
|
+
if (!res.ok) {
|
|
68
|
+
const err = new Error(data.error || `Request failed with status ${res.status}`);
|
|
69
|
+
err.status = res.status;
|
|
70
|
+
err.details = data.details;
|
|
71
|
+
throw err;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return data;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Internal: check guard policies before action creation.
|
|
79
|
+
* Only active when guardMode is 'warn' or 'enforce'.
|
|
80
|
+
* @param {Object} actionDef - Action definition from createAction()
|
|
81
|
+
*/
|
|
82
|
+
async _guardCheck(actionDef) {
|
|
83
|
+
if (this.guardMode === 'off') return;
|
|
84
|
+
|
|
85
|
+
const context = {
|
|
86
|
+
action_type: actionDef.action_type,
|
|
87
|
+
risk_score: actionDef.risk_score,
|
|
88
|
+
systems_touched: actionDef.systems_touched,
|
|
89
|
+
reversible: actionDef.reversible,
|
|
90
|
+
declared_goal: actionDef.declared_goal,
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
let decision;
|
|
94
|
+
try {
|
|
95
|
+
decision = await this.guard(context);
|
|
96
|
+
} catch (err) {
|
|
97
|
+
// Guard API failure is fail-open — log and proceed
|
|
98
|
+
console.warn(`[DashClaw] Guard check failed (proceeding): ${err.message}`);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (this.guardCallback) {
|
|
103
|
+
try { this.guardCallback(decision); } catch { /* ignore callback errors */ }
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const isBlocked = decision.decision === 'block' || decision.decision === 'require_approval';
|
|
107
|
+
|
|
108
|
+
if (this.guardMode === 'warn' && isBlocked) {
|
|
109
|
+
console.warn(
|
|
110
|
+
`[DashClaw] Guard ${decision.decision}: ${decision.reasons.join('; ') || 'no reason'}. Proceeding in warn mode.`
|
|
111
|
+
);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (this.guardMode === 'enforce' && isBlocked) {
|
|
116
|
+
throw new GuardBlockedError(decision);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// ══════════════════════════════════════════════
|
|
121
|
+
// Category 1: Action Recording (6 methods)
|
|
122
|
+
// ══════════════════════════════════════════════
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Create a new action record.
|
|
126
|
+
* @param {Object} action
|
|
127
|
+
* @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
|
|
128
|
+
* @param {string} action.declared_goal - What this action aims to accomplish
|
|
129
|
+
* @param {string} [action.action_id] - Custom action ID (auto-generated if omitted)
|
|
130
|
+
* @param {string} [action.reasoning] - Why the agent decided to take this action
|
|
131
|
+
* @param {string} [action.authorization_scope] - What permissions were granted
|
|
132
|
+
* @param {string} [action.trigger] - What triggered this action
|
|
133
|
+
* @param {string[]} [action.systems_touched] - Systems this action interacts with
|
|
134
|
+
* @param {string} [action.input_summary] - Summary of input data
|
|
135
|
+
* @param {string} [action.parent_action_id] - Parent action if this is a sub-action
|
|
136
|
+
* @param {boolean} [action.reversible=true] - Whether this action can be undone
|
|
137
|
+
* @param {number} [action.risk_score=0] - Risk score 0-100
|
|
138
|
+
* @param {number} [action.confidence=50] - Confidence level 0-100
|
|
139
|
+
* @returns {Promise<{action: Object, action_id: string}>}
|
|
140
|
+
*/
|
|
141
|
+
async createAction(action) {
|
|
142
|
+
await this._guardCheck(action);
|
|
143
|
+
return this._request('/api/actions', 'POST', {
|
|
144
|
+
agent_id: this.agentId,
|
|
145
|
+
agent_name: this.agentName,
|
|
146
|
+
swarm_id: this.swarmId,
|
|
147
|
+
...action
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Update the outcome of an existing action.
|
|
153
|
+
* @param {string} actionId - The action_id to update
|
|
154
|
+
* @param {Object} outcome
|
|
155
|
+
* @param {string} [outcome.status] - New status: completed, failed, cancelled
|
|
156
|
+
* @param {string} [outcome.output_summary] - What happened
|
|
157
|
+
* @param {string[]} [outcome.side_effects] - Unintended consequences
|
|
158
|
+
* @param {string[]} [outcome.artifacts_created] - Files, records, etc. created
|
|
159
|
+
* @param {string} [outcome.error_message] - Error details if failed
|
|
160
|
+
* @param {number} [outcome.duration_ms] - How long it took
|
|
161
|
+
* @param {number} [outcome.cost_estimate] - Estimated cost in USD
|
|
162
|
+
* @returns {Promise<{action: Object}>}
|
|
163
|
+
*/
|
|
164
|
+
async updateOutcome(actionId, outcome) {
|
|
165
|
+
return this._request(`/api/actions/${actionId}`, 'PATCH', {
|
|
166
|
+
...outcome,
|
|
167
|
+
timestamp_end: outcome.timestamp_end || new Date().toISOString()
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Get a list of actions with optional filters.
|
|
173
|
+
* @param {Object} [filters]
|
|
174
|
+
* @param {string} [filters.agent_id] - Filter by agent
|
|
175
|
+
* @param {string} [filters.swarm_id] - Filter by swarm
|
|
176
|
+
* @param {string} [filters.status] - Filter by status
|
|
177
|
+
* @param {string} [filters.action_type] - Filter by type
|
|
178
|
+
* @param {number} [filters.risk_min] - Minimum risk score
|
|
179
|
+
* @param {number} [filters.limit=50] - Max results
|
|
180
|
+
* @param {number} [filters.offset=0] - Pagination offset
|
|
181
|
+
* @returns {Promise<{actions: Object[], total: number, stats: Object}>}
|
|
182
|
+
*/
|
|
183
|
+
async getActions(filters = {}) {
|
|
184
|
+
const params = new URLSearchParams();
|
|
185
|
+
for (const [key, value] of Object.entries(filters)) {
|
|
186
|
+
if (value !== undefined && value !== null && value !== '') {
|
|
187
|
+
params.set(key, String(value));
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return this._request(`/api/actions?${params}`, 'GET');
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Get a single action with its open loops and assumptions.
|
|
195
|
+
* @param {string} actionId
|
|
196
|
+
* @returns {Promise<{action: Object, open_loops: Object[], assumptions: Object[]}>}
|
|
197
|
+
*/
|
|
198
|
+
async getAction(actionId) {
|
|
199
|
+
return this._request(`/api/actions/${actionId}`, 'GET');
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Get root-cause trace for an action.
|
|
204
|
+
* @param {string} actionId
|
|
205
|
+
* @returns {Promise<{action: Object, trace: Object}>}
|
|
206
|
+
*/
|
|
207
|
+
async getActionTrace(actionId) {
|
|
208
|
+
return this._request(`/api/actions/${actionId}/trace`, 'GET');
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Helper: Create an action, run a function, and auto-update the outcome.
|
|
213
|
+
* @param {Object} actionDef - Action definition (same as createAction)
|
|
214
|
+
* @param {Function} fn - Async function to execute. Receives { action_id } as argument.
|
|
215
|
+
* @returns {Promise<*>} - The return value of fn
|
|
216
|
+
*/
|
|
217
|
+
async track(actionDef, fn) {
|
|
218
|
+
const startTime = Date.now();
|
|
219
|
+
const { action_id } = await this.createAction(actionDef);
|
|
220
|
+
|
|
221
|
+
try {
|
|
222
|
+
const result = await fn({ action_id });
|
|
223
|
+
await this.updateOutcome(action_id, {
|
|
224
|
+
status: 'completed',
|
|
225
|
+
duration_ms: Date.now() - startTime,
|
|
226
|
+
output_summary: typeof result === 'string' ? result : JSON.stringify(result)
|
|
227
|
+
});
|
|
228
|
+
return result;
|
|
229
|
+
} catch (error) {
|
|
230
|
+
await this.updateOutcome(action_id, {
|
|
231
|
+
status: 'failed',
|
|
232
|
+
duration_ms: Date.now() - startTime,
|
|
233
|
+
error_message: error.message || String(error)
|
|
234
|
+
}).catch(() => {}); // Don't throw if outcome update fails
|
|
235
|
+
throw error;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// ══════════════════════════════════════════════
|
|
240
|
+
// Category 2: Loops & Assumptions (7 methods)
|
|
241
|
+
// ══════════════════════════════════════════════
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Register an open loop for an action.
|
|
245
|
+
* @param {Object} loop
|
|
246
|
+
* @param {string} loop.action_id - Parent action ID
|
|
247
|
+
* @param {string} loop.loop_type - One of: followup, question, dependency, approval, review, handoff, other
|
|
248
|
+
* @param {string} loop.description - What needs to be resolved
|
|
249
|
+
* @param {string} [loop.priority='medium'] - One of: low, medium, high, critical
|
|
250
|
+
* @param {string} [loop.owner] - Who is responsible for resolving this
|
|
251
|
+
* @returns {Promise<{loop: Object, loop_id: string}>}
|
|
252
|
+
*/
|
|
253
|
+
async registerOpenLoop(loop) {
|
|
254
|
+
return this._request('/api/actions/loops', 'POST', loop);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Resolve or cancel an open loop.
|
|
259
|
+
* @param {string} loopId - The loop_id to resolve
|
|
260
|
+
* @param {string} status - 'resolved' or 'cancelled'
|
|
261
|
+
* @param {string} [resolution] - Resolution description (required when resolving)
|
|
262
|
+
* @returns {Promise<{loop: Object}>}
|
|
263
|
+
*/
|
|
264
|
+
async resolveOpenLoop(loopId, status, resolution) {
|
|
265
|
+
return this._request(`/api/actions/loops/${loopId}`, 'PATCH', {
|
|
266
|
+
status,
|
|
267
|
+
resolution
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Get open loops with optional filters.
|
|
273
|
+
* @param {Object} [filters]
|
|
274
|
+
* @param {string} [filters.status] - Filter by status (open, resolved, cancelled)
|
|
275
|
+
* @param {string} [filters.loop_type] - Filter by loop type
|
|
276
|
+
* @param {string} [filters.priority] - Filter by priority
|
|
277
|
+
* @param {number} [filters.limit=50] - Max results
|
|
278
|
+
* @returns {Promise<{loops: Object[], total: number, stats: Object}>}
|
|
279
|
+
*/
|
|
280
|
+
async getOpenLoops(filters = {}) {
|
|
281
|
+
const params = new URLSearchParams();
|
|
282
|
+
for (const [key, value] of Object.entries(filters)) {
|
|
283
|
+
if (value !== undefined && value !== null && value !== '') {
|
|
284
|
+
params.set(key, String(value));
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return this._request(`/api/actions/loops?${params}`, 'GET');
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Register assumptions made during an action.
|
|
292
|
+
* @param {Object} assumption
|
|
293
|
+
* @param {string} assumption.action_id - Parent action ID
|
|
294
|
+
* @param {string} assumption.assumption - The assumption being made
|
|
295
|
+
* @param {string} [assumption.basis] - Evidence or reasoning for the assumption
|
|
296
|
+
* @param {boolean} [assumption.validated=false] - Whether this has been validated
|
|
297
|
+
* @returns {Promise<{assumption: Object, assumption_id: string}>}
|
|
298
|
+
*/
|
|
299
|
+
async registerAssumption(assumption) {
|
|
300
|
+
return this._request('/api/actions/assumptions', 'POST', assumption);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Get a single assumption by ID.
|
|
305
|
+
* @param {string} assumptionId
|
|
306
|
+
* @returns {Promise<{assumption: Object}>}
|
|
307
|
+
*/
|
|
308
|
+
async getAssumption(assumptionId) {
|
|
309
|
+
return this._request(`/api/actions/assumptions/${assumptionId}`, 'GET');
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Validate or invalidate an assumption.
|
|
314
|
+
* @param {string} assumptionId - The assumption_id to update
|
|
315
|
+
* @param {boolean} validated - true to validate, false to invalidate
|
|
316
|
+
* @param {string} [invalidated_reason] - Required when invalidating
|
|
317
|
+
* @returns {Promise<{assumption: Object}>}
|
|
318
|
+
*/
|
|
319
|
+
async validateAssumption(assumptionId, validated, invalidated_reason) {
|
|
320
|
+
if (typeof validated !== 'boolean') throw new Error('validated must be a boolean');
|
|
321
|
+
if (validated === false && !invalidated_reason) {
|
|
322
|
+
throw new Error('invalidated_reason is required when invalidating an assumption');
|
|
323
|
+
}
|
|
324
|
+
const body = { validated };
|
|
325
|
+
if (invalidated_reason !== undefined) body.invalidated_reason = invalidated_reason;
|
|
326
|
+
return this._request(`/api/actions/assumptions/${assumptionId}`, 'PATCH', body);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Get drift report for assumptions with risk scoring.
|
|
331
|
+
* @param {Object} [filters]
|
|
332
|
+
* @param {string} [filters.action_id] - Filter by action
|
|
333
|
+
* @param {number} [filters.limit=50] - Max results
|
|
334
|
+
* @returns {Promise<{assumptions: Object[], drift_summary: Object}>}
|
|
335
|
+
*/
|
|
336
|
+
async getDriftReport(filters = {}) {
|
|
337
|
+
const params = new URLSearchParams({ drift: 'true' });
|
|
338
|
+
for (const [key, value] of Object.entries(filters)) {
|
|
339
|
+
if (value !== undefined && value !== null && value !== '') {
|
|
340
|
+
params.set(key, String(value));
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
return this._request(`/api/actions/assumptions?${params}`, 'GET');
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// ══════════════════════════════════════════════
|
|
347
|
+
// Category 3: Signals (1 method)
|
|
348
|
+
// ══════════════════════════════════════════════
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Get current risk signals.
|
|
352
|
+
* @returns {Promise<{signals: Object[], counts: {red: number, amber: number, total: number}}>}
|
|
353
|
+
*/
|
|
354
|
+
async getSignals() {
|
|
355
|
+
return this._request('/api/actions/signals', 'GET');
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// ══════════════════════════════════════════════
|
|
359
|
+
// Category 4: Dashboard Data (8 methods)
|
|
360
|
+
// ══════════════════════════════════════════════
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Report token usage snapshot (disabled in dashboard, API still functional).
|
|
364
|
+
* @param {Object} usage
|
|
365
|
+
* @param {number} usage.tokens_in - Input tokens consumed
|
|
366
|
+
* @param {number} usage.tokens_out - Output tokens generated
|
|
367
|
+
* @param {number} [usage.context_used] - Context window tokens used
|
|
368
|
+
* @param {number} [usage.context_max] - Context window max capacity
|
|
369
|
+
* @param {string} [usage.model] - Model name
|
|
370
|
+
* @returns {Promise<{snapshot: Object}>}
|
|
371
|
+
*/
|
|
372
|
+
async reportTokenUsage(usage) {
|
|
373
|
+
return this._request('/api/tokens', 'POST', {
|
|
374
|
+
...usage,
|
|
375
|
+
agent_id: this.agentId
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Record a decision for the learning database.
|
|
381
|
+
* @param {Object} entry
|
|
382
|
+
* @param {string} entry.decision - What was decided
|
|
383
|
+
* @param {string} [entry.context] - Context around the decision
|
|
384
|
+
* @param {string} [entry.reasoning] - Why this decision was made
|
|
385
|
+
* @param {string} [entry.outcome] - 'success', 'failure', or 'pending'
|
|
386
|
+
* @param {number} [entry.confidence] - Confidence level 0-100
|
|
387
|
+
* @returns {Promise<{decision: Object}>}
|
|
388
|
+
*/
|
|
389
|
+
async recordDecision(entry) {
|
|
390
|
+
return this._request('/api/learning', 'POST', {
|
|
391
|
+
...entry,
|
|
392
|
+
agent_id: this.agentId
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Create a goal.
|
|
398
|
+
* @param {Object} goal
|
|
399
|
+
* @param {string} goal.title - Goal title
|
|
400
|
+
* @param {string} [goal.category] - Goal category
|
|
401
|
+
* @param {string} [goal.description] - Detailed description
|
|
402
|
+
* @param {string} [goal.target_date] - Target completion date (ISO string)
|
|
403
|
+
* @param {number} [goal.progress] - Progress 0-100
|
|
404
|
+
* @param {string} [goal.status] - 'active', 'completed', 'paused'
|
|
405
|
+
* @returns {Promise<{goal: Object}>}
|
|
406
|
+
*/
|
|
407
|
+
async createGoal(goal) {
|
|
408
|
+
return this._request('/api/goals', 'POST', {
|
|
409
|
+
...goal,
|
|
410
|
+
agent_id: this.agentId
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Record content creation.
|
|
416
|
+
* @param {Object} content
|
|
417
|
+
* @param {string} content.title - Content title
|
|
418
|
+
* @param {string} [content.platform] - Platform (e.g., 'linkedin', 'twitter')
|
|
419
|
+
* @param {string} [content.status] - 'draft' or 'published'
|
|
420
|
+
* @param {string} [content.url] - Published URL
|
|
421
|
+
* @returns {Promise<{content: Object}>}
|
|
422
|
+
*/
|
|
423
|
+
async recordContent(content) {
|
|
424
|
+
return this._request('/api/content', 'POST', {
|
|
425
|
+
...content,
|
|
426
|
+
agent_id: this.agentId
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* Record a relationship interaction.
|
|
432
|
+
* @param {Object} interaction
|
|
433
|
+
* @param {string} interaction.summary - What happened
|
|
434
|
+
* @param {string} [interaction.contact_name] - Contact name (auto-resolves to contact_id)
|
|
435
|
+
* @param {string} [interaction.contact_id] - Direct contact ID
|
|
436
|
+
* @param {string} [interaction.direction] - 'inbound' or 'outbound'
|
|
437
|
+
* @param {string} [interaction.type] - Interaction type
|
|
438
|
+
* @param {string} [interaction.platform] - Platform used
|
|
439
|
+
* @returns {Promise<{interaction: Object}>}
|
|
440
|
+
*/
|
|
441
|
+
async recordInteraction(interaction) {
|
|
442
|
+
return this._request('/api/relationships', 'POST', {
|
|
443
|
+
...interaction,
|
|
444
|
+
agent_id: this.agentId
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Create a calendar event.
|
|
450
|
+
* @param {Object} event
|
|
451
|
+
* @param {string} event.summary - Event title/summary
|
|
452
|
+
* @param {string} event.start_time - Start time (ISO string)
|
|
453
|
+
* @param {string} [event.end_time] - End time (ISO string)
|
|
454
|
+
* @param {string} [event.location] - Event location
|
|
455
|
+
* @param {string} [event.description] - Event description
|
|
456
|
+
* @returns {Promise<{event: Object}>}
|
|
457
|
+
*/
|
|
458
|
+
async createCalendarEvent(event) {
|
|
459
|
+
return this._request('/api/calendar', 'POST', event);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* Record an idea/inspiration.
|
|
464
|
+
* @param {Object} idea
|
|
465
|
+
* @param {string} idea.title - Idea title
|
|
466
|
+
* @param {string} [idea.description] - Detailed description
|
|
467
|
+
* @param {string} [idea.category] - Category
|
|
468
|
+
* @param {number} [idea.score] - Priority/quality score 0-100
|
|
469
|
+
* @param {string} [idea.status] - 'pending', 'in_progress', 'shipped', 'rejected'
|
|
470
|
+
* @param {string} [idea.source] - Where this idea came from
|
|
471
|
+
* @returns {Promise<{idea: Object}>}
|
|
472
|
+
*/
|
|
473
|
+
async recordIdea(idea) {
|
|
474
|
+
return this._request('/api/inspiration', 'POST', idea);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
/**
|
|
478
|
+
* Report memory health snapshot with entities and topics.
|
|
479
|
+
* @param {Object} report
|
|
480
|
+
* @param {Object} report.health - Health metrics
|
|
481
|
+
* @param {number} report.health.score - Health score 0-100
|
|
482
|
+
* @param {Object[]} [report.entities] - Key entities found in memory
|
|
483
|
+
* @param {Object[]} [report.topics] - Topics/themes found in memory
|
|
484
|
+
* @returns {Promise<{snapshot: Object, entities_count: number, topics_count: number}>}
|
|
485
|
+
*/
|
|
486
|
+
async reportMemoryHealth(report) {
|
|
487
|
+
return this._request('/api/memory', 'POST', report);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
/**
|
|
491
|
+
* Report active connections/integrations for this agent.
|
|
492
|
+
* @param {Object[]} connections - Array of connection objects
|
|
493
|
+
* @param {string} connections[].provider - Service name (e.g., 'anthropic', 'github')
|
|
494
|
+
* @param {string} [connections[].authType] - Auth method
|
|
495
|
+
* @param {string} [connections[].planName] - Plan name
|
|
496
|
+
* @param {string} [connections[].status] - Connection status: active, inactive, error
|
|
497
|
+
* @param {Object|string} [connections[].metadata] - Optional metadata
|
|
498
|
+
* @returns {Promise<{connections: Object[], created: number}>}
|
|
499
|
+
*/
|
|
500
|
+
async reportConnections(connections) {
|
|
501
|
+
return this._request('/api/agents/connections', 'POST', {
|
|
502
|
+
agent_id: this.agentId,
|
|
503
|
+
connections: connections.map(c => ({
|
|
504
|
+
provider: c.provider,
|
|
505
|
+
auth_type: c.authType || c.auth_type || 'api_key',
|
|
506
|
+
plan_name: c.planName || c.plan_name || null,
|
|
507
|
+
status: c.status || 'active',
|
|
508
|
+
metadata: c.metadata || null
|
|
509
|
+
}))
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// ══════════════════════════════════════════════
|
|
514
|
+
// Category 5: Session Handoffs (3 methods)
|
|
515
|
+
// ══════════════════════════════════════════════
|
|
516
|
+
|
|
517
|
+
/**
|
|
518
|
+
* Create a session handoff document.
|
|
519
|
+
* @param {Object} handoff
|
|
520
|
+
* @param {string} handoff.summary - Session summary
|
|
521
|
+
* @param {string} [handoff.session_date] - Date string (defaults to today)
|
|
522
|
+
* @param {string[]} [handoff.key_decisions] - Key decisions made
|
|
523
|
+
* @param {string[]} [handoff.open_tasks] - Tasks still open
|
|
524
|
+
* @param {string} [handoff.mood_notes] - Mood/energy observations
|
|
525
|
+
* @param {string[]} [handoff.next_priorities] - What to focus on next
|
|
526
|
+
* @returns {Promise<{handoff: Object, handoff_id: string}>}
|
|
527
|
+
*/
|
|
528
|
+
async createHandoff(handoff) {
|
|
529
|
+
return this._request('/api/handoffs', 'POST', {
|
|
530
|
+
agent_id: this.agentId,
|
|
531
|
+
...handoff
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* Get handoffs with optional filters.
|
|
537
|
+
* @param {Object} [filters]
|
|
538
|
+
* @param {string} [filters.date] - Filter by session_date
|
|
539
|
+
* @param {number} [filters.limit] - Max results
|
|
540
|
+
* @returns {Promise<{handoffs: Object[], total: number}>}
|
|
541
|
+
*/
|
|
542
|
+
async getHandoffs(filters = {}) {
|
|
543
|
+
const params = new URLSearchParams({ agent_id: this.agentId });
|
|
544
|
+
if (filters.date) params.set('date', filters.date);
|
|
545
|
+
if (filters.limit) params.set('limit', String(filters.limit));
|
|
546
|
+
return this._request(`/api/handoffs?${params}`, 'GET');
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
/**
|
|
550
|
+
* Get the most recent handoff for this agent.
|
|
551
|
+
* @returns {Promise<{handoff: Object|null}>}
|
|
552
|
+
*/
|
|
553
|
+
async getLatestHandoff() {
|
|
554
|
+
return this._request(`/api/handoffs?agent_id=${this.agentId}&latest=true`, 'GET');
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
// ══════════════════════════════════════════════
|
|
558
|
+
// Category 6: Context Manager (7 methods)
|
|
559
|
+
// ══════════════════════════════════════════════
|
|
560
|
+
|
|
561
|
+
/**
|
|
562
|
+
* Capture a key point from the current session.
|
|
563
|
+
* @param {Object} point
|
|
564
|
+
* @param {string} point.content - The key point content
|
|
565
|
+
* @param {string} [point.category] - One of: decision, task, insight, question, general
|
|
566
|
+
* @param {number} [point.importance] - Importance 1-10 (default 5)
|
|
567
|
+
* @param {string} [point.session_date] - Date string (defaults to today)
|
|
568
|
+
* @returns {Promise<{point: Object, point_id: string}>}
|
|
569
|
+
*/
|
|
570
|
+
async captureKeyPoint(point) {
|
|
571
|
+
return this._request('/api/context/points', 'POST', {
|
|
572
|
+
agent_id: this.agentId,
|
|
573
|
+
...point
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
/**
|
|
578
|
+
* Get key points with optional filters.
|
|
579
|
+
* @param {Object} [filters]
|
|
580
|
+
* @param {string} [filters.category] - Filter by category
|
|
581
|
+
* @param {string} [filters.session_date] - Filter by date
|
|
582
|
+
* @param {number} [filters.limit] - Max results
|
|
583
|
+
* @returns {Promise<{points: Object[], total: number}>}
|
|
584
|
+
*/
|
|
585
|
+
async getKeyPoints(filters = {}) {
|
|
586
|
+
const params = new URLSearchParams({ agent_id: this.agentId });
|
|
587
|
+
if (filters.category) params.set('category', filters.category);
|
|
588
|
+
if (filters.session_date) params.set('session_date', filters.session_date);
|
|
589
|
+
if (filters.limit) params.set('limit', String(filters.limit));
|
|
590
|
+
return this._request(`/api/context/points?${params}`, 'GET');
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* Create a context thread for tracking a topic across entries.
|
|
595
|
+
* @param {Object} thread
|
|
596
|
+
* @param {string} thread.name - Thread name (unique per agent per org)
|
|
597
|
+
* @param {string} [thread.summary] - Initial summary
|
|
598
|
+
* @returns {Promise<{thread: Object, thread_id: string}>}
|
|
599
|
+
*/
|
|
600
|
+
async createThread(thread) {
|
|
601
|
+
return this._request('/api/context/threads', 'POST', {
|
|
602
|
+
agent_id: this.agentId,
|
|
603
|
+
...thread
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
/**
|
|
608
|
+
* Add an entry to an existing thread.
|
|
609
|
+
* @param {string} threadId - The thread ID
|
|
610
|
+
* @param {string} content - Entry content
|
|
611
|
+
* @param {string} [entryType] - Entry type (default: 'note')
|
|
612
|
+
* @returns {Promise<{entry: Object, entry_id: string}>}
|
|
613
|
+
*/
|
|
614
|
+
async addThreadEntry(threadId, content, entryType) {
|
|
615
|
+
return this._request(`/api/context/threads/${threadId}/entries`, 'POST', {
|
|
616
|
+
content,
|
|
617
|
+
entry_type: entryType || 'note'
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
/**
|
|
622
|
+
* Close a thread with an optional summary.
|
|
623
|
+
* @param {string} threadId - The thread ID
|
|
624
|
+
* @param {string} [summary] - Final summary
|
|
625
|
+
* @returns {Promise<{thread: Object}>}
|
|
626
|
+
*/
|
|
627
|
+
async closeThread(threadId, summary) {
|
|
628
|
+
const body = { status: 'closed' };
|
|
629
|
+
if (summary) body.summary = summary;
|
|
630
|
+
return this._request(`/api/context/threads/${threadId}`, 'PATCH', body);
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
/**
|
|
634
|
+
* Get threads with optional filters.
|
|
635
|
+
* @param {Object} [filters]
|
|
636
|
+
* @param {string} [filters.status] - Filter by status (active, closed)
|
|
637
|
+
* @param {number} [filters.limit] - Max results
|
|
638
|
+
* @returns {Promise<{threads: Object[], total: number}>}
|
|
639
|
+
*/
|
|
640
|
+
async getThreads(filters = {}) {
|
|
641
|
+
const params = new URLSearchParams({ agent_id: this.agentId });
|
|
642
|
+
if (filters.status) params.set('status', filters.status);
|
|
643
|
+
if (filters.limit) params.set('limit', String(filters.limit));
|
|
644
|
+
return this._request(`/api/context/threads?${params}`, 'GET');
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
/**
|
|
648
|
+
* Get a combined context summary: today's key points + active threads.
|
|
649
|
+
* @returns {Promise<{points: Object[], threads: Object[]}>}
|
|
650
|
+
*/
|
|
651
|
+
async getContextSummary() {
|
|
652
|
+
const today = new Date().toISOString().split('T')[0];
|
|
653
|
+
const [pointsResult, threadsResult] = await Promise.all([
|
|
654
|
+
this.getKeyPoints({ session_date: today }),
|
|
655
|
+
this.getThreads({ status: 'active' }),
|
|
656
|
+
]);
|
|
657
|
+
return {
|
|
658
|
+
points: pointsResult.points,
|
|
659
|
+
threads: threadsResult.threads,
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
// ══════════════════════════════════════════════
|
|
664
|
+
// Category 7: Automation Snippets (4 methods)
|
|
665
|
+
// ══════════════════════════════════════════════
|
|
666
|
+
|
|
667
|
+
/**
|
|
668
|
+
* Save or update a reusable code snippet.
|
|
669
|
+
* @param {Object} snippet
|
|
670
|
+
* @param {string} snippet.name - Snippet name (unique per org, upserts on conflict)
|
|
671
|
+
* @param {string} snippet.code - The snippet code
|
|
672
|
+
* @param {string} [snippet.description] - What this snippet does
|
|
673
|
+
* @param {string} [snippet.language] - Programming language
|
|
674
|
+
* @param {string[]} [snippet.tags] - Tags for categorization
|
|
675
|
+
* @returns {Promise<{snippet: Object, snippet_id: string}>}
|
|
676
|
+
*/
|
|
677
|
+
async saveSnippet(snippet) {
|
|
678
|
+
return this._request('/api/snippets', 'POST', {
|
|
679
|
+
agent_id: this.agentId,
|
|
680
|
+
...snippet
|
|
681
|
+
});
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
/**
|
|
685
|
+
* Search and list snippets.
|
|
686
|
+
* @param {Object} [filters]
|
|
687
|
+
* @param {string} [filters.search] - Search name/description
|
|
688
|
+
* @param {string} [filters.tag] - Filter by tag
|
|
689
|
+
* @param {string} [filters.language] - Filter by language
|
|
690
|
+
* @param {number} [filters.limit] - Max results
|
|
691
|
+
* @returns {Promise<{snippets: Object[], total: number}>}
|
|
692
|
+
*/
|
|
693
|
+
async getSnippets(filters = {}) {
|
|
694
|
+
const params = new URLSearchParams();
|
|
695
|
+
if (filters.search) params.set('search', filters.search);
|
|
696
|
+
if (filters.tag) params.set('tag', filters.tag);
|
|
697
|
+
if (filters.language) params.set('language', filters.language);
|
|
698
|
+
if (filters.limit) params.set('limit', String(filters.limit));
|
|
699
|
+
return this._request(`/api/snippets?${params}`, 'GET');
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
/**
|
|
703
|
+
* Mark a snippet as used (increments use_count).
|
|
704
|
+
* @param {string} snippetId - The snippet ID
|
|
705
|
+
* @returns {Promise<{snippet: Object}>}
|
|
706
|
+
*/
|
|
707
|
+
async useSnippet(snippetId) {
|
|
708
|
+
return this._request(`/api/snippets/${snippetId}/use`, 'POST');
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
/**
|
|
712
|
+
* Delete a snippet.
|
|
713
|
+
* @param {string} snippetId - The snippet ID
|
|
714
|
+
* @returns {Promise<{deleted: boolean, id: string}>}
|
|
715
|
+
*/
|
|
716
|
+
async deleteSnippet(snippetId) {
|
|
717
|
+
return this._request(`/api/snippets?id=${snippetId}`, 'DELETE');
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
// ══════════════════════════════════════════════
|
|
721
|
+
// Category 8: User Preferences (6 methods)
|
|
722
|
+
// ══════════════════════════════════════════════
|
|
723
|
+
|
|
724
|
+
/**
|
|
725
|
+
* Log a user observation (what you noticed about the user).
|
|
726
|
+
* @param {Object} obs
|
|
727
|
+
* @param {string} obs.observation - The observation text
|
|
728
|
+
* @param {string} [obs.category] - Category tag
|
|
729
|
+
* @param {number} [obs.importance] - Importance 1-10
|
|
730
|
+
* @returns {Promise<{observation: Object, observation_id: string}>}
|
|
731
|
+
*/
|
|
732
|
+
async logObservation(obs) {
|
|
733
|
+
return this._request('/api/preferences', 'POST', {
|
|
734
|
+
type: 'observation',
|
|
735
|
+
agent_id: this.agentId,
|
|
736
|
+
...obs
|
|
737
|
+
});
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
/**
|
|
741
|
+
* Set a learned user preference.
|
|
742
|
+
* @param {Object} pref
|
|
743
|
+
* @param {string} pref.preference - The preference description
|
|
744
|
+
* @param {string} [pref.category] - Category tag
|
|
745
|
+
* @param {number} [pref.confidence] - Confidence 0-100
|
|
746
|
+
* @returns {Promise<{preference: Object, preference_id: string}>}
|
|
747
|
+
*/
|
|
748
|
+
async setPreference(pref) {
|
|
749
|
+
return this._request('/api/preferences', 'POST', {
|
|
750
|
+
type: 'preference',
|
|
751
|
+
agent_id: this.agentId,
|
|
752
|
+
...pref
|
|
753
|
+
});
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
/**
|
|
757
|
+
* Log user mood/energy for a session.
|
|
758
|
+
* @param {Object} entry
|
|
759
|
+
* @param {string} entry.mood - Mood description (e.g., 'focused', 'frustrated')
|
|
760
|
+
* @param {string} [entry.energy] - Energy level (e.g., 'high', 'low')
|
|
761
|
+
* @param {string} [entry.notes] - Additional notes
|
|
762
|
+
* @returns {Promise<{mood: Object, mood_id: string}>}
|
|
763
|
+
*/
|
|
764
|
+
async logMood(entry) {
|
|
765
|
+
return this._request('/api/preferences', 'POST', {
|
|
766
|
+
type: 'mood',
|
|
767
|
+
agent_id: this.agentId,
|
|
768
|
+
...entry
|
|
769
|
+
});
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
/**
|
|
773
|
+
* Track an approach and whether it succeeded or failed.
|
|
774
|
+
* @param {Object} entry
|
|
775
|
+
* @param {string} entry.approach - The approach description
|
|
776
|
+
* @param {string} [entry.context] - Context for when to use this approach
|
|
777
|
+
* @param {boolean} [entry.success] - true = worked, false = failed, undefined = just recording
|
|
778
|
+
* @returns {Promise<{approach: Object, approach_id: string}>}
|
|
779
|
+
*/
|
|
780
|
+
async trackApproach(entry) {
|
|
781
|
+
return this._request('/api/preferences', 'POST', {
|
|
782
|
+
type: 'approach',
|
|
783
|
+
agent_id: this.agentId,
|
|
784
|
+
...entry
|
|
785
|
+
});
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
/**
|
|
789
|
+
* Get a summary of all user preference data.
|
|
790
|
+
* @returns {Promise<{summary: Object}>}
|
|
791
|
+
*/
|
|
792
|
+
async getPreferenceSummary() {
|
|
793
|
+
return this._request(`/api/preferences?type=summary&agent_id=${this.agentId}`, 'GET');
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
/**
|
|
797
|
+
* Get tracked approaches with success/fail counts.
|
|
798
|
+
* @param {Object} [filters]
|
|
799
|
+
* @param {number} [filters.limit] - Max results
|
|
800
|
+
* @returns {Promise<{approaches: Object[], total: number}>}
|
|
801
|
+
*/
|
|
802
|
+
async getApproaches(filters = {}) {
|
|
803
|
+
const params = new URLSearchParams({ type: 'approaches', agent_id: this.agentId });
|
|
804
|
+
if (filters.limit) params.set('limit', String(filters.limit));
|
|
805
|
+
return this._request(`/api/preferences?${params}`, 'GET');
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
// ══════════════════════════════════════════════
|
|
809
|
+
// Category 9: Daily Digest (1 method)
|
|
810
|
+
// ══════════════════════════════════════════════
|
|
811
|
+
|
|
812
|
+
/**
|
|
813
|
+
* Get a daily activity digest aggregated from all data sources.
|
|
814
|
+
* @param {string} [date] - Date string YYYY-MM-DD (defaults to today)
|
|
815
|
+
* @returns {Promise<{date: string, digest: Object, summary: Object}>}
|
|
816
|
+
*/
|
|
817
|
+
async getDailyDigest(date) {
|
|
818
|
+
const params = new URLSearchParams({ agent_id: this.agentId });
|
|
819
|
+
if (date) params.set('date', date);
|
|
820
|
+
return this._request(`/api/digest?${params}`, 'GET');
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
// ══════════════════════════════════════════════
|
|
824
|
+
// Category 10: Security Scanning (2 methods)
|
|
825
|
+
// ══════════════════════════════════════════════
|
|
826
|
+
|
|
827
|
+
/**
|
|
828
|
+
* Scan text for sensitive data (API keys, tokens, PII, etc.).
|
|
829
|
+
* Returns findings and redacted text. Does NOT store the original content.
|
|
830
|
+
* @param {string} text - Text to scan
|
|
831
|
+
* @param {string} [destination] - Where this text is headed (for context)
|
|
832
|
+
* @returns {Promise<{clean: boolean, findings_count: number, findings: Object[], redacted_text: string}>}
|
|
833
|
+
*/
|
|
834
|
+
async scanContent(text, destination) {
|
|
835
|
+
return this._request('/api/security/scan', 'POST', {
|
|
836
|
+
text,
|
|
837
|
+
destination,
|
|
838
|
+
agent_id: this.agentId,
|
|
839
|
+
store: false,
|
|
840
|
+
});
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
/**
|
|
844
|
+
* Scan text and store finding metadata (never the content itself).
|
|
845
|
+
* Use this for audit trails of security scans.
|
|
846
|
+
* @param {string} text - Text to scan
|
|
847
|
+
* @param {string} [destination] - Where this text is headed
|
|
848
|
+
* @returns {Promise<{clean: boolean, findings_count: number, findings: Object[], redacted_text: string}>}
|
|
849
|
+
*/
|
|
850
|
+
async reportSecurityFinding(text, destination) {
|
|
851
|
+
return this._request('/api/security/scan', 'POST', {
|
|
852
|
+
text,
|
|
853
|
+
destination,
|
|
854
|
+
agent_id: this.agentId,
|
|
855
|
+
store: true,
|
|
856
|
+
});
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
// ══════════════════════════════════════════════
|
|
860
|
+
// Category 11: Agent Messaging (9 methods)
|
|
861
|
+
// ══════════════════════════════════════════════
|
|
862
|
+
|
|
863
|
+
/**
|
|
864
|
+
* Send a message to another agent or broadcast to all.
|
|
865
|
+
* @param {Object} params
|
|
866
|
+
* @param {string} [params.to] - Target agent ID (omit for broadcast)
|
|
867
|
+
* @param {string} [params.type='info'] - Message type: action|info|lesson|question|status
|
|
868
|
+
* @param {string} [params.subject] - Subject line (max 200 chars)
|
|
869
|
+
* @param {string} params.body - Message body (max 2000 chars)
|
|
870
|
+
* @param {string} [params.threadId] - Thread ID to attach message to
|
|
871
|
+
* @param {boolean} [params.urgent=false] - Mark as urgent
|
|
872
|
+
* @param {string} [params.docRef] - Reference to a shared doc ID
|
|
873
|
+
* @returns {Promise<{message: Object, message_id: string}>}
|
|
874
|
+
*/
|
|
875
|
+
async sendMessage({ to, type, subject, body, threadId, urgent, docRef }) {
|
|
876
|
+
return this._request('/api/messages', 'POST', {
|
|
877
|
+
from_agent_id: this.agentId,
|
|
878
|
+
to_agent_id: to || null,
|
|
879
|
+
message_type: type || 'info',
|
|
880
|
+
subject,
|
|
881
|
+
body,
|
|
882
|
+
thread_id: threadId,
|
|
883
|
+
urgent,
|
|
884
|
+
doc_ref: docRef,
|
|
885
|
+
});
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
/**
|
|
889
|
+
* Get inbox messages for this agent.
|
|
890
|
+
* @param {Object} [params]
|
|
891
|
+
* @param {string} [params.type] - Filter by message type
|
|
892
|
+
* @param {boolean} [params.unread] - Only unread messages
|
|
893
|
+
* @param {string} [params.threadId] - Filter by thread
|
|
894
|
+
* @param {number} [params.limit=50] - Max messages to return
|
|
895
|
+
* @returns {Promise<{messages: Object[], total: number, unread_count: number}>}
|
|
896
|
+
*/
|
|
897
|
+
async getInbox({ type, unread, threadId, limit } = {}) {
|
|
898
|
+
const params = new URLSearchParams({
|
|
899
|
+
agent_id: this.agentId,
|
|
900
|
+
direction: 'inbox',
|
|
901
|
+
});
|
|
902
|
+
if (type) params.set('type', type);
|
|
903
|
+
if (unread) params.set('unread', 'true');
|
|
904
|
+
if (threadId) params.set('thread_id', threadId);
|
|
905
|
+
if (limit) params.set('limit', String(limit));
|
|
906
|
+
return this._request(`/api/messages?${params}`, 'GET');
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
/**
|
|
910
|
+
* Mark messages as read.
|
|
911
|
+
* @param {string[]} messageIds - Array of message IDs to mark read
|
|
912
|
+
* @returns {Promise<{updated: number}>}
|
|
913
|
+
*/
|
|
914
|
+
async markRead(messageIds) {
|
|
915
|
+
return this._request('/api/messages', 'PATCH', {
|
|
916
|
+
message_ids: messageIds,
|
|
917
|
+
action: 'read',
|
|
918
|
+
agent_id: this.agentId,
|
|
919
|
+
});
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
/**
|
|
923
|
+
* Archive messages.
|
|
924
|
+
* @param {string[]} messageIds - Array of message IDs to archive
|
|
925
|
+
* @returns {Promise<{updated: number}>}
|
|
926
|
+
*/
|
|
927
|
+
async archiveMessages(messageIds) {
|
|
928
|
+
return this._request('/api/messages', 'PATCH', {
|
|
929
|
+
message_ids: messageIds,
|
|
930
|
+
action: 'archive',
|
|
931
|
+
agent_id: this.agentId,
|
|
932
|
+
});
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
/**
|
|
936
|
+
* Broadcast a message to all agents in the organization.
|
|
937
|
+
* @param {Object} params
|
|
938
|
+
* @param {string} [params.type='info'] - Message type
|
|
939
|
+
* @param {string} [params.subject] - Subject line
|
|
940
|
+
* @param {string} params.body - Message body
|
|
941
|
+
* @param {string} [params.threadId] - Thread ID
|
|
942
|
+
* @returns {Promise<{message: Object, message_id: string}>}
|
|
943
|
+
*/
|
|
944
|
+
async broadcast({ type, subject, body, threadId }) {
|
|
945
|
+
return this.sendMessage({ to: null, type, subject, body, threadId });
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
/**
|
|
949
|
+
* Create a new message thread for multi-turn conversations.
|
|
950
|
+
* @param {Object} params
|
|
951
|
+
* @param {string} params.name - Thread name
|
|
952
|
+
* @param {string[]} [params.participants] - Agent IDs (null = open to all)
|
|
953
|
+
* @returns {Promise<{thread: Object, thread_id: string}>}
|
|
954
|
+
*/
|
|
955
|
+
async createMessageThread({ name, participants }) {
|
|
956
|
+
return this._request('/api/messages/threads', 'POST', {
|
|
957
|
+
name,
|
|
958
|
+
participants,
|
|
959
|
+
created_by: this.agentId,
|
|
960
|
+
});
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
/**
|
|
964
|
+
* List message threads.
|
|
965
|
+
* @param {Object} [params]
|
|
966
|
+
* @param {string} [params.status] - Filter by status: open|resolved|archived
|
|
967
|
+
* @param {number} [params.limit=20] - Max threads to return
|
|
968
|
+
* @returns {Promise<{threads: Object[], total: number}>}
|
|
969
|
+
*/
|
|
970
|
+
async getMessageThreads({ status, limit } = {}) {
|
|
971
|
+
const params = new URLSearchParams({ agent_id: this.agentId });
|
|
972
|
+
if (status) params.set('status', status);
|
|
973
|
+
if (limit) params.set('limit', String(limit));
|
|
974
|
+
return this._request(`/api/messages/threads?${params}`, 'GET');
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
/**
|
|
978
|
+
* Resolve (close) a message thread.
|
|
979
|
+
* @param {string} threadId - Thread ID to resolve
|
|
980
|
+
* @param {string} [summary] - Resolution summary
|
|
981
|
+
* @returns {Promise<{thread: Object}>}
|
|
982
|
+
*/
|
|
983
|
+
async resolveMessageThread(threadId, summary) {
|
|
984
|
+
return this._request('/api/messages/threads', 'PATCH', {
|
|
985
|
+
thread_id: threadId,
|
|
986
|
+
status: 'resolved',
|
|
987
|
+
summary,
|
|
988
|
+
});
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
/**
|
|
992
|
+
* Create or update a shared workspace document.
|
|
993
|
+
* Upserts by (org_id, name) — updates increment the version.
|
|
994
|
+
* @param {Object} params
|
|
995
|
+
* @param {string} params.name - Document name (unique per org)
|
|
996
|
+
* @param {string} params.content - Document content
|
|
997
|
+
* @returns {Promise<{doc: Object, doc_id: string}>}
|
|
998
|
+
*/
|
|
999
|
+
async saveSharedDoc({ name, content }) {
|
|
1000
|
+
return this._request('/api/messages/docs', 'POST', {
|
|
1001
|
+
name,
|
|
1002
|
+
content,
|
|
1003
|
+
agent_id: this.agentId,
|
|
1004
|
+
});
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
// ══════════════════════════════════════════════
|
|
1008
|
+
// Category 13: Behavior Guard (2 methods)
|
|
1009
|
+
// ══════════════════════════════════════════════
|
|
1010
|
+
|
|
1011
|
+
/**
|
|
1012
|
+
* Check guard policies before executing a risky action.
|
|
1013
|
+
* Returns allow/warn/block/require_approval.
|
|
1014
|
+
* @param {Object} context
|
|
1015
|
+
* @param {string} context.action_type - Action type (required)
|
|
1016
|
+
* @param {number} [context.risk_score] - Risk score 0-100
|
|
1017
|
+
* @param {string[]} [context.systems_touched] - Systems involved
|
|
1018
|
+
* @param {boolean} [context.reversible] - Whether the action is reversible
|
|
1019
|
+
* @param {string} [context.declared_goal] - What the action aims to do
|
|
1020
|
+
* @param {Object} [options]
|
|
1021
|
+
* @param {boolean} [options.includeSignals=false] - Include live signal warnings
|
|
1022
|
+
* @returns {Promise<{decision: string, reasons: string[], warnings: string[], matched_policies: string[], evaluated_at: string}>}
|
|
1023
|
+
*/
|
|
1024
|
+
async guard(context, options = {}) {
|
|
1025
|
+
const params = new URLSearchParams();
|
|
1026
|
+
if (options.includeSignals) params.set('include_signals', 'true');
|
|
1027
|
+
const qs = params.toString();
|
|
1028
|
+
return this._request(`/api/guard${qs ? `?${qs}` : ''}`, 'POST', {
|
|
1029
|
+
...context,
|
|
1030
|
+
agent_id: context.agent_id || this.agentId,
|
|
1031
|
+
});
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
/**
|
|
1035
|
+
* Get recent guard decisions (audit log).
|
|
1036
|
+
* @param {Object} [filters]
|
|
1037
|
+
* @param {string} [filters.decision] - Filter by decision: allow|warn|block|require_approval
|
|
1038
|
+
* @param {number} [filters.limit=20] - Max results
|
|
1039
|
+
* @param {number} [filters.offset=0] - Pagination offset
|
|
1040
|
+
* @returns {Promise<{decisions: Object[], total: number, stats: Object}>}
|
|
1041
|
+
*/
|
|
1042
|
+
async getGuardDecisions(filters = {}) {
|
|
1043
|
+
const params = new URLSearchParams({ agent_id: this.agentId });
|
|
1044
|
+
if (filters.decision) params.set('decision', filters.decision);
|
|
1045
|
+
if (filters.limit) params.set('limit', String(filters.limit));
|
|
1046
|
+
if (filters.offset) params.set('offset', String(filters.offset));
|
|
1047
|
+
return this._request(`/api/guard?${params}`, 'GET');
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
// ─── Bulk Sync ────────────────────────────────────────────
|
|
1051
|
+
|
|
1052
|
+
/**
|
|
1053
|
+
* Sync multiple data categories in a single request.
|
|
1054
|
+
* Every key is optional — only provided categories are processed.
|
|
1055
|
+
* @param {Object} state - Data to sync (connections, memory, goals, learning, content, inspiration, context_points, context_threads, handoffs, preferences, snippets)
|
|
1056
|
+
* @returns {Promise<{results: Object, total_synced: number, total_errors: number, duration_ms: number}>}
|
|
1057
|
+
*/
|
|
1058
|
+
async syncState(state) {
|
|
1059
|
+
return this._request('/api/sync', 'POST', {
|
|
1060
|
+
agent_id: this.agentId,
|
|
1061
|
+
...state,
|
|
1062
|
+
});
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
/**
|
|
1067
|
+
* Error thrown when guardMode is 'enforce' and guard blocks an action.
|
|
1068
|
+
*/
|
|
1069
|
+
class GuardBlockedError extends Error {
|
|
1070
|
+
/**
|
|
1071
|
+
* @param {Object} decision - Guard decision object
|
|
1072
|
+
*/
|
|
1073
|
+
constructor(decision) {
|
|
1074
|
+
const reasons = (decision.reasons || []).join('; ') || 'no reason';
|
|
1075
|
+
super(`Guard blocked action: ${decision.decision}. Reasons: ${reasons}`);
|
|
1076
|
+
this.name = 'GuardBlockedError';
|
|
1077
|
+
this.decision = decision.decision;
|
|
1078
|
+
this.reasons = decision.reasons || [];
|
|
1079
|
+
this.warnings = decision.warnings || [];
|
|
1080
|
+
this.matchedPolicies = decision.matched_policies || [];
|
|
1081
|
+
this.riskScore = decision.risk_score ?? null;
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
// Backward compatibility alias
|
|
1086
|
+
const OpenClawAgent = DashClaw;
|
|
1087
|
+
|
|
1088
|
+
export default DashClaw;
|
|
1089
|
+
export { DashClaw, OpenClawAgent, GuardBlockedError };
|
package/index.cjs
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DashClaw SDK — CommonJS compatibility wrapper.
|
|
3
|
+
* For ESM: import { DashClaw } from 'dashclaw'
|
|
4
|
+
* For CJS: const { DashClaw } = require('dashclaw')
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
let _module;
|
|
8
|
+
|
|
9
|
+
async function loadModule() {
|
|
10
|
+
if (!_module) {
|
|
11
|
+
_module = await import('./dashclaw.js');
|
|
12
|
+
}
|
|
13
|
+
return _module;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Re-export via dynamic import (CJS → ESM bridge)
|
|
17
|
+
module.exports = new Proxy({}, {
|
|
18
|
+
get(target, prop) {
|
|
19
|
+
if (prop === '__esModule') return true;
|
|
20
|
+
if (prop === 'then') return undefined; // Prevent Promise-like behavior
|
|
21
|
+
|
|
22
|
+
// Return a lazy-loading constructor wrapper
|
|
23
|
+
if (prop === 'GuardBlockedError') {
|
|
24
|
+
// Return a placeholder that resolves to the real class
|
|
25
|
+
return class GuardBlockedErrorProxy extends Error {
|
|
26
|
+
constructor(decision) {
|
|
27
|
+
const reasons = (decision.reasons || []).join('; ') || 'no reason';
|
|
28
|
+
super(`Guard blocked action: ${decision.decision}. Reasons: ${reasons}`);
|
|
29
|
+
this.name = 'GuardBlockedError';
|
|
30
|
+
this.decision = decision.decision;
|
|
31
|
+
this.reasons = decision.reasons || [];
|
|
32
|
+
this.warnings = decision.warnings || [];
|
|
33
|
+
this.matchedPolicies = decision.matched_policies || [];
|
|
34
|
+
this.riskScore = decision.risk_score ?? null;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
if (prop === 'DashClaw' || prop === 'OpenClawAgent' || prop === 'default') {
|
|
39
|
+
return class DashClawProxy {
|
|
40
|
+
constructor(opts) {
|
|
41
|
+
// Store options, actual instance created async
|
|
42
|
+
this._opts = opts;
|
|
43
|
+
this._ready = loadModule().then(m => {
|
|
44
|
+
const Cls = m.DashClaw || m.default;
|
|
45
|
+
this._instance = new Cls(opts);
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// For synchronous construction, use DashClaw.create()
|
|
50
|
+
static async create(opts) {
|
|
51
|
+
const mod = await loadModule();
|
|
52
|
+
const Cls = mod.DashClaw || mod.default;
|
|
53
|
+
return new Cls(opts);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// Preferred: async factory
|
|
62
|
+
module.exports.create = async function create(opts) {
|
|
63
|
+
const mod = await loadModule();
|
|
64
|
+
const Cls = mod.DashClaw || mod.default;
|
|
65
|
+
return new Cls(opts);
|
|
66
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "dashclaw",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Full-featured agent toolkit for the DashClaw platform. 57 methods for action recording, context management, session handoffs, security scanning, behavior guard, bulk sync, and more.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./index.cjs",
|
|
7
|
+
"module": "./dashclaw.js",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dashclaw.js",
|
|
11
|
+
"require": "./index.cjs"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dashclaw.js",
|
|
16
|
+
"index.cjs",
|
|
17
|
+
"LICENSE",
|
|
18
|
+
"README.md"
|
|
19
|
+
],
|
|
20
|
+
"keywords": [
|
|
21
|
+
"ai-agent",
|
|
22
|
+
"observability",
|
|
23
|
+
"agent-toolkit",
|
|
24
|
+
"openclaw",
|
|
25
|
+
"dashclaw",
|
|
26
|
+
"action-recording",
|
|
27
|
+
"context-management",
|
|
28
|
+
"security-scanning"
|
|
29
|
+
],
|
|
30
|
+
"author": "DashClaw",
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "git+https://github.com/ucsandman/DashClaw.git",
|
|
35
|
+
"directory": "sdk"
|
|
36
|
+
},
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=18.0.0"
|
|
39
|
+
}
|
|
40
|
+
}
|