@columenlabs/lumen-ai 0.1.134 → 0.1.136
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 +16 -1
- package/dist/assistant/bearings.d.ts +43 -0
- package/dist/assistant/bearings.js +101 -0
- package/dist/assistant/engine.d.ts +18 -4
- package/dist/assistant/engine.js +35 -13
- package/dist/assistant/index.d.ts +1 -0
- package/dist/assistant/index.js +1 -0
- package/dist/assistant/routes.js +4 -0
- package/dist/assistant/web.d.ts +5 -2
- package/dist/assistant/web.js +12 -5
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -99,7 +99,7 @@ app.all('/api/assistant/*', (c) => routes(c.req.raw));
|
|
|
99
99
|
- **ChatGPT sign-in.** In place of their own OpenAI key a person may sign in with their ChatGPT plan (`POST /my-signin`, then `POST /my-signin/finish {url}`): the sign-in opens in a new tab and ends on `http://127.0.0.1:1455/auth/callback`, a page that does not load, whose address the person pastes back. The tokens are sealed as their own key and refreshed before they run out; answers on the plan count tokens and no dollars.
|
|
100
100
|
- **Who pays.** The person's pick (the shared keys by default) while it has a key for the tier's provider and money left this month under its cap (an admin sets the shared keys' cap per person, `PUT /app`), else the other; with neither the panel says why.
|
|
101
101
|
- **Tools.** Each group is Off, Read, Read + draft or Read + write per person. A tool is a read when its MCP `readOnlyHint` is set. On Read + draft a write only drafts a card, and runs as the person when they press Save, after access is checked again. Read + write is offered only for a group with `writes: true`: its writes run at once, and with `undo` set each one's line in the chat has Undo, which calls `undo(caller, { tool, args, result })` (`POST /chats/:id/writes/:step/undo`); the model is told what was undone. Cards drafted one after another show as one stack with Save all. Use `mcpServerTools` (an SDK `McpServer`), `mcpFetchTools` (a server served as a fetch handler, any SDK), `remoteMcpTools` or `plainTools`.
|
|
102
|
-
- **Web.** `fetch` is a plain GET from the Worker (10 s, 2 MB, never the app's own hosts or private addresses). ScrapeQL's own tools join over its MCP API once an admin pastes a Cloudflare Access service token (`PUT /web/scrapeql`).
|
|
102
|
+
- **Web.** `fetch` is a plain GET from the Worker (10 s, 2 MB, never the app's own hosts or private addresses). ScrapeQL's own tools join over its MCP API once an admin pastes a Cloudflare Access service token (`PUT /web/scrapeql`). A Worker on ScrapeQL's own Cloudflare account passes its service binding instead, `scrapeql: { url: 'https://scrapeql/mcp', fetch: env.SCRAPEQL.fetch.bind(env.SCRAPEQL) }`: no token is stored, the tools are always on and Settings shows the link with a Test.
|
|
103
103
|
- **Hand off and advice.** The `escalate` tool hands one task to a higher tier (the person sets next, any or only when asked); Anthropic's advisor tool lets a higher tier send a plan, at most two an answer.
|
|
104
104
|
- **Chats** stream as server-sent events (`POST /chat`); closing the request stops the answer and keeps what was said. Past 150k tokens, or on `/compact`, the history sent to the model becomes a summary. Run `assistant.purge()` once a day to drop chats 30 days after their last message.
|
|
105
105
|
|
|
@@ -141,3 +141,18 @@ The panel's composer lists what can follow three keys as they are typed. The kit
|
|
|
141
141
|
`target` lights the thing on screen while its row is hovered. A page's source replaces a standing one with the same `id`.
|
|
142
142
|
|
|
143
143
|
Text the person highlights on the page is offered first under `#` (Selected text), with where it sits from the nearest `data-assistant-where` attribute. To hand a highlight over from the page, an Ask button calls `composeAssistant({ refs: [selectionRef({ text, where })], text: '' })` (or any refs and text): the panel opens with the chips in the composer, focused for the person to finish.
|
|
144
|
+
|
|
145
|
+
### Bearings
|
|
146
|
+
|
|
147
|
+
Each message the model gets starts with a few lines of bearings: the time now in the person's zone (the panel sends the browser's), UTC, how long since the chat's last message and the app's name. They go in the message, not the system prompt, so the prompt cache holds. An app adds its own lines with `bearings`; each call gets 300 ms, and when it is late or throws its lines are left out.
|
|
148
|
+
|
|
149
|
+
```ts
|
|
150
|
+
createAssistant({
|
|
151
|
+
// ...
|
|
152
|
+
app: 'Notes',
|
|
153
|
+
timeZone: 'America/Los_Angeles', // when the browser sends none
|
|
154
|
+
bearings: ({ now, at, span }) => `Next backup: ${at(nextBackup(now))} (in ${span(nextBackup(now) - now)})`,
|
|
155
|
+
});
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
An app with its own assistant loop builds the same block with `bearings({ timeZone, lastAt, app, extras })` and puts it before the message.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bearings: a few lines at the top of each message telling the model when and where it is (the time
|
|
3
|
+
* in the person's zone, how long since the chat's last message, the app), so it never guesses
|
|
4
|
+
* "today". Kept out of the system prompt so the prompt cache holds. An app adds its own lines (a
|
|
5
|
+
* market's session, a billing day) through `extras`; each gets `extrasMs` and is left out when it is
|
|
6
|
+
* late or throws, so bearings never hold a turn.
|
|
7
|
+
*/
|
|
8
|
+
/** What an app's extra lines are worked out from. */
|
|
9
|
+
export interface BearingsNow {
|
|
10
|
+
/** The moment, ms since the epoch. */
|
|
11
|
+
now: number;
|
|
12
|
+
/** The person's IANA zone (checked). */
|
|
13
|
+
timeZone: string;
|
|
14
|
+
/** A moment in the person's zone: `Tue, Oct 27, 2026, 3:05 PM PDT`. */
|
|
15
|
+
at: (ms: number) => string;
|
|
16
|
+
/** A span as the model reads it: `1h 55m`, `2d 3h`. */
|
|
17
|
+
span: (ms: number) => string;
|
|
18
|
+
}
|
|
19
|
+
/** An app's own bearings: lines (or one line) to add, nothing to add none. */
|
|
20
|
+
export type BearingsExtra = (b: BearingsNow) => string | string[] | null | undefined | Promise<string | string[] | null | undefined>;
|
|
21
|
+
export interface BearingsInput {
|
|
22
|
+
/** The moment; now by default. */
|
|
23
|
+
now?: number;
|
|
24
|
+
/** The person's IANA zone (the browser's); `fallbackZone` when missing or unknown. */
|
|
25
|
+
timeZone?: string;
|
|
26
|
+
/** UTC by default. */
|
|
27
|
+
fallbackZone?: string;
|
|
28
|
+
/** When this chat's last message was, ms; left out on a new chat. */
|
|
29
|
+
lastAt?: number;
|
|
30
|
+
/** The app's name (`App: …`). */
|
|
31
|
+
app?: string;
|
|
32
|
+
extras?: BearingsExtra | BearingsExtra[];
|
|
33
|
+
/** How long each extra may take; 300 ms. */
|
|
34
|
+
extrasMs?: number;
|
|
35
|
+
}
|
|
36
|
+
/** `zone` when Intl knows it, else undefined. */
|
|
37
|
+
export declare function knownZone(zone: string | undefined): string | undefined;
|
|
38
|
+
/** A moment in `timeZone`: `Tue, Oct 27, 2026, 3:05 PM PDT`. */
|
|
39
|
+
export declare function bearingTime(ms: number, timeZone: string): string;
|
|
40
|
+
/** A span: `45s`, `12m`, `1h 55m`, `2d 3h`. */
|
|
41
|
+
export declare function bearingSpan(ms: number): string;
|
|
42
|
+
/** The bearings block, ending in a blank line, to put before the person's message. */
|
|
43
|
+
export declare function bearings(input?: BearingsInput): Promise<string>;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bearings: a few lines at the top of each message telling the model when and where it is (the time
|
|
3
|
+
* in the person's zone, how long since the chat's last message, the app), so it never guesses
|
|
4
|
+
* "today". Kept out of the system prompt so the prompt cache holds. An app adds its own lines (a
|
|
5
|
+
* market's session, a billing day) through `extras`; each gets `extrasMs` and is left out when it is
|
|
6
|
+
* late or throws, so bearings never hold a turn.
|
|
7
|
+
*/
|
|
8
|
+
const EXTRAS_MS = 300;
|
|
9
|
+
/** `zone` when Intl knows it, else undefined. */
|
|
10
|
+
export function knownZone(zone) {
|
|
11
|
+
if (!zone || zone.length > 64)
|
|
12
|
+
return undefined;
|
|
13
|
+
try {
|
|
14
|
+
new Intl.DateTimeFormat('en-US', { timeZone: zone });
|
|
15
|
+
return zone;
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/** A moment in `timeZone`: `Tue, Oct 27, 2026, 3:05 PM PDT`. */
|
|
22
|
+
export function bearingTime(ms, timeZone) {
|
|
23
|
+
return new Intl.DateTimeFormat('en-US', {
|
|
24
|
+
timeZone,
|
|
25
|
+
weekday: 'short',
|
|
26
|
+
month: 'short',
|
|
27
|
+
day: 'numeric',
|
|
28
|
+
year: 'numeric',
|
|
29
|
+
hour: 'numeric',
|
|
30
|
+
minute: '2-digit',
|
|
31
|
+
timeZoneName: 'short',
|
|
32
|
+
}).format(ms);
|
|
33
|
+
}
|
|
34
|
+
/** A span: `45s`, `12m`, `1h 55m`, `2d 3h`. */
|
|
35
|
+
export function bearingSpan(ms) {
|
|
36
|
+
const m = Math.round(Math.abs(ms) / 60_000);
|
|
37
|
+
if (m < 1)
|
|
38
|
+
return `${Math.round(Math.abs(ms) / 1000)}s`;
|
|
39
|
+
if (m < 60)
|
|
40
|
+
return `${m}m`;
|
|
41
|
+
const h = Math.floor(m / 60);
|
|
42
|
+
if (h < 24)
|
|
43
|
+
return m % 60 ? `${h}h ${m % 60}m` : `${h}h`;
|
|
44
|
+
const d = Math.floor(h / 24);
|
|
45
|
+
return h % 24 ? `${d}d ${h % 24}h` : `${d}d`;
|
|
46
|
+
}
|
|
47
|
+
function ago(ms) {
|
|
48
|
+
const m = ms / 60_000;
|
|
49
|
+
if (m < 1)
|
|
50
|
+
return 'just now';
|
|
51
|
+
if (m < 60)
|
|
52
|
+
return `${Math.round(m)} min ago`;
|
|
53
|
+
const h = m / 60;
|
|
54
|
+
if (h < 24)
|
|
55
|
+
return `${Math.round(h)} h ago`;
|
|
56
|
+
const d = Math.round(h / 24);
|
|
57
|
+
return `${d} day${d === 1 ? '' : 's'} ago`;
|
|
58
|
+
}
|
|
59
|
+
async function within(extra, b, ms) {
|
|
60
|
+
let timer;
|
|
61
|
+
const late = new Promise((done) => {
|
|
62
|
+
timer = setTimeout(() => done(undefined), ms);
|
|
63
|
+
});
|
|
64
|
+
try {
|
|
65
|
+
const got = await Promise.race([Promise.resolve().then(() => extra(b)), late]);
|
|
66
|
+
if (!got)
|
|
67
|
+
return [];
|
|
68
|
+
return (Array.isArray(got) ? got : [got]).filter((l) => typeof l === 'string' && l.trim());
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return [];
|
|
72
|
+
}
|
|
73
|
+
finally {
|
|
74
|
+
clearTimeout(timer);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/** The bearings block, ending in a blank line, to put before the person's message. */
|
|
78
|
+
export async function bearings(input = {}) {
|
|
79
|
+
const now = input.now ?? Date.now();
|
|
80
|
+
const timeZone = knownZone(input.timeZone) ?? knownZone(input.fallbackZone) ?? 'UTC';
|
|
81
|
+
const b = {
|
|
82
|
+
now,
|
|
83
|
+
timeZone,
|
|
84
|
+
at: (ms) => bearingTime(ms, timeZone),
|
|
85
|
+
span: bearingSpan,
|
|
86
|
+
};
|
|
87
|
+
const utc = new Date(now).toISOString().slice(11, 16);
|
|
88
|
+
const lines = [
|
|
89
|
+
"Bearings (the server's clock: use it for today, now and how long ago; never guess the date):",
|
|
90
|
+
`Now: ${b.at(now)} (${timeZone}) · ${utc} UTC`,
|
|
91
|
+
];
|
|
92
|
+
if (input.lastAt && input.lastAt < now)
|
|
93
|
+
lines.push(`Last message in this chat: ${ago(now - input.lastAt)} (${b.at(input.lastAt)})`);
|
|
94
|
+
if (input.app)
|
|
95
|
+
lines.push(`App: ${input.app}`);
|
|
96
|
+
const extras = input.extras ? (Array.isArray(input.extras) ? input.extras : [input.extras]) : [];
|
|
97
|
+
const more = await Promise.all(extras.map((e) => within(e, b, input.extrasMs ?? EXTRAS_MS)));
|
|
98
|
+
for (const l of more.flat())
|
|
99
|
+
lines.push(l.slice(0, 400));
|
|
100
|
+
return `${lines.join('\n')}\n\n`;
|
|
101
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { AllowanceRequest, AllowanceState, AllowanceUnit, AssistantAccess, AssistantChat, AssistantChatRow, AssistantMessage, AssistantOwnWay, AssistantProvider, AssistantStatus, DraftItem, StepItem } from '@columenlabs/lumen-ui-types/assistant';
|
|
2
|
+
import { type BearingsExtra, type BearingsNow } from './bearings.js';
|
|
2
3
|
import { type ModelDef, type TaskDef, type TierDef } from './models.js';
|
|
3
4
|
import { State } from './state.js';
|
|
4
5
|
import { type AssistantStore, type Sealer } from './store.js';
|
|
@@ -123,9 +124,13 @@ export interface AssistantOptions {
|
|
|
123
124
|
web?: {
|
|
124
125
|
/** `fetch` options, or false for no fetch. */
|
|
125
126
|
fetch?: WebFetchOptions | false;
|
|
126
|
-
/**
|
|
127
|
+
/**
|
|
128
|
+
* ScrapeQL's MCP address; its tools join once an admin adds the Access token. With `fetch`
|
|
129
|
+
* (a service binding's), ScrapeQL is reached through it: no token, its tools always on.
|
|
130
|
+
*/
|
|
127
131
|
scrapeql?: {
|
|
128
132
|
url: string;
|
|
133
|
+
fetch?: typeof fetch;
|
|
129
134
|
};
|
|
130
135
|
};
|
|
131
136
|
store: AssistantStore;
|
|
@@ -136,8 +141,15 @@ export interface AssistantOptions {
|
|
|
136
141
|
defaultCap?: number;
|
|
137
142
|
/** An allowance on the shared keys a person can ask to raise (Request more). */
|
|
138
143
|
allowance?: AllowanceOptions;
|
|
139
|
-
/** Months and dates in this zone; UTC by default. */
|
|
144
|
+
/** Months and dates in this zone, and a person's zone when their browser sends none; UTC by default. */
|
|
140
145
|
timeZone?: string;
|
|
146
|
+
/** The app's name, told to the model in its bearings (`App: …`). */
|
|
147
|
+
app?: string;
|
|
148
|
+
/**
|
|
149
|
+
* The app's own lines for the bearings at the top of each message (a market's session, a billing
|
|
150
|
+
* day). Given 300 ms; late or failing, its lines are left out.
|
|
151
|
+
*/
|
|
152
|
+
bearings?: (b: BearingsNow, caller: Caller) => ReturnType<BearingsExtra>;
|
|
141
153
|
/** Chats are deleted this many days after their last message (`purge`); 30. */
|
|
142
154
|
keepDays?: number;
|
|
143
155
|
/** The fetch the model providers and ScrapeQL calls go through. */
|
|
@@ -252,8 +264,10 @@ export declare class Assistant {
|
|
|
252
264
|
detail: string;
|
|
253
265
|
at: number;
|
|
254
266
|
}>;
|
|
255
|
-
/**
|
|
256
|
-
|
|
267
|
+
/** ScrapeQL is reached over a service binding, so it needs no token. */
|
|
268
|
+
get boundScrapeql(): boolean;
|
|
269
|
+
/** Lists ScrapeQL's tools with the token (none over a binding): works or why not. */
|
|
270
|
+
testScrapeql(token: AccessToken | null): Promise<{
|
|
257
271
|
ok: boolean;
|
|
258
272
|
detail: string;
|
|
259
273
|
at: number;
|
package/dist/assistant/engine.js
CHANGED
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
import Anthropic from '@anthropic-ai/sdk';
|
|
20
20
|
import { HAND_OFF_WHYS } from '@columenlabs/lumen-ui-types/assistant';
|
|
21
21
|
import OpenAI from 'openai';
|
|
22
|
+
import { bearings } from './bearings.js';
|
|
22
23
|
import { CHATGPT_REDIRECT, SigninError, finishChatgpt, startChatgpt, testChatgpt, } from './chatgpt.js';
|
|
23
24
|
import { ALL_MODELS, DEFAULT_OUT, Meter, PROVIDERS, effortParams, } from './models.js';
|
|
24
25
|
import { DEFAULT_CAP, State, keyDocName, keyState, planDocName, waitText, } from './state.js';
|
|
@@ -357,9 +358,11 @@ export class Assistant {
|
|
|
357
358
|
web: {
|
|
358
359
|
fetch: p.web ?? true,
|
|
359
360
|
scrapeql: this.o.web?.scrapeql
|
|
360
|
-
?
|
|
361
|
-
?
|
|
362
|
-
:
|
|
361
|
+
? this.o.web.scrapeql.fetch
|
|
362
|
+
? { set: true, bound: true }
|
|
363
|
+
: caller.admin
|
|
364
|
+
? keyState(scrapeql, (d) => d.client_id)
|
|
365
|
+
: { set: !!scrapeql }
|
|
363
366
|
: null,
|
|
364
367
|
},
|
|
365
368
|
providers,
|
|
@@ -525,12 +528,13 @@ export class Assistant {
|
|
|
525
528
|
access: 'read',
|
|
526
529
|
source: webFetchTools(this.o.web?.fetch || {}),
|
|
527
530
|
});
|
|
528
|
-
const sq = this.o.web?.scrapeql
|
|
529
|
-
|
|
531
|
+
const sq = this.o.web?.scrapeql;
|
|
532
|
+
const token = sq && !sq.fetch ? await this.state.scrapeql() : null;
|
|
533
|
+
if (sq && (sq.fetch || token))
|
|
530
534
|
sources.push({
|
|
531
535
|
group: 'web',
|
|
532
536
|
access: 'read',
|
|
533
|
-
source: scrapeqlTools(
|
|
537
|
+
source: scrapeqlTools(sq.url, token, sq.fetch ?? this.o.fetch),
|
|
534
538
|
});
|
|
535
539
|
}
|
|
536
540
|
const results = await Promise.allSettled(sources.map((s) => s.source()));
|
|
@@ -708,7 +712,15 @@ export class Assistant {
|
|
|
708
712
|
return;
|
|
709
713
|
}
|
|
710
714
|
}
|
|
711
|
-
const
|
|
715
|
+
const told = await bearings({
|
|
716
|
+
timeZone: req.time_zone,
|
|
717
|
+
fallbackZone: this.o.timeZone,
|
|
718
|
+
lastAt: prev?.at,
|
|
719
|
+
app: this.o.app,
|
|
720
|
+
...(this.o.bearings ? { extras: (b) => this.o.bearings?.(b, caller) } : {}),
|
|
721
|
+
});
|
|
722
|
+
const page = told +
|
|
723
|
+
this.cardNews(doc) +
|
|
712
724
|
(req.refs?.length
|
|
713
725
|
? `The person points at these parts: they are what "this" means, and when the person asks for a change they are the only parts to change. Read anything else you need (the rest of the page, other records, your tools) for context, but leave it as it is unless they ask.\n${JSON.stringify(req.refs).slice(0, 16000)}\n\n`
|
|
714
726
|
: '') +
|
|
@@ -831,7 +843,7 @@ export class Assistant {
|
|
|
831
843
|
/** What the kit adds to the app's instructions: tools, drafts, hand off, advice, limits. */
|
|
832
844
|
rules(main, policy, advisor, fns, failed) {
|
|
833
845
|
const lines = [
|
|
834
|
-
`You are ${this.name}.
|
|
846
|
+
`You are ${this.name}. Each message starts with Bearings: the time now in the person's zone and where they are. Take today, now and how long ago from it; never guess the date.`,
|
|
835
847
|
'Answer in short plain sentences. Use the tools for facts; never guess a number a tool can give you. Call several tools at once when they do not depend on each other.',
|
|
836
848
|
'A tool marked DRAFT ONLY makes a card the person saves or dismisses; it changes nothing by itself. Say what the card will do; never claim it is done. The next message tells you how each card ended.',
|
|
837
849
|
'A tool marked WRITES AT ONCE makes its change when you call it; the person can undo it from the chat. Change only what they asked for, and say what you changed.',
|
|
@@ -1636,20 +1648,30 @@ export class Assistant {
|
|
|
1636
1648
|
return { ok: false, detail: shownError(err).message, at };
|
|
1637
1649
|
}
|
|
1638
1650
|
}
|
|
1639
|
-
/**
|
|
1651
|
+
/** ScrapeQL is reached over a service binding, so it needs no token. */
|
|
1652
|
+
get boundScrapeql() {
|
|
1653
|
+
return !!this.o.web?.scrapeql?.fetch;
|
|
1654
|
+
}
|
|
1655
|
+
/** Lists ScrapeQL's tools with the token (none over a binding): works or why not. */
|
|
1640
1656
|
async testScrapeql(token) {
|
|
1641
1657
|
const at = Date.now();
|
|
1642
|
-
const
|
|
1643
|
-
if (!
|
|
1658
|
+
const sq = this.o.web?.scrapeql;
|
|
1659
|
+
if (!sq)
|
|
1644
1660
|
return { ok: false, detail: 'ScrapeQL is not set up in this app.', at };
|
|
1645
1661
|
try {
|
|
1646
|
-
const open = await scrapeqlTools(url, token, this.o.fetch)();
|
|
1662
|
+
const open = await scrapeqlTools(sq.url, sq.fetch ? null : token, sq.fetch ?? this.o.fetch)();
|
|
1647
1663
|
const n = open.tools.length;
|
|
1648
1664
|
await open.close();
|
|
1649
1665
|
return { ok: true, detail: `ScrapeQL answered with ${n} tool${n === 1 ? '' : 's'}.`, at };
|
|
1650
1666
|
}
|
|
1651
1667
|
catch {
|
|
1652
|
-
return {
|
|
1668
|
+
return {
|
|
1669
|
+
ok: false,
|
|
1670
|
+
detail: sq.fetch
|
|
1671
|
+
? 'ScrapeQL could not be reached over the link.'
|
|
1672
|
+
: 'ScrapeQL refused the token or could not be reached.',
|
|
1673
|
+
at,
|
|
1674
|
+
};
|
|
1653
1675
|
}
|
|
1654
1676
|
}
|
|
1655
1677
|
// ---------------------------------------------------------------- drafts
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
* The ChatGPT plan sign-in's parts are exported on their own too, for an app that keeps the tokens and
|
|
13
13
|
* calls OpenAI itself (`LumenChatgptSignin` is the panel's part for it).
|
|
14
14
|
*/
|
|
15
|
+
export * from './bearings.js';
|
|
15
16
|
export * from './chatgpt.js';
|
|
16
17
|
export * from './engine.js';
|
|
17
18
|
export * from './models.js';
|
package/dist/assistant/index.js
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
* The ChatGPT plan sign-in's parts are exported on their own too, for an app that keeps the tokens and
|
|
13
13
|
* calls OpenAI itself (`LumenChatgptSignin` is the panel's part for it).
|
|
14
14
|
*/
|
|
15
|
+
export * from './bearings.js';
|
|
15
16
|
export * from './chatgpt.js';
|
|
16
17
|
export * from './engine.js';
|
|
17
18
|
export * from './models.js';
|
package/dist/assistant/routes.js
CHANGED
|
@@ -273,6 +273,8 @@ export function assistantRoutes(app, o) {
|
|
|
273
273
|
const no = admin();
|
|
274
274
|
if (no)
|
|
275
275
|
return no;
|
|
276
|
+
if (a.boundScrapeql)
|
|
277
|
+
return fail("ScrapeQL is reached over the app's link; it needs no token.", 409, 'bound');
|
|
276
278
|
const b = await body(req);
|
|
277
279
|
const client_id = str(b.client_id, 200);
|
|
278
280
|
const client_secret = str(b.client_secret, 400);
|
|
@@ -300,6 +302,8 @@ export function assistantRoutes(app, o) {
|
|
|
300
302
|
const no = admin();
|
|
301
303
|
if (no)
|
|
302
304
|
return no;
|
|
305
|
+
if (a.boundScrapeql)
|
|
306
|
+
return json(await a.testScrapeql(null));
|
|
303
307
|
const d = await a.state.scrapeql();
|
|
304
308
|
if (!d)
|
|
305
309
|
return fail('No ScrapeQL token is set.', 404);
|
package/dist/assistant/web.d.ts
CHANGED
|
@@ -41,5 +41,8 @@ export interface AccessToken {
|
|
|
41
41
|
client_id: string;
|
|
42
42
|
client_secret: string;
|
|
43
43
|
}
|
|
44
|
-
/**
|
|
45
|
-
|
|
44
|
+
/**
|
|
45
|
+
* ScrapeQL's MCP tools over its API, with an Access service token, or none over a service
|
|
46
|
+
* binding (`f` is then the binding's fetch). Every one of them reads.
|
|
47
|
+
*/
|
|
48
|
+
export declare function scrapeqlTools(url: string, token: AccessToken | null, f?: typeof fetch): ToolSource;
|
package/dist/assistant/web.js
CHANGED
|
@@ -155,14 +155,21 @@ export function webFetchTools(o = {}) {
|
|
|
155
155
|
return open;
|
|
156
156
|
};
|
|
157
157
|
}
|
|
158
|
-
/**
|
|
158
|
+
/**
|
|
159
|
+
* ScrapeQL's MCP tools over its API, with an Access service token, or none over a service
|
|
160
|
+
* binding (`f` is then the binding's fetch). Every one of them reads.
|
|
161
|
+
*/
|
|
159
162
|
export function scrapeqlTools(url, token, f) {
|
|
160
163
|
const remote = remoteMcpTools({
|
|
161
164
|
url,
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
165
|
+
...(token
|
|
166
|
+
? {
|
|
167
|
+
headers: {
|
|
168
|
+
'CF-Access-Client-Id': token.client_id,
|
|
169
|
+
'CF-Access-Client-Secret': token.client_secret,
|
|
170
|
+
},
|
|
171
|
+
}
|
|
172
|
+
: {}),
|
|
166
173
|
...(f ? { fetch: f } : {}),
|
|
167
174
|
});
|
|
168
175
|
return async () => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@columenlabs/lumen-ai",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.136",
|
|
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",
|
|
@@ -41,8 +41,8 @@
|
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@anthropic-ai/sdk": "0.133.0",
|
|
44
|
-
"@columenlabs/lumen-ops": "0.1.
|
|
45
|
-
"@columenlabs/lumen-ui-types": "0.1.
|
|
44
|
+
"@columenlabs/lumen-ops": "0.1.136",
|
|
45
|
+
"@columenlabs/lumen-ui-types": "0.1.136",
|
|
46
46
|
"openai": "7.32.0",
|
|
47
47
|
"zod": "^4.3.6"
|
|
48
48
|
},
|