eve-hub 0.12.0 → 0.13.1
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/README.md +1 -1
- package/package.json +2 -3
- package/prepare.mjs +2 -10
- package/stubs.mjs +1 -1
- package/tools/show.d.ts +8 -0
- package/tools/show.js +42 -5
- package/vendor/world-turso.mjs +0 -6416
- package/world.mjs +0 -10721
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# eve-hub
|
|
2
2
|
|
|
3
|
+
## 0.13.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- `show` opens a web page (`uri`) or a file from the bot's sandbox (`path`) in a tab beside the thread.
|
|
8
|
+
|
|
9
|
+
## 0.13.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- b2a7266: Bots run plain eve on Vercel's Workflow storage: the kit no longer wires in a workflow world or exports `eve-hub/world`.
|
|
14
|
+
|
|
3
15
|
## 0.12.0
|
|
4
16
|
|
|
5
17
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -61,7 +61,7 @@ A deployed workspace (lib/workspace-template.mjs, staged by hosted-hub/scripts/s
|
|
|
61
61
|
|
|
62
62
|
Releases go through Changesets, as in eve. A PR that changes the kit adds a changeset (`pnpm changeset`) and applies it (`pnpm changeset version`); when it merges, `.github/workflows/release.yml` runs `changeset publish` (no bot commits to main: its ruleset allows only PR merges): the kit's `prepack` stages the package into `dist/npm` (its `publishConfig.directory`), npm gets it, and the `eve-hub@<version>` GitHub release is created. npm auth is the repo's `NPM_TOKEN` secret (an npm automation token). The template pins the version this checkout carries.
|
|
63
63
|
|
|
64
|
-
The template's main gets a Vercel Sandbox and only the kit stubs that run without the hub's machine (lib/workspace-template.mjs `DEPLOYED_KIT_FILES`; eve's own bash replaces the kit's). The template's builder also mounts eve-code (`eve/extensions/code`: `apply_patch`, `grep`, the review skills, a read-only worker) and `eve/extensions/git` (`git({})`), both with no `github` option. The bot uses `gh` and `git` from bash, whose GitHub token is the sandbox firewall's (the Vercel sandbox `create_agent` writes, lib/agent-template.mjs `sandboxFile`), and no `gh` tool is contributed. A deployed workspace's eve is the one pin in `hosted-hub/bots/package.json`, which staging copies over the template's (an eve snapshot with
|
|
64
|
+
The template's main gets a Vercel Sandbox and only the kit stubs that run without the hub's machine (lib/workspace-template.mjs `DEPLOYED_KIT_FILES`; eve's own bash replaces the kit's). The template's builder also mounts eve-code (`eve/extensions/code`: `apply_patch`, `grep`, the review skills, a read-only worker) and `eve/extensions/git` (`git({})`), both with no `github` option. The bot uses `gh` and `git` from bash, whose GitHub token is the sandbox firewall's (the Vercel sandbox `create_agent` writes, lib/agent-template.mjs `sandboxFile`), and no `gh` tool is contributed. A deployed workspace's eve is the one pin in `hosted-hub/bots/package.json`, which staging copies over the template's (an eve snapshot with `eve/extensions/git`; `hosted-hub/test/stage-workspace.test.mjs` checks the staged imports resolve against it). The hub and local bots stay on the hub's eve. The published tarball (`scripts/pack.mjs`) leaves out the kit__worker extension, which mounts only on the hub's machine. The hub reaches each bot's eve channel with `Authorization: Bearer $BOT_TOKEN` (`bot-token.mjs`); the kit's calls to the hub (`registry-fetch.mjs`, at `ROSTER_REGISTRY_URL`) carry `Authorization: Bearer $HUB_TOKEN` when set. The hosted hub keeps Vercel Authentication on: a deployed bot sends its project's OIDC token in `x-vercel-trusted-oidc-idp-token` too (the hub project lists each bots project in its Trusted Sources, `hosted-hub/src/onboard/trust.ts` `trustBots`, and never the bypass secret, even while its deployment still holds the env), and a bot on a Mac sends `x-vercel-protection-bypass: $HUB_PROTECTION_BYPASS` (the hub project's automation bypass secret) when it has one.
|
|
65
65
|
|
|
66
66
|
## Vercel Kit connection
|
|
67
67
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eve-hub",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.1",
|
|
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",
|
|
@@ -29,8 +29,7 @@
|
|
|
29
29
|
"./bot-token": "./bot-token.mjs",
|
|
30
30
|
"./hub-oidc": "./hub-oidc.mjs",
|
|
31
31
|
"./prepare": "./prepare.mjs",
|
|
32
|
-
"./vercel": "./vercel.mjs"
|
|
33
|
-
"./world": "./world.mjs"
|
|
32
|
+
"./vercel": "./vercel.mjs"
|
|
34
33
|
},
|
|
35
34
|
"peerDependencies": {
|
|
36
35
|
"eve": "*",
|
package/prepare.mjs
CHANGED
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
// token opens it and pays for model calls) with only the kit stubs that run
|
|
10
10
|
// off the hub's machine (DEPLOYED_KIT_FILES); a hosted bot whose sandbox.ts
|
|
11
11
|
// is the host's fails the build, and the build never writes a sandbox;
|
|
12
|
-
// - the kit extension, host-only workers and kit stubs are dropped, the
|
|
13
|
-
//
|
|
12
|
+
// - the kit extension, host-only workers and kit stubs are dropped, the
|
|
13
|
+
// Vercel Kit connection is wired in, every bot gets eve's git extension, and the
|
|
14
14
|
// workspace's memory and each bot's team architecture are embedded in the bots' memory providers (a function
|
|
15
15
|
// has no workspace cwd);
|
|
16
16
|
// - /hub/manifest: the roster and the commit sha the deployment was built
|
|
@@ -79,12 +79,6 @@ export default defineChannel({
|
|
|
79
79
|
|
|
80
80
|
export const MANIFEST_FILE = "hub.manifest.json";
|
|
81
81
|
|
|
82
|
-
export function injectWorkflowWorld(agent, file) {
|
|
83
|
-
const count = agent.split("defineAgent({").length - 1;
|
|
84
|
-
if (count !== 1) throw new Error(`${file}: expected exactly one defineAgent({, found ${count}`);
|
|
85
|
-
return agent.replace("defineAgent({", `defineAgent({\n experimental: { workflow: { world: "eve-hub/world" } },`);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
82
|
// Removes `file` under `bot`, then any directory it leaves empty.
|
|
89
83
|
function removeStub(bot, file) {
|
|
90
84
|
unlinkSync(join(bot, file));
|
|
@@ -159,8 +153,6 @@ export function prepareDeployment(dir) {
|
|
|
159
153
|
for (const item of readdirSync(bot, { recursive: true })) {
|
|
160
154
|
if (item.endsWith(join("extensions", "kit.ts"))) removeStub(bot, item);
|
|
161
155
|
}
|
|
162
|
-
const agentFile = join(bot, "agent.ts");
|
|
163
|
-
writeFileSync(agentFile, injectWorkflowWorld(readFileSync(agentFile, "utf8"), agentFile));
|
|
164
156
|
// Slack for the onboarding skill comes through Vercel Kit, with the project's KIT_CONNECTOR;
|
|
165
157
|
// without one the connection resolves to nothing.
|
|
166
158
|
mkdirSync(join(bot, "connections"), { recursive: true });
|
package/stubs.mjs
CHANGED
|
@@ -8,7 +8,7 @@ export const KIT_FILES = {
|
|
|
8
8
|
"hooks/sandbox.ts": ["Migrates legacy Docker sessions to the host sandbox."],
|
|
9
9
|
"tools/bash.ts": ["eve's bash with a default time limit (kills the process group) and the workspace's code location."],
|
|
10
10
|
"tools/react.ts": ["React to a message with an emoji."],
|
|
11
|
-
"tools/show.ts": ["Move the human's screen to a thread, DM, or message."],
|
|
11
|
+
"tools/show.ts": ["Move the human's screen to a thread, DM, or message, or open a page or file beside the thread."],
|
|
12
12
|
"tools/browser.ts": ["Its own headless browser, watched live in the Computer view."],
|
|
13
13
|
"tools/attach_image.ts": ["Puts an image (a sandbox file or a browser screenshot) on this turn's reply."],
|
|
14
14
|
"tools/view_image.ts": ["Shows the bot an image (a sandbox file or a browser screenshot) so it can see it."],
|
package/tools/show.d.ts
CHANGED
|
@@ -2,17 +2,25 @@ declare const _default: import("eve/tools").ToolDefinition<{
|
|
|
2
2
|
threadId?: string | undefined;
|
|
3
3
|
agent?: string | undefined;
|
|
4
4
|
messageId?: string | undefined;
|
|
5
|
+
uri?: string | undefined;
|
|
6
|
+
path?: string | undefined;
|
|
7
|
+
line?: number | undefined;
|
|
5
8
|
reason: string;
|
|
6
9
|
}, {
|
|
7
10
|
ok: boolean;
|
|
11
|
+
opened?: string | undefined;
|
|
8
12
|
}> & {
|
|
9
13
|
execute(input: {
|
|
10
14
|
threadId?: string | undefined;
|
|
11
15
|
agent?: string | undefined;
|
|
12
16
|
messageId?: string | undefined;
|
|
17
|
+
uri?: string | undefined;
|
|
18
|
+
path?: string | undefined;
|
|
19
|
+
line?: number | undefined;
|
|
13
20
|
reason: string;
|
|
14
21
|
}, ctx: import("eve/tools").ToolContext): Promise<{
|
|
15
22
|
ok: boolean;
|
|
23
|
+
opened?: string | undefined;
|
|
16
24
|
}>;
|
|
17
25
|
};
|
|
18
26
|
export default _default;
|
package/tools/show.js
CHANGED
|
@@ -1,24 +1,61 @@
|
|
|
1
|
+
import { basename } from "node:path";
|
|
2
|
+
import { pathToFileURL } from "node:url";
|
|
1
3
|
import { defineTool } from "eve/tools";
|
|
2
4
|
import { z } from "zod";
|
|
3
5
|
import { registryFetch } from "../registry-fetch.mjs";
|
|
4
6
|
// Moves the human's workspace UI, e.g. to a thread you just opened or a
|
|
5
|
-
// message they need to look at
|
|
7
|
+
// message they need to look at, or opens a page or file in the pane beside
|
|
8
|
+
// the thread. They see a note saying you did it and why.
|
|
9
|
+
//
|
|
10
|
+
// A file goes as file:// on a local hub (the bot's files are the human's) and,
|
|
11
|
+
// on a hosted hub (a deployed bot, or HUB_TOKEN set), as a snapshot uploaded to the hub
|
|
12
|
+
// (hub://upload/<sha>.<ext>), since the human's Mac can't read the sandbox.
|
|
6
13
|
export default defineTool({
|
|
7
|
-
description: "Move the human's screen to a place in the workspace: a thread (threadId), a bot's DM (agent), optionally scrolled to and highlighting one message (messageId). Use when you want them to look at something now. Rarely: only when they must look now.",
|
|
14
|
+
description: "Move the human's screen to a place in the workspace: a thread (threadId), a bot's DM (agent), optionally scrolled to and highlighting one message (messageId). Or open a web page (uri) or a file from your sandbox (path: markdown, text or code, an image, a PDF; line for text) in a tab beside this thread, without moving them. Use when you want them to look at something now. Rarely move their screen: only when they must look now.",
|
|
8
15
|
inputSchema: z.object({
|
|
9
16
|
threadId: z.string().optional().describe("Thread id, e.g. thr_0002."),
|
|
10
17
|
agent: z.string().optional().describe("Open this bot's DM instead of a thread."),
|
|
11
18
|
messageId: z.string().optional().describe("Also scroll to and highlight this message, like opening a link to it."),
|
|
19
|
+
uri: z.string().optional().describe("An http(s) page to open beside the thread."),
|
|
20
|
+
path: z.string().optional().describe("A file in your sandbox to open beside the thread, e.g. /workspace/repo/PLAN.md."),
|
|
21
|
+
line: z.number().int().positive().optional().describe("With path: the line a text or code file opens at."),
|
|
12
22
|
reason: z.string().describe("One short line shown to the human, e.g. 'the plan is ready for review'."),
|
|
13
23
|
}),
|
|
14
|
-
async execute(input, ctx) {
|
|
24
|
+
async execute({ path, uri, line, ...input }, ctx) {
|
|
25
|
+
if (path && uri)
|
|
26
|
+
throw new Error("pass path or uri, not both");
|
|
27
|
+
if (line && !path)
|
|
28
|
+
throw new Error("line goes with path");
|
|
29
|
+
let open = uri, title;
|
|
30
|
+
if (path) {
|
|
31
|
+
title = basename(path);
|
|
32
|
+
const sandbox = await ctx.getSandbox();
|
|
33
|
+
if (process.env.VERCEL || process.env.HUB_TOKEN) {
|
|
34
|
+
const bytes = await sandbox.readBinaryFile({ path });
|
|
35
|
+
if (!bytes)
|
|
36
|
+
throw new Error(`no file at ${path}`);
|
|
37
|
+
const res = await registryFetch(`/api/uploads`, {
|
|
38
|
+
method: "POST",
|
|
39
|
+
headers: { "content-type": "application/octet-stream", "x-filename": encodeURIComponent(basename(path)) },
|
|
40
|
+
body: new Uint8Array(bytes),
|
|
41
|
+
signal: AbortSignal.timeout(30_000),
|
|
42
|
+
});
|
|
43
|
+
const body = await res.json().catch(() => ({}));
|
|
44
|
+
if (!res.ok)
|
|
45
|
+
throw new Error(`show failed: the upload was refused (${body.error ?? res.status})`);
|
|
46
|
+
open = `hub://upload/${body.id}`;
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
open = pathToFileURL(sandbox.resolvePath(path)).href;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
15
52
|
const res = await registryFetch(`/api/ui`, {
|
|
16
53
|
method: "POST",
|
|
17
54
|
headers: { "content-type": "application/json" },
|
|
18
|
-
body: JSON.stringify({ ...input, sessionId: ctx.session.id }),
|
|
55
|
+
body: JSON.stringify({ ...input, ...(open ? { uri: open, ...(line ? { line } : {}), ...(title ? { title } : {}) } : {}), sessionId: ctx.session.id }),
|
|
19
56
|
});
|
|
20
57
|
if (!res.ok)
|
|
21
58
|
throw new Error(`show failed: ${res.status} ${await res.text()}`);
|
|
22
|
-
return { ok: true };
|
|
59
|
+
return { ok: true, ...(open ? { opened: open } : {}) };
|
|
23
60
|
},
|
|
24
61
|
});
|