eve-hub 0.6.22 → 0.6.24
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 +12 -0
- package/package.json +1 -1
- package/stubs.mjs +5 -1
- package/sync-kit.mjs +9 -4
- package/templates/execution-context-hosted-main.md +1 -1
- package/world.mjs +16 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# eve-hub
|
|
2
2
|
|
|
3
|
+
## 0.6.24
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- A hosted main keeps its dash and compact schedules: the deploy build and the kit bump write them into a repo that lacks them, and skip a stub whose slot the repo already authors in another source format (dash.mjs, dash.md), which eve would fail as two sources for one slot. Hosted dash has no PR data and compact has nothing to compact until hosted sessions record their last model call; prune stays off.
|
|
8
|
+
|
|
9
|
+
## 0.6.23
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- A bot's world folds queued messages: a turn that ends releases the messages held for it.
|
|
14
|
+
|
|
3
15
|
## 0.6.22
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eve-hub",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.24",
|
|
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/stubs.mjs
CHANGED
|
@@ -53,9 +53,13 @@ export const MAIN_KIT_FILES = {
|
|
|
53
53
|
// the mention gate (the
|
|
54
54
|
// daemon's per-process HUB_AGENT and patched model credential), the tools that
|
|
55
55
|
// remove or restart bots on the hub's disk (a deployment changes its roster by
|
|
56
|
-
// commit; create_agent is that commit, made by the hosted hub), memory (the workspace's files), and main's
|
|
56
|
+
// commit; create_agent is that commit, made by the hosted hub), memory (the workspace's files), and main's prune schedule (it cleans the hub machine's disk).
|
|
57
|
+
// main's compact and dash schedules stay: off the hub's machine compact finds no session to compact
|
|
58
|
+
// (hosted sessions carry no last-model-call time), and dash has no PR data (its lookup runs `gh`).
|
|
57
59
|
export const DEPLOYED_KIT_FILES = new Set([
|
|
58
60
|
"channels/eve.ts",
|
|
61
|
+
"schedules/compact.ts",
|
|
62
|
+
"schedules/dash.ts",
|
|
59
63
|
"skills/onboarding-profile.ts",
|
|
60
64
|
"tools/onboarding_scripts.ts",
|
|
61
65
|
...["react", "show", "update_thread", "list_threads", "read_thread", "post_message"].map((t) => `tools/${t}.ts`),
|
package/sync-kit.mjs
CHANGED
|
@@ -12,14 +12,18 @@ import {
|
|
|
12
12
|
const KIT_DIRS = ["tools", "skills", "hooks", "channels", "schedules", "instructions", "memory", "connections"];
|
|
13
13
|
const SUBAGENT = /^(?:subagents\/[^/]+\/)+/;
|
|
14
14
|
const STUB_FILE = new RegExp(`^(${KIT_DIRS.join("|")})/[^/]+\\.ts$`);
|
|
15
|
+
// eve takes one source per slot (dash.ts, dash.mjs and dash.md all claim schedules/dash); a .d.ts, an asset, or a markdown file eve doesn't read there (only schedules and instructions take .md) claims none.
|
|
16
|
+
const SOURCE_FILE = new RegExp(`^(?:(?:${KIT_DIRS.join("|")})/[^/]+\\.[mc]?[tj]s|(?:schedules|instructions)/[^/]+\\.md)$`);
|
|
17
|
+
const isSource = (file) => SOURCE_FILE.test(file.replace(SUBAGENT, "")) && !/\.d\.[mc]?ts$/.test(file);
|
|
18
|
+
const slotOf = (file) => file.replace(/\.[^./]+$/, "");
|
|
15
19
|
const KIT_IMPORT = new RegExp(`^\\s*(?:import|export)\\b(?:[^;"']*?\\bfrom)?\\s*["']eve-hub/((?:${KIT_DIRS.join("|")})/[\\w-]+)["']`, "gm");
|
|
16
20
|
const ALL_STUBS = { ...KIT_FILES, ...MAIN_KIT_FILES };
|
|
17
21
|
const HOST_ONLY = [...Object.keys(ALL_STUBS), ...Object.keys(MEMORY_STUBS)].filter((file) => !DEPLOYED_KIT_FILES.has(file));
|
|
18
22
|
const moduleOf = (file) => (MEMORY_STUBS[file] ? file.slice(0, -3) : kitPath(ALL_STUBS[file][1] ?? file).slice("eve-hub/".length));
|
|
19
23
|
const HOST_ONLY_MODULES = new Set(HOST_ONLY.map(moduleOf));
|
|
20
24
|
|
|
21
|
-
/** Whether `file` (relative to a bot's agent/ folder, subagents included) is one syncKit reads. */
|
|
22
|
-
export const isKitFile = (file) => file === "instructions.md" || file === MAIN_INSTRUCTIONS ||
|
|
25
|
+
/** Whether `file` (relative to a bot's agent/ folder, subagents included) is one syncKit reads: a stub, or any source that holds a stub's slot. */
|
|
26
|
+
export const isKitFile = (file) => file === "instructions.md" || file === MAIN_INSTRUCTIONS || isSource(file);
|
|
23
27
|
|
|
24
28
|
// Every kit module the file imports, from its import and export lines only.
|
|
25
29
|
function kitImports(text) {
|
|
@@ -80,7 +84,8 @@ export function syncKit({ bots, modules }) {
|
|
|
80
84
|
else change.report.push({ bot, file, reason: `imports eve-hub/${host}, which needs the hub's own machine` });
|
|
81
85
|
}
|
|
82
86
|
}
|
|
83
|
-
|
|
87
|
+
const slots = new Set(Object.keys(files).filter((file) => isSource(file)).map(slotOf));
|
|
88
|
+
for (const file of wanted) if (!slots.has(slotOf(file))) write(file, content(file));
|
|
84
89
|
}
|
|
85
90
|
return change;
|
|
86
91
|
}
|
|
@@ -97,7 +102,7 @@ export function readBotFiles(agentDir, prefix = "") {
|
|
|
97
102
|
}
|
|
98
103
|
for (const dir of KIT_DIRS) {
|
|
99
104
|
if (!existsSync(join(agentDir, dir))) continue;
|
|
100
|
-
for (const entry of readdirSync(join(agentDir, dir), { withFileTypes: true })) if (entry.isFile() && entry.name
|
|
105
|
+
for (const entry of readdirSync(join(agentDir, dir), { withFileTypes: true })) if (entry.isFile() && isKitFile(`${dir}/${entry.name}`)) read(`${dir}/${entry.name}`);
|
|
101
106
|
}
|
|
102
107
|
if (existsSync(join(agentDir, "subagents"))) {
|
|
103
108
|
for (const entry of readdirSync(join(agentDir, "subagents"), { withFileTypes: true })) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
- **Team.** `create_agent` takes `runsOn` (`hosted` or `local`) and commits the new bot to the workspace repository; it joins with the next deployment, and a `local` bot then runs on the owner's Mac. There is no `remove_agent`: the roster is `hub.workspace.json`, and a deployment changes it by commit. If nobody on the team covers the work well, ask the human before going on.
|
|
2
2
|
- **Restarts.** There is no `restart_agent` or `restart_hub`: you cannot restart a bot or the hub. A new deployment replaces the running bots.
|
|
3
3
|
- **Memory.** Add an entry as your Memory section says, by committing the file to the workspace repository (see Changing it above). With no repository, write nothing and tell the human to link one first.
|
|
4
|
-
- **Schedules.**
|
|
4
|
+
- **Schedules.** Your `dash` schedule runs every 8 minutes: it classifies the threads that changed into the Dash and rewrites track summaries that have news, without PR data (a function has no `gh`). `compact` is deployed but finds nothing to compact until hosted sessions record their last model call. `prune` is not deployed: it cleans the hub machine's disk.
|
package/world.mjs
CHANGED
|
@@ -430,12 +430,13 @@ var OPEN_TURN_STATUSES = "('streaming','cancelling')";
|
|
|
430
430
|
// ../../hosted-hub/src/domain/wake.ts
|
|
431
431
|
var escape = (s2) => s2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
432
432
|
var mentioned = (roster, text) => roster.filter((bot) => new RegExp(`(^|[^\\w@])@${escape(bot)}(?![\\w-])`, "i").test(text));
|
|
433
|
-
|
|
433
|
+
var hasAll = (text) => /(^|[^\w@])@all(?![\w-])/i.test(text);
|
|
434
|
+
function recipients(roster, text, from, threadId2 = "", participants = []) {
|
|
434
435
|
if (threadId2.startsWith("dm-")) {
|
|
435
436
|
const bot = threadId2.slice(3);
|
|
436
437
|
return bot !== from.name && roster.includes(bot) ? [bot] : [];
|
|
437
438
|
}
|
|
438
|
-
const named = mentioned(roster, text).filter((bot) => bot !== from.name);
|
|
439
|
+
const named = [.../* @__PURE__ */ new Set([...mentioned(roster, text), ...hasAll(text) ? participants.filter((b) => roster.includes(b)) : []])].filter((bot) => bot !== from.name);
|
|
439
440
|
if (from.kind === "agent") return named;
|
|
440
441
|
return named.length ? named : roster.slice(0, 1);
|
|
441
442
|
}
|
|
@@ -462,7 +463,8 @@ var deliveryId = (message, bot) => `dlv_${message}_${bot}`;
|
|
|
462
463
|
var turnId = (session, chunkIndex) => `trn_${session}_${chunkIndex}`;
|
|
463
464
|
|
|
464
465
|
// ../../hosted-hub/src/store/routing.ts
|
|
465
|
-
|
|
466
|
+
var PARTICIPANTS = (thread) => `SELECT bot FROM sessions WHERE thread_id = ${thread} UNION SELECT author FROM messages WHERE thread_id = ${thread} AND author_kind = 'agent'`;
|
|
467
|
+
function routingStatements(messageId2, threadId2, bots, now, held = {}, steer = false) {
|
|
466
468
|
const fresh = "EXISTS (SELECT 1 FROM messages WHERE id = ?)";
|
|
467
469
|
return [
|
|
468
470
|
...bots.flatMap((bot) => [
|
|
@@ -477,7 +479,9 @@ function routingStatements(messageId2, threadId2, bots, now) {
|
|
|
477
479
|
threadId2,
|
|
478
480
|
bot,
|
|
479
481
|
messageId2
|
|
480
|
-
)
|
|
482
|
+
),
|
|
483
|
+
...steer ? [s("UPDATE deliveries SET steer = 1 WHERE id = ? AND accepted_at IS NULL", deliveryId(messageId2, bot))] : [],
|
|
484
|
+
...held[bot] ? [s("UPDATE deliveries SET held_at = ?, held_scope = ? WHERE id = ? AND accepted_at IS NULL", now, held[bot], deliveryId(messageId2, bot))] : []
|
|
481
485
|
])
|
|
482
486
|
];
|
|
483
487
|
}
|
|
@@ -26962,10 +26966,13 @@ var DELIVERY_STATUS = `CASE WHEN d.accepted_at IS NOT NULL OR ${LINKED} THEN COA
|
|
|
26962
26966
|
WHERE r.session_id = d.session_id AND r.status = 'pending' AND rs.ended_at IS NULL
|
|
26963
26967
|
AND r.created_at <= COALESCE((SELECT t.started_at FROM turn_deliveries td JOIN turns t ON t.id = td.turn_id WHERE td.delivery_id = d.id), d.accepted_at) LIMIT 1),
|
|
26964
26968
|
CASE WHEN ${LINKED} THEN 'seen' ELSE 'delivered' END)
|
|
26965
|
-
WHEN d.failed_at IS NOT NULL THEN 'failed' WHEN d.revoked_at IS NOT NULL THEN 'skipped'
|
|
26969
|
+
WHEN d.failed_at IS NOT NULL THEN 'failed' WHEN d.revoked_at IS NOT NULL THEN 'skipped'
|
|
26970
|
+
WHEN d.held_at IS NOT NULL THEN CASE d.held_scope WHEN 'bot' THEN 'queued until free' ELSE 'queued' END ELSE 'pending' END`;
|
|
26966
26971
|
|
|
26967
26972
|
// ../../hosted-hub/src/store/fold.ts
|
|
26968
26973
|
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,
|
|
26974
|
+
(SELECT json_group_array(bot) FROM (${PARTICIPANTS("s.thread_id")})) AS participants,
|
|
26975
|
+
(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,
|
|
26969
26976
|
(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}
|
|
26970
26977
|
ORDER BY t.started_at DESC LIMIT 1) AS open_turn
|
|
26971
26978
|
FROM sessions s LEFT JOIN session_folds f ON f.session_id = s.id WHERE s.id = ?`;
|
|
@@ -26974,6 +26981,8 @@ function foldState(row) {
|
|
|
26974
26981
|
return {
|
|
26975
26982
|
session: { id: String(row.id), thread: String(row.thread_id), bot: String(row.bot) },
|
|
26976
26983
|
roster: JSON.parse(String(row.roster)),
|
|
26984
|
+
participants: JSON.parse(String(row.participants)),
|
|
26985
|
+
held: row.held == null ? null : String(row.held),
|
|
26977
26986
|
foldedIndex: Number(row.folded_index),
|
|
26978
26987
|
open: row.open_turn ? JSON.parse(String(row.open_turn)) : null
|
|
26979
26988
|
};
|
|
@@ -27007,6 +27016,7 @@ function fold(state, chunks, now = Date.now()) {
|
|
|
27007
27016
|
const reset = (t) => log("turn.delta", { turnId: t.id, index, reset: true, delta: t.text });
|
|
27008
27017
|
const end = (status, error63 = null) => {
|
|
27009
27018
|
if (!open3) return;
|
|
27019
|
+
if (state.held) deliver.push(state.held);
|
|
27010
27020
|
reduceActivity(open3.activity, { type: "turn.cancelled" }, at, cutOff(open3, "turn.cancelled"));
|
|
27011
27021
|
Object.assign(open3, { status: open3.status === "cancelling" ? "cancelled" : status, endedAt: at, error: error63 });
|
|
27012
27022
|
touched.set(open3.id, open3);
|
|
@@ -27103,7 +27113,7 @@ function fold(state, chunks, now = Date.now()) {
|
|
|
27103
27113
|
SELECT ?, ?, ?, 'agent', ?, ?, ? WHERE ? <> ''
|
|
27104
27114
|
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));
|
|
27105
27115
|
log("message.created", { messageId: messageId2, author: session.bot, authorKind: "agent" }, IF_INSERTED);
|
|
27106
|
-
const to = recipients(state.roster, text, { name: session.bot, kind: "agent" }, session.thread);
|
|
27116
|
+
const to = recipients(state.roster, text, { name: session.bot, kind: "agent" }, session.thread, state.participants);
|
|
27107
27117
|
stmts.push(...routingStatements(messageId2, session.thread, to, at));
|
|
27108
27118
|
deliver.push(messageId2);
|
|
27109
27119
|
}
|