eve-hub 0.14.7 → 0.14.9
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/hub-upload.d.ts +1 -1
- package/hub-upload.js +2 -2
- package/package.json +2 -1
- package/tools/post_message.d.ts +1 -1
- package/tools/post_message.js +48 -13
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# eve-hub
|
|
2
2
|
|
|
3
|
+
## 0.14.9
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- `post_message` finds links with a markdown parser instead of a regex. A sandbox path in code (a fence or an inline span) stays as written, a label with brackets (nested or escaped) keeps its link, a `#…` fragment stays on the rewritten link, a `%20` in the path is decoded, and a reference definition's target is rewritten too. The 700-character cap and the `![]()` refusal read the same links, so an image written inside code is no longer refused.
|
|
8
|
+
|
|
9
|
+
## 0.14.8
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- `packKit` (the tarball the hub's tests and hosted workspaces' staging use) compiles the kit inside `packages/agent-kit/dist`, where a publish compiles it, instead of a temp dir. The compiler now resolves eve and the kit's other dependencies there too, so a declaration only a publish rejected (TS2883 in 0.14.5) fails the tests before merge.
|
|
14
|
+
|
|
3
15
|
## 0.14.7
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
package/hub-upload.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ export type Uploaded = {
|
|
|
11
11
|
};
|
|
12
12
|
export declare const hostedHub: () => boolean;
|
|
13
13
|
export declare const IMAGE_FILE: RegExp;
|
|
14
|
-
/** A markdown link target that names a sandbox file: an absolute path, or one under the home directory. */
|
|
14
|
+
/** A markdown link target that names a sandbox file: an absolute path (not a `//host` one), or one under the home directory. */
|
|
15
15
|
export declare const isSandboxPath: (target: string) => boolean;
|
|
16
16
|
export declare function readSandboxFile(sandbox: SandboxFiles, path: string): Promise<Uint8Array>;
|
|
17
17
|
export declare function uploadBytes(bytes: Uint8Array, name: string, what: string): Promise<Uploaded>;
|
package/hub-upload.js
CHANGED
|
@@ -8,8 +8,8 @@ import { pathToFileURL } from "node:url";
|
|
|
8
8
|
import { registryFetch } from "./registry-fetch.mjs";
|
|
9
9
|
export const hostedHub = () => Boolean(process.env.VERCEL || process.env.HUB_TOKEN);
|
|
10
10
|
export const IMAGE_FILE = /\.(png|jpe?g|gif|webp|heic)$/i;
|
|
11
|
-
/** A markdown link target that names a sandbox file: an absolute path, or one under the home directory. */
|
|
12
|
-
export const isSandboxPath = (target) => /^(
|
|
11
|
+
/** A markdown link target that names a sandbox file: an absolute path (not a `//host` one), or one under the home directory. */
|
|
12
|
+
export const isSandboxPath = (target) => /^(\/(?!\/)|\$HOME\/|~\/)/.test(target);
|
|
13
13
|
// The sandbox resolves `~/`; `$HOME/` is the same place spelled as the shell does.
|
|
14
14
|
const sandboxPath = (path) => path.replace(/^\$HOME\//, "~/");
|
|
15
15
|
export async function readSandboxFile(sandbox, path) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eve-hub",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.9",
|
|
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",
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
"ai": "^7.0.137",
|
|
42
42
|
"jose": "^6.2.3",
|
|
43
43
|
"jsonc-parser": "3.3.1",
|
|
44
|
+
"mdast-util-from-markdown": "^2.1.0",
|
|
44
45
|
"omggif": "1.0.10",
|
|
45
46
|
"smol-toml": "^1.9.1",
|
|
46
47
|
"tree-sitter-bash": "0.25.1",
|
package/tools/post_message.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export declare const MAX_CHARS = 700;
|
|
|
3
3
|
export declare const postLength: (text: string) => number;
|
|
4
4
|
/** The error for a text the thread shouldn't carry, or null. */
|
|
5
5
|
export declare function textError(text: string): string | null;
|
|
6
|
-
/** The text with each sandbox-path link target replaced by where the human's app opens it. */
|
|
6
|
+
/** The text with each sandbox-path link target replaced by where the human's app opens it; a `#…` fragment stays on it. Each file is opened once. */
|
|
7
7
|
export declare function rewriteDocLinks(text: string, open: (path: string) => Promise<string>): Promise<string>;
|
|
8
8
|
declare const _default: import("eve/tools").ToolDefinition<{
|
|
9
9
|
threadId?: string | undefined;
|
package/tools/post_message.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { defineTool } from "eve/tools";
|
|
2
|
+
import { fromMarkdown } from "mdast-util-from-markdown";
|
|
2
3
|
import { z } from "zod";
|
|
3
4
|
import { documentUrl, hostedHub, isSandboxPath, uploadBytes, uploadFile } from "../hub-upload.js";
|
|
4
5
|
import { registryFetch } from "../registry-fetch.mjs";
|
|
@@ -23,30 +24,64 @@ import { registryFetch } from "../registry-fetch.mjs";
|
|
|
23
24
|
// move), the text is its summary, `metadata` its PR and sha. The hub keeps the
|
|
24
25
|
// latest one per bot and thread for the workspace state and the Dash.
|
|
25
26
|
export const MAX_CHARS = 700;
|
|
26
|
-
const LINK = /\[([^\]]*)\]\(([^)\s]+)\)/g;
|
|
27
|
-
const IMAGE_SYNTAX = /!\[[^\]]*\]\([^)]*\)/;
|
|
28
27
|
const SCREENSHOT = "screenshot";
|
|
28
|
+
// The markdown parser finds the links and where each destination starts, so a path in code is never read as a
|
|
29
|
+
// link and brackets in a label (nested or escaped) never cut it.
|
|
30
|
+
function destinations(text) {
|
|
31
|
+
const spans = new Map();
|
|
32
|
+
function enter(token) {
|
|
33
|
+
spans.set(this.stack.at(-1), token);
|
|
34
|
+
}
|
|
35
|
+
const tree = fromMarkdown(text, { mdastExtensions: [{ enter: { resource: enter, definitionDestination: enter } }] });
|
|
36
|
+
const found = [];
|
|
37
|
+
const visit = (node) => {
|
|
38
|
+
const span = spans.get(node);
|
|
39
|
+
if (span && node.url !== undefined && (node.type === "link" || node.type === "image" || node.type === "definition")) {
|
|
40
|
+
const to = node.type === "definition" ? node.position.end.offset : span.end.offset;
|
|
41
|
+
found.push({ kind: node.type, from: span.start.offset, to, url: node.url, title: node.title });
|
|
42
|
+
}
|
|
43
|
+
node.children?.forEach(visit);
|
|
44
|
+
};
|
|
45
|
+
visit(tree);
|
|
46
|
+
return found;
|
|
47
|
+
}
|
|
29
48
|
/** The characters the cap counts: the text without its links' targets. */
|
|
30
|
-
export const postLength = (text) => text.
|
|
49
|
+
export const postLength = (text) => destinations(text).reduce((n, d) => n - (d.to - d.from) + (d.kind === "definition" ? 0 : "()".length), text.length);
|
|
31
50
|
/** The error for a text the thread shouldn't carry, or null. */
|
|
32
51
|
export function textError(text) {
|
|
33
|
-
if (
|
|
52
|
+
if (destinations(text).some((d) => d.kind === "image"))
|
|
34
53
|
return "Images don't go in the text: pass the file's sandbox path in `images` and it shows inline";
|
|
35
54
|
if (postLength(text) > MAX_CHARS)
|
|
36
55
|
return "Too long: move the detail into a doc and link it";
|
|
37
56
|
return null;
|
|
38
57
|
}
|
|
39
|
-
|
|
58
|
+
const PATH_AND_FRAGMENT = /^([^?#]*)(?:\?[^#]*)?(#.*)?$/;
|
|
59
|
+
/** The text with each sandbox-path link target replaced by where the human's app opens it; a `#…` fragment stays on it. Each file is opened once. */
|
|
40
60
|
export async function rewriteDocLinks(text, open) {
|
|
41
|
-
const links =
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
links.forEach((m, i) => {
|
|
45
|
-
const start = m.index + m[0].length - m[2].length - 1;
|
|
46
|
-
out += text.slice(at, start) + urls[i];
|
|
47
|
-
at = start + m[2].length;
|
|
61
|
+
const links = destinations(text).flatMap((d) => {
|
|
62
|
+
const [, path, fragment = ""] = PATH_AND_FRAGMENT.exec(d.url);
|
|
63
|
+
return d.kind !== "image" && isSandboxPath(path) ? [{ ...d, file: decode(path), fragment }] : [];
|
|
48
64
|
});
|
|
49
|
-
|
|
65
|
+
if (!links.length)
|
|
66
|
+
return text;
|
|
67
|
+
const files = [...new Set(links.map((l) => l.file))];
|
|
68
|
+
const opened = new Map(await Promise.all(files.map(async (file) => [file, await open(file)])));
|
|
69
|
+
let out = text;
|
|
70
|
+
for (const l of links.sort((a, b) => b.from - a.from)) {
|
|
71
|
+
const url = `${opened.get(l.file)}${l.fragment}`;
|
|
72
|
+
const target = /[\s()<>]/.test(url) ? `<${url}>` : url;
|
|
73
|
+
const title = l.title == null ? "" : ` "${l.title.replaceAll("\\", "\\\\").replaceAll('"', '\\"')}"`;
|
|
74
|
+
out = `${out.slice(0, l.from)}${l.kind === "definition" ? `${target}${title}` : `(${target}${title})`}${out.slice(l.to)}`;
|
|
75
|
+
}
|
|
76
|
+
return out;
|
|
77
|
+
}
|
|
78
|
+
function decode(path) {
|
|
79
|
+
try {
|
|
80
|
+
return decodeURIComponent(path);
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return path;
|
|
84
|
+
}
|
|
50
85
|
}
|
|
51
86
|
async function screenshot(sessionId) {
|
|
52
87
|
if (hostedHub())
|