@columenlabs/lumen-ai 0.1.252 → 0.1.254

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
@@ -254,7 +254,7 @@ Drafts and writes are cards in the chat's own storage (`lumen_cards` in the agen
254
254
 
255
255
  **The panel.** `LumenAssistant` reads a Think chat with no change. `ask(input, sink)` runs one answer and sends the panel's own events (`AssistantEvent`: the chat, the text, each tool line, cards, hand-off sections, advice, the use) to `sink`. The app's `POST /chat` returns `askResponse((sink) => chat.ask(input, sink), { signal, stop: () => chat.stop() })`, the same server-sent events `assistantRoutes` sends. `view()` gives a stored chat as the panel's items, and `usage()` gives the chat's use so far. A card's id is its tool call's, so the panel's Save, Dismiss and Undo go to `saveCard`, `dismissCard` and `undoCard`. The app keeps the list of chats, and each chat is its own agent, such as a facet of the person's store. `chunkEvents` and `itemsOf` are the mapping, for an app that streams some other way.
256
256
 
257
- **Tool lines.** Each tool call shows as a line with the tool's own `title` (an MCP tool's `title`, a plain tool's `title`), kept in the chat's storage so a chat read back words its lines the same; a tool with no title shows its name. An app that keeps its insides off the screen overrides `plainSteps()` to return `true`: every line reads its tool's title, else a word for what it did (Look something up, Make a change, Draft a change), with no tool id, row count or error text (a failed line says `didn't work`), and a hand-off section reads Take a closer look with no tier or task. The details go to the app's log instead: `stepFailed({ tool, error, ms })` hears each failed call with its whole error (override it to log it with the chat's ids), and `onStep` carries `error` and `caller` on a failed tool or hand-off. `stepTitleOf(name, kind)` gives a line's title for an app's own card answers.
257
+ **Tool lines.** Each tool call shows as a line with the tool's own `title` (an MCP tool's `title`, a plain tool's `title`), kept in the chat's storage so a chat read back words its lines the same; a tool with no title shows its name. An app that keeps its insides off the screen overrides `plainSteps()` to return `true`: every line reads its tool's title, else a word for what it did (Look something up, Make a change, Draft a change), with no tool id, row count or error text (a failed line says `didn't work`), and a hand-off section reads Take a closer look with no tier or task. The details go to the app's log instead: `stepFailed({ tool, error, ms })` hears each failed call with its whole error (override it to log it with the chat's ids), and `onStep` carries `error` and `caller` on a failed tool or hand-off. `stepTitleOf(name, kind)` gives a line's title for an app's own card answers. **Step lines say what a call did** (`stepLine(c)`): override it to word each call as a verb and what it acted on, from the call's own arguments and answer (`c.input`, `c.output`): present tense while it runs (`state: 'running'`, sent again once the arguments are in), past tense with a short result when it answered (`'done'`), and "Couldn't …" when it failed (`'failed'`, which then shows no `didn't work`). Return `undefined` to keep the tool's title. The kit words its own web lookup (Looking up / Looked up the question). A finished line is kept with the chat, so a chat read back reads the same. In a plain chat the tool search that turns more tools on shows no line.
258
258
 
259
259
  **A write in progress.** A tool line starts (`step_start`) as soon as the model starts the call, while it is still writing the arguments. For a Read + write tool it carries `write: true`, and `useAssistantChat` marks that running step `writing` until its result lands or the answer ends, so an app can show what the tool changes as being changed now (a pending edit is not ready to accept while the model rewrites it).
260
260
 
@@ -17,6 +17,7 @@ import { type ModelMessage, type ToolSet } from 'ai';
17
17
  import type { ModelDef } from '../assistant/models.js';
18
18
  import { type AgentConnector, type ConnectorAt } from './connectors.js';
19
19
  import { type ModelKey } from './models.js';
20
+ import { type StepCall } from './stream.js';
20
21
  import { type AiToolsOptions, type OpenedTools, type SourceAt } from './tools.js';
21
22
  import { UsageMeter } from './usage.js';
22
23
  /** What a plan's extra tools get: the open tools, a meter of their own and the cap. */
@@ -197,6 +198,16 @@ export declare abstract class LumenAgent<Env = unknown> extends Think<Env> {
197
198
  * (`false`) unless the app overrides it; the app logs the details from `onStep`.
198
199
  */
199
200
  protected plainSteps(): boolean;
201
+ /**
202
+ * A tool call's line in the person's words: a verb and what it acted on, from the call's own
203
+ * arguments and answer. `running` is present tense ("Editing Lid"), `done` past tense with a short
204
+ * result when it helps ("Edited Lid: wall 2 → 3 mm"), `failed` says it did not ("Couldn't edit
205
+ * Lid"). Return `undefined` to keep the tool's title. A plain chat's line names no tool id, model,
206
+ * engine or code. The kit words its own web lookup; the app words its tools by overriding this
207
+ * (and may call `super.stepLine(c)` for the kit's). A finished line is kept with the chat, so a
208
+ * chat read back shows the same words.
209
+ */
210
+ protected stepLine(c: StepCall): string | undefined;
200
211
  /**
201
212
  * A tool call this answer failed: its name and whole error (a plain line shows neither). Does
202
213
  * nothing unless the app overrides it, e.g. to log it with the chat's ids.
@@ -201,12 +201,45 @@ export class LumenAgent extends Think {
201
201
  plainSteps() {
202
202
  return false;
203
203
  }
204
+ /**
205
+ * A tool call's line in the person's words: a verb and what it acted on, from the call's own
206
+ * arguments and answer. `running` is present tense ("Editing Lid"), `done` past tense with a short
207
+ * result when it helps ("Edited Lid: wall 2 → 3 mm"), `failed` says it did not ("Couldn't edit
208
+ * Lid"). Return `undefined` to keep the tool's title. A plain chat's line names no tool id, model,
209
+ * engine or code. The kit words its own web lookup; the app words its tools by overriding this
210
+ * (and may call `super.stepLine(c)` for the kit's). A finished line is kept with the chat, so a
211
+ * chat read back shows the same words.
212
+ */
213
+ stepLine(c) {
214
+ if (c.tool !== 'web__lookup')
215
+ return undefined;
216
+ const q = c.input?.question;
217
+ const question = typeof q === 'string' ? q.trim() : '';
218
+ if (!question)
219
+ return undefined;
220
+ return c.state === 'running'
221
+ ? `Looking up ${question}`
222
+ : c.state === 'failed'
223
+ ? `Couldn't look up ${question}`
224
+ : `Looked up ${question}`;
225
+ }
204
226
  /** How this chat words its tool lines: plain or not, with the titles of every tool it offered. */
205
227
  #names() {
206
228
  return {
207
229
  plain: this.plainSteps(),
208
230
  titleOf: (n) => this.#turn?.tools.info.find((i) => i.name === n)?.title ??
209
231
  this.#get('titles')?.[n],
232
+ lineOf: (c) => {
233
+ if (c.state === 'running')
234
+ return this.stepLine(c);
235
+ const kept = this.#get(`line:${c.id}`);
236
+ if (kept)
237
+ return kept;
238
+ const line = this.stepLine(c);
239
+ if (line)
240
+ this.#put(`line:${c.id}`, line.slice(0, 200));
241
+ return line;
242
+ },
210
243
  };
211
244
  }
212
245
  /**
@@ -17,7 +17,28 @@ export interface StepNames {
17
17
  plain?: boolean;
18
18
  /** The tool's own title, when it has one. */
19
19
  titleOf?: (name: string) => string | undefined;
20
+ /**
21
+ * A line that says what this call did and to what (`LumenAgent.stepLine`): present tense while it
22
+ * runs, past tense with a short result when it answered, "Couldn't …" when it failed. Left out
23
+ * (or `undefined`), the line reads the tool's title.
24
+ */
25
+ lineOf?: (c: StepCall) => string | undefined;
20
26
  }
