eve-hub 0.6.32 → 0.6.33
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/CHANGELOG.md +6 -0
- package/mention-tokens.d.mts +1 -0
- package/mention-tokens.mjs +15 -0
- package/package.json +1 -1
- package/schedules/compact.js +2 -1
- package/schedules/dash.js +2 -1
- package/schedules/hub.d.mts +2 -1
- package/schedules/hub.mjs +27 -3
- package/world.mjs +96 -20
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# eve-hub
|
|
2
2
|
|
|
3
|
+
## 0.6.33
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Hosted hub: a schedule's run history is what its handler reports (hub.track: a start, an end and the error), so a dispatch is one run however often it loads state; a commit names its run. The mention rule moves to the kit (mention-tokens.mjs).
|
|
8
|
+
|
|
3
9
|
## 0.6.32
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function mentionTokens(text: unknown): string[];
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { codeRanges } from "./mention-gate.mjs";
|
|
2
|
+
|
|
3
|
+
// The one definition of an actual mention: `@handle` at a word boundary (not
|
|
4
|
+
// foo@bar, @@x, \@x or a URL's /@x) and outside code (codeRanges). Every reader of
|
|
5
|
+
// handles (routing, aliases, @all, notifications) takes its tokens from here; anything else
|
|
6
|
+
// is text. Returns the lowercased handles in order, duplicates kept.
|
|
7
|
+
export function mentionTokens(text) {
|
|
8
|
+
const s = String(text ?? "");
|
|
9
|
+
const code = codeRanges(s);
|
|
10
|
+
const out = [];
|
|
11
|
+
for (const match of s.matchAll(/(?<![\w@./\\-])@([a-z0-9][a-z0-9-]*)/gi)) {
|
|
12
|
+
if (!code.some(([a, b]) => match.index >= a && match.index < b)) out.push(match[1].toLowerCase());
|
|
13
|
+
}
|
|
14
|
+
return out;
|
|
15
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eve-hub",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.33",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"description": "Bot-side code shared by every hub agent: registry tools, the roster hook, the eve channel, the host sandbox and browser, and model helpers.",
|
|
6
6
|
"type": "module",
|
package/schedules/compact.js
CHANGED
package/schedules/dash.js
CHANGED
|
@@ -15,6 +15,7 @@ import { modelSummarize } from "./track-summaries.mjs";
|
|
|
15
15
|
export default defineSchedule({
|
|
16
16
|
cron: "*/8 * * * *",
|
|
17
17
|
async run() {
|
|
18
|
-
|
|
18
|
+
const hub = createHub();
|
|
19
|
+
await hub.track("dash", () => dashPass(hub, { classify: modelClassify({ evaluate }), summarize: modelSummarize({ generateText }) }));
|
|
19
20
|
},
|
|
20
21
|
});
|
package/schedules/hub.d.mts
CHANGED
|
@@ -9,10 +9,11 @@ export type Hub = {
|
|
|
9
9
|
snapshot(): Promise<Record<string, unknown>>;
|
|
10
10
|
roster(): Promise<unknown>;
|
|
11
11
|
load(id: string): Promise<unknown>;
|
|
12
|
-
loadVersioned(id: string): Promise<{ state: unknown; savedAt: string | null }>;
|
|
12
|
+
loadVersioned(id: string): Promise<{ state: unknown; savedAt: string | null; paused: boolean }>;
|
|
13
13
|
save(id: string, state: unknown): Promise<unknown>;
|
|
14
14
|
commit(id: string, withState: { state?: unknown }, posts: Post[]): Promise<unknown>;
|
|
15
15
|
post(id: string, text: string, key?: string): Promise<unknown>;
|
|
16
|
+
track<T>(id: string, fn: () => Promise<T>): Promise<T | undefined>;
|
|
16
17
|
compact(threadId: string, bot: string, by: string, report?: { trigger?: string; reportTo?: string }): Promise<{ status: number; ok: boolean; body: unknown }>;
|
|
17
18
|
dashChecked(prFacts?: Record<string, { head: string | null; ci: string }>): Promise<Ok & { checkedAt: string }>;
|
|
18
19
|
setSummary(path: string, text: string): Promise<unknown>;
|
package/schedules/hub.mjs
CHANGED
|
@@ -23,6 +23,8 @@ export function createHub({ base = registryBase(), agent = "main", fetchImpl = f
|
|
|
23
23
|
if (!r.ok) throw new Error(`${init?.method ?? "GET"} ${path}: ${r.status} ${typeof r.body === "string" ? r.body : JSON.stringify(r.body)}`.slice(0, 400));
|
|
24
24
|
return r.body;
|
|
25
25
|
};
|
|
26
|
+
// The run the hub is following for this process's handler (`track`), so a commit says which run it belongs to.
|
|
27
|
+
let run = null;
|
|
26
28
|
const threadOf = (id) => `sch-${agent}-${id.replaceAll("/", ".")}`;
|
|
27
29
|
const statePath = (id) => `/api/schedules/${encodeURIComponent(agent)}/${id.split("/").map(encodeURIComponent).join("/")}/state`;
|
|
28
30
|
return {
|
|
@@ -32,11 +34,31 @@ export function createHub({ base = registryBase(), agent = "main", fetchImpl = f
|
|
|
32
34
|
// The state and when it was saved (the version a run decided on).
|
|
33
35
|
loadVersioned: async (id) => {
|
|
34
36
|
const body = await must(statePath(id));
|
|
35
|
-
return { state: body?.state ?? null, savedAt: body?.savedAt ?? null };
|
|
37
|
+
return { state: body?.state ?? null, savedAt: body?.savedAt ?? null, paused: body?.paused === true };
|
|
36
38
|
},
|
|
37
39
|
save: (id, state) => must(statePath(id), { method: "PUT", body: JSON.stringify({ state }) }),
|
|
38
40
|
// Save `state` (if given: { state }) and post `posts` in one step.
|
|
39
|
-
commit: (id, withState, posts) => must(statePath(id), { method: "PUT", body: JSON.stringify({ ...withState, posts }) }),
|
|
41
|
+
commit: (id, withState, posts) => must(statePath(id), { method: "PUT", body: JSON.stringify({ ...withState, posts, ...(run?.id === id ? { run: run.run } : {}) }) }),
|
|
42
|
+
// One dispatch of the schedule `id`: the hub is told it started (it answers with the run's id and whether the
|
|
43
|
+
// schedule is paused, which skips `fn`) and how it ended, so the hub's run history is what the handler did. A hub
|
|
44
|
+
// that doesn't keep run history (the local one) answers 404 to the start, and `fn` runs untracked.
|
|
45
|
+
track: async (id, fn) => {
|
|
46
|
+
const started = await call(`${statePath(id).slice(0, -"/state".length)}/runs`, { method: "POST", body: "{}" }).catch(() => null);
|
|
47
|
+
if (!started?.ok) return fn();
|
|
48
|
+
if (started.body.paused) return;
|
|
49
|
+
run = { id, run: started.body.run };
|
|
50
|
+
const end = (error) => call(`${statePath(id).slice(0, -"/state".length)}/runs/${encodeURIComponent(run.run)}/end`, { method: "POST", body: JSON.stringify(error ? { error: error.slice(0, 2000) } : {}) }).catch(() => null);
|
|
51
|
+
try {
|
|
52
|
+
const result = await fn();
|
|
53
|
+
await end();
|
|
54
|
+
return result;
|
|
55
|
+
} catch (error) {
|
|
56
|
+
await end(String(error?.message ?? error) || "the run failed");
|
|
57
|
+
throw error;
|
|
58
|
+
} finally {
|
|
59
|
+
run = null;
|
|
60
|
+
}
|
|
61
|
+
},
|
|
40
62
|
// A one-off post outside a run (runOnce commits through `commit`).
|
|
41
63
|
post: (id, text, key) => must(`/api/threads/${encodeURIComponent(threadOf(id))}/messages`, { method: "POST", body: JSON.stringify({ author: "system", text, ...(key ? { clientId: key } : {}) }) }),
|
|
42
64
|
// With `report` ({ trigger, reportTo: "<bot>/<schedule>" }) the hub posts
|
|
@@ -117,10 +139,12 @@ export function toPosts(notices) {
|
|
|
117
139
|
// the same keys and the hub skips the posts it already has. Nothing is
|
|
118
140
|
// lost and nothing posted twice. `decide(snap, saved, now)` ->
|
|
119
141
|
// { posts: [text], state, changed }; `snapshot: false` skips reading
|
|
120
|
-
// /api/state (decide gets null).
|
|
142
|
+
// /api/state (decide gets null). A paused schedule (the state's `paused`)
|
|
143
|
+
// decides and commits nothing.
|
|
121
144
|
export async function runOnce(hub, id, decide, { now = Date.now(), snapshot = true } = {}) {
|
|
122
145
|
const [snap, loaded] = await Promise.all([snapshot ? hub.snapshot() : null, hub.loadVersioned(id)]);
|
|
123
146
|
const saved = loaded.state;
|
|
147
|
+
if (loaded.paused) return { posts: [], state: saved };
|
|
124
148
|
const { posts = [], state, changed } = await decide(snap, saved, now);
|
|
125
149
|
const texts = toPosts(posts.filter((t) => typeof t === "string" && t.trim()));
|
|
126
150
|
if (!changed && !texts.length) return { posts: [], state: saved };
|
package/world.mjs
CHANGED
|
@@ -545,6 +545,79 @@ function stepValues(sessionId2, step) {
|
|
|
545
545
|
return [sessionId2, step.sequence, step.stepIndex, Date.parse(step.at), b.inputTokens, b.outputTokens, b.cacheReadTokens, b.cacheWriteTokens, b.costUsd];
|
|
546
546
|
}
|
|
547
547
|
|
|
548
|
+
// mention-gate.mjs
|
|
549
|
+
function codeRanges(text) {
|
|
550
|
+
const s2 = String(text ?? "");
|
|
551
|
+
const ranges = [];
|
|
552
|
+
const lines = [];
|
|
553
|
+
for (let at = 0; at <= s2.length; ) {
|
|
554
|
+
const nl = s2.indexOf("\n", at);
|
|
555
|
+
const end = nl < 0 ? s2.length : nl;
|
|
556
|
+
lines.push({ start: at, end, text: s2.slice(at, end) });
|
|
557
|
+
if (nl < 0) break;
|
|
558
|
+
at = nl + 1;
|
|
559
|
+
}
|
|
560
|
+
const fenced = /* @__PURE__ */ new Set();
|
|
561
|
+
for (let i = 0; i < lines.length; i++) {
|
|
562
|
+
const open3 = lines[i].text.match(/^ {0,3}(`{3,}|~{3,})(.*)$/);
|
|
563
|
+
if (!open3 || open3[1][0] === "`" && open3[2].includes("`")) continue;
|
|
564
|
+
const close = new RegExp(`^ {0,3}${open3[1][0] === "`" ? "`" : "~"}{${open3[1].length},}\\s*$`);
|
|
565
|
+
const j = lines.findIndex((l, k) => k > i && close.test(l.text));
|
|
566
|
+
if (j < 0) continue;
|
|
567
|
+
ranges.push([lines[i].start, lines[j].end]);
|
|
568
|
+
for (let k = i; k <= j; k++) fenced.add(k);
|
|
569
|
+
i = j;
|
|
570
|
+
}
|
|
571
|
+
let para = [];
|
|
572
|
+
const flush = () => {
|
|
573
|
+
if (!para.length) return;
|
|
574
|
+
const from = para[0].start;
|
|
575
|
+
const t = s2.slice(from, para.at(-1).end);
|
|
576
|
+
const runs = [...t.matchAll(/`+/g)].map((m) => ({ at: m.index, n: m[0].length }));
|
|
577
|
+
for (let i = 0; i < runs.length; i++) {
|
|
578
|
+
let { at, n } = runs[i];
|
|
579
|
+
let slash = 0;
|
|
580
|
+
for (let b = at - 1; b >= 0 && t[b] === "\\"; b--) slash++;
|
|
581
|
+
if (slash % 2 === 1) {
|
|
582
|
+
at++;
|
|
583
|
+
n--;
|
|
584
|
+
if (!n) continue;
|
|
585
|
+
}
|
|
586
|
+
const j = runs.findIndex((r, k) => k > i && r.n === n);
|
|
587
|
+
if (j < 0) continue;
|
|
588
|
+
ranges.push([from + at, from + runs[j].at + n]);
|
|
589
|
+
i = j;
|
|
590
|
+
}
|
|
591
|
+
para = [];
|
|
592
|
+
};
|
|
593
|
+
lines.forEach((l, i) => {
|
|
594
|
+
if (!fenced.has(i) && l.text.trim()) para.push(l);
|
|
595
|
+
flush();
|
|
596
|
+
});
|
|
597
|
+
return ranges;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
// mention-tokens.mjs
|
|
601
|
+
function mentionTokens(text) {
|
|
602
|
+
const s2 = String(text ?? "");
|
|
603
|
+
const code = codeRanges(s2);
|
|
604
|
+
const out = [];
|
|
605
|
+
for (const match of s2.matchAll(/(?<![\w@./\\-])@([a-z0-9][a-z0-9-]*)/gi)) {
|
|
606
|
+
if (!code.some(([a, b]) => match.index >= a && match.index < b)) out.push(match[1].toLowerCase());
|
|
607
|
+
}
|
|
608
|
+
return out;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
// ../../hosted-hub/src/store/notifications.ts
|
|
612
|
+
var NOTIFICATION_READS = s("SELECT person, notification_id FROM notification_reads");
|
|
613
|
+
var NOTIFICATIONS = s("SELECT message_id, person FROM notifications");
|
|
614
|
+
var names = (text, { person, names: names2 }) => {
|
|
615
|
+
const known = new Set([person, ...names2].map((n) => n.toLowerCase()));
|
|
616
|
+
return mentionTokens(text).some((t) => known.has(t));
|
|
617
|
+
};
|
|
618
|
+
var record = (id, person) => s("INSERT OR IGNORE INTO notifications (message_id, person) SELECT ?, ? WHERE EXISTS (SELECT 1 FROM messages WHERE id = ?)", id, person, id);
|
|
619
|
+
var notificationFor = (messageId2, kind, text, notify) => notify && kind === "agent" && names(text, notify) ? [record(messageId2, notify.person)] : [];
|
|
620
|
+
|
|
548
621
|
// ../../hosted-hub/src/store/subagents.ts
|
|
549
622
|
var UP = `up(session_id, parent) AS (
|
|
550
623
|
SELECT session_id, parent FROM subagent_links WHERE session_id = ?
|
|
@@ -828,7 +901,7 @@ __export(external_exports, {
|
|
|
828
901
|
properties: () => _properties,
|
|
829
902
|
property: () => _property,
|
|
830
903
|
readonly: () => readonly,
|
|
831
|
-
record: () =>
|
|
904
|
+
record: () => record2,
|
|
832
905
|
refine: () => refine,
|
|
833
906
|
regex: () => _regex,
|
|
834
907
|
regexes: () => regexes_exports,
|
|
@@ -17271,14 +17344,14 @@ function rewriteKeyNames(ctx) {
|
|
|
17271
17344
|
bySchema.set(entry.schema, entry);
|
|
17272
17345
|
}
|
|
17273
17346
|
const rewrites = /* @__PURE__ */ new Map();
|
|
17274
|
-
for (const
|
|
17275
|
-
const seen = ctx.seen.get(
|
|
17276
|
-
const
|
|
17277
|
-
if (!
|
|
17347
|
+
for (const record4 of pendingRecords.get(ctx) ?? []) {
|
|
17348
|
+
const seen = ctx.seen.get(record4);
|
|
17349
|
+
const names2 = (seen?.def ?? seen?.schema)?.propertyNames;
|
|
17350
|
+
if (!names2 || names2 === true || rewrites.has(names2))
|
|
17278
17351
|
continue;
|
|
17279
|
-
const rewritten = stringifyKeyNames(bySchema,
|
|
17280
|
-
if (rewritten !==
|
|
17281
|
-
rewrites.set(
|
|
17352
|
+
const rewritten = stringifyKeyNames(bySchema, names2, /* @__PURE__ */ new Set());
|
|
17353
|
+
if (rewritten !== names2)
|
|
17354
|
+
rewrites.set(names2, rewritten);
|
|
17282
17355
|
}
|
|
17283
17356
|
if (!rewrites.size)
|
|
17284
17357
|
return;
|
|
@@ -17740,7 +17813,7 @@ __export(schemas_exports2, {
|
|
|
17740
17813
|
preprocess: () => preprocess,
|
|
17741
17814
|
promise: () => promise,
|
|
17742
17815
|
readonly: () => readonly,
|
|
17743
|
-
record: () =>
|
|
17816
|
+
record: () => record2,
|
|
17744
17817
|
refine: () => refine,
|
|
17745
17818
|
set: () => set,
|
|
17746
17819
|
strictObject: () => strictObject,
|
|
@@ -18842,7 +18915,7 @@ var ZodRecord = /* @__PURE__ */ $constructor("ZodRecord", (inst, def) => {
|
|
|
18842
18915
|
inst.keyType = def.keyType;
|
|
18843
18916
|
inst.valueType = def.valueType;
|
|
18844
18917
|
});
|
|
18845
|
-
function
|
|
18918
|
+
function record2(keyType, valueType, params) {
|
|
18846
18919
|
if (!valueType || !valueType._zod) {
|
|
18847
18920
|
return new ZodRecord({
|
|
18848
18921
|
type: "record",
|
|
@@ -19317,7 +19390,7 @@ var stringbool = (...args) => _stringbool({
|
|
|
19317
19390
|
}, ...args);
|
|
19318
19391
|
function json(params) {
|
|
19319
19392
|
const jsonSchema = lazy(() => {
|
|
19320
|
-
return union([string2(params), number2(), boolean2(), _null3(), array(jsonSchema),
|
|
19393
|
+
return union([string2(params), number2(), boolean2(), _null3(), array(jsonSchema), record2(string2(), jsonSchema)]);
|
|
19321
19394
|
});
|
|
19322
19395
|
return jsonSchema;
|
|
19323
19396
|
}
|
|
@@ -25254,14 +25327,14 @@ function rewriteKeyNames2(ctx) {
|
|
|
25254
25327
|
bySchema.set(entry.schema, entry);
|
|
25255
25328
|
}
|
|
25256
25329
|
const rewrites = /* @__PURE__ */ new Map();
|
|
25257
|
-
for (const
|
|
25258
|
-
const seen = ctx.seen.get(
|
|
25259
|
-
const
|
|
25260
|
-
if (!
|
|
25330
|
+
for (const record4 of pendingRecords2.get(ctx) ?? []) {
|
|
25331
|
+
const seen = ctx.seen.get(record4);
|
|
25332
|
+
const names2 = (seen?.def ?? seen?.schema)?.propertyNames;
|
|
25333
|
+
if (!names2 || names2 === true || rewrites.has(names2))
|
|
25261
25334
|
continue;
|
|
25262
|
-
const rewritten = stringifyKeyNames2(bySchema,
|
|
25263
|
-
if (rewritten !==
|
|
25264
|
-
rewrites.set(
|
|
25335
|
+
const rewritten = stringifyKeyNames2(bySchema, names2, /* @__PURE__ */ new Set());
|
|
25336
|
+
if (rewritten !== names2)
|
|
25337
|
+
rewrites.set(names2, rewritten);
|
|
25265
25338
|
}
|
|
25266
25339
|
if (!rewrites.size)
|
|
25267
25340
|
return;
|
|
@@ -26092,7 +26165,7 @@ var ZodRecord2 = /* @__PURE__ */ $constructor2("ZodRecord", (inst, def) => {
|
|
|
26092
26165
|
inst.keyType = def.keyType;
|
|
26093
26166
|
inst.valueType = def.valueType;
|
|
26094
26167
|
});
|
|
26095
|
-
function
|
|
26168
|
+
function record3(keyType, valueType, params) {
|
|
26096
26169
|
if (!valueType || !valueType._zod) {
|
|
26097
26170
|
return new ZodRecord2({
|
|
26098
26171
|
type: "record",
|
|
@@ -26362,7 +26435,7 @@ function superRefine2(fn, params) {
|
|
|
26362
26435
|
|
|
26363
26436
|
// ../wire/src/common.ts
|
|
26364
26437
|
var maybe = (schema) => schema.nullish();
|
|
26365
|
-
var map2 = (id, value) =>
|
|
26438
|
+
var map2 = (id, value) => record3(string5(), value).meta({ id });
|
|
26366
26439
|
|
|
26367
26440
|
// ../wire/src/live.ts
|
|
26368
26441
|
var TaskRow = object2({
|
|
@@ -27034,6 +27107,7 @@ var DELIVERY_STATUS = `CASE WHEN d.accepted_at IS NOT NULL OR ${LINKED} THEN COA
|
|
|
27034
27107
|
var FOLD_STATE_SQL = `SELECT s.id, s.thread_id, s.bot, COALESCE(f.folded_index, -1) AS folded_index, (SELECT json_group_array(name) FROM (SELECT name FROM bots ORDER BY position)) AS roster,
|
|
27035
27108
|
(SELECT json_group_array(bot) FROM (${PARTICIPANTS("s.thread_id")})) AS participants,
|
|
27036
27109
|
(SELECT d.message_id FROM deliveries d WHERE d.bot = s.bot AND d.held_at IS NOT NULL AND d.held_paused IS NOT 1 AND d.accepted_at IS NULL AND d.failed_at IS NULL AND d.revoked_at IS NULL ORDER BY d.rowid LIMIT 1) AS held,
|
|
27110
|
+
(SELECT json_object('person', person, 'names', json(names)) FROM human_notify) AS notify,
|
|
27037
27111
|
(SELECT json_object('status', t.status, 'id', t.id, 'text', t.reply_text, 'lastStep', json(t.last_step), 'activity', json(t.activity)) FROM turns t WHERE t.session_id = s.id AND t.status IN ${OPEN_TURN_STATUSES}
|
|
27038
27112
|
ORDER BY t.started_at DESC LIMIT 1) AS open_turn
|
|
27039
27113
|
FROM sessions s LEFT JOIN session_folds f ON f.session_id = s.id WHERE s.id = ?`;
|
|
@@ -27044,6 +27118,7 @@ function foldState(row) {
|
|
|
27044
27118
|
roster: JSON.parse(String(row.roster)),
|
|
27045
27119
|
participants: JSON.parse(String(row.participants)),
|
|
27046
27120
|
held: row.held == null ? null : String(row.held),
|
|
27121
|
+
notify: row.notify ? JSON.parse(String(row.notify)) : null,
|
|
27047
27122
|
foldedIndex: Number(row.folded_index),
|
|
27048
27123
|
open: row.open_turn ? JSON.parse(String(row.open_turn)) : null
|
|
27049
27124
|
};
|
|
@@ -27174,6 +27249,7 @@ function fold(state, chunks, now = Date.now()) {
|
|
|
27174
27249
|
SELECT ?, ?, ?, 'agent', ?, ?, ? WHERE ? <> ''
|
|
27175
27250
|
AND EXISTS (SELECT 1 FROM turns WHERE id = ? AND status NOT IN ('cancelling','cancelled') AND NOT (${REVOKED_TURN}))`, messageId2, session.thread, session.bot, text, open3.id, at, text, open3.id));
|
|
27176
27251
|
log("message.created", { messageId: messageId2, author: session.bot, authorKind: "agent" }, IF_INSERTED);
|
|
27252
|
+
stmts.push(...notificationFor(messageId2, "agent", text, state.notify ?? void 0));
|
|
27177
27253
|
const to = recipients(state.roster, text, { name: session.bot, kind: "agent" }, session.thread, state.participants);
|
|
27178
27254
|
stmts.push(...routingStatements(messageId2, session.thread, to, at, { by: session.bot, joins: true }));
|
|
27179
27255
|
deliver.push(messageId2);
|