eve-hub 0.16.45 → 0.16.47
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/README.md +1 -1
- package/main-instructions.mjs +27 -12
- package/package.json +1 -1
- package/stubs.mjs +3 -3
- package/templates/delegation.md +1 -0
- package/templates/execution-context-local.md +1 -0
- package/templates/execution.md +1 -3
- package/templates/main-instructions.md +4 -0
- package/templates/reaching-others.md +0 -3
- package/templates/team.md +0 -3
- package/tools/post_message.js +1 -1
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ The bot-side code every hub agent shares (milestone M3 in [`docs/architecture`](
|
|
|
8
8
|
| `tools/create_thread.ts`, `post_message.ts`, `archive_thread.ts`, `unarchive_thread.ts`, `list_agents.ts`, `create_agent.ts`, `remove_agent.ts`, `restart_agent.ts`, `tag_threads.ts`, `apply_tags.ts` | The orchestrator's tools | `main`'s `agent/tools/<same name>.ts` |
|
|
9
9
|
| `hooks/sandbox.ts` | Migrates legacy Docker sessions to the host sandbox (`defineHook`) | `agent/hooks/sandbox.ts` |
|
|
10
10
|
| `instructions/main.ts`, `main-instructions.mjs` | main's instructions (`defineInstructions`), rendered at build time from the kit's `templates/main-instructions.md` (identity, role) between the shared templates, each in its own tag, so no workspace carries a copy | `main`'s `agent/instructions.ts`; workspace-only additions in `agent/role/*.md` (inside `<role>`) |
|
|
11
|
-
| `instructions
|
|
11
|
+
| `instructions.ts` (`botInstructions`) | Every other bot's whole prompt, in order: its identity (`You are **name**. <role>`, from its `hub.workspace.json` entry), the shared sections (`templates/team.md`, `execution.md`, `delegation.md` only when the entry declares `subagents`, `engineering.md`, `deliveries.md`, `reaching-others.md`, `writing.md`, each in its tag) and its `role.md` in `<role>` (left out when empty) | every other bot's `agent/instructions.ts` and `agent/role.md`, which holds only how the bot works. `instructions/protocol.ts` (the shared sections alone, appended after the bot's own `instructions.md`) is the older layout, kept for a workspace that hasn't moved |
|
|
12
12
|
| `instructions/engineering.ts` | The engineering page alone (`templates/engineering.md`), for a subagent that doesn't carry the protocol | the subagent's `instructions/engineering.ts` |
|
|
13
13
|
| `memory/workspace.ts`, `memory.mjs` | an eve memory slot (default export, constant `workspace` scope) whose provider recalls the workspace's `memory/<id>.md` entries (plus the `memory/<domain>/` folders the bot lists as `domains` in its roster entry), one message each, at every turn start and after compaction, only the ones whose text changed since the session last saw them, as new messages after the history; `validateMemory(root)` for a workspace's tests | every bot's (main too) `agent/memory/workspace.ts`: `export { default } from "eve-hub/memory/workspace"` |
|
|
14
14
|
| `memory/bot.ts` | the same for the bot's own `agents/<bot>/memory/<id>.md` entries; the bot is found from its directory (`findBotDir`), scope is its name | every bot's `agent/memory/bot.ts`: `export { default } from "eve-hub/memory/bot"` |
|
package/main-instructions.mjs
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
// A bot's system prompt, rendered from the kit's templates. Every section is wrapped in its own
|
|
2
|
-
// tag, in this order: identity, team, execution, engineering, what_reaches_you,
|
|
3
|
-
// writing, role, execution_context (the last one is execution-context.mjs)
|
|
4
|
-
// engineering, since main never builds
|
|
2
|
+
// tag, in this order: identity, team, execution, delegation, engineering, what_reaches_you,
|
|
3
|
+
// reaching_others, writing, role, execution_context (the last one is execution-context.mjs).
|
|
4
|
+
// main's leaves out engineering and delegation, since main never builds and has no subagents; a
|
|
5
|
+
// bot's delegation is there only when its hub.workspace.json entry declares `subagents`. eve joins a bot's
|
|
5
6
|
// instruction sources with a blank line, flat instructions.{md,ts} first, then instructions/*
|
|
6
7
|
// by file name, so the order is made in code: one source renders identity, the shared sections
|
|
7
8
|
// and role together (botInstructions, mainInstructions), and instructions/execution-context.ts
|
|
@@ -16,9 +17,11 @@ import { WorkspaceError, coordinatorOf, findWorkspaceRoot, readWorkspaceFile } f
|
|
|
16
17
|
|
|
17
18
|
const HEAD = "main-instructions.md";
|
|
18
19
|
const ENGINEERING = "engineering.md";
|
|
20
|
+
const DELEGATION = "delegation";
|
|
19
21
|
const SHARED = [
|
|
20
22
|
["team", "team.md"],
|
|
21
23
|
["execution", "execution.md"],
|
|
24
|
+
[DELEGATION, "delegation.md"],
|
|
22
25
|
[ENGINEERING.slice(0, -3), ENGINEERING],
|
|
23
26
|
["what_reaches_you", "deliveries.md"],
|
|
24
27
|
["reaching_others", "reaching-others.md"],
|
|
@@ -42,18 +45,26 @@ export function templatesDir({ env = process.env, cwd = process.cwd() } = {}) {
|
|
|
42
45
|
export const section = (tag, body) => `<${tag}>\n${body.trim()}\n</${tag}>`;
|
|
43
46
|
const template = (name, options) => readFileSync(join(templatesDir(options), name), "utf8");
|
|
44
47
|
|
|
45
|
-
//
|
|
46
|
-
|
|
48
|
+
// main's template starts with its identity paragraph, then how it works: the coordinator's identity is the kit's, not the roster's.
|
|
49
|
+
function splitRole(text, source = "role file") {
|
|
47
50
|
const [identity, ...rest] = text.trim().split(/\n\s*\n/);
|
|
48
51
|
const role = rest.join("\n\n").trim();
|
|
49
52
|
if (!identity || !role) throw new Error(`${source} needs an identity paragraph, a blank line, then the role`);
|
|
50
53
|
return { identity, role };
|
|
51
54
|
}
|
|
52
55
|
|
|
56
|
+
// Every other bot's identity is its roster line, the one place a bot's one-line role is written:
|
|
57
|
+
// hub.workspace.json `role`. Its role.md holds only how it works. A role.md made before that still
|
|
58
|
+
// opens with its own "You are **name**." paragraph; that one is dropped, so the roster's is the only one.
|
|
59
|
+
export const identityOf = ({ name, role }) => `You are **${name}**. ${role}`;
|
|
60
|
+
export const roleBody = (text, bot) => text.trim().replace(new RegExp(`^You are \\*\\*${bot}\\*\\*[^]*?(?:\\n\\s*\\n|$)`), "").trim();
|
|
61
|
+
|
|
53
62
|
// What eve joins: each source's content with a blank line between, and trims only the whole. So a
|
|
54
63
|
// source ends at its closing tag, with no newline of its own.
|
|
55
|
-
const assemble = ({ identity, role }, additions, shared, options) =>
|
|
56
|
-
|
|
64
|
+
const assemble = ({ identity, role }, additions, shared, options) => {
|
|
65
|
+
const body = [role, ...additions].filter(Boolean).join("\n\n");
|
|
66
|
+
return [section("identity", identity), sections(shared, options), ...(body ? [section("role", body)] : [])].join("\n\n");
|
|
67
|
+
};
|
|
57
68
|
|
|
58
69
|
// A bot's own additions to its role: agents/<bot>/agent/role/*.md, by file name, inside <role>.
|
|
59
70
|
// They can't sit in instructions/*.md: eve loads that folder itself, after execution-context.ts,
|
|
@@ -74,15 +85,19 @@ function additionsOf(bot, options) {
|
|
|
74
85
|
return readdirSync(dir).filter((f) => f.endsWith(".md")).sort().map((f) => readFileSync(join(dir, f), "utf8").trim());
|
|
75
86
|
}
|
|
76
87
|
|
|
77
|
-
// Main never builds, so it goes without <engineering>.
|
|
78
|
-
const MAIN_SHARED = SHARED.filter(([tag]) => tag !== ENGINEERING.slice(0, -3));
|
|
88
|
+
// Main never builds and has no subagents, so it goes without <engineering> and <delegation>.
|
|
89
|
+
const MAIN_SHARED = SHARED.filter(([tag]) => tag !== ENGINEERING.slice(0, -3) && tag !== DELEGATION);
|
|
79
90
|
export const mainInstructions = (options) => assemble(splitRole(template(HEAD, options), HEAD), additionsOf(null, options), MAIN_SHARED, options);
|
|
80
91
|
|
|
81
|
-
// Every other bot: agents/<bot>/agent/role.md
|
|
92
|
+
// Every other bot: its roster entry's role is the identity, agents/<bot>/agent/role.md is the role.
|
|
82
93
|
export { ROLE_DIR, ROLE_FILE };
|
|
83
94
|
export function botInstructions(bot, options) {
|
|
84
|
-
const
|
|
85
|
-
|
|
95
|
+
const root = findWorkspaceRoot(options);
|
|
96
|
+
const entry = readWorkspaceFile(root).agents.find((agent) => agent.name === bot);
|
|
97
|
+
if (!entry) throw new WorkspaceError(`${bot} is not in ${join(root, "hub.workspace.json")}`);
|
|
98
|
+
const role = roleBody(readFileSync(join(root, "agents", bot, "agent", ROLE_FILE), "utf8"), bot);
|
|
99
|
+
const shared = Object.keys(entry.subagents ?? {}).length ? SHARED : SHARED.filter(([tag]) => tag !== DELEGATION);
|
|
100
|
+
return assemble({ identity: identityOf(entry), role }, additionsOf(bot, options), shared, options);
|
|
86
101
|
}
|
|
87
102
|
|
|
88
103
|
// The engineering section alone, for a subagent that doesn't carry the shared sections
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eve-hub",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.47",
|
|
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
|
@@ -87,8 +87,8 @@ export const PROTOCOL_STUB = `// The shared sections every bot carries, from pac
|
|
|
87
87
|
// Don't copy them there.
|
|
88
88
|
export { default } from "eve-hub/instructions/protocol";
|
|
89
89
|
`;
|
|
90
|
-
// A bot with a role.md (agents/<bot>/agent/role.md:
|
|
91
|
-
//
|
|
90
|
+
// A bot with a role.md (agents/<bot>/agent/role.md: how it works; who it is comes from its
|
|
91
|
+
// hub.workspace.json `role`): instructions.ts renders its whole prompt, in order: identity, the shared sections, role
|
|
92
92
|
// (packages/agent-kit/main-instructions.mjs). eve puts instructions.ts before instructions/*, so
|
|
93
93
|
// the execution context stub follows.
|
|
94
94
|
export { ROLE_DIR, ROLE_FILE };
|
|
@@ -96,7 +96,7 @@ export const botInstructionsStub = (name) => `import { defineInstructions } from
|
|
|
96
96
|
import { botInstructions } from "eve-hub/main-instructions";
|
|
97
97
|
|
|
98
98
|
// ${name}'s system prompt: its role.md between the kit's templates (packages/agent-kit/templates/).
|
|
99
|
-
// Edit
|
|
99
|
+
// Edit how ${name} works in role.md, and who ${name} is in hub.workspace.json, not here.
|
|
100
100
|
export default defineInstructions({ content: botInstructions(${JSON.stringify(name)}) });
|
|
101
101
|
`;
|
|
102
102
|
const BOT_STUB = /^import \{ defineInstructions \} from "eve\/instructions";\s*import \{ botInstructions \} from "eve-hub\/main-instructions";\s*export default defineInstructions\(\{ content: botInstructions\("[a-z][a-z0-9-]*"\) \}\);?$/;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
- Your reader, editor and verifier run on models far cheaper than yours: reading goes to reader, specified edits to editor, pass/fail checks (running tests included) to verifier. When work splits, count the pieces before you route: three files to trace is three readers, two test runs to check is two verifiers. Never merge pieces, and never keep one of them yourself. Hand every read, check and specified edit to its tier, even one on its own: a test run, a CI log, a read across files, a repo search. Keep a task only when it is one or two tool calls with short output. Fan out work that splits into independent pieces: one tier call per piece, all in one step. Judgment always stays with you.
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
- **The shell is the owner's.** `git`, `gh` and the keychain use the owner's own sign-ins. Programs come from Homebrew (`brew`) or the owner's own installs; `brew list` and `which` tell you what's there.
|
|
6
6
|
- **The owner's checkouts are theirs.** Unless the owner asks, never edit, commit, stash or switch branches in one. Work in your own `git worktree add`, and leave theirs as you found them.
|
|
7
7
|
- **Coding agents keep their sessions here:** Claude Code in `~/.claude/projects/`, Codex in `~/.codex/sessions/` and `~/.codex/state_*.sqlite` (`~/.codex` is the default for `CODEX_HOME`), OpenCode in `~/.local/share/opencode/`, and Cursor in `~/Library/Application Support/Cursor/User/` and `~/.cursor/`. They hold the owner's prompts and code. Read them for facts such as the folder, branch, title and last activity. Never quote their contents or post them.
|
|
8
|
+
- **Never take over the human's screen.** No clicking, typing or raising windows on the real display. Work offscreen, in background windows or with `hubctl`; anything that needs the real display waits until the human says it's free.
|
|
8
9
|
- **Ask first,** with what you'll run and why, before you:
|
|
9
10
|
- read the keychain (`security`) or any credential file;
|
|
10
11
|
- install, upgrade or remove a program (`brew install`, `npm -g`, `pip`);
|
package/templates/execution.md
CHANGED
|
@@ -15,8 +15,7 @@
|
|
|
15
15
|
- Why: every question costs the human a context switch, and most answers are in the code or the history.
|
|
16
16
|
- **Split the work**
|
|
17
17
|
- Do the part your role owns; hand the rest to the role that owns it, because skipping a role skips its check.
|
|
18
|
-
-
|
|
19
|
-
- Before you hand work to a reader, editor, verifier or `agent` copy, load the `fanout` skill and follow it: the receiver starts with none of your context.
|
|
18
|
+
- Before you hand work to an `agent` copy (or a reader, editor or verifier), load the `fanout` skill and follow it: the receiver starts with none of your context.
|
|
20
19
|
- A subagent with no commit, push, report or verdict in 30 minutes is stuck: cancel it and finish the work yourself.
|
|
21
20
|
- **Waiting**
|
|
22
21
|
- Never sleep or poll more than 60 seconds in one command. Check CI or a deploy once, report, and say what you're waiting on.
|
|
@@ -29,4 +28,3 @@
|
|
|
29
28
|
- If the human says stop or hold off, do it and confirm in one line.
|
|
30
29
|
- **Your footprint**
|
|
31
30
|
- Delete the scratch checkouts, test threads and probe data your runs made, and stop the servers you started, before the turn ends.
|
|
32
|
-
- Never take over the human's screen (no clicking, typing or raising windows on the real display). Work offscreen, in background windows or with `hubctl`; anything that needs the real display waits until the human says it's free.
|
|
@@ -21,6 +21,10 @@ You are the **coordinator**, the human's chief of staff. The human talks to you
|
|
|
21
21
|
- **A merge can unblock other threads:** on a merge report, `list_threads` for threads that wait on it (a PR stacked on it, a build or release of it, a follow-up gated on it), and in the same turn post in each to its owning bot with its next step (rebase, build, start).
|
|
22
22
|
- **A post addressed to you (a report, verdict or answer):** never end the turn on a reaction alone. Reply with the next step: who acts, on what, by when. If the step is a wait (CI, a run, the human), name it and arm a wake-up. React alone only when the post changes nothing and the next step is already named, armed and live.
|
|
23
23
|
|
|
24
|
+
**What the human decides.** Scope: the owner of a thread posts a brief and the human confirms it before anything is built. After that, only merge approval and anything that needs their eyes wait on them.
|
|
25
|
+
|
|
26
|
+
**Change a running turn.** `post_message` with `policy: "steer"` adds your input to the turn a bot is running; `"abort"` stops it and your text starts its next turn. Use them only to correct work already under way; otherwise queue.
|
|
27
|
+
|
|
24
28
|
**Keep the workspace legible.** Keep tags tidy with `tag_threads` and `apply_tags`, and say in one line what changed. On the Dash, the hub sets ready, and a deferral the human made stays. Tags and paths the human set stay too. In a track chat, answer from that track's threads, their states and PRs, and post new work in the thread that owns it.
|
|
25
29
|
|
|
26
30
|
**Memory.** A preference that should outlast the task becomes `memory/<id>.md` (every bot) or `agents/<bot>/memory/<id>.md` (one bot). It has front matter `id`, `status: active`, `since` and `evidence` (`[thr_…/msg_…]`), then the rule in at most 3 lines. A replaced entry gets `status: superseded` and `superseded_by`, never a rewrite. The human's profile is `memory/human.md`, one page every bot recalls each turn; only onboarding and the weekly workspace review write it, each in a draft PR.
|
|
@@ -12,9 +12,6 @@
|
|
|
12
12
|
- Copy the handle from `<team_status>`, never from an old message.
|
|
13
13
|
- **DMs**
|
|
14
14
|
- In a DM, mentions are links and wake nobody, so tag every bot you refer to.
|
|
15
|
-
- **Changing someone's running turn**
|
|
16
|
-
- `policy: "steer"` adds your input to the turn it's running; `"abort"` stops it and your text starts its next turn.
|
|
17
|
-
- Use them only to correct work already under way; otherwise queue.
|
|
18
15
|
- **Phase**
|
|
19
16
|
- When the thread's phase changes (planning, working, reviewing, done), add a status post: at most 8 plain words, no @-mention.
|
|
20
17
|
- The brief, question or handoff is its own post, without `status`: the app draws a status post as a grey phase row, and the hub refuses one that carries more.
|
package/templates/team.md
CHANGED
|
@@ -11,6 +11,3 @@
|
|
|
11
11
|
- A branch has one owner. Anyone else sends the owner input or stacks a branch on top.
|
|
12
12
|
- A follow-up found elsewhere gets a link to the owning thread and is discussed there.
|
|
13
13
|
- Why: two bots instructing one builder, or pushing one branch, undo each other's work.
|
|
14
|
-
- **What the human decides**
|
|
15
|
-
- Scope: the owner posts a brief and the human confirms it before anything is built (see *Engineering*).
|
|
16
|
-
- After that, only merge approval and anything that needs their eyes wait on them.
|
package/tools/post_message.js
CHANGED
|
@@ -197,7 +197,7 @@ export default defineTool({
|
|
|
197
197
|
.extend({ ref: z.string().optional().describe("What the row opens. Default: this post.") })
|
|
198
198
|
.optional()
|
|
199
199
|
.describe("The thread's tracker row this post marks, written with the post: e.g. on a status post handing a PR to review, {id: 'review', title: 'Review the fix', state: 'started', owner: 'reviewer'}. The hub merges it by id, so the same id on a later post moves the same row, and stamps the time and your name when its state, owner or wait changes. Optional."),
|
|
200
|
-
note: noteInput.optional().describe("One entry in the thread's timeline of notes, the Artifacts pane's list, newest first: {kind, text, key?, about?}.
|
|
200
|
+
note: noteInput.optional().describe("One entry in the thread's timeline of notes, the Artifacts pane's list, newest first: {kind, text, key?, about?}. The hub refuses a post that has a `verdict`, `status: \"done\"`, or an artifact of kind `plan` and no `note`."),
|
|
201
201
|
}),
|
|
202
202
|
async execute({ threadId, agent, text, images, policy, status, metadata, verdict, decisions, tested, artifact, step, note }, ctx) {
|
|
203
203
|
const bad = textError(text);
|