27
+ /** One tool call as its line is worded: its name, arguments, answer and where it stands. */
28
+ export interface StepCall {
29
+ id: string;
30
+ /** The tool's name as the model called it (`group__tool`). */
31
+ tool: string;
32
+ input?: unknown;
33
+ /** Its answer, once it answered. */
34
+ output?: unknown;
35
+ state: 'running' | 'done' | 'failed';
36
+ }
37
+ /**
38
+ * Whether a call shows no line at all: in a plain chat, the tool search that turns more tools on is
39
+ * the assistant finding its own tools, not something it did for the person.
40
+ */
41
+ export declare function hiddenStep(name: string, names?: StepNames): boolean;
21
42
  /** What a plain line says when a call failed: the assistant reads the error and goes on. */
22
43
  export declare const PLAIN_FAILED = "didn't work";
23
44
  /** A plain hand-off's heading: a closer look, never the tier it went to. */
@@ -1,10 +1,32 @@
1
1
  import { rowCount } from '../assistant/tools.js';
2
2
  import { webStep } from '../assistant/web.js';
3
+ import { TOOL_SEARCH } from './search.js';
3
4
  /** A tool's name as a line's title: `notes__find_notes` → `find notes`. */
4
5
  export function stepTitle(name) {
5
6
  const own = name.includes('__') ? name.slice(name.indexOf('__') + 2) : name;
6
7
  return own.replace(/_/g, ' ');
7
8
  }
