@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 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-sonnet-5-5"),
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 plan to your system prompt.
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, { io: "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) => (e instanceof Error ? e.message : String(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({ path, old_string: oldString, new_string: newString, replace_all: replaceAll, task_id: this.#task() }))
105
- .replacements,
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(), { status: "running", progress, message });
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: { triggered_by: this.me, triggered_from: "chat", tag_usernames: [this.me] },
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
- { input_schema: capabilitySchema(key, cap), show_in_chat: cap.showInChat ?? true },
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, { status: "retry", progress: 0, message: "Agent restarted; re-queuing task" })
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, { status: "running", progress: 0 });
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" ? { kind: "join", workspaceId: m.workspace_id } : { kind: "chat", message: m, thread });
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, { status: "failed", message, ...ctx?.settle() });
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" ? payload.channel_id : task.channel_id, task.id, Array.isArray(payload.tag_usernames) ? payload.tag_usernames.map(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({ step_name: "read_message", workspace_id: message.workspace_id, channel_id: message.channel_id });
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: "transient" });
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, { status: "completed", progress: 1, ...ctx.settle() });
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
- await this.client.tasks.update(taskId, { status: "failed", message: errorMessage(e), ...ctx.settle() });
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 { Context } from "./agent.js";
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 conversational messages (and errors) are kept. */
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 plan, so the model knows
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;