@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/dist/ai.js CHANGED
@@ -2,6 +2,7 @@
2
2
  * and chat threads turned into model messages. */
3
3
  import { jsonSchema, tool } from "ai";
4
4
  import { z } from "zod";
5
+ import { taggedUsernames } from "./agent.js";
5
6
  const MAX_LINES_PER_READ = 500;
6
7
  const MAX_LINES_PER_WRITE = 400;
7
8
  /** `ls -R`-style listing: grouped per directory, folders first, titles aligned. */
@@ -18,10 +19,14 @@ export function formatDocumentList(documents) {
18
19
  .map((dir) => {
19
20
  const entries = groups
20
21
  .get(dir)
21
- .sort((a, b) => Number(isFolder(b)) - Number(isFolder(a)) || leaf(a).localeCompare(leaf(b)));
22
+ ?.sort((a, b) => Number(isFolder(b)) - Number(isFolder(a)) ||
23
+ leaf(a).localeCompare(leaf(b))) ?? [];
22
24
  const names = entries.map((d) => leaf(d) + (isFolder(d) ? "/" : ""));
23
25
  const width = Math.max(...names.map((n) => n.length));
24
- return [`${dir}:`, ...entries.map((d, i) => ` ${names[i].padEnd(width)} ${d.title}`)].join("\n");
26
+ return [
27
+ `${dir}:`,
28
+ ...entries.map((d, i) => ` ${names[i]?.padEnd(width)} ${d.title}`),
29
+ ].join("\n");
25
30
  })
26
31
  .join("\n\n");
27
32
  }
