@columenlabs/lumen-ai 0.1.230 → 0.1.231
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 +8 -0
- package/dist/assistant/engine.d.ts +30 -2
- package/dist/assistant/engine.js +52 -12
- package/dist/assistant/routes.js +9 -0
- package/dist/assistant/state.d.ts +14 -3
- package/dist/assistant/state.js +24 -8
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -87,7 +87,15 @@ const assistant = createAssistant({
|
|
|
87
87
|
sealer, // the app's own sealing: keys and chats are stored sealed
|
|
88
88
|
// A provider's key when none is set in Settings.
|
|
89
89
|
appKey: async (provider) => (provider === 'openai' ? env.OPENAI_API_KEY : env.ANTHROPIC_API_KEY),
|
|
90
|
+
// Bring your own AI, per caller (a feature flag, a person's plan): which own ways Settings offers
|
|
91
|
+
// and the routes accept, whether their own key falls back to the shared one, and who finishes a
|
|
92
|
+
// turn when a ChatGPT plan runs out ('shared': the same model on the app's key, shared spend).
|
|
93
|
+
// ownWays: async (caller) => ({ openai: ['key', 'plan'] }),
|
|
94
|
+
// ownFallback: async (caller) => true,
|
|
95
|
+
// planFallback: 'shared',
|
|
90
96
|
});
|
|
97
|
+
// A check the app always runs on its own key, whoever pays the person's answers:
|
|
98
|
+
// await assistant.complete(caller, { task: 'scope', system, prompt, payer: 'app' });
|
|
91
99
|
|
|
92
100
|
const routes = assistantRoutes(assistant, {
|
|
93
101
|
base: '/api/assistant',
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type { AllowanceRequest, AllowanceState, AllowanceUnit, AssistantAccess, AssistantChat, AssistantChatRow, AssistantEffort, AssistantMessage, AssistantOwnWay, AssistantProvider, AssistantStatus, UsagePart } from '@columenlabs/lumen-ui-types/assistant';
|
|
1
|
+
import type { AllowanceRequest, AllowanceState, AllowanceUnit, AssistantAccess, AssistantChat, AssistantChatRow, AssistantEffort, AssistantMessage, AssistantOwnWay, AssistantPayer, AssistantProvider, AssistantStatus, UsagePart } from '@columenlabs/lumen-ui-types/assistant';
|
|
2
2
|
import type { AgentTurnEnd, AgentTurnPlan } from '../agent/agent.js';
|
|
3
3
|
import { type BearingsExtra, type BearingsNow } from './bearings.js';
|
|
4
4
|
import { type ModelDef, type TaskDef, type TierDef } from './models.js';
|
|
5
|
-
import { State } from './state.js';
|
|
5
|
+
import { type OwnWays, State } from './state.js';
|
|
6
6
|
import { type AssistantStore, type Sealer } from './store.js';
|
|
7
7
|
import { type ToolSource } from './tools.js';
|
|
8
8
|
import { type AccessToken, type WebFetchOptions } from './web.js';
|
|
@@ -194,6 +194,23 @@ export interface AssistantOptions {
|
|
|
194
194
|
keepDays?: number;
|
|
195
195
|
/** The fetch the model providers and ScrapeQL calls go through. */
|
|
196
196
|
fetch?: typeof fetch;
|
|
197
|
+
/**
|
|
198
|
+
* Which of their own ways (an API key, a plan sign-in) a person may pay with, per provider: a
|
|
199
|
+
* provider left out has none, and Settings shows only these. Left out, every way of every
|
|
200
|
+
* provider (feature flags or a person's plan decide it per caller).
|
|
201
|
+
*/
|
|
202
|
+
ownWays?: (caller: Caller) => OwnWays | Promise<OwnWays>;
|
|
203
|
+
/**
|
|
204
|
+
* Whether a person who pays with their own key first moves to the shared key when theirs is at
|
|
205
|
+
* its cap or their plan is used up. True by default.
|
|
206
|
+
*/
|
|
207
|
+
ownFallback?: (caller: Caller) => boolean | Promise<boolean>;
|
|
208
|
+
/**
|
|
209
|
+
* Who answers when a ChatGPT plan reaches its usage limit mid-turn. 'claude' (default): the
|
|
210
|
+
* app's smallest Claude on the same payer's Anthropic key. 'shared': the same model on the app's
|
|
211
|
+
* shared key, counted as shared spend (only when `ownFallback` allows it).
|
|
212
|
+
*/
|
|
213
|
+
planFallback?: 'claude' | 'shared';
|
|
197
214
|
onStep?: (s: StepRecord) => void | Promise<void>;
|
|
198
215
|
}
|
|
199
216
|
/** A turn's failure the person can act on. */
|
|
@@ -250,6 +267,12 @@ export declare class Assistant {
|
|
|
250
267
|
private mainTier;
|
|
251
268
|
private access;
|
|
252
269
|
private groupById;
|
|
270
|
+
/** The own ways the app lets this person pay with (`ownWays`); undefined is every way. */
|
|
271
|
+
ownWaysOf(caller: Caller): Promise<OwnWays | undefined>;
|
|
272
|
+
/** Whether this person may use `way` of their own on `provider`. */
|
|
273
|
+
mayOwn(caller: Caller, provider: AssistantProvider, way: AssistantOwnWay): Promise<boolean>;
|
|
274
|
+
/** The app's rules for who pays this person's answers (own ways, fallback). */
|
|
275
|
+
private payingRules;
|
|
253
276
|
/**
|
|
254
277
|
* One turn as a Think agent's plan (`LumenAgent` in `@columenlabs/lumen-ai/agent`): the person's
|
|
255
278
|
* tier and its model on whoever pays, the tool groups at their access, the kit's rules, the
|
|
@@ -349,6 +372,11 @@ export declare class Assistant {
|
|
|
349
372
|
* task does more than one kind of work (a first try, a repair). Left out, it is the task.
|
|
350
373
|
*/
|
|
351
374
|
tag?: string;
|
|
375
|
+
/**
|
|
376
|
+
* Only this payer: 'app' runs the call on the shared key whatever the person pays with (a
|
|
377
|
+
* check the app always runs on its own key). Left out, whoever pays the person's answers.
|
|
378
|
+
*/
|
|
379
|
+
payer?: AssistantPayer;
|
|
352
380
|
}): Promise<{
|
|
353
381
|
text: string;
|
|
354
382
|
usd: number;
|
package/dist/assistant/engine.js
CHANGED
|
@@ -229,6 +229,23 @@ export class Assistant {
|
|
|
229
229
|
groupById(id) {
|
|
230
230
|
return this.groups.find((g) => g.id === id);
|
|
231
231
|
}
|
|
232
|
+
/** The own ways the app lets this person pay with (`ownWays`); undefined is every way. */
|
|
233
|
+
async ownWaysOf(caller) {
|
|
234
|
+
return this.o.ownWays ? await this.o.ownWays(caller) : undefined;
|
|
235
|
+
}
|
|
236
|
+
/** Whether this person may use `way` of their own on `provider`. */
|
|
237
|
+
async mayOwn(caller, provider, way) {
|
|
238
|
+
const ways = await this.ownWaysOf(caller);
|
|
239
|
+
return !ways || !!ways[provider]?.includes(way);
|
|
240
|
+
}
|
|
241
|
+
/** The app's rules for who pays this person's answers (own ways, fallback). */
|
|
242
|
+
async payingRules(caller, only) {
|
|
243
|
+
const [ways, fallback] = await Promise.all([
|
|
244
|
+
this.ownWaysOf(caller),
|
|
245
|
+
this.o.ownFallback ? this.o.ownFallback(caller) : true,
|
|
246
|
+
]);
|
|
247
|
+
return { ...(ways ? { ways } : {}), fallback, ...(only ? { only } : {}) };
|
|
248
|
+
}
|
|
232
249
|
// ---------------------------------------------------------------- a turn on Think
|
|
233
250
|
/**
|
|
234
251
|
* One turn as a Think agent's plan (`LumenAgent` in `@columenlabs/lumen-ai/agent`): the person's
|
|
@@ -246,7 +263,8 @@ export class Assistant {
|
|
|
246
263
|
? this.tierById(input.tier)
|
|
247
264
|
: undefined;
|
|
248
265
|
const main = await this.session(caller, lower ?? picked);
|
|
249
|
-
const
|
|
266
|
+
const rules = await this.payingRules(caller);
|
|
267
|
+
const { paying, wait } = await this.state.paying(caller.id, main.model.provider, rules);
|
|
250
268
|
if (!paying) {
|
|
251
269
|
const code = wait ?? 'not_connected';
|
|
252
270
|
throw new AssistantError(waitText(code, main.model.provider, !!this.o.allowance), code, 402);
|
|
@@ -256,13 +274,26 @@ export class Assistant {
|
|
|
256
274
|
return apiKey ? { apiKey, ...(paying.plans[provider] ? { plan: true } : {}) } : null;
|
|
257
275
|
};
|
|
258
276
|
const key = keyOf(main.model.provider) ?? { apiKey: paying.apiKey };
|
|
259
|
-
// A ChatGPT plan used up for now: the
|
|
260
|
-
// the
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
277
|
+
// A ChatGPT plan used up for now: rather than the turn ending in the limit's error, the same
|
|
278
|
+
// model on the app's shared key ('shared'), or the app's first Claude (its smallest) on the
|
|
279
|
+
// same payer's Anthropic key.
|
|
280
|
+
let fallback;
|
|
281
|
+
let sharedLabel;
|
|
282
|
+
if (key.plan && this.o.planFallback === 'shared') {
|
|
283
|
+
const shared = rules.fallback !== false
|
|
284
|
+
? (await this.state.paying(caller.id, main.model.provider, { only: 'app' })).paying
|
|
285
|
+
: null;
|
|
286
|
+
if (shared) {
|
|
287
|
+
sharedLabel = `${main.meter.label} · shared key`;
|
|
288
|
+
fallback = { model: main.model, key: { apiKey: shared.apiKey }, label: sharedLabel };
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
else if (key.plan) {
|
|
292
|
+
const claude = this.models.find((m) => m.provider === 'anthropic');
|
|
293
|
+
const claudeKey = claude ? keyOf('anthropic') : null;
|
|
294
|
+
if (claude && claudeKey)
|
|
295
|
+
fallback = { model: claude, key: claudeKey, label: `${main.tier.label} · ${claude.label}` };
|
|
296
|
+
}
|
|
266
297
|
const p = await this.state.person(caller.id);
|
|
267
298
|
const policy = p.hand_off ?? 'next';
|
|
268
299
|
const advisor = await this.advisorFor(caller, p.advice, main);
|
|
@@ -341,9 +372,16 @@ export class Assistant {
|
|
|
341
372
|
: {},
|
|
342
373
|
};
|
|
343
374
|
const settle = async (end) => {
|
|
375
|
+
// What the shared key answered after a plan ran out is the app's spend, not the person's.
|
|
376
|
+
const onShared = sharedLabel ? end.parts.filter((x) => x.label === sharedLabel) : [];
|
|
377
|
+
const onPayer = onShared.length ? end.parts.filter((x) => !onShared.includes(x)) : end.parts;
|
|
344
378
|
await this.state
|
|
345
|
-
.addSpend(caller.id, paying.payer,
|
|
379
|
+
.addSpend(caller.id, paying.payer, onPayer, { task: 'chat', ms: end.ms })
|
|
346
380
|
.catch(() => undefined);
|
|
381
|
+
if (onShared.length)
|
|
382
|
+
await this.state
|
|
383
|
+
.addSpend(caller.id, 'app', onShared, { task: 'chat' })
|
|
384
|
+
.catch(() => undefined);
|
|
347
385
|
if (input.chat)
|
|
348
386
|
await this.state
|
|
349
387
|
.addUsage(caller.id, { id: `chat:${input.chat}`, kind: 'chat', title: message.split('\n')[0] || 'Chat' }, end.parts)
|
|
@@ -495,6 +533,7 @@ export class Assistant {
|
|
|
495
533
|
const mainModel = (await this.pick(caller, main)).model;
|
|
496
534
|
// The binding's last test has no token doc to sit in; it is read beside the rest.
|
|
497
535
|
const linkTest = this.o.web?.scrapeql?.fetch ? this.state.scrapeqlLink() : null;
|
|
536
|
+
const ownWays = await this.ownWaysOf(caller);
|
|
498
537
|
const [p, providers, scrapeql, spend, appCap, ownCap, { paying, wait }] = await Promise.all([
|
|
499
538
|
this.state.person(caller.id),
|
|
500
539
|
Promise.all(this.providers.map(async (id) => {
|
|
@@ -520,13 +559,14 @@ export class Assistant {
|
|
|
520
559
|
? { plan: keyState(plan?.doc ?? null, (d) => d.api_key) }
|
|
521
560
|
: {}),
|
|
522
561
|
...(use ? { use } : {}),
|
|
562
|
+
...(ownWays ? { own_ways: ownWays[id] ?? [] } : {}),
|
|
523
563
|
};
|
|
524
564
|
})),
|
|
525
565
|
this.o.web?.scrapeql ? this.state.scrapeql() : Promise.resolve(null),
|
|
526
566
|
this.state.spend(caller.id),
|
|
527
567
|
this.state.appCap(),
|
|
528
568
|
this.state.ownCap(caller.id),
|
|
529
|
-
this.state.paying(caller.id, mainModel.provider),
|
|
569
|
+
this.payingRules(caller).then((r) => this.state.paying(caller.id, mainModel.provider, r)),
|
|
530
570
|
]);
|
|
531
571
|
const link = await linkTest;
|
|
532
572
|
const tiers = await Promise.all(this.tiers.map(async (t) => {
|
|
@@ -925,7 +965,7 @@ export class Assistant {
|
|
|
925
965
|
async complete(caller, o) {
|
|
926
966
|
const began = Date.now();
|
|
927
967
|
const s = await this.session(caller, await this.mainTier(caller, o.task));
|
|
928
|
-
const { paying, wait } = await this.state.paying(caller.id, s.model.provider);
|
|
968
|
+
const { paying, wait } = await this.state.paying(caller.id, s.model.provider, await this.payingRules(caller, o.payer));
|
|
929
969
|
if (!paying) {
|
|
930
970
|
const code = wait ?? 'not_connected';
|
|
931
971
|
throw new AssistantError(waitText(code, s.model.provider, !!this.o.allowance), code, 402);
|
|
@@ -1012,7 +1052,7 @@ export class Assistant {
|
|
|
1012
1052
|
*/
|
|
1013
1053
|
async image(caller, o) {
|
|
1014
1054
|
const began = Date.now();
|
|
1015
|
-
const { paying, wait } = await this.state.paying(caller.id, 'openai');
|
|
1055
|
+
const { paying, wait } = await this.state.paying(caller.id, 'openai', await this.payingRules(caller));
|
|
1016
1056
|
if (!paying) {
|
|
1017
1057
|
const code = wait ?? 'not_connected';
|
|
1018
1058
|
throw new AssistantError(waitText(code, 'openai', !!this.o.allowance), code, 402);
|
package/dist/assistant/routes.js
CHANGED
|
@@ -173,6 +173,15 @@ export function assistantRoutes(app, o) {
|
|
|
173
173
|
return fail(`This app runs no ${asked} models.`, 404);
|
|
174
174
|
const prov = provider ?? 'anthropic';
|
|
175
175
|
const docName = keyDocName(prov);
|
|
176
|
+
// Adding a way of their own the app does not let this person use (`ownWays`); removing one
|
|
177
|
+
// always works.
|
|
178
|
+
const addsOwn = route === 'PUT /my-key'
|
|
179
|
+
? 'key'
|
|
180
|
+
: route === 'POST /my-signin' || route === 'POST /my-signin/finish'
|
|
181
|
+
? 'plan'
|
|
182
|
+
: null;
|
|
183
|
+
if (addsOwn && !(await a.mayOwn(caller, prov, addsOwn)))
|
|
184
|
+
return fail("That isn't available on your account.", 403, 'forbidden');
|
|
176
185
|
try {
|
|
177
186
|
switch (route) {
|
|
178
187
|
case 'GET /status':
|
|
@@ -97,6 +97,17 @@ export interface RequestsDoc {
|
|
|
97
97
|
export type SpendRow = UsagePart & {
|
|
98
98
|
payer: AssistantPayer;
|
|
99
99
|
};
|
|
100
|
+
/** The ways of their own a person may pay with, per provider; a provider left out has none. */
|
|
101
|
+
export type OwnWays = Partial<Record<AssistantProvider, AssistantOwnWay[]>>;
|
|
102
|
+
/** How `paying` may choose: the app's limits on a person's own keys, and one payer only. */
|
|
103
|
+
export interface PayingRules {
|
|
104
|
+
/** The own ways this person may use (`AssistantOptions.ownWays`); left out, every way. */
|
|
105
|
+
ways?: OwnWays;
|
|
106
|
+
/** False: a person who pays with their own key first never moves to the shared key. */
|
|
107
|
+
fallback?: boolean;
|
|
108
|
+
/** Only this payer: an app's own check always runs on the shared key (`complete({ payer })`). */
|
|
109
|
+
only?: AssistantPayer;
|
|
110
|
+
}
|
|
100
111
|
/**
|
|
101
112
|
* One task's use of one model this month: what a kind of work cost (a chat answer, a background
|
|
102
113
|
* task), beside `rows`, which stay per tier and payer.
|
|
@@ -219,7 +230,7 @@ export declare class State {
|
|
|
219
230
|
/** The plan's access token, refreshed first when it is about to run out; null once it fails. */
|
|
220
231
|
planToken(id: string, provider: AssistantProvider): Promise<string | null>;
|
|
221
232
|
/** The person's own key or plan, the one in use first, the other when that one fails. */
|
|
222
|
-
ownApiKey(id: string, provider: AssistantProvider): Promise<{
|
|
233
|
+
ownApiKey(id: string, provider: AssistantProvider, ways?: OwnWays): Promise<{
|
|
223
234
|
key: string;
|
|
224
235
|
plan: boolean;
|
|
225
236
|
} | null>;
|
|
@@ -253,12 +264,12 @@ export declare class State {
|
|
|
253
264
|
/** One month's history (this month by default) beside its spend, and the months that have one. */
|
|
254
265
|
usage(id: string, month?: string): Promise<UsageHistory>;
|
|
255
266
|
/** The payer's key of each provider, those set, and which are plan sign-ins. */
|
|
256
|
-
keysOf(id: string, payer: AssistantPayer): Promise<{
|
|
267
|
+
keysOf(id: string, payer: AssistantPayer, ways?: OwnWays): Promise<{
|
|
257
268
|
keys: Partial<Record<"anthropic" | "openai" | "xai", string>>;
|
|
258
269
|
plans: Partial<Record<"anthropic" | "openai" | "xai", boolean>>;
|
|
259
270
|
}>;
|
|
260
271
|
/** Who pays the person's next answer on `provider`'s model, or why nobody can. */
|
|
261
|
-
paying(id: string, provider: AssistantProvider): Promise<{
|
|
272
|
+
paying(id: string, provider: AssistantProvider, rules?: PayingRules): Promise<{
|
|
262
273
|
paying: Paying | null;
|
|
263
274
|
wait: AssistantWait | null;
|
|
264
275
|
}>;
|
package/dist/assistant/state.js
CHANGED
|
@@ -193,7 +193,10 @@ export class State {
|
|
|
193
193
|
}
|
|
194
194
|
}
|
|
195
195
|
/** The person's own key or plan, the one in use first, the other when that one fails. */
|
|
196
|
-
async ownApiKey(id, provider) {
|
|
196
|
+
async ownApiKey(id, provider, ways) {
|
|
197
|
+
const allowed = ways ? (ways[provider] ?? []) : null;
|
|
198
|
+
if (allowed && !allowed.length)
|
|
199
|
+
return null;
|
|
197
200
|
const use = await this.ownUse(id, provider);
|
|
198
201
|
if (!use)
|
|
199
202
|
return null;
|
|
@@ -205,7 +208,12 @@ export class State {
|
|
|
205
208
|
const t = await this.planToken(id, provider);
|
|
206
209
|
return t ? { key: t, plan: true } : null;
|
|
207
210
|
};
|
|
208
|
-
|
|
211
|
+
const may = (w) => !allowed || allowed.includes(w);
|
|
212
|
+
const tryKey = async () => (may('key') ? key() : null);
|
|
213
|
+
const tryPlan = async () => (may('plan') ? plan() : null);
|
|
214
|
+
return use === 'key'
|
|
215
|
+
? ((await tryKey()) ?? (await tryPlan()))
|
|
216
|
+
: ((await tryPlan()) ?? (await tryKey()));
|
|
209
217
|
}
|
|
210
218
|
/** USD a month on the person's own keys. */
|
|
211
219
|
async ownCap(id) {
|
|
@@ -340,11 +348,11 @@ export class State {
|
|
|
340
348
|
};
|
|
341
349
|
}
|
|
342
350
|
/** The payer's key of each provider, those set, and which are plan sign-ins. */
|
|
343
|
-
async keysOf(id, payer) {
|
|
351
|
+
async keysOf(id, payer, ways) {
|
|
344
352
|
const ids = Object.keys(PROVIDERS);
|
|
345
353
|
const found = await Promise.all(ids.map((p) => payer === 'app'
|
|
346
354
|
? this.appApiKey(p).then((k) => (k ? { key: k.key, plan: false } : null))
|
|
347
|
-
: this.ownApiKey(id, p)));
|
|
355
|
+
: this.ownApiKey(id, p, ways)));
|
|
348
356
|
const keys = {};
|
|
349
357
|
const plans = {};
|
|
350
358
|
ids.forEach((p, i) => {
|
|
@@ -357,11 +365,11 @@ export class State {
|
|
|
357
365
|
return { keys, plans };
|
|
358
366
|
}
|
|
359
367
|
/** Who pays the person's next answer on `provider`'s model, or why nobody can. */
|
|
360
|
-
async paying(id, provider) {
|
|
368
|
+
async paying(id, provider, rules = {}) {
|
|
361
369
|
const [person, appKeys, ownKeys, spend, ownCap] = await Promise.all([
|
|
362
370
|
this.person(id),
|
|
363
371
|
this.keysOf(id, 'app'),
|
|
364
|
-
this.keysOf(id, 'own'),
|
|
372
|
+
this.keysOf(id, 'own', rules.ways),
|
|
365
373
|
this.spend(id),
|
|
366
374
|
this.ownCap(id),
|
|
367
375
|
]);
|
|
@@ -389,8 +397,16 @@ export class State {
|
|
|
389
397
|
}
|
|
390
398
|
: null,
|
|
391
399
|
};
|
|
392
|
-
const first = person.pay ?? 'app';
|
|
393
|
-
const order =
|
|
400
|
+
const first = rules.only ?? person.pay ?? 'app';
|
|
401
|
+
const order = rules.only
|
|
402
|
+
? [rules.only]
|
|
403
|
+
: first === 'own'
|
|
404
|
+
? rules.fallback === false && sources.own
|
|
405
|
+
? ['own']
|
|
406
|
+
: ['own', 'app']
|
|
407
|
+
: ['app', 'own'];
|
|
408
|
+
if (rules.only)
|
|
409
|
+
sources[rules.only === 'app' ? 'own' : 'app'] = null;
|
|
394
410
|
for (const p of order) {
|
|
395
411
|
const s = sources[p];
|
|
396
412
|
if (s && s.left > 0)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@columenlabs/lumen-ai",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.231",
|
|
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",
|
|
@@ -52,8 +52,8 @@
|
|
|
52
52
|
"@ai-sdk/openai": "4.0.108",
|
|
53
53
|
"@anthropic-ai/sdk": "0.133.0",
|
|
54
54
|
"@cloudflare/think": "0.23.0",
|
|
55
|
-
"@columenlabs/lumen-ops": "0.1.
|
|
56
|
-
"@columenlabs/lumen-ui-types": "0.1.
|
|
55
|
+
"@columenlabs/lumen-ops": "0.1.231",
|
|
56
|
+
"@columenlabs/lumen-ui-types": "0.1.231",
|
|
57
57
|
"agents": "0.32.0",
|
|
58
58
|
"ai": "7.0.162",
|
|
59
59
|
"just-bash": "3.6.0",
|