@dana4/sdk 0.1.0 → 0.6.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/README.md +5 -3
- package/dist/agent.d.ts +42 -4
- package/dist/agent.js +86 -21
- package/dist/ai.d.ts +7 -6
- package/dist/ai.js +175 -38
- package/dist/cli.js +11 -2
- package/dist/client.d.ts +57 -0
- package/dist/client.js +43 -10
- package/dist/cloudflare.js +10 -3
- package/dist/enroll.js +13 -4
- package/dist/run.js +7 -2
- package/dist/schema.d.ts +3748 -3503
- package/package.json +14 -3
package/README.md
CHANGED
|
@@ -147,6 +147,7 @@ Every handler gets a `Context` that is already bound to its workspace, channel a
|
|
|
147
147
|
| `ctx.docs.append(path, content)` | Append to a document. |
|
|
148
148
|
| `ctx.docs.edit(path, old, new, replaceAll?)` | Exact-string replace; returns the number of replacements. |
|
|
149
149
|
| `ctx.plan.set / add / complete` | Manage the channel's plan, a markdown checklist. |
|
|
150
|
+
| `ctx.memory.set(memory)` | Replace the channel's memory, markdown notes shared by its agents and people. |
|
|
150
151
|
| `ctx.assets.upload(data, { name, ext })` | Store a file. Put the returned `url` (`dana4-asset://…`) in content. |
|
|
151
152
|
| `ctx.progress(fraction, message?)` | Report progress (0–1) to people watching the task. |
|
|
152
153
|
| `ctx.cost({ usd?, credits?, detail? })` | Record spend, e.g. model usage. It adds up and is charged once, when the task ends. |
|
|
@@ -175,7 +176,7 @@ export const agent = defineAgent({
|
|
|
175
176
|
onChat: async ({ message, thread }, ctx) => {
|
|
176
177
|
const recent = (await ctx.client.chat.messages(message.channel_id)).messages;
|
|
177
178
|
const result = await generateText({
|
|
178
|
-
model: anthropic("claude-
|
|
179
|
+
model: anthropic("claude-haiku-5-5"),
|
|
179
180
|
system: threadInstructions("You are a helpful assistant in a Dana4 workspace.", thread),
|
|
180
181
|
messages: [
|
|
181
182
|
...chatHistory(recent, { me: ctx.me, exclude: message.id }),
|
|
@@ -190,9 +191,10 @@ export const agent = defineAgent({
|
|
|
190
191
|
});
|
|
191
192
|
```
|
|
192
193
|
|
|
194
|
+
- `ctx.tickets.create({ title, description?, status?, blocked_by? })` (and the `create_ticket` tool) puts a ticket on the board; tickets are referred to by their per-workspace number, so `blocked_by: [12]` means #12.
|
|
193
195
|
- `dana4Tools(ctx, { exclude, delegation })`: drop individual tools with `exclude: ["send_message", …]`, or turn off the per-capability delegation tools with `delegation: false`.
|
|
194
196
|
- `chatHistory(messages, { me, exclude })` converts a thread into model messages, oldest first. Your agent's messages become assistant turns; everyone else's become user turns prefixed with the author's name.
|
|
195
|
-
- `threadInstructions(base, thread)` appends the thread's topics, intent and
|
|
197
|
+
- `threadInstructions(base, thread)` appends the thread's topics, intent, plan and memory to your system prompt.
|
|
196
198
|
|
|
197
199
|
## Running your agent
|
|
198
200
|
|
|
@@ -250,7 +252,7 @@ Set the key with `wrangler secret put DANA4_API_KEY`. Set `DANA4_URL` only if yo
|
|
|
250
252
|
|
|
251
253
|
## The low-level client
|
|
252
254
|
|
|
253
|
-
`Dana4Client` wraps every endpoint of the Dana4 SDK API, grouped by resource: `agents`, `tasks`, `docs`, `chat`, `workspaces`, `automations`, `workflows` and `assets`. Use it for scripts, or for integrations that aren't long-running agents.
|
|
255
|
+
`Dana4Client` wraps every endpoint of the Dana4 SDK API, grouped by resource: `agents`, `tasks`, `docs`, `chat`, `workspaces`, `tickets`, `automations`, `workflows` and `assets`. Use it for scripts, or for integrations that aren't long-running agents.
|
|
254
256
|
|
|
255
257
|
```ts
|
|
256
258
|
import { Dana4Client } from "@dana4/sdk";
|
package/dist/agent.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import type { ChatThread, Dana4Client, Document, DocumentType, Json, JsonObject, Message, MessageCategory, PresenceActivity, TakenTask, UpdateTaskPayload } from "./client.js";
|
|
2
|
+
import type { ChatThread, CreateTicketBody, Dana4Client, Document, DocumentType, Json, JsonObject, Message, MessageCategory, PresenceActivity, TakenTask, UpdateTaskPayload } from "./client.js";
|
|
3
3
|
/** A unit of work exposed to Dana4: workflows assign it, chat agents call it.
|
|
4
4
|
* Return the step's result (any JSON) to complete the task; throw to fail it. */
|
|
5
5
|
export type Capability<I extends z.ZodObject = z.ZodObject, R = unknown> = {
|
|
@@ -56,6 +56,8 @@ export type Logger = Pick<Console, "info" | "warn" | "error">;
|
|
|
56
56
|
/** The JSON schema advertised for a capability. The server only forwards the
|
|
57
57
|
* payload keys a schema declares, so the base fields must be declared too. */
|
|
58
58
|
export declare function capabilitySchema(key: string, cap: Capability): JsonObject;
|
|
59
|
+
/** Usernames @-mentioned in `body`, lowercased; the server's `extract_tagged_usernames`. */
|
|
60
|
+
export declare const taggedUsernames: (body: string) => string[];
|
|
59
61
|
/** Everything a handler needs, bound to its workspace, channel and task, so ids
|
|
60
62
|
* never have to be threaded through calls. */
|
|
61
63
|
export declare class Context {
|
|
@@ -88,6 +90,16 @@ export declare class Context {
|
|
|
88
90
|
}): Promise<Message>;
|
|
89
91
|
/** Workspace params (API keys, feature flags, …) set in workspace settings. */
|
|
90
92
|
params(): Promise<JsonObject>;
|
|
93
|
+
readonly tickets: {
|
|
94
|
+
/** Put a ticket on this workspace's board; `blocked_by` takes ticket numbers. */
|
|
95
|
+
create: (ticket: Omit<CreateTicketBody, "workspace_id">) => Promise<{
|
|
96
|
+
blocked_by: number[];
|
|
97
|
+
number: number;
|
|
98
|
+
status: import("./schema.js").components["schemas"]["BoardStatus"];
|
|
99
|
+
title: string;
|
|
100
|
+
workspace_id: string;
|
|
101
|
+
}>;
|
|
102
|
+
};
|
|
91
103
|
readonly docs: {
|
|
92
104
|
/** Read by path; shows this agent as `activity` on the document. */
|
|
93
105
|
get: (path: string, activity?: PresenceActivity) => Promise<Document>;
|
|
@@ -143,9 +155,11 @@ export declare class Context {
|
|
|
143
155
|
direct?: boolean;
|
|
144
156
|
document_id?: string | null;
|
|
145
157
|
intent?: string | null;
|
|
158
|
+
memory?: string | null;
|
|
146
159
|
messages: import("./schema.js").components["schemas"]["Message"][];
|
|
147
160
|
parent_channel_id?: string | null;
|
|
148
161
|
plan?: string | null;
|
|
162
|
+
tickets?: import("./schema.js").components["schemas"]["TicketSummary"][];
|
|
149
163
|
topics: import("./schema.js").components["schemas"]["TopicSummary"][];
|
|
150
164
|
}[]>;
|
|
151
165
|
create: (doc: {
|
|
@@ -166,6 +180,7 @@ export declare class Context {
|
|
|
166
180
|
document_id: string;
|
|
167
181
|
id: string;
|
|
168
182
|
intent?: string | null;
|
|
183
|
+
memory?: string | null;
|
|
169
184
|
owners: string[];
|
|
170
185
|
parent_channel_id?: string | null;
|
|
171
186
|
parent_message_id?: string | null;
|
|
@@ -180,6 +195,7 @@ export declare class Context {
|
|
|
180
195
|
document_id: string;
|
|
181
196
|
id: string;
|
|
182
197
|
intent?: string | null;
|
|
198
|
+
memory?: string | null;
|
|
183
199
|
owners: string[];
|
|
184
200
|
parent_channel_id?: string | null;
|
|
185
201
|
parent_message_id?: string | null;
|
|
@@ -194,6 +210,25 @@ export declare class Context {
|
|
|
194
210
|
document_id: string;
|
|
195
211
|
id: string;
|
|
196
212
|
intent?: string | null;
|
|
213
|
+
memory?: string | null;
|
|
214
|
+
owners: string[];
|
|
215
|
+
parent_channel_id?: string | null;
|
|
216
|
+
parent_message_id?: string | null;
|
|
217
|
+
plan?: string | null;
|
|
218
|
+
plan_completed: boolean;
|
|
219
|
+
private: boolean;
|
|
220
|
+
time: import("./schema.js").components["schemas"]["Timestamps"];
|
|
221
|
+
title: string;
|
|
222
|
+
workspace_id: string;
|
|
223
|
+
}>;
|
|
224
|
+
};
|
|
225
|
+
/** The channel's memory: markdown notes shared by its agents and people. Answers the updated channel. */
|
|
226
|
+
readonly memory: {
|
|
227
|
+
set: (memory: string, channelId?: string | null) => Promise<{
|
|
228
|
+
document_id: string;
|
|
229
|
+
id: string;
|
|
230
|
+
intent?: string | null;
|
|
231
|
+
memory?: string | null;
|
|
197
232
|
owners: string[];
|
|
198
233
|
parent_channel_id?: string | null;
|
|
199
234
|
parent_message_id?: string | null;
|
|
@@ -219,18 +254,21 @@ export declare class Context {
|
|
|
219
254
|
/** Report progress (0–1) and optionally a message people see in the channel. */
|
|
220
255
|
progress(progress: number, message?: string): Promise<void>;
|
|
221
256
|
/** Record spend. It accumulates and is charged once, when the task ends. */
|
|
222
|
-
cost({ usd, credits, detail }: {
|
|
257
|
+
cost({ usd, credits, detail, }: {
|
|
223
258
|
usd?: number;
|
|
224
259
|
credits?: number;
|
|
225
260
|
detail?: JsonObject;
|
|
226
261
|
}): void;
|
|
262
|
+
/** Record tools the LLM called, for debugging. Saved on the task when it
|
|
263
|
+
* ends. By convention `{ name, args }`, plus `error` when one failed. */
|
|
264
|
+
toolCalls(...calls: JsonObject[]): void;
|
|
227
265
|
/** Queue another agent's capability. It runs asynchronously and posts its
|
|
228
266
|
* result back here, @-mentioning this agent. Answers the new task's id. */
|
|
229
267
|
delegate(agent: string, capability: string, inputs?: JsonObject): Promise<string>;
|
|
230
268
|
/** The result of an earlier step in this task's journey, or null if not ready. */
|
|
231
269
|
stepResult(stepId: string): Promise<Json>;
|
|
232
|
-
/** @internal The accumulated cost, in `PATCH /tasks/{id}` shape. */
|
|
233
|
-
settle(): Pick<UpdateTaskPayload, "cost_usd" | "credits_cost" | "cost_details">;
|
|
270
|
+
/** @internal The accumulated cost and tool calls, in `PATCH /tasks/{id}` shape. */
|
|
271
|
+
settle(): Pick<UpdateTaskPayload, "cost_usd" | "credits_cost" | "cost_details" | "tool_calls">;
|
|
234
272
|
}
|
|
235
273
|
/** What `onJoin` gets: there is no task yet, so no document writes. */
|
|
236
274
|
export type JoinContext = Pick<Context, "client" | "me" | "workspaceId" | "send" | "params">;
|
package/dist/agent.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { skippedFromHistory } from "./client.js";
|
|
2
3
|
/** Typed helper: infers `input` in `run` from the schema. */
|
|
3
4
|
export const capability = (c) => c;
|
|
4
5
|
export const defineAgent = (agent) => agent;
|
|
@@ -7,7 +8,9 @@ const BASE_FIELDS = ["workspace_id", "task_id", "channel_id", "tag_usernames"];
|
|
|
7
8
|
* payload keys a schema declares, so the base fields must be declared too. */
|
|
8
9
|
export function capabilitySchema(key, cap) {
|
|
9
10
|
// Input mode: a field with a default is optional to whoever calls it.
|
|
10
|
-
const { $schema: _, ...schema } = z.toJSONSchema(cap.input, {
|
|
11
|
+
const { $schema: _, ...schema } = z.toJSONSchema(cap.input, {
|
|
12
|
+
io: "input",
|
|
13
|
+
});
|
|
11
14
|
return {
|
|
12
15
|
...schema,
|
|
13
16
|
title: cap.title ?? key,
|
|
@@ -22,9 +25,9 @@ export function capabilitySchema(key, cap) {
|
|
|
22
25
|
required: [...(schema.required ?? []), "workspace_id", "task_id"],
|
|
23
26
|
};
|
|
24
27
|
}
|
|
25
|
-
const errorMessage = (e) =>
|
|
28
|
+
const errorMessage = (e) => e instanceof Error ? e.message : String(e);
|
|
26
29
|
/** Usernames @-mentioned in `body`, lowercased; the server's `extract_tagged_usernames`. */
|
|
27
|
-
const taggedUsernames = (body) => body
|
|
30
|
+
export const taggedUsernames = (body) => body
|
|
28
31
|
.split(/\s+/)
|
|
29
32
|
.map((w) => w.replace(/^[^\p{L}\p{N}@._-]+|[^\p{L}\p{N}@._-]+$/gu, ""))
|
|
30
33
|
.filter((w) => w.length > 1 && w.startsWith("@"))
|
|
@@ -43,6 +46,7 @@ export class Context {
|
|
|
43
46
|
#usd = 0;
|
|
44
47
|
#credits = 0;
|
|
45
48
|
#costDetails = [];
|
|
49
|
+
#toolCalls = [];
|
|
46
50
|
constructor(client,
|
|
47
51
|
/** This agent's username. */
|
|
48
52
|
me, workspaceId,
|
|
@@ -78,6 +82,10 @@ export class Context {
|
|
|
78
82
|
params() {
|
|
79
83
|
return this.client.workspaces.params(this.workspaceId);
|
|
80
84
|
}
|
|
85
|
+
tickets = {
|
|
86
|
+
/** Put a ticket on this workspace's board; `blocked_by` takes ticket numbers. */
|
|
87
|
+
create: (ticket) => this.client.tickets.create({ ...ticket, workspace_id: this.workspaceId }),
|
|
88
|
+
};
|
|
81
89
|
docs = {
|
|
82
90
|
/** Read by path; shows this agent as `activity` on the document. */
|
|
83
91
|
get: async (path, activity = "reading") => {
|
|
@@ -101,8 +109,13 @@ export class Context {
|
|
|
101
109
|
replace: (path, content) => this.client.docs.replace({ path, content, task_id: this.#task() }),
|
|
102
110
|
append: (path, content) => this.client.docs.append({ path, content, task_id: this.#task() }),
|
|
103
111
|
/** Exact-string replace, applied server-side. Answers the replacement count. */
|
|
104
|
-
edit: async (path, oldString, newString, replaceAll = false) => (await this.client.docs.edit({
|
|
105
|
-
|
|
112
|
+
edit: async (path, oldString, newString, replaceAll = false) => (await this.client.docs.edit({
|
|
113
|
+
path,
|
|
114
|
+
old_string: oldString,
|
|
115
|
+
new_string: newString,
|
|
116
|
+
replace_all: replaceAll,
|
|
117
|
+
task_id: this.#task(),
|
|
118
|
+
})).replacements,
|
|
106
119
|
};
|
|
107
120
|
/** The channel's plan: a markdown checklist. Each call answers the updated channel. */
|
|
108
121
|
plan = {
|
|
@@ -110,6 +123,10 @@ export class Context {
|
|
|
110
123
|
complete: (item, channelId = this.channelId) => this.client.chat.completePlanItem(this.#channel(channelId), item),
|
|
111
124
|
add: (item, channelId = this.channelId) => this.client.chat.addPlanItem(this.#channel(channelId), item),
|
|
112
125
|
};
|
|
126
|
+
/** The channel's memory: markdown notes shared by its agents and people. Answers the updated channel. */
|
|
127
|
+
memory = {
|
|
128
|
+
set: (memory, channelId = this.channelId) => this.client.chat.setMemory(this.#channel(channelId), memory),
|
|
129
|
+
};
|
|
113
130
|
#channel(channelId) {
|
|
114
131
|
if (!channelId)
|
|
115
132
|
throw new Error("No channel: pass channelId explicitly");
|
|
@@ -121,15 +138,24 @@ export class Context {
|
|
|
121
138
|
};
|
|
122
139
|
/** Report progress (0–1) and optionally a message people see in the channel. */
|
|
123
140
|
async progress(progress, message) {
|
|
124
|
-
await this.client.tasks.update(this.#task(), {
|
|
141
|
+
await this.client.tasks.update(this.#task(), {
|
|
142
|
+
status: "running",
|
|
143
|
+
progress,
|
|
144
|
+
message,
|
|
145
|
+
});
|
|
125
146
|
}
|
|
126
147
|
/** Record spend. It accumulates and is charged once, when the task ends. */
|
|
127
|
-
cost({ usd = 0, credits = 0, detail }) {
|
|
148
|
+
cost({ usd = 0, credits = 0, detail, }) {
|
|
128
149
|
this.#usd += usd;
|
|
129
150
|
this.#credits += credits;
|
|
130
151
|
if (detail)
|
|
131
152
|
this.#costDetails.push(detail);
|
|
132
153
|
}
|
|
154
|
+
/** Record tools the LLM called, for debugging. Saved on the task when it
|
|
155
|
+
* ends. By convention `{ name, args }`, plus `error` when one failed. */
|
|
156
|
+
toolCalls(...calls) {
|
|
157
|
+
this.#toolCalls.push(...calls);
|
|
158
|
+
}
|
|
133
159
|
/** Queue another agent's capability. It runs asynchronously and posts its
|
|
134
160
|
* result back here, @-mentioning this agent. Answers the new task's id. */
|
|
135
161
|
delegate(agent, capability, inputs = {}) {
|
|
@@ -138,19 +164,24 @@ export class Context {
|
|
|
138
164
|
task_id: this.#task(),
|
|
139
165
|
channel_id: this.channelId,
|
|
140
166
|
inputs,
|
|
141
|
-
metadata: {
|
|
167
|
+
metadata: {
|
|
168
|
+
triggered_by: this.me,
|
|
169
|
+
triggered_from: "chat",
|
|
170
|
+
tag_usernames: [this.me],
|
|
171
|
+
},
|
|
142
172
|
});
|
|
143
173
|
}
|
|
144
174
|
/** The result of an earlier step in this task's journey, or null if not ready. */
|
|
145
175
|
stepResult(stepId) {
|
|
146
176
|
return this.client.tasks.stepResult(this.#task(), stepId);
|
|
147
177
|
}
|
|
148
|
-
/** @internal The accumulated cost, in `PATCH /tasks/{id}` shape. */
|
|
178
|
+
/** @internal The accumulated cost and tool calls, in `PATCH /tasks/{id}` shape. */
|
|
149
179
|
settle() {
|
|
150
180
|
return {
|
|
151
181
|
cost_usd: this.#usd || undefined,
|
|
152
182
|
credits_cost: this.#credits || undefined,
|
|
153
183
|
cost_details: this.#costDetails.length ? this.#costDetails : undefined,
|
|
184
|
+
tool_calls: this.#toolCalls.length ? this.#toolCalls : undefined,
|
|
154
185
|
};
|
|
155
186
|
}
|
|
156
187
|
}
|
|
@@ -177,7 +208,10 @@ export class AgentRuntime {
|
|
|
177
208
|
version,
|
|
178
209
|
capabilities: Object.fromEntries(Object.entries(capabilities).map(([key, cap]) => [
|
|
179
210
|
key,
|
|
180
|
-
{
|
|
211
|
+
{
|
|
212
|
+
input_schema: capabilitySchema(key, cap),
|
|
213
|
+
show_in_chat: cap.showInChat ?? true,
|
|
214
|
+
},
|
|
181
215
|
])),
|
|
182
216
|
},
|
|
183
217
|
});
|
|
@@ -192,7 +226,11 @@ export class AgentRuntime {
|
|
|
192
226
|
/** Put a task interrupted by a crash back in the queue. Never throws. */
|
|
193
227
|
async requeue(taskId) {
|
|
194
228
|
await this.client.tasks
|
|
195
|
-
.update(taskId, {
|
|
229
|
+
.update(taskId, {
|
|
230
|
+
status: "retry",
|
|
231
|
+
progress: 0,
|
|
232
|
+
message: "Agent restarted; re-queuing task",
|
|
233
|
+
})
|
|
196
234
|
.catch((e) => this.log.error("dana4: re-queuing failed:", errorMessage(e)));
|
|
197
235
|
}
|
|
198
236
|
/** Take the next task and chat thread. A taken task is acked (moved to
|
|
@@ -202,7 +240,10 @@ export class AgentRuntime {
|
|
|
202
240
|
try {
|
|
203
241
|
const taken = await this.client.tasks.take();
|
|
204
242
|
if (taken) {
|
|
205
|
-
await this.client.tasks.update(taken.task.id, {
|
|
243
|
+
await this.client.tasks.update(taken.task.id, {
|
|
244
|
+
status: "running",
|
|
245
|
+
progress: 0,
|
|
246
|
+
});
|
|
206
247
|
work.push({ kind: "task", taken });
|
|
207
248
|
}
|
|
208
249
|
}
|
|
@@ -223,14 +264,16 @@ export class AgentRuntime {
|
|
|
223
264
|
* messages (no `from`, e.g. a failed task's error) never count in a DM, or
|
|
224
265
|
* the agent would answer its own errors forever. */
|
|
225
266
|
addressed(thread) {
|
|
226
|
-
const theirs = thread.messages.filter((m) => m.from_username !== this.me);
|
|
267
|
+
const theirs = thread.messages.filter((m) => m.from_username !== this.me && !skippedFromHistory(m));
|
|
227
268
|
const last = theirs.filter((m) => m.from).at(-1);
|
|
228
269
|
if (thread.direct && last)
|
|
229
270
|
return [{ kind: "chat", message: last, thread }];
|
|
230
271
|
const me = this.me.toLowerCase();
|
|
231
272
|
return theirs
|
|
232
273
|
.filter((m) => taggedUsernames(m.message).includes(me))
|
|
233
|
-
.map((m) => m.category === "invited_to_workspace"
|
|
274
|
+
.map((m) => m.category === "invited_to_workspace"
|
|
275
|
+
? { kind: "join", workspaceId: m.workspace_id }
|
|
276
|
+
: { kind: "chat", message: m, thread });
|
|
234
277
|
}
|
|
235
278
|
/** Do one piece of work. Never throws: failures close the task as failed.
|
|
236
279
|
* `save` gets the work back whenever it gains state a crash must not lose (a
|
|
@@ -250,7 +293,11 @@ export class AgentRuntime {
|
|
|
250
293
|
}
|
|
251
294
|
}
|
|
252
295
|
async #runTask({ task, payload }) {
|
|
253
|
-
const fail = (message, ctx) => this.client.tasks.update(task.id, {
|
|
296
|
+
const fail = (message, ctx) => this.client.tasks.update(task.id, {
|
|
297
|
+
status: "failed",
|
|
298
|
+
message,
|
|
299
|
+
...ctx?.settle(),
|
|
300
|
+
});
|
|
254
301
|
const step = task.step_type;
|
|
255
302
|
const spec = typeof step === "object" && "agent" in step ? step.agent : null;
|
|
256
303
|
if (!spec || spec.name !== this.me)
|
|
@@ -262,12 +309,17 @@ export class AgentRuntime {
|
|
|
262
309
|
const parsed = cap.input.safeParse(own);
|
|
263
310
|
if (!parsed.success)
|
|
264
311
|
return void (await fail(`Invalid input for ${spec.capability}: ${z.prettifyError(parsed.error)}`));
|
|
265
|
-
const ctx = new Context(this.client, this.me, String(payload.workspace_id), typeof payload.channel_id === "string"
|
|
312
|
+
const ctx = new Context(this.client, this.me, String(payload.workspace_id), typeof payload.channel_id === "string"
|
|
313
|
+
? payload.channel_id
|
|
314
|
+
: task.channel_id, task.id, Array.isArray(payload.tag_usernames)
|
|
315
|
+
? payload.tag_usernames.map(String)
|
|
316
|
+
: []);
|
|
266
317
|
let result;
|
|
267
318
|
try {
|
|
268
319
|
result = await cap.run(parsed.data, ctx);
|
|
269
320
|
}
|
|
270
321
|
catch (e) {
|
|
322
|
+
this.log.error(`dana4: ${spec.capability} failed:`, e);
|
|
271
323
|
return void (await fail(errorMessage(e), ctx));
|
|
272
324
|
}
|
|
273
325
|
await this.client.tasks.update(task.id, {
|
|
@@ -284,21 +336,34 @@ export class AgentRuntime {
|
|
|
284
336
|
const { message, thread } = work;
|
|
285
337
|
let taskId = work.taskId;
|
|
286
338
|
if (!taskId) {
|
|
287
|
-
taskId = await this.client.tasks.start({
|
|
339
|
+
taskId = await this.client.tasks.start({
|
|
340
|
+
step_name: "read_message",
|
|
341
|
+
workspace_id: message.workspace_id,
|
|
342
|
+
channel_id: message.channel_id,
|
|
343
|
+
});
|
|
288
344
|
save?.({ ...work, taskId });
|
|
289
345
|
}
|
|
290
346
|
const ctx = new Context(this.client, this.me, message.workspace_id, message.channel_id, taskId, [this.me]);
|
|
291
347
|
try {
|
|
292
348
|
const thinking = this.agent.thinking ?? "*Thinking...*";
|
|
293
349
|
if (thinking)
|
|
294
|
-
await ctx.send(thinking, { category: "
|
|
350
|
+
await ctx.send(thinking, { category: "skip_from_history" });
|
|
295
351
|
await this.agent.onChat({ message, thread }, ctx);
|
|
296
|
-
await this.client.tasks.update(taskId, {
|
|
352
|
+
await this.client.tasks.update(taskId, {
|
|
353
|
+
status: "completed",
|
|
354
|
+
progress: 1,
|
|
355
|
+
...ctx.settle(),
|
|
356
|
+
});
|
|
297
357
|
}
|
|
298
358
|
catch (e) {
|
|
299
359
|
// The server posts a failed task's message into its channel, so the
|
|
300
|
-
// person who asked sees it.
|
|
301
|
-
|
|
360
|
+
// person who asked sees it. Log it too, with the stack, for the host.
|
|
361
|
+
this.log.error("dana4: chat failed:", e);
|
|
362
|
+
await this.client.tasks.update(taskId, {
|
|
363
|
+
status: "failed",
|
|
364
|
+
message: errorMessage(e),
|
|
365
|
+
...ctx.settle(),
|
|
366
|
+
});
|
|
302
367
|
}
|
|
303
368
|
}
|
|
304
369
|
}
|
package/dist/ai.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** Glue for the Vercel AI SDK (`ai`): Dana4 tools ready for `generateText`,
|
|
2
2
|
* and chat threads turned into model messages. */
|
|
3
3
|
import { type ModelMessage, type ToolSet } from "ai";
|
|
4
|
-
import type
|
|
4
|
+
import { type Context } from "./agent.js";
|
|
5
5
|
import type { ChatThread, DocumentHeader, JsonObject, Message } from "./client.js";
|
|
6
6
|
/** `ls -R`-style listing: grouped per directory, folders first, titles aligned. */
|
|
7
7
|
export declare function formatDocumentList(documents: DocumentHeader[]): string;
|
|
@@ -16,7 +16,7 @@ export declare function insertAt(document: string, line: number, content: string
|
|
|
16
16
|
/** A capability's input schema as a tool schema: only primitive params (the
|
|
17
17
|
* platform resolves documents and the like), and never the ids it injects. */
|
|
18
18
|
export declare function capabilityToolSchema(schema: JsonObject | null | undefined): JsonObject;
|
|
19
|
-
export type Dana4ToolName = "list_documents" | "grep_document" | "search_documents" | "read_document" | "get_step_result" | "create_document" | "write_document" | "append_document" | "edit_document" | "insert_in_document" | "send_message" | "set_plan" | "complete_plan_item" | "add_plan_item";
|
|
19
|
+
export type Dana4ToolName = "list_documents" | "grep_document" | "search_documents" | "read_document" | "get_step_result" | "create_document" | "write_document" | "append_document" | "edit_document" | "insert_in_document" | "send_message" | "set_plan" | "complete_plan_item" | "add_plan_item" | "set_memory" | "create_ticket";
|
|
20
20
|
/** Tools for reading and writing the workspace's documents, messaging, and the
|
|
21
21
|
* thread's plan — plus, unless `delegation: false`, one `<agent>__<capability>`
|
|
22
22
|
* tool per capability other agents in the workspace expose. Tool errors go back
|
|
@@ -27,11 +27,12 @@ export declare function dana4Tools(ctx: Context, opts?: {
|
|
|
27
27
|
}): Promise<ToolSet>;
|
|
28
28
|
/** A thread's messages as model messages, oldest first: this agent's own
|
|
29
29
|
* messages become assistant turns, everyone else's user turns prefixed with
|
|
30
|
-
* the author. Only
|
|
30
|
+
* the author. Only conversation is kept; orchestrator notices and errors only
|
|
31
|
+
* when they @-tag the agent, and status lines never. */
|
|
31
32
|
export declare function chatHistory(messages: Message[], { me, exclude }: {
|
|
32
33
|
me: string;
|
|
33
34
|
exclude?: string;
|
|
34
35
|
}): ModelMessage[];
|
|
35
|
-
/** `base` followed by the thread's topics, intent and
|
|
36
|
-
* what the conversation is for. */
|
|
37
|
-
export declare function threadInstructions(base: string, thread: Pick<ChatThread, "topics" | "intent" | "plan">): string;
|
|
36
|
+
/** `base` followed by the thread's topics, the board, intent, plan and memory, so the
|
|
37
|
+
* model knows what the conversation is for. */
|
|
38
|
+
export declare function threadInstructions(base: string, thread: Pick<ChatThread, "topics" | "tickets" | "intent" | "plan" | "memory">): string;
|