@dana4/sdk 0.1.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.
@@ -0,0 +1,498 @@
1
+ import type { components } from "./schema.js";
2
+ type S = components["schemas"];
3
+ export type Agent = S["Agent"];
4
+ export type AgentDescription = S["AgentDescription"];
5
+ export type Automation = S["Automation"];
6
+ export type Channel = S["Channel"];
7
+ export type ChatThread = S["ChatThread"];
8
+ export type CreateAutomationBody = S["CreateAutomationBody"];
9
+ export type CreateWorkflowPayload = S["CreateWorkflowPayload"];
10
+ export type Document = S["Document"];
11
+ export type DocumentHeader = S["DocumentHeader"];
12
+ export type DocumentType = S["DocumentType"];
13
+ export type EnrollAgentResponse = S["EnrollAgentResponse"];
14
+ export type Message = S["Message"];
15
+ export type MessageCategory = S["MessageCategory"];
16
+ export type Pipe = S["Pipe"];
17
+ export type PresenceActivity = S["PresenceActivity"];
18
+ export type SearchResult = S["SearchResult"];
19
+ export type Task = S["Task"];
20
+ export type TaskStatus = S["TaskStatus"];
21
+ /** `cost_details` is optional on the wire; utoipa marks it required. */
22
+ export type UpdateTaskPayload = Omit<S["UpdateTaskPayload"], "cost_details"> & {
23
+ cost_details?: JsonObject[] | null;
24
+ };
25
+ export type Workflow = S["Workflow"];
26
+ export type WorkflowRef = S["WorkflowRef"];
27
+ export type WorkspaceRef = S["WorkspaceRef"];
28
+ export type Json = string | number | boolean | null | Json[] | {
29
+ [key: string]: Json;
30
+ };
31
+ export type JsonObject = {
32
+ [key: string]: Json;
33
+ };
34
+ /** What `POST /tasks/take` hands out (the spec types it as a bare `Value`). */
35
+ export type TakenTask = {
36
+ task: Task;
37
+ payload: JsonObject;
38
+ };
39
+ export declare const DEFAULT_URL = "https://app.dana4.io";
40
+ /** A non-2xx answer from Dana4. `status` 401 means a revoked key or a workspace
41
+ * the agent is not a member of; 409 on a document edit means a stale read. */
42
+ export declare class Dana4Error extends Error {
43
+ readonly status: number;
44
+ readonly body: string;
45
+ constructor(status: number, body: string, what: string);
46
+ }
47
+ export type ClientOptions = {
48
+ /** The agent's key (`d4a_…`). Get one with `npx @dana4/sdk enroll`. */
49
+ apiKey: string;
50
+ /** Dana4 host, e.g. `http://localhost:8000`. Defaults to production. */
51
+ url?: string;
52
+ fetch?: typeof fetch;
53
+ };
54
+ type Query = Record<string, string | number | boolean | Date | null | undefined>;
55
+ /** Typed access to every `/api-sdk/v1` endpoint, grouped by resource. Every
56
+ * method throws `Dana4Error` on failure; none returns a silent `false`. */
57
+ export declare class Dana4Client {
58
+ #private;
59
+ readonly url: string;
60
+ constructor({ apiKey, url, fetch: f }: ClientOptions);
61
+ /** Raw call, for anything the grouped methods don't cover. JSON responses are
62
+ * parsed; text ones (ids, paths) come back as strings. */
63
+ request<T>(method: string, path: string, opts?: {
64
+ body?: unknown;
65
+ query?: Query;
66
+ }): Promise<T>;
67
+ readonly agents: {
68
+ /** Advertise the schema/bio/description; answers who this key belongs to. */
69
+ updateMe: (body: S["UpdateAgentPayload"]) => Promise<{
70
+ bio?: string | null;
71
+ description?: string | null;
72
+ last_seen_at?: string | null;
73
+ schema?: null | components["schemas"]["AgentSchema"];
74
+ status: components["schemas"]["AgentStatus"];
75
+ user_id: string;
76
+ username: string;
77
+ }>;
78
+ /** Queue another agent's capability; answers the new task's id. */
79
+ execute: (username: string, body: {
80
+ tool_key: string;
81
+ task_id: string;
82
+ channel_id?: string | null;
83
+ inputs?: JsonObject;
84
+ metadata?: JsonObject;
85
+ }) => Promise<string>;
86
+ };
87
+ readonly tasks: {
88
+ take: () => Promise<TakenTask | null>;
89
+ active: () => Promise<{
90
+ channel_id: string;
91
+ cost_detail?: {
92
+ [key: string]: unknown;
93
+ }[];
94
+ cost_usd?: number | null;
95
+ execution_duration?: number | null;
96
+ id: string;
97
+ journey_id: string;
98
+ order: number;
99
+ params?: {
100
+ [key: string]: unknown;
101
+ };
102
+ progress: number;
103
+ result?: null | components["schemas"]["JourneyStepResult"];
104
+ started_at?: number | null;
105
+ status: components["schemas"]["TaskStatus"];
106
+ step_id: string;
107
+ step_type: components["schemas"]["JourneyStepType"];
108
+ time: components["schemas"]["Timestamps"];
109
+ }[]>;
110
+ assigned: () => Promise<{
111
+ channel_id: string;
112
+ cost_detail?: {
113
+ [key: string]: unknown;
114
+ }[];
115
+ cost_usd?: number | null;
116
+ execution_duration?: number | null;
117
+ id: string;
118
+ journey_id: string;
119
+ order: number;
120
+ params?: {
121
+ [key: string]: unknown;
122
+ };
123
+ progress: number;
124
+ result?: null | components["schemas"]["JourneyStepResult"];
125
+ started_at?: number | null;
126
+ status: components["schemas"]["TaskStatus"];
127
+ step_id: string;
128
+ step_type: components["schemas"]["JourneyStepType"];
129
+ time: components["schemas"]["Timestamps"];
130
+ }[]>;
131
+ unassigned: () => Promise<{
132
+ channel_id: string;
133
+ cost_detail?: {
134
+ [key: string]: unknown;
135
+ }[];
136
+ cost_usd?: number | null;
137
+ execution_duration?: number | null;
138
+ id: string;
139
+ journey_id: string;
140
+ order: number;
141
+ params?: {
142
+ [key: string]: unknown;
143
+ };
144
+ progress: number;
145
+ result?: null | components["schemas"]["JourneyStepResult"];
146
+ started_at?: number | null;
147
+ status: components["schemas"]["TaskStatus"];
148
+ step_id: string;
149
+ step_type: components["schemas"]["JourneyStepType"];
150
+ time: components["schemas"]["Timestamps"];
151
+ }[]>;
152
+ claim: (taskId: string, capability: string) => Promise<{
153
+ channel_id: string;
154
+ cost_detail?: {
155
+ [key: string]: unknown;
156
+ }[];
157
+ cost_usd?: number | null;
158
+ execution_duration?: number | null;
159
+ id: string;
160
+ journey_id: string;
161
+ order: number;
162
+ params?: {
163
+ [key: string]: unknown;
164
+ };
165
+ progress: number;
166
+ result?: null | components["schemas"]["JourneyStepResult"];
167
+ started_at?: number | null;
168
+ status: components["schemas"]["TaskStatus"];
169
+ step_id: string;
170
+ step_type: components["schemas"]["JourneyStepType"];
171
+ time: components["schemas"]["Timestamps"];
172
+ }>;
173
+ /** Start a one-step journey (e.g. `read_message`); answers its task id. */
174
+ start: (body: {
175
+ step_name: string;
176
+ workspace_id: string;
177
+ channel_id: string;
178
+ params?: JsonObject | null;
179
+ }) => Promise<string>;
180
+ update: (taskId: string, body: UpdateTaskPayload) => Promise<string>;
181
+ /** Workspace params merged with the journey's params. */
182
+ params: (taskId: string) => Promise<JsonObject>;
183
+ stepResult: (taskId: string, stepId: string) => Promise<Json>;
184
+ };
185
+ readonly docs: {
186
+ list: (workspaceId: string, opts?: {
187
+ path?: string;
188
+ recursive?: boolean;
189
+ }) => Promise<{
190
+ chunk_of?: string | null;
191
+ document_type?: components["schemas"]["DocumentType"];
192
+ id: string;
193
+ key: string;
194
+ order?: number | null;
195
+ parent_id?: string | null;
196
+ path: string;
197
+ state?: components["schemas"]["DocumentState"];
198
+ time: components["schemas"]["Timestamps"];
199
+ title: string;
200
+ workspace_id: string;
201
+ }[]>;
202
+ getByPath: (workspaceId: string, path: string) => Promise<{
203
+ chunk_of?: string | null;
204
+ content: string;
205
+ document_type?: components["schemas"]["DocumentType"];
206
+ hash: string;
207
+ id: string;
208
+ key: string;
209
+ metadata?: {
210
+ [key: string]: unknown;
211
+ };
212
+ order?: number | null;
213
+ origin_channel_id?: string | null;
214
+ parent_id?: string | null;
215
+ path: string;
216
+ public?: boolean;
217
+ source_asset_id?: string | null;
218
+ state?: components["schemas"]["DocumentState"];
219
+ time: components["schemas"]["Timestamps"];
220
+ title: string;
221
+ workspace_id: string;
222
+ }>;
223
+ get: (documentId: string) => Promise<{
224
+ chunk_of?: string | null;
225
+ content: string;
226
+ document_type?: components["schemas"]["DocumentType"];
227
+ hash: string;
228
+ id: string;
229
+ key: string;
230
+ metadata?: {
231
+ [key: string]: unknown;
232
+ };
233
+ order?: number | null;
234
+ origin_channel_id?: string | null;
235
+ parent_id?: string | null;
236
+ path: string;
237
+ public?: boolean;
238
+ source_asset_id?: string | null;
239
+ state?: components["schemas"]["DocumentState"];
240
+ time: components["schemas"]["Timestamps"];
241
+ title: string;
242
+ workspace_id: string;
243
+ }>;
244
+ byChannel: (channelId: string) => Promise<{
245
+ chunk_of?: string | null;
246
+ content: string;
247
+ document_type?: components["schemas"]["DocumentType"];
248
+ hash: string;
249
+ id: string;
250
+ key: string;
251
+ metadata?: {
252
+ [key: string]: unknown;
253
+ };
254
+ order?: number | null;
255
+ origin_channel_id?: string | null;
256
+ parent_id?: string | null;
257
+ path: string;
258
+ public?: boolean;
259
+ source_asset_id?: string | null;
260
+ state?: components["schemas"]["DocumentState"];
261
+ time: components["schemas"]["Timestamps"];
262
+ title: string;
263
+ workspace_id: string;
264
+ }>;
265
+ source: (documentId: string) => Promise<{
266
+ asset_id: string;
267
+ s3_key: string;
268
+ url: string;
269
+ }>;
270
+ search: (workspaceId: string, q: string, limit?: number) => Promise<{
271
+ distance?: number | null;
272
+ document_type?: components["schemas"]["DocumentType"];
273
+ ft_score?: number | null;
274
+ id: string;
275
+ order?: number | null;
276
+ path: string;
277
+ score: number;
278
+ snippet: string;
279
+ time: components["schemas"]["Timestamps"];
280
+ title: string;
281
+ workspace_id: string;
282
+ }[]>;
283
+ /** Every thread on a document; the root discussion has no `parent_channel_id`. */
284
+ messages: (documentId: string, range?: {
285
+ since?: Date;
286
+ until?: Date;
287
+ }) => Promise<{
288
+ channel_id: string;
289
+ direct?: boolean;
290
+ document_id?: string | null;
291
+ intent?: string | null;
292
+ messages: components["schemas"]["Message"][];
293
+ parent_channel_id?: string | null;
294
+ plan?: string | null;
295
+ topics: components["schemas"]["TopicSummary"][];
296
+ }[]>;
297
+ create: (body: S["CreateDocumentPayload"]) => Promise<string>;
298
+ replace: (body: S["UpdateDocumentPayload"]) => Promise<string>;
299
+ append: (body: S["AppendDocumentPayload"]) => Promise<string>;
300
+ edit: (body: S["EditDocumentPayload"]) => Promise<{
301
+ path: string;
302
+ replacements: number;
303
+ }>;
304
+ presence: (documentId: string, activity: PresenceActivity) => Promise<string>;
305
+ };
306
+ readonly chat: {
307
+ /** The next channel with unread messages for this agent. Marks them read:
308
+ * delivery is at-most-once. `channel_id` is "" when there is nothing. */
309
+ take: () => Promise<{
310
+ channel_id: string;
311
+ direct?: boolean;
312
+ document_id?: string | null;
313
+ intent?: string | null;
314
+ messages: components["schemas"]["Message"][];
315
+ parent_channel_id?: string | null;
316
+ plan?: string | null;
317
+ topics: components["schemas"]["TopicSummary"][];
318
+ }>;
319
+ messages: (channelId: string, range?: {
320
+ since?: Date;
321
+ until?: Date;
322
+ }) => Promise<{
323
+ channel_id: string;
324
+ direct?: boolean;
325
+ document_id?: string | null;
326
+ intent?: string | null;
327
+ messages: components["schemas"]["Message"][];
328
+ parent_channel_id?: string | null;
329
+ plan?: string | null;
330
+ topics: components["schemas"]["TopicSummary"][];
331
+ }>;
332
+ send: (body: S["SendMessagePayload"]) => Promise<{
333
+ category: components["schemas"]["MessageCategory"];
334
+ channel_id: string;
335
+ from?: string | null;
336
+ from_username: string;
337
+ id: string;
338
+ message: string;
339
+ time: components["schemas"]["Timestamps"];
340
+ workspace_id: string;
341
+ }>;
342
+ privateChannels: (workspaceId: string) => Promise<{
343
+ document_id: string;
344
+ id: string;
345
+ intent?: string | null;
346
+ owners: string[];
347
+ parent_channel_id?: string | null;
348
+ parent_message_id?: string | null;
349
+ plan?: string | null;
350
+ plan_completed: boolean;
351
+ private: boolean;
352
+ time: components["schemas"]["Timestamps"];
353
+ title: string;
354
+ workspace_id: string;
355
+ }[]>;
356
+ setPlan: (channelId: string, plan: string) => Promise<{
357
+ document_id: string;
358
+ id: string;
359
+ intent?: string | null;
360
+ owners: string[];
361
+ parent_channel_id?: string | null;
362
+ parent_message_id?: string | null;
363
+ plan?: string | null;
364
+ plan_completed: boolean;
365
+ private: boolean;
366
+ time: components["schemas"]["Timestamps"];
367
+ title: string;
368
+ workspace_id: string;
369
+ }>;
370
+ completePlanItem: (channelId: string, item: string) => Promise<{
371
+ document_id: string;
372
+ id: string;
373
+ intent?: string | null;
374
+ owners: string[];
375
+ parent_channel_id?: string | null;
376
+ parent_message_id?: string | null;
377
+ plan?: string | null;
378
+ plan_completed: boolean;
379
+ private: boolean;
380
+ time: components["schemas"]["Timestamps"];
381
+ title: string;
382
+ workspace_id: string;
383
+ }>;
384
+ addPlanItem: (channelId: string, item: string) => Promise<{
385
+ document_id: string;
386
+ id: string;
387
+ intent?: string | null;
388
+ owners: string[];
389
+ parent_channel_id?: string | null;
390
+ parent_message_id?: string | null;
391
+ plan?: string | null;
392
+ plan_completed: boolean;
393
+ private: boolean;
394
+ time: components["schemas"]["Timestamps"];
395
+ title: string;
396
+ workspace_id: string;
397
+ }>;
398
+ };
399
+ readonly workspaces: {
400
+ list: () => Promise<{
401
+ id: string;
402
+ name: string;
403
+ }[]>;
404
+ params: (workspaceId: string) => Promise<JsonObject>;
405
+ blockers: (workspaceId: string) => Promise<{
406
+ always: boolean;
407
+ blocked_at?: string | null;
408
+ id: string;
409
+ output: components["schemas"]["Requirement"];
410
+ source?: null | components["schemas"]["Resource"];
411
+ task_id: string;
412
+ workspace_id: string;
413
+ }[]>;
414
+ /** Every agent in the workspace with its chat-visible capabilities. */
415
+ agents: (workspaceId: string) => Promise<{
416
+ capabilities: components["schemas"]["AgentCapability"][];
417
+ description?: string | null;
418
+ metadata?: Record<string, never> | null;
419
+ name: string;
420
+ username: string;
421
+ }[]>;
422
+ automations: (workspaceId: string) => Promise<{
423
+ created_by: string;
424
+ enabled: boolean;
425
+ id: string;
426
+ last_run_at?: string | null;
427
+ name: string;
428
+ next_run_at?: string | null;
429
+ params?: {
430
+ [key: string]: unknown;
431
+ };
432
+ run_count: number;
433
+ schedule_day?: number | null;
434
+ schedule_frequency: components["schemas"]["ScheduleFrequency"];
435
+ schedule_time: string;
436
+ schedule_timezone: string;
437
+ time: components["schemas"]["Timestamps"];
438
+ workflow_id?: string | null;
439
+ workspace_id: string;
440
+ yaml_definition?: string | null;
441
+ }[]>;
442
+ workflows: (workspaceId: string) => Promise<{
443
+ description: string;
444
+ id: string;
445
+ name: string;
446
+ workspace: string;
447
+ }[]>;
448
+ };
449
+ readonly automations: {
450
+ create: (body: CreateAutomationBody) => Promise<{
451
+ created_by: string;
452
+ enabled: boolean;
453
+ id: string;
454
+ last_run_at?: string | null;
455
+ name: string;
456
+ next_run_at?: string | null;
457
+ params?: {
458
+ [key: string]: unknown;
459
+ };
460
+ run_count: number;
461
+ schedule_day?: number | null;
462
+ schedule_frequency: components["schemas"]["ScheduleFrequency"];
463
+ schedule_time: string;
464
+ schedule_timezone: string;
465
+ time: components["schemas"]["Timestamps"];
466
+ workflow_id?: string | null;
467
+ workspace_id: string;
468
+ yaml_definition?: string | null;
469
+ }>;
470
+ delete: (automationId: string) => Promise<string>;
471
+ };
472
+ readonly workflows: {
473
+ create: (body: CreateWorkflowPayload) => Promise<{
474
+ definition: string;
475
+ description: string;
476
+ id: string;
477
+ name: string;
478
+ params: {
479
+ [key: string]: components["schemas"]["WorkflowParamDef"];
480
+ };
481
+ time: components["schemas"]["Timestamps"];
482
+ workspace: string;
483
+ }>;
484
+ };
485
+ readonly assets: {
486
+ /** Upload bytes to the workspace's bucket and register the asset. Reference
487
+ * it in content as `url` (`dana4-asset://…`); Dana4 signs it on read. */
488
+ upload: (taskId: string, data: BodyInit, { name, ext }: {
489
+ name: string;
490
+ ext: string;
491
+ }) => Promise<{
492
+ key: string;
493
+ assetId: string;
494
+ url: string;
495
+ }>;
496
+ };
497
+ }
498
+ export {};
package/dist/client.js ADDED
@@ -0,0 +1,124 @@
1
+ export const DEFAULT_URL = "https://app.dana4.io";
2
+ /** A non-2xx answer from Dana4. `status` 401 means a revoked key or a workspace
3
+ * the agent is not a member of; 409 on a document edit means a stale read. */
4
+ export class Dana4Error extends Error {
5
+ status;
6
+ body;
7
+ constructor(status, body, what) {
8
+ super(`${what} failed: ${status} ${body}`);
9
+ this.status = status;
10
+ this.body = body;
11
+ this.name = "Dana4Error";
12
+ }
13
+ }
14
+ /** Typed access to every `/api-sdk/v1` endpoint, grouped by resource. Every
15
+ * method throws `Dana4Error` on failure; none returns a silent `false`. */
16
+ export class Dana4Client {
17
+ url;
18
+ #apiKey;
19
+ #fetch;
20
+ constructor({ apiKey, url = DEFAULT_URL, fetch: f = fetch }) {
21
+ if (!apiKey)
22
+ throw new Error("No Dana4 API key. Get one with `npx @dana4/sdk enroll --username <name>`.");
23
+ this.url = url.replace(/\/+$/, "");
24
+ this.#apiKey = apiKey;
25
+ // Called detached: Workers' fetch throws "Illegal invocation" when `this` is the client.
26
+ this.#fetch = (input, init) => f(input, init);
27
+ }
28
+ /** Raw call, for anything the grouped methods don't cover. JSON responses are
29
+ * parsed; text ones (ids, paths) come back as strings. */
30
+ async request(method, path, opts = {}) {
31
+ const url = new URL(`${this.url}/api-sdk/v1${path}`);
32
+ for (const [k, v] of Object.entries(opts.query ?? {})) {
33
+ if (v != null)
34
+ url.searchParams.set(k, v instanceof Date ? v.toISOString() : String(v));
35
+ }
36
+ const res = await this.#fetch(url, {
37
+ method,
38
+ headers: {
39
+ Authorization: `Bearer ${this.#apiKey}`,
40
+ ...(opts.body !== undefined && { "Content-Type": "application/json" }),
41
+ },
42
+ body: opts.body === undefined ? undefined : JSON.stringify(opts.body),
43
+ });
44
+ if (!res.ok)
45
+ throw new Dana4Error(res.status, await res.text(), `${method} ${path}`);
46
+ const isJson = res.headers.get("content-type")?.includes("json");
47
+ return (isJson ? await res.json() : await res.text());
48
+ }
49
+ agents = {
50
+ /** Advertise the schema/bio/description; answers who this key belongs to. */
51
+ updateMe: (body) => this.request("PATCH", "/agents/me", { body }),
52
+ /** Queue another agent's capability; answers the new task's id. */
53
+ execute: (username, body) => this.request("POST", `/agents/${encodeURIComponent(username)}/execute`, { body }).then((r) => r.task_id),
54
+ };
55
+ tasks = {
56
+ take: () => this.request("POST", "/tasks/take"),
57
+ active: () => this.request("GET", "/tasks/active"),
58
+ assigned: () => this.request("GET", "/tasks/assigned"),
59
+ unassigned: () => this.request("GET", "/tasks/unassigned"),
60
+ claim: (taskId, capability) => this.request("POST", `/tasks/${taskId}/claim`, { body: { capability } }),
61
+ /** Start a one-step journey (e.g. `read_message`); answers its task id. */
62
+ start: (body) => this.request("POST", "/tasks", { body: { params: null, ...body } }),
63
+ update: (taskId, body) => this.request("PATCH", `/tasks/${taskId}`, { body }),
64
+ /** Workspace params merged with the journey's params. */
65
+ params: (taskId) => this.request("GET", `/tasks/${taskId}/params`),
66
+ stepResult: (taskId, stepId) => this.request("GET", `/tasks/${taskId}/steps/${stepId}/result`),
67
+ };
68
+ docs = {
69
+ list: (workspaceId, opts = {}) => this.request("GET", `/workspaces/${workspaceId}/documents`, { query: { path: "/", recursive: false, ...opts } }),
70
+ getByPath: (workspaceId, path) => this.request("GET", `/workspaces/${workspaceId}/documents/by-path`, { query: { path } }),
71
+ get: (documentId) => this.request("GET", `/documents/${documentId}`),
72
+ byChannel: (channelId) => this.request("GET", `/channels/${channelId}/document`),
73
+ source: (documentId) => this.request("GET", `/documents/${documentId}/source`),
74
+ search: (workspaceId, q, limit) => this.request("GET", `/workspaces/${workspaceId}/documents/search`, { query: { q, limit } }),
75
+ /** Every thread on a document; the root discussion has no `parent_channel_id`. */
76
+ messages: (documentId, range = {}) => this.request("GET", `/documents/${documentId}/messages`, { query: range }),
77
+ create: (body) => this.request("POST", "/documents", { body }),
78
+ replace: (body) => this.request("POST", "/documents/replace", { body }),
79
+ append: (body) => this.request("POST", "/documents/append", { body }),
80
+ edit: (body) => this.request("POST", "/documents/edit", { body }),
81
+ presence: (documentId, activity) => this.request("PUT", `/documents/${documentId}/presence`, { body: { activity } }),
82
+ };
83
+ chat = {
84
+ /** The next channel with unread messages for this agent. Marks them read:
85
+ * delivery is at-most-once. `channel_id` is "" when there is nothing. */
86
+ take: () => this.request("POST", "/chats/take"),
87
+ messages: (channelId, range = {}) => this.request("GET", `/channels/${channelId}/messages`, { query: range }),
88
+ send: (body) => this.request("POST", "/messages", { body }),
89
+ privateChannels: (workspaceId) => this.request("GET", `/workspaces/${workspaceId}/private-channels`),
90
+ setPlan: (channelId, plan) => this.request("PUT", `/channels/${channelId}/plan`, { body: { plan } }),
91
+ completePlanItem: (channelId, item) => this.request("POST", `/channels/${channelId}/plan/complete`, { body: { item } }),
92
+ addPlanItem: (channelId, item) => this.request("POST", `/channels/${channelId}/plan/items`, { body: { item } }),
93
+ };
94
+ workspaces = {
95
+ list: () => this.request("GET", "/workspaces"),
96
+ params: (workspaceId) => this.request("GET", `/workspaces/${workspaceId}/params`),
97
+ blockers: (workspaceId) => this.request("GET", `/workspaces/${workspaceId}/blockers`),
98
+ /** Every agent in the workspace with its chat-visible capabilities. */
99
+ agents: (workspaceId) => this.request("GET", `/workspaces/${workspaceId}/agents`),
100
+ automations: (workspaceId) => this.request("GET", `/workspaces/${workspaceId}/automations`),
101
+ workflows: (workspaceId) => this.request("GET", `/workspaces/${workspaceId}/workflows`),
102
+ };
103
+ automations = {
104
+ create: (body) => this.request("POST", "/automations", { body }),
105
+ delete: (automationId) => this.request("DELETE", `/automations/${automationId}`),
106
+ };
107
+ workflows = {
108
+ create: (body) => this.request("POST", "/workflows", { body }),
109
+ };
110
+ assets = {
111
+ /** Upload bytes to the workspace's bucket and register the asset. Reference
112
+ * it in content as `url` (`dana4-asset://…`); Dana4 signs it on read. */
113
+ upload: async (taskId, data, { name, ext }) => {
114
+ const { url, key } = await this.request("POST", "/uploads", {
115
+ body: { task_id: taskId, asset_name: name, file_ext: ext },
116
+ });
117
+ const put = await this.#fetch(url, { method: "PUT", body: data });
118
+ if (!put.ok)
119
+ throw new Dana4Error(put.status, await put.text(), "asset upload");
120
+ const assetId = await this.request("POST", "/assets", { body: { task_id: taskId, key } });
121
+ return { key, assetId, url: `dana4-asset://${key}` };
122
+ },
123
+ };
124
+ }
@@ -0,0 +1,39 @@
1
+ /** Run a Dana4 agent as a Cloudflare Agent (a Durable Object). Alarms drive the
2
+ * polling, and every piece of work runs in a durable fiber, so a chat taken
3
+ * from Dana4 survives an eviction or deploy mid-reply.
4
+ *
5
+ * ```ts
6
+ * export class MyAgent extends Dana4Agent<Env> {
7
+ * agent = myAgentDefinition;
8
+ * }
9
+ * export default {
10
+ * // Any call wakes the Durable Object, which then keeps polling by itself;
11
+ * // a cron trigger restarts it if it ever stops.
12
+ * scheduled: async (_c, env) => (await getAgentByName(env.MyAgent, "default")).ping(),
13
+ * };
14
+ * ```
15
+ * Secrets: `DANA4_API_KEY`, and `DANA4_URL` unless it's production. */
16
+ import { Agent, type FiberRecoveryContext } from "agents";
17
+ import { type AgentDefinition, AgentRuntime } from "./agent.js";
18
+ export type Dana4Env = {
19
+ DANA4_API_KEY: string;
20
+ DANA4_URL?: string;
21
+ };
22
+ export declare abstract class Dana4Agent<Env extends Dana4Env = Dana4Env, State = unknown> extends Agent<Env, State> {
23
+ #private;
24
+ abstract readonly agent: AgentDefinition;
25
+ /** Seconds between polls. */
26
+ pollInterval: number;
27
+ get runtime(): AgentRuntime;
28
+ onStart(): Promise<void>;
29
+ /** One round: take work, run each item in its own fiber. Overlapping rounds
30
+ * are skipped by the scheduler, and fibers aren't awaited, so a long task
31
+ * never holds up the next poll. */
32
+ poll(): Promise<void>;
33
+ /** Tasks go back to the queue. Chats and joins run again (the server already
34
+ * marked them read) in a new fiber: recovery hooks are short-lived, a chat
35
+ * isn't. A chat reuses the task it opened. */
36
+ onFiberRecovered(ctx: FiberRecoveryContext): Promise<void>;
37
+ /** Does nothing; calling it wakes the object (and so starts polling). */
38
+ ping(): Promise<void>;
39
+ }