eve-hub 0.13.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 CHANGED
@@ -1,5 +1,11 @@
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
+
3
9
  ## 0.13.0
4
10
 
5
11
  ### Minor Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eve-hub",
3
- "version": "0.13.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",
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. They see a note saying you did it and why.
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
  });