From b258db712b843eef2572b1ddd93de00e84829111 Mon Sep 17 00:00:00 2001 From: prha Date: Tue, 4 Aug 2026 13:21:08 -0700 Subject: [PATCH 1/3] feat(eve-selfmod): add source editing subagent Signed-off-by: prha --- .changeset/fresh-selfmod-package.md | 5 ++ .changeset/source-workspace-exports.md | 5 ++ packages/eve-selfmod/.gitignore | 3 + packages/eve-selfmod/README.md | 34 ++++++++ packages/eve-selfmod/extension/extension.ts | 3 + .../eve-selfmod/extension/instructions.ts | 9 +++ packages/eve-selfmod/package.json | 77 +++++++++++++++++++ packages/eve-selfmod/src/agent.ts | 22 ++++++ packages/eve-selfmod/src/sandbox.ts | 26 +++++++ packages/eve-selfmod/tsconfig.json | 9 +++ packages/eve-selfmod/tsconfig.scaffold.json | 12 +++ .../authored-module-loader.scenario.test.ts | 5 +- .../src/internal/authored-module-loader.ts | 2 + pnpm-lock.yaml | 16 ++++ scripts/assert-changeset-publish-packages.mjs | 2 +- 15 files changed, 228 insertions(+), 2 deletions(-) create mode 100644 .changeset/fresh-selfmod-package.md create mode 100644 .changeset/source-workspace-exports.md create mode 100644 packages/eve-selfmod/.gitignore create mode 100644 packages/eve-selfmod/README.md create mode 100644 packages/eve-selfmod/extension/extension.ts create mode 100644 packages/eve-selfmod/extension/instructions.ts create mode 100644 packages/eve-selfmod/package.json create mode 100644 packages/eve-selfmod/src/agent.ts create mode 100644 packages/eve-selfmod/src/sandbox.ts create mode 100644 packages/eve-selfmod/tsconfig.json create mode 100644 packages/eve-selfmod/tsconfig.scaffold.json diff --git a/.changeset/fresh-selfmod-package.md b/.changeset/fresh-selfmod-package.md new file mode 100644 index 000000000..ab50b6e5f --- /dev/null +++ b/.changeset/fresh-selfmod-package.md @@ -0,0 +1,5 @@ +--- +"eve-selfmod": patch +--- + +Publish a development-only source editing subagent kit that mounts authored agent source read-write and provides its instructions through a mounted extension, allowing future tools and skills to ship without scaffold changes. diff --git a/.changeset/source-workspace-exports.md b/.changeset/source-workspace-exports.md new file mode 100644 index 000000000..abac54c56 --- /dev/null +++ b/.changeset/source-workspace-exports.md @@ -0,0 +1,5 @@ +--- +"eve": patch +--- + +Honor `eve-source` package export conditions when bundling authored modules so linked workspace capability packages work before their distribution build exists. diff --git a/packages/eve-selfmod/.gitignore b/packages/eve-selfmod/.gitignore new file mode 100644 index 000000000..828bc4f17 --- /dev/null +++ b/packages/eve-selfmod/.gitignore @@ -0,0 +1,3 @@ +dist +scaffold +*.tsbuildinfo diff --git a/packages/eve-selfmod/README.md b/packages/eve-selfmod/README.md new file mode 100644 index 000000000..4a39405b0 --- /dev/null +++ b/packages/eve-selfmod/README.md @@ -0,0 +1,34 @@ +# eve-selfmod + +A development-only source editing subagent for [eve](https://eve.dev). + +Install the package and scaffold a declared subagent from the eve registry: + +```sh +eve add eve-selfmod +``` + +The command creates: + +```text +agent/subagents/selfmod/ +├── agent.ts +├── sandbox.ts +└── extensions/ + └── selfmod.ts +``` + +```ts +// agent/subagents/selfmod/agent.ts +export { default } from "eve-selfmod/agent"; + +// agent/subagents/selfmod/sandbox.ts +export { default } from "eve-selfmod/sandbox"; + +// agent/subagents/selfmod/extensions/selfmod.ts +export { default } from "eve-selfmod"; +``` + +The sandbox mounts the application's authored `agent/` directory read-write at `/source`. The subagent can inspect and edit that source directly with eve's default bash and filesystem tools during development. + +The mounted extension provides the source-editing instructions. Future tools, skills, and other extension contributions can ship from `eve-selfmod` without changing the registry scaffold. diff --git a/packages/eve-selfmod/extension/extension.ts b/packages/eve-selfmod/extension/extension.ts new file mode 100644 index 000000000..eb45471fd --- /dev/null +++ b/packages/eve-selfmod/extension/extension.ts @@ -0,0 +1,3 @@ +import { defineExtension } from "eve/extension"; + +export default defineExtension(); diff --git a/packages/eve-selfmod/extension/instructions.ts b/packages/eve-selfmod/extension/instructions.ts new file mode 100644 index 000000000..216211090 --- /dev/null +++ b/packages/eve-selfmod/extension/instructions.ts @@ -0,0 +1,9 @@ +import { defineInstructions } from "eve/instructions"; + +export default defineInstructions({ + markdown: `You are a source editor for an eve application. + +The live authored agent directory is mounted read-write at /source. Use /workspace only for temporary files. Inspect /source with bash, glob, grep, and read_file instead of guessing filenames. Runtime skill paths under $HOME/.agents and /workspace/skills belong to the caller's sandbox, are not authored source, and may not exist here. Never search those runtime paths even when the caller mentions them; discover the corresponding authored definition under /source instead. /source is the mounted authored agent directory, not the application or package root. Do not look for package.json, src/, or other project-level files. Do not assume conventional filenames such as tools/index.ts or skills//SKILL.md. eve definitions are filesystem-first and may be flat files. Read the exact discovered paths before editing them. + +Make requested source changes directly with bash or write_file. Keep changes minimal and modify only what the developer requested. “Minimal” means the smallest change that actually implements the request, not necessarily the smallest diff. Do not substitute prompting for implementation when the requested behavior requires deterministic logic or an external side effect. You cannot access application files outside the authored agent directory or run host binaries such as git, node, pnpm, or tsc. Source changes do not affect the current turn; after a successful development rebuild, they take effect on the next turn. Return a concise summary of files changed and any validation limitations.`, +}); diff --git a/packages/eve-selfmod/package.json b/packages/eve-selfmod/package.json new file mode 100644 index 000000000..5b2431ab1 --- /dev/null +++ b/packages/eve-selfmod/package.json @@ -0,0 +1,77 @@ +{ + "name": "eve-selfmod", + "version": "0.0.0", + "description": "Development-only source editing subagent for eve applications.", + "keywords": [ + "agents", + "eve", + "extension", + "self-modifying" + ], + "homepage": "https://github.com/vercel/eve#readme", + "bugs": { + "url": "https://github.com/vercel/eve/issues" + }, + "license": "Apache-2.0", + "repository": { + "type": "git", + "url": "git+https://github.com/vercel/eve.git", + "directory": "packages/eve-selfmod" + }, + "files": [ + "dist", + "scaffold", + "README.md" + ], + "type": "module", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "default": "./dist/index.mjs" + }, + "./agent": { + "eve-source": "./src/agent.ts", + "types": "./scaffold/agent.d.ts", + "import": "./scaffold/agent.js", + "default": "./scaffold/agent.js" + }, + "./sandbox": { + "eve-source": "./src/sandbox.ts", + "types": "./scaffold/sandbox.d.ts", + "import": "./scaffold/sandbox.js", + "default": "./scaffold/sandbox.js" + }, + "./tools": { + "types": "./dist/tools/index.d.ts", + "default": "./dist/tools/index.mjs" + } + }, + "publishConfig": { + "access": "public" + }, + "scripts": { + "build": "eve extension build && tsc -p tsconfig.scaffold.json", + "prepare": "pnpm run build", + "typecheck": "tsc -p tsconfig.json && tsc -p tsconfig.scaffold.json --noEmit" + }, + "dependencies": { + "just-bash": "3.1.0" + }, + "devDependencies": { + "@types/node": "catalog:", + "eve": "workspace:*", + "typescript": "catalog:" + }, + "peerDependencies": { + "eve": "workspace:*" + }, + "engines": { + "node": ">=24" + }, + "eve": { + "extension": { + "source": "./extension", + "dist": "./dist/extension" + } + } +} diff --git a/packages/eve-selfmod/src/agent.ts b/packages/eve-selfmod/src/agent.ts new file mode 100644 index 000000000..6827b61a0 --- /dev/null +++ b/packages/eve-selfmod/src/agent.ts @@ -0,0 +1,22 @@ +import { defineAgent, defineDynamic } from "eve"; + +export default defineDynamic({ + build: { externalDependencies: ["eve-selfmod"] }, + events: { + "session.started": () => + process.env.EVE_DEV === "1" + ? defineAgent({ + description: + "Delegate here when the developer asks to change this eve agent or its authored source. " + + "Describe the requested behavior change without guessing source paths or passing runtime skill paths under $HOME/.agents or /workspace/skills; the subagent discovers authored files under its /source mount. " + + "Treat requests for persistent changes to future behavior or capabilities as source-modification requests, even when the developer does not mention files or source code. " + + "Infer persistence from the request and conversation rather than waiting for phrases such as “modify your source.” " + + "For example, asking the agent to stop always doing something, add a capability, or change future responses calls for inspecting and editing the authored source instead of providing a one-turn workaround. " + + "Resolve short follow-ups such as “yes” or “do it” against the preceding conversation. " + + "Source edits do not affect the caller’s current turn. After this subagent reports changes, do not invoke edited tools or attempt runtime verification until a new user turn. " + + "If whether the requested change should persist is genuinely ambiguous, ask one concise clarifying question.", + model: "anthropic/claude-sonnet-5", + }) + : null, + }, +}); diff --git a/packages/eve-selfmod/src/sandbox.ts b/packages/eve-selfmod/src/sandbox.ts new file mode 100644 index 000000000..dbe4cec6b --- /dev/null +++ b/packages/eve-selfmod/src/sandbox.ts @@ -0,0 +1,26 @@ +import { resolve } from "node:path"; + +import { defineSandbox } from "eve/sandbox"; +import { justbash } from "eve/sandbox/just-bash"; +import { MountableFs, ReadWriteFs } from "just-bash"; + +export default defineSandbox({ + backend: justbash({ + async filesystem({ appRoot, defaultFilesystem }) { + await defaultFilesystem.mkdir("/source", { recursive: true }); + return new MountableFs({ + base: defaultFilesystem, + mounts: [ + { + filesystem: new ReadWriteFs({ + allowSymlinks: false, + maxFileReadSize: Number.MAX_SAFE_INTEGER, + root: resolve(appRoot, "agent"), + }), + mountPoint: "/source", + }, + ], + }); + }, + }), +}); diff --git a/packages/eve-selfmod/tsconfig.json b/packages/eve-selfmod/tsconfig.json new file mode 100644 index 000000000..914d8f409 --- /dev/null +++ b/packages/eve-selfmod/tsconfig.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "../../tsconfig.json", + "compilerOptions": { + "noEmit": true, + "rootDir": "." + }, + "include": ["extension/**/*.ts", "src/**/*.ts"] +} diff --git a/packages/eve-selfmod/tsconfig.scaffold.json b/packages/eve-selfmod/tsconfig.scaffold.json new file mode 100644 index 000000000..ec64de939 --- /dev/null +++ b/packages/eve-selfmod/tsconfig.scaffold.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "./tsconfig.json", + "compilerOptions": { + "declaration": true, + "declarationMap": true, + "noEmit": false, + "outDir": "./scaffold", + "rootDir": "./src" + }, + "include": ["src/**/*.ts"] +} diff --git a/packages/eve/src/internal/authored-module-loader.scenario.test.ts b/packages/eve/src/internal/authored-module-loader.scenario.test.ts index ad3993797..cdb0f5964 100644 --- a/packages/eve/src/internal/authored-module-loader.scenario.test.ts +++ b/packages/eve/src/internal/authored-module-loader.scenario.test.ts @@ -316,7 +316,10 @@ describe("loadAuthoredModuleNamespace", () => { JSON.stringify( { exports: { - "./exa-linkedin": "./src/exa-linkedin.ts", + "./exa-linkedin": { + "eve-source": "./src/exa-linkedin.ts", + default: "./dist/exa-linkedin.js", + }, }, name: "@repo/enrichment", type: "module", diff --git a/packages/eve/src/internal/authored-module-loader.ts b/packages/eve/src/internal/authored-module-loader.ts index e90b0fb7e..c9e87d5e4 100644 --- a/packages/eve/src/internal/authored-module-loader.ts +++ b/packages/eve/src/internal/authored-module-loader.ts @@ -302,6 +302,7 @@ export async function bundleAuthoredModuleMapForGeneration(input: { platform: "node", plugins, resolve: { + conditionNames: ["eve-source", "node", "import", "default"], extensions: [...RESOLVE_EXTENSIONS], }, tsconfig: resolveAuthoredTsConfigPath(packageRoot), @@ -415,6 +416,7 @@ async function buildAuthoredModuleBundle( platform: "node", plugins, resolve: { + conditionNames: ["eve-source", "node", "import", "default"], extensions: [...RESOLVE_EXTENSIONS], }, tsconfig: tsconfigPath, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b0eef5572..5f185b21c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1424,6 +1424,22 @@ importers: specifier: 'catalog:' version: 4.1.10(@edge-runtime/vm@3.2.0)(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(vite@8.1.5(@types/node@25.9.1)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0)) + packages/eve-selfmod: + dependencies: + just-bash: + specifier: 3.1.0 + version: 3.1.0(supports-color@10.2.2) + devDependencies: + '@types/node': + specifier: 'catalog:' + version: 25.9.1 + eve: + specifier: workspace:* + version: link:../eve + typescript: + specifier: 'catalog:' + version: 7.0.2 + packages: '@agent-browser/eve@0.33.0': diff --git a/scripts/assert-changeset-publish-packages.mjs b/scripts/assert-changeset-publish-packages.mjs index 1fc514644..be3932f05 100644 --- a/scripts/assert-changeset-publish-packages.mjs +++ b/scripts/assert-changeset-publish-packages.mjs @@ -1,7 +1,7 @@ import { access, readdir, readFile } from "node:fs/promises"; import { join } from "node:path"; -const allowedPublicPackages = new Set(["eve"]); +const allowedPublicPackages = new Set(["eve", "eve-selfmod"]); const workspaceRoots = ["apps", "packages"]; async function readJson(path) { From f1dcfd0b84775a7be6aa369da819b00d7f0dbb03 Mon Sep 17 00:00:00 2001 From: prha Date: Wed, 5 Aug 2026 13:57:17 -0700 Subject: [PATCH 2/3] feat(eve-selfmod): add configurable selfmod kit Signed-off-by: prha --- .changeset/fresh-selfmod-package.md | 2 +- packages/eve-selfmod/README.md | 22 +++++--- .../eve-selfmod/extension/instructions.ts | 4 +- packages/eve-selfmod/package.json | 10 +++- .../scripts/add-selfmod-export.mjs | 9 ++++ packages/eve-selfmod/src/agent.ts | 50 +++++++++++-------- packages/eve-selfmod/src/index.test.ts | 36 +++++++++++++ packages/eve-selfmod/src/index.ts | 35 +++++++++++++ packages/eve-selfmod/tsconfig.scaffold.json | 3 +- pnpm-lock.yaml | 3 ++ 10 files changed, 140 insertions(+), 34 deletions(-) create mode 100644 packages/eve-selfmod/scripts/add-selfmod-export.mjs create mode 100644 packages/eve-selfmod/src/index.test.ts create mode 100644 packages/eve-selfmod/src/index.ts diff --git a/.changeset/fresh-selfmod-package.md b/.changeset/fresh-selfmod-package.md index ab50b6e5f..a725d8c1f 100644 --- a/.changeset/fresh-selfmod-package.md +++ b/.changeset/fresh-selfmod-package.md @@ -2,4 +2,4 @@ "eve-selfmod": patch --- -Publish a development-only source editing subagent kit that mounts authored agent source read-write and provides its instructions through a mounted extension, allowing future tools and skills to ship without scaffold changes. +Publish a configurable, development-only source editing subagent kit that mounts authored agent source read-write and provides its instructions through a mounted extension. The registry scaffold uses one `defineSelfmod` configuration for the subagent and sandbox. diff --git a/packages/eve-selfmod/README.md b/packages/eve-selfmod/README.md index 4a39405b0..f9d20ccd4 100644 --- a/packages/eve-selfmod/README.md +++ b/packages/eve-selfmod/README.md @@ -5,13 +5,14 @@ A development-only source editing subagent for [eve](https://eve.dev). Install the package and scaffold a declared subagent from the eve registry: ```sh -eve add eve-selfmod +eve add experimental/selfmod ``` -The command creates: +The command creates one selfmod configuration and thin filesystem entrypoints: ```text agent/subagents/selfmod/ +├── config.ts ├── agent.ts ├── sandbox.ts └── extensions/ @@ -19,16 +20,23 @@ agent/subagents/selfmod/ ``` ```ts +// agent/subagents/selfmod/config.ts +import { defineSelfmod } from "eve-selfmod"; + +export const selfmod = defineSelfmod({ + model: "anthropic/claude-sonnet-5", +}); + // agent/subagents/selfmod/agent.ts -export { default } from "eve-selfmod/agent"; +import { selfmod } from "./config"; +export default selfmod.agent; // agent/subagents/selfmod/sandbox.ts -export { default } from "eve-selfmod/sandbox"; +import { selfmod } from "./config"; +export default selfmod.sandbox; // agent/subagents/selfmod/extensions/selfmod.ts export { default } from "eve-selfmod"; ``` -The sandbox mounts the application's authored `agent/` directory read-write at `/source`. The subagent can inspect and edit that source directly with eve's default bash and filesystem tools during development. - -The mounted extension provides the source-editing instructions. Future tools, skills, and other extension contributions can ship from `eve-selfmod` without changing the registry scaffold. +`defineSelfmod` returns the coordinated subagent, sandbox, and extension definitions. Set `model` in the shared configuration to choose the selfmod subagent's model; it defaults to `anthropic/claude-sonnet-5`. The extension entrypoint remains a direct package mount so eve can discover its extension contributions statically. With no configuration it preserves the development-only behavior: the sandbox mounts the application's authored `agent/` directory read-write at `/source`, where the subagent can inspect and edit it with eve's default bash and filesystem tools. diff --git a/packages/eve-selfmod/extension/instructions.ts b/packages/eve-selfmod/extension/instructions.ts index 216211090..23e0f8917 100644 --- a/packages/eve-selfmod/extension/instructions.ts +++ b/packages/eve-selfmod/extension/instructions.ts @@ -3,7 +3,7 @@ import { defineInstructions } from "eve/instructions"; export default defineInstructions({ markdown: `You are a source editor for an eve application. -The live authored agent directory is mounted read-write at /source. Use /workspace only for temporary files. Inspect /source with bash, glob, grep, and read_file instead of guessing filenames. Runtime skill paths under $HOME/.agents and /workspace/skills belong to the caller's sandbox, are not authored source, and may not exist here. Never search those runtime paths even when the caller mentions them; discover the corresponding authored definition under /source instead. /source is the mounted authored agent directory, not the application or package root. Do not look for package.json, src/, or other project-level files. Do not assume conventional filenames such as tools/index.ts or skills//SKILL.md. eve definitions are filesystem-first and may be flat files. Read the exact discovered paths before editing them. +The live authored agent directory is mounted read-write at /source. Use /workspace only for temporary files. Inspect /source with bash, glob, grep, and read_file instead of guessing filenames. Batch independent discovery, searches, and file reads into as few tool calls as possible; prefer one shell call containing multiple independent commands over separate bash, grep, sed, or cat calls for each path. Runtime skill paths under $HOME/.agents and /workspace/skills belong to the caller's sandbox, are not authored source, and may not exist here. Never search those runtime paths even when the caller mentions them; discover the corresponding authored definition under /source instead. /source is the mounted authored agent directory, not the application or package root. Do not look for package.json, src/, or other project-level files. Do not assume conventional filenames such as tools/index.ts or skills//SKILL.md. eve definitions are filesystem-first and may be flat files. Read the exact discovered paths before editing them. -Make requested source changes directly with bash or write_file. Keep changes minimal and modify only what the developer requested. “Minimal” means the smallest change that actually implements the request, not necessarily the smallest diff. Do not substitute prompting for implementation when the requested behavior requires deterministic logic or an external side effect. You cannot access application files outside the authored agent directory or run host binaries such as git, node, pnpm, or tsc. Source changes do not affect the current turn; after a successful development rebuild, they take effect on the next turn. Return a concise summary of files changed and any validation limitations.`, +Make requested source changes directly with bash or write_file. Before overwriting an existing file with write_file, call read_file on that exact path; reading it through bash, cat, grep, or sed does not satisfy the overwrite guard. Keep changes minimal and modify only what the developer requested. “Minimal” means the smallest change that actually implements the request, not necessarily the smallest diff. Do not substitute prompting for implementation when the requested behavior requires deterministic logic or an external side effect. You cannot access application files outside the authored agent directory or run host binaries such as git, node, pnpm, or tsc. Source changes do not affect the current turn; after a successful development rebuild, they take effect on the next turn. Return a concise summary of files changed and any validation limitations.`, }); diff --git a/packages/eve-selfmod/package.json b/packages/eve-selfmod/package.json index 5b2431ab1..89b02fa75 100644 --- a/packages/eve-selfmod/package.json +++ b/packages/eve-selfmod/package.json @@ -41,6 +41,10 @@ "import": "./scaffold/sandbox.js", "default": "./scaffold/sandbox.js" }, + "./extension": { + "types": "./dist/index.d.ts", + "default": "./dist/index.mjs" + }, "./tools": { "types": "./dist/tools/index.d.ts", "default": "./dist/tools/index.mjs" @@ -50,8 +54,9 @@ "access": "public" }, "scripts": { - "build": "eve extension build && tsc -p tsconfig.scaffold.json", + "build": "eve extension build && tsc -p tsconfig.scaffold.json && node scripts/add-selfmod-export.mjs", "prepare": "pnpm run build", + "test:unit": "vitest run src", "typecheck": "tsc -p tsconfig.json && tsc -p tsconfig.scaffold.json --noEmit" }, "dependencies": { @@ -60,7 +65,8 @@ "devDependencies": { "@types/node": "catalog:", "eve": "workspace:*", - "typescript": "catalog:" + "typescript": "catalog:", + "vitest": "catalog:" }, "peerDependencies": { "eve": "workspace:*" diff --git a/packages/eve-selfmod/scripts/add-selfmod-export.mjs b/packages/eve-selfmod/scripts/add-selfmod-export.mjs new file mode 100644 index 000000000..ad1f66d6f --- /dev/null +++ b/packages/eve-selfmod/scripts/add-selfmod-export.mjs @@ -0,0 +1,9 @@ +import { appendFile } from "node:fs/promises"; + +await Promise.all([ + appendFile("dist/index.mjs", '\nexport { defineSelfmod } from "../scaffold/index.js";\n'), + appendFile( + "dist/index.d.ts", + '\nexport { defineSelfmod, type SelfmodOptions } from "../scaffold/index.js";\n', + ), +]); diff --git a/packages/eve-selfmod/src/agent.ts b/packages/eve-selfmod/src/agent.ts index 6827b61a0..5ea67e82e 100644 --- a/packages/eve-selfmod/src/agent.ts +++ b/packages/eve-selfmod/src/agent.ts @@ -1,22 +1,30 @@ -import { defineAgent, defineDynamic } from "eve"; +import { defineAgent, defineDynamic, type AgentModelDefinition } from "eve"; -export default defineDynamic({ - build: { externalDependencies: ["eve-selfmod"] }, - events: { - "session.started": () => - process.env.EVE_DEV === "1" - ? defineAgent({ - description: - "Delegate here when the developer asks to change this eve agent or its authored source. " + - "Describe the requested behavior change without guessing source paths or passing runtime skill paths under $HOME/.agents or /workspace/skills; the subagent discovers authored files under its /source mount. " + - "Treat requests for persistent changes to future behavior or capabilities as source-modification requests, even when the developer does not mention files or source code. " + - "Infer persistence from the request and conversation rather than waiting for phrases such as “modify your source.” " + - "For example, asking the agent to stop always doing something, add a capability, or change future responses calls for inspecting and editing the authored source instead of providing a one-turn workaround. " + - "Resolve short follow-ups such as “yes” or “do it” against the preceding conversation. " + - "Source edits do not affect the caller’s current turn. After this subagent reports changes, do not invoke edited tools or attempt runtime verification until a new user turn. " + - "If whether the requested change should persist is genuinely ambiguous, ask one concise clarifying question.", - model: "anthropic/claude-sonnet-5", - }) - : null, - }, -}); +/** Default model used by selfmod when `defineSelfmod` does not override it. */ +export const DEFAULT_SELFMOD_MODEL = "anthropic/claude-sonnet-5"; + +/** Creates selfmod's development-only dynamic subagent definition. */ +export function createSelfmodAgent(model: AgentModelDefinition = DEFAULT_SELFMOD_MODEL) { + return defineDynamic({ + build: { externalDependencies: ["eve-selfmod"] }, + events: { + "session.started": () => + process.env.EVE_DEV === "1" + ? defineAgent({ + description: + "Delegate here when the developer asks to change this eve agent or its authored source. " + + "Describe the requested behavior change without guessing source paths or passing runtime skill paths under $HOME/.agents or /workspace/skills; the subagent discovers authored files under its /source mount. " + + "Treat requests for persistent changes to future behavior or capabilities as source-modification requests, even when the developer does not mention files or source code. " + + "Infer persistence from the request and conversation rather than waiting for phrases such as “modify your source.” " + + "For example, asking the agent to stop always doing something, add a capability, or change future responses calls for inspecting and editing the authored source instead of providing a one-turn workaround. " + + "Resolve short follow-ups such as “yes” or “do it” against the preceding conversation. " + + "Source edits do not affect the caller’s current turn. After this subagent reports changes, do not invoke edited tools or attempt runtime verification until a new user turn. " + + "If whether the requested change should persist is genuinely ambiguous, ask one concise clarifying question.", + model, + }) + : null, + }, + }); +} + +export default createSelfmodAgent(); diff --git a/packages/eve-selfmod/src/index.test.ts b/packages/eve-selfmod/src/index.test.ts new file mode 100644 index 000000000..568036644 --- /dev/null +++ b/packages/eve-selfmod/src/index.test.ts @@ -0,0 +1,36 @@ +import { afterEach, describe, expect, it } from "vitest"; + +import { DEFAULT_SELFMOD_MODEL } from "./agent.js"; +import { defineSelfmod } from "./index.js"; + +const originalEveDev = process.env.EVE_DEV; + +afterEach(() => { + if (originalEveDev === undefined) { + delete process.env.EVE_DEV; + } else { + process.env.EVE_DEV = originalEveDev; + } +}); + +describe("defineSelfmod", () => { + it("returns the coordinated definitions with the default model", async () => { + process.env.EVE_DEV = "1"; + + const selfmod = defineSelfmod(); + const definition = await selfmod.agent.events["session.started"]?.({}, {} as never); + + expect(definition).toMatchObject({ model: DEFAULT_SELFMOD_MODEL }); + expect(selfmod.extension).toBeDefined(); + expect(selfmod.sandbox).toBeDefined(); + }); + + it("configures the subagent model", async () => { + process.env.EVE_DEV = "1"; + + const selfmod = defineSelfmod({ model: "openai/gpt-5" }); + const definition = await selfmod.agent.events["session.started"]?.({}, {} as never); + + expect(definition).toMatchObject({ model: "openai/gpt-5" }); + }); +}); diff --git a/packages/eve-selfmod/src/index.ts b/packages/eve-selfmod/src/index.ts new file mode 100644 index 000000000..640b9284b --- /dev/null +++ b/packages/eve-selfmod/src/index.ts @@ -0,0 +1,35 @@ +import { createRequire } from "node:module"; + +import type { AgentModelDefinition } from "eve"; +import type { NoConfigExtensionHandle } from "eve/extension"; + +import type { createSelfmodAgent } from "./agent.js"; +import type sandboxDefinition from "./sandbox.js"; + +const require = createRequire(import.meta.url); + +/** Configuration for the selfmod subagent kit. */ +export interface SelfmodOptions { + /** + * Model used by the selfmod subagent. + * + * @default "anthropic/claude-sonnet-5" + */ + readonly model?: AgentModelDefinition; +} + +/** + * Defines the coordinated subagent, sandbox, and extension declarations for + * selfmod. With no options, selfmod is available only during local development. + */ +export function defineSelfmod(options: SelfmodOptions = {}) { + const agentModule = require("eve-selfmod/agent") as { + readonly createSelfmodAgent: typeof createSelfmodAgent; + }; + + return { + agent: agentModule.createSelfmodAgent(options.model), + extension: require("eve-selfmod/extension").default as NoConfigExtensionHandle, + sandbox: require("eve-selfmod/sandbox").default as typeof sandboxDefinition, + }; +} diff --git a/packages/eve-selfmod/tsconfig.scaffold.json b/packages/eve-selfmod/tsconfig.scaffold.json index ec64de939..6a7377f81 100644 --- a/packages/eve-selfmod/tsconfig.scaffold.json +++ b/packages/eve-selfmod/tsconfig.scaffold.json @@ -8,5 +8,6 @@ "outDir": "./scaffold", "rootDir": "./src" }, - "include": ["src/**/*.ts"] + "include": ["src/**/*.ts"], + "exclude": ["src/**/*.test.ts"] } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5f185b21c..61f9d4b45 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1439,6 +1439,9 @@ importers: typescript: specifier: 'catalog:' version: 7.0.2 + vitest: + specifier: 'catalog:' + version: 4.1.10(@edge-runtime/vm@3.2.0)(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(vite@8.1.5(@types/node@25.9.1)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.49.0)(tsx@4.21.0)(yaml@2.9.0)) packages: From f13a4d20c1f07bc4e4c63e928c56e57f7f401b28 Mon Sep 17 00:00:00 2001 From: prha Date: Wed, 5 Aug 2026 14:17:38 -0700 Subject: [PATCH 3/3] fix selfmod export Signed-off-by: prha --- packages/eve-selfmod/README.md | 2 +- packages/eve-selfmod/package.json | 6 +++++- packages/eve-selfmod/scripts/add-selfmod-export.mjs | 9 --------- 3 files changed, 6 insertions(+), 11 deletions(-) delete mode 100644 packages/eve-selfmod/scripts/add-selfmod-export.mjs diff --git a/packages/eve-selfmod/README.md b/packages/eve-selfmod/README.md index f9d20ccd4..e3094d22e 100644 --- a/packages/eve-selfmod/README.md +++ b/packages/eve-selfmod/README.md @@ -21,7 +21,7 @@ agent/subagents/selfmod/ ```ts // agent/subagents/selfmod/config.ts -import { defineSelfmod } from "eve-selfmod"; +import { defineSelfmod } from "eve-selfmod/config"; export const selfmod = defineSelfmod({ model: "anthropic/claude-sonnet-5", diff --git a/packages/eve-selfmod/package.json b/packages/eve-selfmod/package.json index 89b02fa75..ee7bab4a3 100644 --- a/packages/eve-selfmod/package.json +++ b/packages/eve-selfmod/package.json @@ -29,6 +29,10 @@ "types": "./dist/index.d.ts", "default": "./dist/index.mjs" }, + "./config": { + "types": "./scaffold/index.d.ts", + "default": "./scaffold/index.js" + }, "./agent": { "eve-source": "./src/agent.ts", "types": "./scaffold/agent.d.ts", @@ -54,7 +58,7 @@ "access": "public" }, "scripts": { - "build": "eve extension build && tsc -p tsconfig.scaffold.json && node scripts/add-selfmod-export.mjs", + "build": "eve extension build && tsc -p tsconfig.scaffold.json", "prepare": "pnpm run build", "test:unit": "vitest run src", "typecheck": "tsc -p tsconfig.json && tsc -p tsconfig.scaffold.json --noEmit" diff --git a/packages/eve-selfmod/scripts/add-selfmod-export.mjs b/packages/eve-selfmod/scripts/add-selfmod-export.mjs deleted file mode 100644 index ad1f66d6f..000000000 --- a/packages/eve-selfmod/scripts/add-selfmod-export.mjs +++ /dev/null @@ -1,9 +0,0 @@ -import { appendFile } from "node:fs/promises"; - -await Promise.all([ - appendFile("dist/index.mjs", '\nexport { defineSelfmod } from "../scaffold/index.js";\n'), - appendFile( - "dist/index.d.ts", - '\nexport { defineSelfmod, type SelfmodOptions } from "../scaffold/index.js";\n', - ), -]);