@@ -39,10 +44,13 @@ export function grep(content, pattern, context = 3) {
39
44
  const shown = new Set(hits.flatMap((i) => Array.from({ length: 2 * context + 1 }, (_, k) => i - context + k)));
40
45
  const out = [];
41
46
  let prev = -2;
42
- for (const i of [...shown].filter((i) => i >= 0 && i < lines.length).sort((a, b) => a - b)) {
47
+ for (const i of [...shown]
48
+ .filter((i) => i >= 0 && i < lines.length)
49
+ .sort((a, b) => a - b)) {
43
50
  if (i !== prev + 1 && out.length)
44
51
  out.push("---");
45
- out.push(`${regex.test(lines[i]) ? ">" : " "} ${i + 1}: ${lines[i]}`);
52
+ const line = lines[i] ?? "";
53
+ out.push(`${regex.test(line) ? ">" : " "} ${i + 1}: ${line}`);
46
54
  prev = i;
47
55
  }
48
56
  return { matches: hits.length, lines: lines.length, text: out.join("\n") };
@@ -52,7 +60,7 @@ export function insertAt(document, line, content) {
52
60
  const lines = document.match(/[^\n]*\n|[^\n]+$/g) ?? [];
53
61
  const at = Math.max(0, Math.min(line - 1, lines.length));
54
62
  // Appending after an unterminated last line: end it first, or the two join.
55
- if (at === lines.length && at > 0 && !lines[at - 1].endsWith("\n"))
63
+ if (at === lines.length && at > 0 && !lines[at - 1]?.endsWith("\n"))
56
64
  lines[at - 1] += "\n";
57
65
  lines.splice(at, 0, content.endsWith("\n") ? content : `${content}\n`);
58
66
  return lines.join("");
@@ -67,11 +75,21 @@ export function capabilityToolSchema(schema) {
67
75
  if (["workspace_id", "task_id"].includes(name))
68
76
  return false;
69
77
  const t = p?.type;
70
- return typeof t === "string" ? PRIMITIVE.has(t) : Array.isArray(t) && t.every((x) => typeof x === "string" && PRIMITIVE.has(x));
78
+ return typeof t === "string"
79
+ ? PRIMITIVE.has(t)
80
+ : Array.isArray(t) &&
81
+ t.every((x) => typeof x === "string" && PRIMITIVE.has(x));
71
82
  });
72
83
  const kept = new Set(keep.map(([n]) => n));
73
- const required = Array.isArray(schema?.required) ? schema.required.filter((r) => typeof r === "string" && kept.has(r)) : [];
74
- return { type: "object", additionalProperties: false, properties: Object.fromEntries(keep), required };
84
+ const required = Array.isArray(schema?.required)
85
+ ? schema.required.filter((r) => typeof r === "string" && kept.has(r))
86
+ : [];
87
+ return {
88
+ type: "object",
89
+ additionalProperties: false,
90
+ properties: Object.fromEntries(keep),
91
+ required,
92
+ };
75
93
  }
76
94
  /** Tools for reading and writing the workspace's documents, messaging, and the
77
95
  * thread's plan — plus, unless `delegation: false`, one `<agent>__<capability>`
@@ -80,9 +98,12 @@ export function capabilityToolSchema(schema) {
80
98
  export async function dana4Tools(ctx, opts = {}) {
81
99
  const tools = {
82
100
  list_documents: tool({
83
- description: "List documents in a folder with their paths and titles. Optionally recurse into subfolders.",
101
+ description: "List documents in a folder with their paths and titles. Optionally recurse into subfolders. To find documents about a topic, use search_documents instead: listing returns every document and costs far more tokens.",
84
102
  inputSchema: z.object({
85
- path: z.string().default("/").describe("Folder to list, as a path (e.g. '/foo/bar'). Defaults to the root."),
103
+ path: z
104
+ .string()
105
+ .default("/")
106
+ .describe("Folder to list, as a path (e.g. '/foo/bar'). Defaults to the root."),
86
107
  recursive: z
87
108
  .boolean()
88
109
  .default(false)
@@ -94,19 +115,33 @@ export async function dana4Tools(ctx, opts = {}) {
94
115
  description: "Search for a text pattern within a document. Returns matching lines with line numbers and surrounding context. Use this BEFORE read_document to find relevant sections without reading the entire file.",
95
116
  inputSchema: z.object({
96
117
  path: z.string().describe("Path of the document to search in."),
97
- pattern: z.string().describe("Text or regex pattern to search for. Case-insensitive."),
98
- context_lines: z.number().int().default(3).describe("Number of lines to show before and after each match."),
118
+ pattern: z
119
+ .string()
120
+ .describe("Text or regex pattern to search for. Case-insensitive."),
121
+ context_lines: z
122
+ .number()
123
+ .int()
124
+ .default(3)
125
+ .describe("Number of lines to show before and after each match."),
99
126
  }),
100
127
  execute: async ({ path, pattern, context_lines }) => {
101
128
  const r = grep((await ctx.docs.get(path)).content, pattern, context_lines);
102
- return r.matches ? `Found ${r.matches} match(es) in ${path} (${r.lines} lines total):\n${r.text}` : `No matches for '${pattern}' in ${path}`;
129
+ return r.matches
130
+ ? `Found ${r.matches} match(es) in ${path} (${r.lines} lines total):\n${r.text}`
131
+ : `No matches for '${pattern}' in ${path}`;
103
132
  },
104
133
  }),
105
134
  search_documents: tool({
106
135
  description: "Search across ALL documents in the workspace by meaning or keyword, returning the most relevant documents with a snippet and relevance score. Use this when you don't know which document holds the content you need. Use grep_document instead to search within a single known document.",
107
136
  inputSchema: z.object({
108
- query: z.string().describe("Natural-language or keyword query to search across all documents in the workspace."),
109
- limit: z.number().int().optional().describe("Max number of results to return. Defaults to the server default."),
137
+ query: z
138
+ .string()
139
+ .describe("Natural-language or keyword query to search across all documents in the workspace."),
140
+ limit: z
141
+ .number()
142
+ .int()
143
+ .optional()
144
+ .describe("Max number of results to return. Defaults to the server default."),
110
145
  }),
111
146
  execute: async ({ query, limit }) => {
112
147
  const results = await ctx.docs.search(query, limit);
@@ -114,7 +149,10 @@ export async function dana4Tools(ctx, opts = {}) {
114
149
  return `No documents matched '${query}'.`;
115
150
  return [
116
151
  `Found ${results.length} result(s) for '${query}':\n`,
117
- ...results.flatMap((r) => [`- ${r.path} — ${r.title} (score: ${r.score.toFixed(3)})`, ...(r.snippet ? [` ${r.snippet}`] : [])]),
152
+ ...results.flatMap((r) => [
153
+ `- ${r.path} — ${r.title} (score: ${r.score.toFixed(3)})`,
154
+ ...(r.snippet ? [` ${r.snippet}`] : []),
155
+ ]),
118
156
  ].join("\n");
119
157
  },
120
158
  }),
@@ -122,7 +160,11 @@ export async function dana4Tools(ctx, opts = {}) {
122
160
  description: `Read a document's content with line numbers. Returns up to ${MAX_LINES_PER_READ} lines.\n\nUsage:\n- Use grep_document first to find relevant line numbers, then read targeted ranges.\n- Use offset and limit to read specific sections. Avoid reading entire large documents.\n- Lines are prefixed with line numbers: \`42: content here\`.\n- Call this tool in parallel when reading multiple documents.`,
123
161
  inputSchema: z.object({
124
162
  path: z.string().describe("The path to the document to read."),
125
- offset: z.number().int().optional().describe("Line number to start reading from (1-indexed). Defaults to 1."),
163
+ offset: z
164
+ .number()
165
+ .int()
166
+ .optional()
167
+ .describe("Line number to start reading from (1-indexed). Defaults to 1."),
126
168
  limit: z
127
169
  .number()
128
170
  .int()
@@ -138,18 +180,24 @@ export async function dana4Tools(ctx, opts = {}) {
138
180
  .slice(start, end)
139
181
  .map((l, i) => `${start + i + 1}: ${l}`)
140
182
  .join("\n");
141
- const more = end < lines.length ? `\n\n[Showing lines ${start + 1}-${end} of ${lines.length}. Use offset=${end + 1} to read more.]` : "";
183
+ const more = end < lines.length
184
+ ? `\n\n[Showing lines ${start + 1}-${end} of ${lines.length}. Use offset=${end + 1} to read more.]`
185
+ : "";
142
186
  return `Title: ${doc.title}\nPath: ${path}\nTotal lines: ${lines.length}\nContent:\n\n${body}${more}\n`;
143
187
  },
144
188
  }),
145
189
  get_step_result: tool({
146
190
  description: "Fetch the result produced by an earlier step in the current journey, by its step id. Returns the result as JSON, or a note if the step has not produced a result yet.",
147
191
  inputSchema: z.object({
148
- step_id: z.string().describe("The id of the step whose result to fetch (e.g. 'step_brand_questions')."),
192
+ step_id: z
193
+ .string()
194
+ .describe("The id of the step whose result to fetch (e.g. 'step_brand_questions')."),
149
195
  }),
150
196
  execute: async ({ step_id }) => {
151
197
  const result = await ctx.stepResult(step_id);
152
- return result == null ? `No result available for step '${step_id}' yet (it may not have run or completed).` : JSON.stringify(result, null, 2);
198
+ return result == null
199
+ ? `No result available for step '${step_id}' yet (it may not have run or completed).`
200
+ : JSON.stringify(result, null, 2);
153
201
  },
154
202
  }),
155
203
  create_document: tool({
@@ -173,7 +221,10 @@ export async function dana4Tools(ctx, opts = {}) {
173
221
  description: `Overwrite a document's entire content. Max ~${MAX_LINES_PER_WRITE} lines.\n\nONLY use for small documents. For anything larger, use create_document + append_document.\nFor targeted changes, prefer edit_document or insert_in_document instead.`,
174
222
  inputSchema: z.object({
175
223
  path: z.string().describe("Path to the document to overwrite."),
176
- content: z.string().default("").describe("Full replacement content. Must be under ~200 lines."),
224
+ content: z
225
+ .string()
226
+ .default("")
227
+ .describe("Full replacement content. Must be under ~200 lines."),
177
228
  }),
178
229
  execute: async ({ path, content }) => {
179
230
  if (!content)
@@ -189,7 +240,9 @@ export async function dana4Tools(ctx, opts = {}) {
189
240
  description: "Append content to the end of an existing document.\n\nUse this for building large documents incrementally:\n1. create_document with opening structure\n2. append_document for each section\nKeep each append under ~300 lines.",
190
241
  inputSchema: z.object({
191
242
  path: z.string().describe("Path of the document to append to."),
192
- content: z.string().describe("Content to append to the end of the document."),
243
+ content: z
244
+ .string()
245
+ .describe("Content to append to the end of the document."),
193
246
  }),
194
247
  execute: async ({ path, content }) => {
195
248
  await ctx.docs.append(path, content);
@@ -200,9 +253,16 @@ export async function dana4Tools(ctx, opts = {}) {
200
253
  description: "Find and replace exact text in a document.\n\nUsage:\n- You MUST read_document or grep_document first to see the exact text.\n- old_str must match exactly (whitespace, indentation, line breaks).\n- old_str must be unique in the document. Include surrounding lines if needed, or set replace_all to change every occurrence.\n- Do NOT include line number prefixes (e.g. `42: `) in old_str/new_str.",
201
254
  inputSchema: z.object({
202
255
  path: z.string().describe("Path to the document to modify."),
203
- old_str: z.string().describe("Exact text to find and replace. Must match uniquely. Include surrounding lines for context if needed."),
204
- new_str: z.string().describe("Replacement text. Must differ from old_str."),
205
- replace_all: z.boolean().default(false).describe("Replace every occurrence instead of requiring a unique match."),
256
+ old_str: z
257
+ .string()
258
+ .describe("Exact text to find and replace. Must match uniquely. Include surrounding lines for context if needed."),
259
+ new_str: z
260
+ .string()
261
+ .describe("Replacement text. Must differ from old_str."),
262
+ replace_all: z
263
+ .boolean()
264
+ .default(false)
265
+ .describe("Replace every occurrence instead of requiring a unique match."),
206
266
  }),
207
267
  execute: async ({ path, old_str, new_str, replace_all }) => {
208
268
  try {
@@ -222,15 +282,47 @@ export async function dana4Tools(ctx, opts = {}) {
222
282
  .number()
223
283
  .int()
224
284
  .describe("Line number to insert BEFORE (1-indexed). Use 0 to insert at the very beginning. Use a line number beyond the end to append."),
225
- content: z.string().describe("Content to insert at the specified line."),
285
+ content: z
286
+ .string()
287
+ .describe("Content to insert at the specified line."),
226
288
  }),
227
289
  execute: async ({ path, line, content }) => {
228
290
  const doc = await ctx.docs.get(path, "editing");
229
291
  await ctx.docs.replace(path, insertAt(doc.content, line, content));
230
- const n = (content.endsWith("\n") ? content : `${content}\n`).split("\n").length - 1;
292
+ const n = (content.endsWith("\n") ? content : `${content}\n`).split("\n")
293
+ .length - 1;
231
294
  return `Inserted ${n} line(s) at line ${line} in ${path}`;
232
295
  },
233
296
  }),
297
+ create_ticket: tool({
298
+ description: "Create a ticket on the workspace's board. Tickets are identified by their number (#42), unique within the workspace; blocked_by takes those numbers.",
299
+ inputSchema: z.object({
300
+ title: z.string().describe("The ticket's title."),
301
+ description: z
302
+ .string()
303
+ .optional()
304
+ .describe("What needs doing, in markdown."),
305
+ status: z
306
+ .enum([
307
+ "backlog",
308
+ "todo",
309
+ "in_progress",
310
+ "in_review",
311
+ "done",
312
+ "canceled",
313
+ ])
314
+ .optional()
315
+ .describe("The board column. Defaults to backlog."),
316
+ blocked_by: z
317
+ .array(z.number().int())
318
+ .default([])
319
+ .describe("Numbers of the tickets that must finish first."),
320
+ }),
321
+ execute: async (ticket) => {
322
+ const t = await ctx.tickets.create(ticket);
323
+ return `Ticket #${t.number} created in workspace ${ctx.workspaceId}`;
324
+ },
325
+ }),
234
326
  send_message: tool({
235
327
  description: "Send a message to a channel. Uses the current channel if channel_id is omitted.",
236
328
  inputSchema: z.object({
@@ -248,20 +340,42 @@ export async function dana4Tools(ctx, opts = {}) {
248
340
  plan: z
249
341
  .string()
250
342
  .describe("The full plan as a markdown checklist, e.g. `- [x] research\n- [ ] draft`. This REPLACES the existing plan, so include every item, not just the changed ones. Use this to author the initial plan; prefer complete_plan_item / add_plan_item for incremental edits."),
251
- channel_id: z.string().optional().describe("The id of the channel. Defaults to the current channel."),
343
+ channel_id: z
344
+ .string()
345
+ .optional()
346
+ .describe("The id of the channel. Defaults to the current channel."),
252
347
  }),
253
348
  execute: async ({ plan, channel_id }) => {
254
349
  const ch = await ctx.plan.set(plan, channel_id ?? ctx.channelId);
255
350
  return planStatus(ch.id, ch.plan_completed);
256
351
  },
257
352
  }),
353
+ set_memory: tool({
354
+ description: "Overwrite this message thread's memory: markdown notes shared by its agents and people.\n\nUse it to log what happened, what worked, what failed and what to remember next run. The current memory is in your instructions; send the whole updated text, not just the new notes.",
355
+ inputSchema: z.object({
356
+ memory: z
357
+ .string()
358
+ .describe("The full memory as markdown. This REPLACES the existing memory, so start from the current one in your instructions and edit it: keep what is still useful, add what happened, what worked, what failed and what to remember next run, and drop what is stale."),
359
+ channel_id: z
360
+ .string()
361
+ .optional()
362
+ .describe("The id of the channel. Defaults to the current channel."),
363
+ }),
364
+ execute: async ({ memory, channel_id }) => {
365
+ const ch = await ctx.memory.set(memory, channel_id ?? ctx.channelId);
366
+ return `Memory updated for channel ${ch.id}.`;
367
+ },
368
+ }),
258
369
  complete_plan_item: tool({
259
370
  description: "Check off one item in this message thread's plan as you finish it.\n\nUsage:\n- The current plan (a markdown checklist) is in your instructions.\n- Pass the item's exact label text — just that one item, not the whole plan.\n- If the text matches no item or several, you'll get an error; retry with more of the item's wording.\n- The thread is complete once every item is checked.",
260
371
  inputSchema: z.object({
261
372
  item: z
262
373
  .string()
263
374
  .describe("The exact label text of the checklist item to check off (the words after the `[ ]`), as shown in the current plan in your instructions. Matched ignoring case and extra spaces; it must be unambiguous."),
264
- channel_id: z.string().optional().describe("The id of the channel. Defaults to the current channel."),
375
+ channel_id: z
376
+ .string()
377
+ .optional()
378
+ .describe("The id of the channel. Defaults to the current channel."),
265
379
  }),
266
380
  execute: async ({ item, channel_id }) => {
267
381
  try {
@@ -276,8 +390,13 @@ export async function dana4Tools(ctx, opts = {}) {
276
390
  add_plan_item: tool({
277
391
  description: "Append a new unchecked item to this message thread's plan.\n\nUse this to record a step you've discovered; pass just the new item's text, not the whole plan.",
278
392
  inputSchema: z.object({
279
- item: z.string().describe("The text of the new unchecked checklist item to append."),
280
- channel_id: z.string().optional().describe("The id of the channel. Defaults to the current channel."),
393
+ item: z
394
+ .string()
395
+ .describe("The text of the new unchecked checklist item to append."),
396
+ channel_id: z
397
+ .string()
398
+ .optional()
399
+ .describe("The id of the channel. Defaults to the current channel."),
281
400
  }),
282
401
  execute: async ({ item, channel_id }) => {
283
402
  const ch = await ctx.plan.add(item, channel_id ?? ctx.channelId);
@@ -290,7 +409,9 @@ export async function dana4Tools(ctx, opts = {}) {
290
409
  if (opts.delegation === false)
291
410
  return tools;
292
411
  // Without the lookup the chat still has every other tool, so don't fail it.
293
- const agents = await ctx.client.workspaces.agents(ctx.workspaceId).catch((e) => {
412
+ const agents = await ctx.client.workspaces
413
+ .agents(ctx.workspaceId)
414
+ .catch((e) => {
294
415
  console.warn("dana4: listing agents failed; no delegation tools:", e instanceof Error ? e.message : e);
295
416
  return [];
296
417
  });
@@ -302,7 +423,8 @@ export async function dana4Tools(ctx, opts = {}) {
302
423
  for (let i = 2; name in tools; i++)
303
424
  name = `${base.slice(0, 63 - String(i).length)}_${i}`;
304
425
  tools[name] = tool({
305
- description: cap.description || `Trigger \`${cap.key}\` from agent \`${agent.username}\`.`,
426
+ description: cap.description ||
427
+ `Trigger \`${cap.key}\` from agent \`${agent.username}\`.`,
306
428
  inputSchema: jsonSchema(capabilityToolSchema(cap.input_schema)),
307
429
  execute: async (inputs) => {
308
430
  const taskId = await ctx.delegate(agent.username, cap.key, inputs);
@@ -315,23 +437,38 @@ export async function dana4Tools(ctx, opts = {}) {
315
437
  }
316
438
  /** A thread's messages as model messages, oldest first: this agent's own
317
439
  * messages become assistant turns, everyone else's user turns prefixed with
318
- * the author. Only conversational messages (and errors) are kept. */
440
+ * the author. Only conversation is kept; orchestrator notices and errors only
441
+ * when they @-tag the agent, and status lines never. */
319
442
  export function chatHistory(messages, { me, exclude }) {
320
443
  return messages
321
- .filter((m) => (m.category === "default" || m.category === "error") && m.id !== exclude)
444
+ .filter((m) => m.id !== exclude &&
445
+ (m.category === "default" ||
446
+ ((m.category === "error" || m.category === "notice") &&
447
+ taggedUsernames(m.message).includes(me.toLowerCase()))))
322
448
  .sort((a, b) => a.time.created_at.localeCompare(b.time.created_at))
323
449
  .map((m) => {
324
450
  const content = `${m.from_username}: ${m.category === "error" ? "ERROR: " : ""}${m.message}`;
325
- return m.from_username === me ? { role: "assistant", content } : { role: "user", content };
451
+ return m.from_username === me
452
+ ? { role: "assistant", content }
453
+ : { role: "user", content };
326
454
  });
327
455
  }
328
- /** `base` followed by the thread's topics, intent and plan, so the model knows
329
- * what the conversation is for. */
456
+ /** `base` followed by the thread's topics, the board, intent, plan and memory, so the
457
+ * model knows what the conversation is for. */
330
458
  export function threadInstructions(base, thread) {
459
+ const tickets = thread.tickets ?? [];
331
460
  const context = [
332
461
  ...thread.topics.map((t) => `Thread topic: ${t.title} — ${t.summary}`),
462
+ ...(tickets.length
463
+ ? [
464
+ `Board:\n${tickets
465
+ .map((t) => `- ${t.number != null ? `#${t.number} ` : ""}${t.title} [${t.board_status}]${t.assignee ? ` @${t.assignee}` : ""}`)
466
+ .join("\n")}`,
467
+ ]
468
+ : []),
333
469
  ...(thread.intent ? [`Thread intent: ${thread.intent}`] : []),
334
470
  ...(thread.plan ? [`Thread plan:\n${thread.plan}`] : []),
471
+ ...(thread.memory ? [`Thread memory:\n${thread.memory}`] : []),
335
472
  ].join("\n");
336
473
  return [base, context].filter(Boolean).join("\n\n");
337
474
  }
package/dist/cli.js CHANGED
@@ -3,7 +3,12 @@ import { parseArgs } from "node:util";
3
3
  import { enroll } from "./enroll.js";
4
4
  const { values, positionals } = parseArgs({
5
5
  allowPositionals: true,
6
- options: { username: { type: "string" }, bio: { type: "string" }, description: { type: "string" }, url: { type: "string" } },
6
+ options: {
7
+ username: { type: "string" },
8
+ bio: { type: "string" },
9
+ description: { type: "string" },
10
+ url: { type: "string" },
11
+ },
7
12
  });
8
13
  const username = values.username;
9
14
  if (positionals[0] !== "enroll" || !username) {
@@ -11,6 +16,10 @@ if (positionals[0] !== "enroll" || !username) {
11
16
  process.exitCode = 2;
12
17
  }
13
18
  else {
14
- const key = await enroll({ ...values, username, url: values.url ?? process.env.DANA4_URL });
19
+ const key = await enroll({
20
+ ...values,
21
+ username,
22
+ url: values.url ?? process.env.DANA4_URL,
23
+ });
15
24
  console.log(`\nApproved. Your API key (shown once), e.g. for DANA4_API_KEY:\n\n${key}`);
16
25
  }
package/dist/client.d.ts CHANGED
@@ -6,6 +6,7 @@ export type Automation = S["Automation"];
6
6
  export type Channel = S["Channel"];
7
7
  export type ChatThread = S["ChatThread"];
8
8
  export type CreateAutomationBody = S["CreateAutomationBody"];
9
+ export type CreateTicketBody = S["CreateTicketBody"];
9
10
  export type CreateWorkflowPayload = S["CreateWorkflowPayload"];
10
11
  export type Document = S["Document"];
11
12
  export type DocumentHeader = S["DocumentHeader"];
@@ -13,9 +14,13 @@ export type DocumentType = S["DocumentType"];
13
14
  export type EnrollAgentResponse = S["EnrollAgentResponse"];
14
15
  export type Message = S["Message"];
15
16
  export type MessageCategory = S["MessageCategory"];
17
+ /** Routine status lines (progress, "Thinking...", blockers): never conversation.
18
+ * `transient` and `dependencies_status` are legacy values on old rows. */
19
+ export declare const skippedFromHistory: (m: Pick<Message, "category">) => boolean;
16
20
  export type Pipe = S["Pipe"];
17
21
  export type PresenceActivity = S["PresenceActivity"];
18
22
  export type SearchResult = S["SearchResult"];
23
+ export type SdkTicket = S["SdkTicket"];
19
24
  export type Task = S["Task"];
20
25
  export type TaskStatus = S["TaskStatus"];
21
26
  /** `cost_details` is optional on the wire; utoipa marks it required. */
@@ -95,6 +100,7 @@ export declare class Dana4Client {
95
100
  execution_duration?: number | null;
96
101
  id: string;
97
102
  journey_id: string;
103
+ number?: number | null;
98
104
  order: number;
99
105
  params?: {
100
106
  [key: string]: unknown;
@@ -106,6 +112,9 @@ export declare class Dana4Client {
106
112
  step_id: string;
107
113
  step_type: components["schemas"]["JourneyStepType"];
108
114
  time: components["schemas"]["Timestamps"];
115
+ tool_calls?: {
116
+ [key: string]: unknown;
117
+ }[];
109
118
  }[]>;
110
119
  assigned: () => Promise<{
111
120
  channel_id: string;
@@ -116,6 +125,7 @@ export declare class Dana4Client {
116
125
  execution_duration?: number | null;
117
126
  id: string;
118
127
  journey_id: string;
128
+ number?: number | null;
119
129
  order: number;
120
130
  params?: {
121
131
  [key: string]: unknown;
@@ -127,6 +137,9 @@ export declare class Dana4Client {
127
137
  step_id: string;
128
138
  step_type: components["schemas"]["JourneyStepType"];
129
139
  time: components["schemas"]["Timestamps"];
140
+ tool_calls?: {
141
+ [key: string]: unknown;
142
+ }[];
130
143
  }[]>;
131
144
  unassigned: () => Promise<{
132
145
  channel_id: string;
@@ -137,6 +150,7 @@ export declare class Dana4Client {
137
150
  execution_duration?: number | null;
138
151
  id: string;
139
152
  journey_id: string;
153
+ number?: number | null;
140
154
  order: number;
141
155
  params?: {
142
156
  [key: string]: unknown;
@@ -148,6 +162,9 @@ export declare class Dana4Client {
148
162
  step_id: string;
149
163
  step_type: components["schemas"]["JourneyStepType"];
150
164
  time: components["schemas"]["Timestamps"];
165
+ tool_calls?: {
166
+ [key: string]: unknown;
167
+ }[];
151
168
  }[]>;
152
169
  claim: (taskId: string, capability: string) => Promise<{
153
170
  channel_id: string;
@@ -158,6 +175,7 @@ export declare class Dana4Client {
158
175
  execution_duration?: number | null;
159
176
  id: string;
160
177
  journey_id: string;
178
+ number?: number | null;
161
179
  order: number;
162
180
  params?: {
163
181
  [key: string]: unknown;
@@ -169,6 +187,9 @@ export declare class Dana4Client {
169
187
  step_id: string;
170
188
  step_type: components["schemas"]["JourneyStepType"];
171
189
  time: components["schemas"]["Timestamps"];
190
+ tool_calls?: {
191
+ [key: string]: unknown;
192
+ }[];
172
193
  }>;
173
194
  /** Start a one-step journey (e.g. `read_message`); answers its task id. */
174
195
  start: (body: {
@@ -289,9 +310,11 @@ export declare class Dana4Client {
289
310
  direct?: boolean;
290
311
  document_id?: string | null;
291
312
  intent?: string | null;
313
+ memory?: string | null;
292
314
  messages: components["schemas"]["Message"][];
293
315
  parent_channel_id?: string | null;
294
316
  plan?: string | null;
317
+ tickets?: components["schemas"]["TicketSummary"][];
295
318
  topics: components["schemas"]["TopicSummary"][];
296
319
  }[]>;
297
320
  create: (body: S["CreateDocumentPayload"]) => Promise<string>;
@@ -311,9 +334,11 @@ export declare class Dana4Client {
311
334
  direct?: boolean;
312
335
  document_id?: string | null;
313
336
  intent?: string | null;
337
+ memory?: string | null;
314
338
  messages: components["schemas"]["Message"][];
315
339
  parent_channel_id?: string | null;
316
340
  plan?: string | null;
341
+ tickets?: components["schemas"]["TicketSummary"][];
317
342
  topics: components["schemas"]["TopicSummary"][];
318
343
  }>;
319
344
  messages: (channelId: string, range?: {
@@ -324,9 +349,11 @@ export declare class Dana4Client {
324
349
  direct?: boolean;
325
350
  document_id?: string | null;
326
351
  intent?: string | null;
352
+ memory?: string | null;
327
353
  messages: components["schemas"]["Message"][];
328
354
  parent_channel_id?: string | null;
329
355
  plan?: string | null;
356
+ tickets?: components["schemas"]["TicketSummary"][];
330
357
  topics: components["schemas"]["TopicSummary"][];
331
358
  }>;
332
359
  send: (body: S["SendMessagePayload"]) => Promise<{
@@ -343,6 +370,7 @@ export declare class Dana4Client {
343
370
  document_id: string;
344
371
  id: string;
345
372
  intent?: string | null;
373
+ memory?: string | null;
346
374
  owners: string[];
347
375
  parent_channel_id?: string | null;
348
376
  parent_message_id?: string | null;
@@ -357,6 +385,22 @@ export declare class Dana4Client {
357
385
  document_id: string;
358
386
  id: string;
359
387
  intent?: string | null;
388
+ memory?: string | null;
389
+ owners: string[];
390
+ parent_channel_id?: string | null;
391
+ parent_message_id?: string | null;
392
+ plan?: string | null;
393
+ plan_completed: boolean;
394
+ private: boolean;
395
+ time: components["schemas"]["Timestamps"];
396
+ title: string;
397
+ workspace_id: string;
398
+ }>;
399
+ setMemory: (channelId: string, memory: string) => Promise<{
400
+ document_id: string;
401
+ id: string;
402
+ intent?: string | null;
403
+ memory?: string | null;
360
404
  owners: string[];
361
405
  parent_channel_id?: string | null;
362
406
  parent_message_id?: string | null;
@@ -371,6 +415,7 @@ export declare class Dana4Client {
371
415
  document_id: string;
372
416
  id: string;
373
417
  intent?: string | null;
418
+ memory?: string | null;
374
419
  owners: string[];
375
420
  parent_channel_id?: string | null;
376
421
  parent_message_id?: string | null;
@@ -385,6 +430,7 @@ export declare class Dana4Client {
385
430
  document_id: string;
386
431
  id: string;
387
432
  intent?: string | null;
433
+ memory?: string | null;
388
434
  owners: string[];
389
435
  parent_channel_id?: string | null;
390
436
  parent_message_id?: string | null;
@@ -469,6 +515,17 @@ export declare class Dana4Client {
469
515
  }>;
470
516
  delete: (automationId: string) => Promise<string>;
471
517
  };
518
+ readonly tickets: {
519
+ /** Put a ticket on the workspace's board. Tickets are referred to by their
520
+ * per-workspace number (`blocked_by: [12]` means #12). */
521
+ create: (body: CreateTicketBody) => Promise<{
522
+ blocked_by: number[];
523
+ number: number;
524
+ status: components["schemas"]["BoardStatus"];
525
+ title: string;
526
+ workspace_id: string;
527
+ }>;
528
+ };
472
529
  readonly workflows: {
473
530
  create: (body: CreateWorkflowPayload) => Promise<{
474
531
  definition: string;