9
+ /** A line's text kept to one short line. */
10
+ const oneLine = (t) => {
11
+ const s = (t ?? '').replace(/\s+/g, ' ').trim();
12
+ return s ? s.slice(0, 120) : undefined;
13
+ };
14
+ /** The line the app gave for this call, when it gave one; never throws. */
15
+ function lineFor(names, c) {
16
+ try {
17
+ return oneLine(names.lineOf?.(c));
18
+ }
19
+ catch {
20
+ return undefined;
21
+ }
22
+ }
23
+ /**
24
+ * Whether a call shows no line at all: in a plain chat, the tool search that turns more tools on is
25
+ * the assistant finding its own tools, not something it did for the person.
26
+ */
27
+ export function hiddenStep(name, names = {}) {
28
+ return !!names.plain && name === TOOL_SEARCH;
29
+ }
8
30
  /** What a plain line says when a call failed: the assistant reads the error and goes on. */
9
31
  export const PLAIN_FAILED = "didn't work";
10
32
  /** A plain hand-off's heading: a closer look, never the tier it went to. */
@@ -88,15 +110,27 @@ function settledCall(id, c, out, ms, cards, screens = new Map(), names = {}) {
88
110
  const image = stepImage(o?.image);
89
111
  const web = webStep(c.name, c.input, out.output, !!error || obj(out.output)?.ok === false);
90
112
  const wrote = card?.kind === 'write' || (o && typeof o.change === 'string');
113
+ // The app's own line says what the call did; a failed one already says it did not ("Couldn't …").
114
+ const line = lineFor(names, {
115
+ id,
116
+ tool: c.name,
117
+ input: c.input,
118
+ ...(error
119
+ ? {}
120
+ : { output: o && 'result' in o && o.change !== undefined ? o.result : out.output }),
121
+ state: error ? 'failed' : 'done',
122
+ });
91
123
  const step = {
92
124
  kind: 'step',
93
125
  id,
94
126
  tool: c.name,
95
- title: shownTitle(c.name, names, wrote ? 'write' : 'read'),
127
+ title: line ?? shownTitle(c.name, names, wrote ? 'write' : 'read'),
96
128
  ok: !error,
97
129
  ms,
98
130
  ...(rows !== undefined && !error && !names.plain ? { rows } : {}),
99
- ...(error ? { error: names.plain ? PLAIN_FAILED : error.slice(0, 300) } : {}),
131
+ ...(error && !(line && names.plain)
132
+ ? { error: names.plain ? PLAIN_FAILED : error.slice(0, 300) }
133
+ : {}),
100
134
  ...(wrote
101
135
  ? { write: { undo: true, status: card?.status === 'undone' ? 'undone' : 'done' } }
102
136
  : {}),
@@ -129,7 +163,10 @@ export function chunkEvents(o = {}) {
129
163
  advising = true;
130
164
  return [{ event: 'advice', data: { tier: 'advisor', label: o.advisor ?? 'Advice' } }];
131
165
  }
132
- if (name === 'escalate' || name === 'screen' || o.kindOf?.(name) === 'draft')
166
+ if (name === 'escalate' ||
167
+ name === 'screen' ||
168
+ o.kindOf?.(name) === 'draft' ||
169
+ hiddenStep(name, names))
133
170
  return [];
134
171
  // A tool that makes a change at once says so as it starts, so an app can show that what it
135
172
  // changes is being changed now, before the model has finished writing the call.
@@ -150,7 +187,23 @@ export function chunkEvents(o = {}) {
150
187
  case 'tool-input-available': {
151
188
  const c = calls.get(id) ?? { name: String(chunk.toolName ?? ''), t0: Date.now() };
152
189
  calls.set(id, { ...c, input: chunk.input });
153
- return [];
190
+ // Its arguments are in: the running line can say what it is doing and to what.
191
+ if (c.name === 'advisor' ||
192
+ c.name === 'escalate' ||
193
+ c.name === 'screen' ||
194
+ o.kindOf?.(c.name) === 'draft' ||
195
+ hiddenStep(c.name, names))
196
+ return [];
197
+ const line = lineFor(names, { id, tool: c.name, input: chunk.input, state: 'running' });
198
+ if (!line)
199
+ return [];
200
+ const write = o.kindOf?.(c.name) === 'write';
201
+ return [
202
+ {
203
+ event: 'step_start',
204
+ data: { id, tool: c.name, title: line, ...(write ? { write } : {}) },
205
+ },
206
+ ];
154
207
  }
155
208
  case 'tool-output-available':
156
209
  case 'tool-output-error': {
@@ -164,6 +217,8 @@ export function chunkEvents(o = {}) {
164
217
  return [{ event: 'advice_done', data: error ? { error } : {} }];
165
218
  }
166
219
  const ms = Date.now() - c.t0;
220
+ if (hiddenStep(c.name, names))
221
+ return [];
167
222
  const out = obj(chunk.output);
168
223
  const said = error ?? (out && typeof out.error === 'string' ? out.error : undefined);
169
224
  if (said !== undefined) {
@@ -256,6 +311,8 @@ export function itemsOf(messages, cards, screens = new Map(), steers = {}, names
256
311
  items.push({ kind: 'advice', tier: 'advisor', label: 'Advice' });
257
312
  continue;
258
313
  }
314
+ if (hiddenStep(tool, names))
315
+ continue;
259
316
  const ended = p.state === 'output-available'
260
317
  ? { output: p.output }
261
318
  : p.state === 'output-error'
@@ -16,7 +16,7 @@ import { webLookupTools } from '../subagents/web.js';
16
16
  import { bearings } from './bearings.js';
17
17
  import { CHATGPT_REDIRECT, SigninError, finishChatgpt, startChatgpt, testChatgpt, } from './chatgpt.js';
18
18
  import { ALL_MODELS, DEFAULT_OUT, Meter, PROVIDERS, XAI_BASE_URL, effortParams, } from './models.js';
19
- import { DEFAULT_CAP, State, keyDocName, keyState, ownKeyUsd, ownPlanCalls, planDocName, waitText, withProvider, } from './state.js';
19
+ import { DEFAULT_CAP, State, keyDocName, keyState, ownKeyUsd, ownPlanCalls, ownPlanUsd, planDocName, waitText, withProvider, } from './state.js';
20
20
  import { Docs } from './store.js';
21
21
  import { toolName, withDeadline } from './tools.js';
22
22
  import { scrapeqlTools, webFetchTools } from './web.js';
@@ -410,6 +410,7 @@ export class Assistant {
410
410
  title: message.split('\n')[0] || 'Chat',
411
411
  own_usd: ownKeyUsd(paying, onPayer),
412
412
  own_plan_calls: ownPlanCalls(paying, onPayer),
413
+ own_plan_usd: ownPlanUsd(paying, onPayer),
413
414
  }, end.parts)
414
415
  .catch(() => undefined);
415
416
  const total = end.parts.reduce((a, m) => ({
@@ -1096,6 +1097,7 @@ export class Assistant {
1096
1097
  title: o.run?.title ?? task?.label ?? o.task,
1097
1098
  own_usd: ownKeyUsd(paying, [part]),
1098
1099
  own_plan_calls: ownPlanCalls(paying, [part]),
1100
+ own_plan_usd: ownPlanUsd(paying, [part]),
1099
1101
  }, [part])
1100
1102
  .catch(() => undefined);
1101
1103
  }
@@ -1185,6 +1187,7 @@ export class Assistant {
1185
1187
  title: o.run?.title ?? task?.label ?? o.task,
1186
1188
  own_usd: ownKeyUsd(paying, [part]),
1187
1189
  own_plan_calls: ownPlanCalls(paying, [part]),
1190
+ own_plan_usd: ownPlanUsd(paying, [part]),
1188
1191
  }, [part])
1189
1192
  .catch(() => undefined);
1190
1193
  }
@@ -104,8 +104,19 @@ export type SpendRow = UsagePart & {
104
104
  };
105
105
  /** The provider a part ran on: by its model id, else by the model's name at the end of its label. */
106
106
  export declare function providerOfPart(p: UsagePart): AssistantProvider | undefined;
107
- /** Spend rows with the provider each ran on, so a page can tell a removed key's spend apart. */
108
- export declare function withProvider<R extends UsagePart>(rows: R[]): R[];
107
+ /**
108
+ * What a part's tokens cost at its model's API rates: the estimate shown for a plan sign-in's calls,
109
+ * which cost the person nothing. Cache writes count as plain input. Undefined for an unknown model.
110
+ */
111
+ export declare function listUsd(p: UsagePart): number | undefined;
112
+ /**
113
+ * Spend rows with the provider each ran on, so a page can tell a removed key's spend apart, and
114
+ * a $0 row (a plan's calls) with what it would cost at API rates (`list_usd`).
115
+ */
116
+ export declare function withProvider<R extends UsagePart>(rows: R[]): (R & {
117
+ provider?: "anthropic" | "openai" | "xai" | undefined;
118
+ list_usd?: number | undefined;
119
+ })[];
109
120
  /** USD of `parts` the person's own API keys paid: a provider signed in with a plan costs $0. */
110
121
  export declare function ownKeyUsd(paying: {
111
122
  payer: AssistantPayer;
@@ -116,6 +127,11 @@ export declare function ownPlanCalls(paying: {
116
127
  payer: AssistantPayer;
117
128
  plans: Paying['plans'];
118
129
  }, parts: UsagePart[]): number;
130
+ /** Those plan calls at the models' API rates: an estimate of what the plan saved the person. */
131
+ export declare function ownPlanUsd(paying: {
132
+ payer: AssistantPayer;
133
+ plans: Paying['plans'];
134
+ }, parts: UsagePart[]): number;
119
135
  /**
120
136
  * What counts toward the person's own cap: own rows paid by an API key (a plan sign-in costs
121
137
  * nothing per call), on a provider they can still pay with. A row from before ways were recorded
@@ -282,7 +298,8 @@ export declare class State {
282
298
  * Adds one answer's use to a chat or run in this month's history (`usage:YYYY-MM`): one entry a
283
299
  * chat or run, its parts merged, the newest `USAGE_KEPT` kept. `title` names a new entry only;
284
300
  * `own_usd` is what of this answer the person's own key or plan paid, added to the entry's;
285
- * `own_plan_calls` the calls their plan sign-in answered (free to them, so counted, not priced).
301
+ * `own_plan_calls` the calls their plan sign-in answered (free to them, so counted, not priced),
302
+ * `own_plan_usd` those calls at API rates (an estimate).
286
303
  */
287
304
  addUsage(id: string, e: {
288
305
  id: string;
@@ -291,6 +308,7 @@ export declare class State {
291
308
  title: string;
292
309
  own_usd?: number;
293
310
  own_plan_calls?: number;
311
+ own_plan_usd?: number;
294
312
  }, parts: UsagePart[]): Promise<void>;
295
313
  /** One month's history (this month by default) beside its spend, and the months that have one. */
296
314
  usage(id: string, month?: string): Promise<UsageHistory>;
@@ -14,11 +14,30 @@ export function providerOfPart(p) {
14
14
  KNOWN_MODELS.find((x) => p.label === x.label || p.label.endsWith(` · ${x.label}`));
15
15
  return m?.provider;
16
16
  }
17
- /** Spend rows with the provider each ran on, so a page can tell a removed key's spend apart. */
17
+ /**
18
+ * What a part's tokens cost at its model's API rates: the estimate shown for a plan sign-in's calls,
19
+ * which cost the person nothing. Cache writes count as plain input. Undefined for an unknown model.
20
+ */
21
+ export function listUsd(p) {
22
+ const m = KNOWN_MODELS.find((x) => x.id === p.model) ??
23
+ KNOWN_MODELS.find((x) => p.label === x.label || p.label.endsWith(` · ${x.label}`));
24
+ if (!m)
25
+ return undefined;
26
+ return (Math.max(0, p.in - p.read) * m.in + p.read * m.read + p.out * m.out) / 1e6;
27
+ }
28
+ /**
29
+ * Spend rows with the provider each ran on, so a page can tell a removed key's spend apart, and
30
+ * a $0 row (a plan's calls) with what it would cost at API rates (`list_usd`).
31
+ */
18
32
  export function withProvider(rows) {
19
33
  return rows.map((r) => {
20
34
  const provider = providerOfPart(r);
21
- return provider ? { ...r, provider } : r;
35
+ const list = r.calls && r.usd < 0.0005 ? listUsd(r) : undefined;
36
+ return {
37
+ ...r,
38
+ ...(provider ? { provider } : {}),
39
+ ...(list !== undefined ? { list_usd: list } : {}),
40
+ };
22
41
  });
23
42
  }
24
43
  /** USD of `parts` the person's own API keys paid: a provider signed in with a plan costs $0. */
@@ -43,6 +62,17 @@ export function ownPlanCalls(paying, parts) {
43
62
  })
44
63
  .reduce((a, p) => a + p.calls, 0);
45
64
  }
65
+ /** Those plan calls at the models' API rates: an estimate of what the plan saved the person. */
66
+ export function ownPlanUsd(paying, parts) {
67
+ if (paying.payer !== 'own')
68
+ return 0;
69
+ return parts
70
+ .filter((p) => {
71
+ const prov = providerOfPart(p);
72
+ return !!(prov && paying.plans[prov]);
73
+ })
74
+ .reduce((a, p) => a + (listUsd(p) ?? 0), 0);
75
+ }
46
76
  /**
47
77
  * What counts toward the person's own cap: own rows paid by an API key (a plan sign-in costs
48
78
  * nothing per call), on a provider they can still pay with. A row from before ways were recorded
@@ -364,7 +394,8 @@ export class State {
364
394
  * Adds one answer's use to a chat or run in this month's history (`usage:YYYY-MM`): one entry a
365
395
  * chat or run, its parts merged, the newest `USAGE_KEPT` kept. `title` names a new entry only;
366
396
  * `own_usd` is what of this answer the person's own key or plan paid, added to the entry's;
367
- * `own_plan_calls` the calls their plan sign-in answered (free to them, so counted, not priced).
397
+ * `own_plan_calls` the calls their plan sign-in answered (free to them, so counted, not priced),
398
+ * `own_plan_usd` those calls at API rates (an estimate).
368
399
  */
369
400
  async addUsage(id, e, parts) {
370
401
  if (!parts.some((p) => p.calls))
@@ -378,6 +409,7 @@ export class State {
378
409
  const sum = (k) => merged.reduce((a, p) => a + p[k], 0);
379
410
  const own = (prev?.own_usd ?? 0) + (e.own_usd ?? 0);
380
411
  const plan = (prev?.own_plan_calls ?? 0) + (e.own_plan_calls ?? 0);
412
+ const planUsd = (prev?.own_plan_usd ?? 0) + (e.own_plan_usd ?? 0);
381
413
  entries.unshift({
382
414
  id: e.id,
383
415
  kind: e.kind,
@@ -392,6 +424,7 @@ export class State {
392
424
  usd: sum('usd'),
393
425
  ...(own > 0 ? { own_usd: own } : {}),
394
426
  ...(plan > 0 ? { own_plan_calls: plan } : {}),
427
+ ...(planUsd > 0 ? { own_plan_usd: planUsd } : {}),
395
428
  parts: merged,
396
429
  });
397
430
  return { entries: entries.slice(0, USAGE_KEPT) };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@columenlabs/lumen-ai",
3
- "version": "0.1.252",
3
+ "version": "0.1.254",
4
4
  "description": "Lumen's assistant and agent parts: a chat assistant's server (Claude tiers, tools over MCP, the web, hand off, spend caps), serving an MCP server from a Worker, read-only tool sets and metered tool calls.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -56,8 +56,8 @@
56
56
  "@ai-sdk/openai": "4.0.108",
57
57
  "@anthropic-ai/sdk": "0.133.0",
58
58
  "@cloudflare/think": "0.23.0",
59
- "@columenlabs/lumen-ops": "0.1.252",
60
- "@columenlabs/lumen-ui-types": "0.1.252",
59
+ "@columenlabs/lumen-ops": "0.1.254",
60
+ "@columenlabs/lumen-ui-types": "0.1.254",
61
61
  "agents": "0.32.0",
62
62
  "ai": "7.0.162",
63
63
  "just-bash": "3.6.0",