eve 0.11.0 → 0.11.2
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/dist/src/execution/sandbox/bindings/docker.js +1 -1
- package/dist/src/execution/sandbox/bindings/microsandbox-lifecycle.js +1 -1
- package/dist/src/execution/sandbox/bindings/microsandbox-runtime.d.ts +2 -1
- package/dist/src/execution/sandbox/bindings/microsandbox-runtime.js +1 -1
- package/dist/src/execution/sandbox/bindings/vercel-errors.d.ts +1 -0
- package/dist/src/execution/sandbox/bindings/vercel-errors.js +1 -2
- package/dist/src/execution/sandbox/bindings/vercel.d.ts +0 -5
- package/dist/src/execution/sandbox/bindings/vercel.js +1 -1
- package/dist/src/harness/code-mode.js +1 -1
- package/dist/src/harness/tool-loop.js +1 -1
- package/dist/src/harness/tools.d.ts +12 -0
- package/dist/src/harness/tools.js +1 -1
- package/dist/src/internal/application/package.js +1 -1
- package/dist/src/setup/scaffold/create/project.js +1 -1
- package/docs/README.md +1 -1
- package/docs/concepts/default-harness.md +14 -14
- package/docs/concepts/sessions-runs-and-streaming.md +27 -27
- package/docs/getting-started.mdx +5 -1
- package/docs/guides/frontend/overview.mdx +1 -1
- package/docs/{introduction.md → introduction.mdx} +5 -1
- package/docs/meta.json +0 -1
- package/docs/reference/cli.md +0 -2
- package/docs/{legal.md → responsible-use.md} +2 -4
- package/docs/tools/human-in-the-loop.md +83 -0
- package/docs/tools/meta.json +4 -0
- package/docs/{tools.mdx → tools/overview.mdx} +12 -35
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# eve
|
|
2
2
|
|
|
3
|
+
## 0.11.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- dbac239: Fix dynamic connection tools so approval gates from OpenAPI and other connection-backed tools are preserved when the tools are exposed to the model. Calls to connections with `approval: always()` now correctly park for HITL approval before execution.
|
|
8
|
+
|
|
9
|
+
## 0.11.1
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- e7cdefd: Handle missing sandbox template and session state more gracefully across Vercel, Microsandbox, and Docker backends. Eve now treats stale Vercel template references, missing Microsandbox session/template snapshots, and Docker template image races as recoverable provisioning misses so the runtime can rebuild or create a fresh sandbox automatically.
|
|
14
|
+
|
|
3
15
|
## 0.11.0
|
|
4
16
|
|
|
5
17
|
### Minor Changes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{randomUUID}from"node:crypto";import{expectDockerSuccess}from"#execution/sandbox/bindings/docker-utils.js";import{markDevelopmentSandboxBackendInitialized}from"#execution/sandbox/development-run.js";import{writeSandboxSeedFiles}from"#execution/sandbox/bindings/local-backend-utils.js";import{assertDockerDaemonAvailable,createDockerCli}from"#execution/sandbox/bindings/docker-cli.js";import{DOCKER_SANDBOX_LABEL,runDockerBaseSetup,startDockerContainer}from"#execution/sandbox/bindings/docker-container.js";import{setDockerNetworkPolicy}from"#execution/sandbox/bindings/docker-network.js";import{createDockerSandboxOptionsHash,resolveDockerSandboxOptions}from"#execution/sandbox/bindings/docker-options.js";import{createDockerInternalSession}from"#execution/sandbox/bindings/docker-session.js";import{DOCKER_TEMPLATE_IMAGE_REPOSITORY,dockerImageExists,dockerTemplateImageReference,ensureDockerBaseImage,pruneDockerSandboxTemplates,resolveDockerTemplateMarkerPath,touchDockerTemplateMarker}from"#execution/sandbox/bindings/docker-templates.js";import{createLoggingSandboxSession}from"#execution/sandbox/logging-session.js";import{buildSandboxSession}from"#execution/sandbox/session.js";import{SandboxTemplateNotProvisionedError}from"#public/definitions/sandbox-backend.js";const DOCKER_BACKEND_NAME=`docker`;function createDockerSandboxBackend(c={}){let l=c.dockerCli??createDockerCli(),u=resolveDockerSandboxOptions(c.createOptions),d=createDockerSandboxOptionsHash(u),f;function ensureDaemon(){return f??=assertDockerDaemonAvailable(l).catch(e=>{throw f=void 0,e}),f}return{name:DOCKER_BACKEND_NAME,async prewarm(n){n.log?.(`checking Docker daemon`),await ensureDaemon();let i={optionsHash:d,templateKey:n.templateKey},a=dockerTemplateImageReference(i),o=resolveDockerTemplateMarkerPath(n.runtimeContext.appRoot,i);if(n.log?.(`checking cached template image "${a}"`),await dockerImageExists(l,a))return n.log?.(`reusing cached template image`),await touchDockerTemplateMarker(o,a),{reused:!0};n.log?.(`checking base image "${u.image}"`),await ensureDockerBaseImage(l,u);let s=`${n.templateKey}-build-${randomUUID().slice(0,8)}`;n.log?.(`starting template build container`),await startDockerContainer({cli:l,containerName:s,image:u.image,initialNetworkPolicy:`allow-all`,options:u,role:`template-build`});try{n.log?.(`preparing base runtime inside container`),await runDockerBaseSetup(l,s),u.networkPolicy!==`allow-all`&&(n.log?.(`applying network policy`),await setDockerNetworkPolicy(l,s,u.networkPolicy));let e=buildSandboxSession(createDockerInternalSession({cli:l,containerName:s,id:n.templateKey}),e=>setDockerNetworkPolicy(l,s,e));n.bootstrap!==void 0&&(n.log?.(`running sandbox bootstrap`),await n.bootstrap({use:async()=>createLoggingSandboxSession({log:n.log,session:e})})),n.seedFiles.length>0&&n.log?.(`writing ${n.seedFiles.length} seed file(s)`),await writeSandboxSeedFiles(e,n.seedFiles),n.log?.(`stopping template build container`),expectDockerSuccess(await l.run([`stop`,`-t`,`0`,s]),`stop template build container "${s}"`),n.log?.(`committing template image "${a}"`),expectDockerSuccess(await l.run([`commit`,`--change`,`LABEL ${DOCKER_SANDBOX_LABEL}=1`,`--change`,`LABEL ${DOCKER_SANDBOX_LABEL}.role=template`,`--change`,`LABEL ${DOCKER_SANDBOX_LABEL}.template-key=${n.templateKey}`,s,a]),`commit sandbox template image "${a}"`),await touchDockerTemplateMarker(o,a)}finally{await l.run([`rm`,`-f`,s]).catch(()=>{})}return{reused:!1}},async create(e){await ensureDaemon(),markDevelopmentSandboxBackendInitialized(DOCKER_BACKEND_NAME);let r=getDockerContainerName(e.existingMetadata)??e.sessionKey,i=await l.run([`container`,`inspect`,`--format`,`{{.State.Running}}`,r]);if(i.exitCode===0)i.stdout.trim()!==`true`&&expectDockerSuccess(await l.run([`start`,r]),`restart sandbox session container "${r}"`);else{let t;if(e.templateKey===null)await ensureDockerBaseImage(l,u),t=u.image;else{let n={optionsHash:d,templateKey:e.templateKey};if(t=dockerTemplateImageReference(n),!await dockerImageExists(l,t))throw new SandboxTemplateNotProvisionedError({backendName:DOCKER_BACKEND_NAME,templateKey:e.templateKey});await touchDockerTemplateMarker(resolveDockerTemplateMarkerPath(e.runtimeContext.appRoot,n),t)}await startDockerContainer({cli:l,containerName:r,image:t,initialNetworkPolicy:e.templateKey===null?`allow-all`:u.networkPolicy,options:u,role:`session`,tags:e.tags}),e.templateKey===null&&(await runDockerBaseSetup(l,r),u.networkPolicy!==`allow-all`&&await setDockerNetworkPolicy(l,r,u.networkPolicy))}let a=buildSandboxSession(createDockerInternalSession({cli:l,containerName:r,id:e.sessionKey}),e=>setDockerNetworkPolicy(l,r,e));return{session:a,useSessionFn:async()=>a,async captureState(){return{backendName:DOCKER_BACKEND_NAME,metadata:{containerName:r},sessionKey:e.sessionKey}},async dispose(){}}}}}function getDockerContainerName(e){let t=e?.containerName;return typeof t==`string`?t:void 0}export{DOCKER_BACKEND_NAME,DOCKER_TEMPLATE_IMAGE_REPOSITORY,createDockerSandboxBackend,pruneDockerSandboxTemplates};
|
|
1
|
+
import{randomUUID}from"node:crypto";import{expectDockerSuccess}from"#execution/sandbox/bindings/docker-utils.js";import{markDevelopmentSandboxBackendInitialized}from"#execution/sandbox/development-run.js";import{writeSandboxSeedFiles}from"#execution/sandbox/bindings/local-backend-utils.js";import{assertDockerDaemonAvailable,createDockerCli}from"#execution/sandbox/bindings/docker-cli.js";import{DOCKER_SANDBOX_LABEL,runDockerBaseSetup,startDockerContainer}from"#execution/sandbox/bindings/docker-container.js";import{setDockerNetworkPolicy}from"#execution/sandbox/bindings/docker-network.js";import{createDockerSandboxOptionsHash,resolveDockerSandboxOptions}from"#execution/sandbox/bindings/docker-options.js";import{createDockerInternalSession}from"#execution/sandbox/bindings/docker-session.js";import{DOCKER_TEMPLATE_IMAGE_REPOSITORY,dockerImageExists,dockerTemplateImageReference,ensureDockerBaseImage,pruneDockerSandboxTemplates,resolveDockerTemplateMarkerPath,touchDockerTemplateMarker}from"#execution/sandbox/bindings/docker-templates.js";import{createLoggingSandboxSession}from"#execution/sandbox/logging-session.js";import{buildSandboxSession}from"#execution/sandbox/session.js";import{SandboxTemplateNotProvisionedError}from"#public/definitions/sandbox-backend.js";const DOCKER_BACKEND_NAME=`docker`;function createDockerSandboxBackend(c={}){let l=c.dockerCli??createDockerCli(),u=resolveDockerSandboxOptions(c.createOptions),d=createDockerSandboxOptionsHash(u),f;function ensureDaemon(){return f??=assertDockerDaemonAvailable(l).catch(e=>{throw f=void 0,e}),f}return{name:DOCKER_BACKEND_NAME,async prewarm(n){n.log?.(`checking Docker daemon`),await ensureDaemon();let i={optionsHash:d,templateKey:n.templateKey},a=dockerTemplateImageReference(i),o=resolveDockerTemplateMarkerPath(n.runtimeContext.appRoot,i);if(n.log?.(`checking cached template image "${a}"`),await dockerImageExists(l,a))return n.log?.(`reusing cached template image`),await touchDockerTemplateMarker(o,a),{reused:!0};n.log?.(`checking base image "${u.image}"`),await ensureDockerBaseImage(l,u);let s=`${n.templateKey}-build-${randomUUID().slice(0,8)}`;n.log?.(`starting template build container`),await startDockerContainer({cli:l,containerName:s,image:u.image,initialNetworkPolicy:`allow-all`,options:u,role:`template-build`});try{n.log?.(`preparing base runtime inside container`),await runDockerBaseSetup(l,s),u.networkPolicy!==`allow-all`&&(n.log?.(`applying network policy`),await setDockerNetworkPolicy(l,s,u.networkPolicy));let e=buildSandboxSession(createDockerInternalSession({cli:l,containerName:s,id:n.templateKey}),e=>setDockerNetworkPolicy(l,s,e));n.bootstrap!==void 0&&(n.log?.(`running sandbox bootstrap`),await n.bootstrap({use:async()=>createLoggingSandboxSession({log:n.log,session:e})})),n.seedFiles.length>0&&n.log?.(`writing ${n.seedFiles.length} seed file(s)`),await writeSandboxSeedFiles(e,n.seedFiles),n.log?.(`stopping template build container`),expectDockerSuccess(await l.run([`stop`,`-t`,`0`,s]),`stop template build container "${s}"`),n.log?.(`committing template image "${a}"`),expectDockerSuccess(await l.run([`commit`,`--change`,`LABEL ${DOCKER_SANDBOX_LABEL}=1`,`--change`,`LABEL ${DOCKER_SANDBOX_LABEL}.role=template`,`--change`,`LABEL ${DOCKER_SANDBOX_LABEL}.template-key=${n.templateKey}`,s,a]),`commit sandbox template image "${a}"`),await touchDockerTemplateMarker(o,a)}finally{await l.run([`rm`,`-f`,s]).catch(()=>{})}return{reused:!1}},async create(e){await ensureDaemon(),markDevelopmentSandboxBackendInitialized(DOCKER_BACKEND_NAME);let r=getDockerContainerName(e.existingMetadata)??e.sessionKey,i=await l.run([`container`,`inspect`,`--format`,`{{.State.Running}}`,r]);if(i.exitCode===0)i.stdout.trim()!==`true`&&expectDockerSuccess(await l.run([`start`,r]),`restart sandbox session container "${r}"`);else{let t;if(e.templateKey===null)await ensureDockerBaseImage(l,u),t=u.image;else{let n={optionsHash:d,templateKey:e.templateKey};if(t=dockerTemplateImageReference(n),!await dockerImageExists(l,t))throw new SandboxTemplateNotProvisionedError({backendName:DOCKER_BACKEND_NAME,templateKey:e.templateKey});await touchDockerTemplateMarker(resolveDockerTemplateMarkerPath(e.runtimeContext.appRoot,n),t)}try{await startDockerContainer({cli:l,containerName:r,image:t,initialNetworkPolicy:e.templateKey===null?`allow-all`:u.networkPolicy,options:u,role:`session`,tags:e.tags})}catch(t){throw e.templateKey===null?t:new SandboxTemplateNotProvisionedError({backendName:DOCKER_BACKEND_NAME,templateKey:e.templateKey})}e.templateKey===null&&(await runDockerBaseSetup(l,r),u.networkPolicy!==`allow-all`&&await setDockerNetworkPolicy(l,r,u.networkPolicy))}let a=buildSandboxSession(createDockerInternalSession({cli:l,containerName:r,id:e.sessionKey}),e=>setDockerNetworkPolicy(l,r,e));return{session:a,useSessionFn:async()=>a,async captureState(){return{backendName:DOCKER_BACKEND_NAME,metadata:{containerName:r},sessionKey:e.sessionKey}},async dispose(){}}}}}function getDockerContainerName(e){let t=e?.containerName;return typeof t==`string`?t:void 0}export{DOCKER_BACKEND_NAME,DOCKER_TEMPLATE_IMAGE_REPOSITORY,createDockerSandboxBackend,pruneDockerSandboxTemplates};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{dirname}from"node:path";import{mkdir,rename,rm}from"node:fs/promises";import{resolveSandboxCacheDirectory}from"#internal/application/paths.js";import{randomUUID}from"node:crypto";import{withDevelopmentSandboxMetadataPathTag}from"#execution/sandbox/development-run.js";import{createFileBackedInternalSandboxSession,touchDirectory,writeSandboxSeedFiles}from"#execution/sandbox/bindings/local-backend-utils.js";import{createLoggingSandboxSession}from"#execution/sandbox/logging-session.js";import{buildSandboxSession}from"#execution/sandbox/session.js";import{SandboxTemplateNotProvisionedError}from"#public/definitions/sandbox-backend.js";import{MICROSANDBOX_METADATA_VERSION,readSessionMetadata,readSessionMetadataRecord,readTemplateMetadata,resolveMicrosandboxMetadataPath,writeTemplateMetadata}from"#execution/sandbox/bindings/microsandbox-metadata.js";import{connectMicrosandbox,createPreparedMicrosandbox,createProviderName,doesPathExist,loadMicrosandboxModule,removeSnapshotIfExists,sandboxExists,snapshotExists}from"#execution/sandbox/bindings/microsandbox-runtime.js";import{resolveMicrosandboxSessionRootPath,resolveMicrosandboxTemplateRootPath}from"#execution/sandbox/bindings/microsandbox-templates.js";const activeMicrosandboxSessionHandles=new Map;async function prewarmMicrosandboxTemplate(i){i.prewarmInput.log?.(`loading microsandbox runtime`);let a=await loadMicrosandboxModule({appRoot:i.prewarmInput.runtimeContext.appRoot,log:i.prewarmInput.log,options:i.options}),u=resolveMicrosandboxTemplateRootPath(resolveSandboxCacheDirectory(i.prewarmInput.runtimeContext.appRoot),i.prewarmInput.templateKey),f=resolveMicrosandboxMetadataPath(u);i.prewarmInput.log?.(`checking cached snapshot`);let p=await readTemplateMetadata(f);if(p?.optionsHash===i.optionsHash&&await snapshotExists(a,p.snapshotName))return i.prewarmInput.log?.(`reusing cached snapshot`),await touchDirectory(u),{reused:!0};let h=createProviderName(`eve-sbx-tpl`,i.prewarmInput.templateKey,i.optionsHash),_=`${u}.${randomUUID()}.tmp`,v=createProviderName(`eve-sbx-tpl-tmp`,`${i.prewarmInput.templateKey}:${randomUUID()}`);await removeSnapshotIfExists(a,h),await rm(_,{force:!0,recursive:!0}),await mkdir(_,{recursive:!0}),i.prewarmInput.log?.(`creating template VM from image "${i.options.image}"`);let y=await createPreparedMicrosandbox({log:i.prewarmInput.log,module:a,name:v,networkPolicy:i.options.networkPolicy,options:i.options,sessionKey:i.prewarmInput.templateKey,setupBaseRuntime:!0,tags:void 0}),b=buildSandboxSession(createMicrosandboxInternalSession(y),async e=>{await y.setNetworkPolicy(e)});try{i.prewarmInput.bootstrap!==void 0&&(i.prewarmInput.log?.(`running sandbox bootstrap`),await i.prewarmInput.bootstrap({use:async e=>(e?.networkPolicy!==void 0&&await y.setNetworkPolicy(e.networkPolicy),createLoggingSandboxSession({log:i.prewarmInput.log,session:b}))})),i.prewarmInput.seedFiles.length>0&&i.prewarmInput.log?.(`writing ${i.prewarmInput.seedFiles.length} seed file(s)`),await writeSandboxSeedFiles(b,i.prewarmInput.seedFiles),i.prewarmInput.log?.(`snapshotting template VM`),await y.stopAndSnapshot(h),await writeTemplateMetadata(resolveMicrosandboxMetadataPath(_),{optionsHash:i.optionsHash,snapshotName:h,version:MICROSANDBOX_METADATA_VERSION}),await mkdir(dirname(u),{recursive:!0}),await rm(u,{force:!0,recursive:!0});try{await rename(_,u)}catch(e){if(await doesPathExist(u))return{reused:!0};throw e}}finally{await y.removePersisted(),await rm(_,{force:!0,recursive:!0}).catch(()=>{})}return{reused:!1}}async function createMicrosandboxHandle(e){let t=await loadMicrosandboxModule({appRoot:e.createInput.runtimeContext.appRoot,options:e.options}),n=resolveSandboxCacheDirectory(e.createInput.runtimeContext.appRoot),r=resolveMicrosandboxSessionRootPath(n,e.createInput.sessionKey),a=createActiveMicrosandboxSessionKey(r,e.optionsHash),o=activeMicrosandboxSessionHandles.get(a);if(o!==void 0)return o;let s=resolveMicrosandboxMetadataPath(r),c=readSessionMetadataRecord(e.createInput.existingMetadata)??await readSessionMetadata(s),l=withDevelopmentSandboxMetadataPathTag(e.createInput.tags,s);if(c?.optionsHash===e.optionsHash&&(await sandboxExists(t,c.sandboxName)||c.stateSnapshotName!==void 0&&await snapshotExists(t,c.stateSnapshotName)))
|
|
1
|
+
import{dirname}from"node:path";import{mkdir,rename,rm}from"node:fs/promises";import{resolveSandboxCacheDirectory}from"#internal/application/paths.js";import{randomUUID}from"node:crypto";import{withDevelopmentSandboxMetadataPathTag}from"#execution/sandbox/development-run.js";import{createFileBackedInternalSandboxSession,touchDirectory,writeSandboxSeedFiles}from"#execution/sandbox/bindings/local-backend-utils.js";import{createLoggingSandboxSession}from"#execution/sandbox/logging-session.js";import{buildSandboxSession}from"#execution/sandbox/session.js";import{SandboxTemplateNotProvisionedError}from"#public/definitions/sandbox-backend.js";import{MICROSANDBOX_METADATA_VERSION,readSessionMetadata,readSessionMetadataRecord,readTemplateMetadata,resolveMicrosandboxMetadataPath,writeTemplateMetadata}from"#execution/sandbox/bindings/microsandbox-metadata.js";import{connectMicrosandbox,createPreparedMicrosandbox,createProviderName,doesPathExist,isMicrosandboxNotFoundError,loadMicrosandboxModule,removeSnapshotIfExists,sandboxExists,snapshotExists}from"#execution/sandbox/bindings/microsandbox-runtime.js";import{resolveMicrosandboxSessionRootPath,resolveMicrosandboxTemplateRootPath}from"#execution/sandbox/bindings/microsandbox-templates.js";const activeMicrosandboxSessionHandles=new Map;async function prewarmMicrosandboxTemplate(i){i.prewarmInput.log?.(`loading microsandbox runtime`);let a=await loadMicrosandboxModule({appRoot:i.prewarmInput.runtimeContext.appRoot,log:i.prewarmInput.log,options:i.options}),u=resolveMicrosandboxTemplateRootPath(resolveSandboxCacheDirectory(i.prewarmInput.runtimeContext.appRoot),i.prewarmInput.templateKey),f=resolveMicrosandboxMetadataPath(u);i.prewarmInput.log?.(`checking cached snapshot`);let p=await readTemplateMetadata(f);if(p?.optionsHash===i.optionsHash&&await snapshotExists(a,p.snapshotName))return i.prewarmInput.log?.(`reusing cached snapshot`),await touchDirectory(u),{reused:!0};let h=createProviderName(`eve-sbx-tpl`,i.prewarmInput.templateKey,i.optionsHash),_=`${u}.${randomUUID()}.tmp`,v=createProviderName(`eve-sbx-tpl-tmp`,`${i.prewarmInput.templateKey}:${randomUUID()}`);await removeSnapshotIfExists(a,h),await rm(_,{force:!0,recursive:!0}),await mkdir(_,{recursive:!0}),i.prewarmInput.log?.(`creating template VM from image "${i.options.image}"`);let y=await createPreparedMicrosandbox({log:i.prewarmInput.log,module:a,name:v,networkPolicy:i.options.networkPolicy,options:i.options,sessionKey:i.prewarmInput.templateKey,setupBaseRuntime:!0,tags:void 0}),b=buildSandboxSession(createMicrosandboxInternalSession(y),async e=>{await y.setNetworkPolicy(e)});try{i.prewarmInput.bootstrap!==void 0&&(i.prewarmInput.log?.(`running sandbox bootstrap`),await i.prewarmInput.bootstrap({use:async e=>(e?.networkPolicy!==void 0&&await y.setNetworkPolicy(e.networkPolicy),createLoggingSandboxSession({log:i.prewarmInput.log,session:b}))})),i.prewarmInput.seedFiles.length>0&&i.prewarmInput.log?.(`writing ${i.prewarmInput.seedFiles.length} seed file(s)`),await writeSandboxSeedFiles(b,i.prewarmInput.seedFiles),i.prewarmInput.log?.(`snapshotting template VM`),await y.stopAndSnapshot(h),await writeTemplateMetadata(resolveMicrosandboxMetadataPath(_),{optionsHash:i.optionsHash,snapshotName:h,version:MICROSANDBOX_METADATA_VERSION}),await mkdir(dirname(u),{recursive:!0}),await rm(u,{force:!0,recursive:!0});try{await rename(_,u)}catch(e){if(await doesPathExist(u))return{reused:!0};throw e}}finally{await y.removePersisted(),await rm(_,{force:!0,recursive:!0}).catch(()=>{})}return{reused:!1}}async function createMicrosandboxHandle(e){let t=await loadMicrosandboxModule({appRoot:e.createInput.runtimeContext.appRoot,options:e.options}),n=resolveSandboxCacheDirectory(e.createInput.runtimeContext.appRoot),r=resolveMicrosandboxSessionRootPath(n,e.createInput.sessionKey),a=createActiveMicrosandboxSessionKey(r,e.optionsHash),o=activeMicrosandboxSessionHandles.get(a);if(o!==void 0)return o;let s=resolveMicrosandboxMetadataPath(r),c=readSessionMetadataRecord(e.createInput.existingMetadata)??await readSessionMetadata(s),l=withDevelopmentSandboxMetadataPathTag(e.createInput.tags,s);if(c?.optionsHash===e.optionsHash&&(await sandboxExists(t,c.sandboxName)||c.stateSnapshotName!==void 0&&await snapshotExists(t,c.stateSnapshotName))){let n=await connectMicrosandbox({metadata:c,metadataPath:s,module:t,options:e.options,sessionKey:e.createInput.sessionKey,tags:l});if(n!==null)return cacheHandle(a,createHandle(n,e.backendName,e.optionsHash,()=>{activeMicrosandboxSessionHandles.delete(a)}))}let d=null;if(e.createInput.templateKey!==null){let r=await readTemplateMetadata(resolveMicrosandboxMetadataPath(resolveMicrosandboxTemplateRootPath(n,e.createInput.templateKey)));if(r===null||r.optionsHash!==e.optionsHash||!await snapshotExists(t,r.snapshotName))throw new SandboxTemplateNotProvisionedError({backendName:e.backendName,templateKey:e.createInput.templateKey});d=r.snapshotName}let m=createProviderName(`eve-sbx-ses`,`${e.createInput.sessionKey}:${randomUUID()}`),g;try{g=await createPreparedMicrosandbox({fromSnapshot:d??void 0,module:t,name:m,networkPolicy:e.options.networkPolicy,options:e.options,sessionKey:e.createInput.sessionKey,setupBaseRuntime:d===null,tags:l})}catch(t){throw d!==null&&e.createInput.templateKey!==null&&isMicrosandboxNotFoundError(t)?new SandboxTemplateNotProvisionedError({backendName:e.backendName,templateKey:e.createInput.templateKey}):t}return await g.writeMetadata(s,e.optionsHash),cacheHandle(a,createHandle(g,e.backendName,e.optionsHash,()=>{activeMicrosandboxSessionHandles.delete(a)}))}function createHandle(e,t,n,r){return{session:buildSandboxSession(createMicrosandboxInternalSession(e),async t=>{await e.setNetworkPolicy(t)}),useSessionFn:async t=>(t?.networkPolicy!==void 0&&await e.setNetworkPolicy(t.networkPolicy),buildSandboxSession(createMicrosandboxInternalSession(e),async t=>{await e.setNetworkPolicy(t)})),async captureState(){return{backendName:t,metadata:{...await e.captureState(n)},sessionKey:e.id}},async dispose(){r?.(),await e.detach()}}}function createMicrosandboxInternalSession(e){return createFileBackedInternalSandboxSession({id:e.id,sandbox:e})}function createActiveMicrosandboxSessionKey(e,t){return`${e}\0${t}`}function cacheHandle(e,t){return activeMicrosandboxSessionHandles.set(e,t),t}function clearActiveMicrosandboxSessionHandlesForTest(){activeMicrosandboxSessionHandles.clear()}export{clearActiveMicrosandboxSessionHandlesForTest,createMicrosandboxHandle,prewarmMicrosandboxTemplate};
|
|
@@ -47,7 +47,7 @@ export declare function connectMicrosandbox(input: {
|
|
|
47
47
|
readonly options: ResolvedMicrosandboxOptions;
|
|
48
48
|
readonly sessionKey: string;
|
|
49
49
|
readonly tags?: SandboxBackendTags;
|
|
50
|
-
}): Promise<MicrosandboxVm>;
|
|
50
|
+
}): Promise<MicrosandboxVm | null>;
|
|
51
51
|
/**
|
|
52
52
|
* Loads the microsandbox npm package and ensures its VM runtime is
|
|
53
53
|
* installed. During `eve dev`, both are installed automatically when
|
|
@@ -73,3 +73,4 @@ export declare function removeSnapshotIfExists(module: MicrosandboxModule, snaps
|
|
|
73
73
|
export declare function createProviderName(prefix: string, key: string, extra?: string): string;
|
|
74
74
|
export declare function createStableHash(value: string): string;
|
|
75
75
|
export declare function doesPathExist(path: string): Promise<boolean>;
|
|
76
|
+
export declare function isMicrosandboxNotFoundError(error: unknown): boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{posix}from"node:path";import{access}from"node:fs/promises";import{createHash,randomUUID}from"node:crypto";import{isEveDevEnvironment,loadOptionalEnginePackage}from"#internal/application/optional-package-install.js";import{WORKSPACE_ROOT}from"#runtime/workspace/types.js";import{withDevelopmentSandboxTags}from"#execution/sandbox/development-run.js";import{shellQuote}from"#execution/sandbox/shell-quote.js";import{assertMicrosandboxPlatformCandidate,ensureMicrosandboxBaseRuntime}from"#execution/sandbox/bindings/microsandbox-platform.js";import{MICROSANDBOX_METADATA_VERSION,writeSessionMetadata}from"#execution/sandbox/bindings/microsandbox-metadata.js";import{MICROSANDBOX_USER}from"#execution/sandbox/bindings/microsandbox-options.js";import{applyMicrosandboxNetwork,createMicrosandboxNetworkPlan,createTransformBrokerEnvironment}from"#execution/sandbox/bindings/microsandbox-network.js";import{adaptMicrosandboxExecToSandboxProcess}from"#execution/sandbox/bindings/microsandbox-process.js";const MICROSANDBOX_STOP_TIMEOUT_MS=1e4;var MicrosandboxVm=class{#e;#t;#n;#r;#i;#a;#o;constructor(e,t,n,r,i,a,o){this.#e=e,this.#i=t,this.#a=n,this.#n=r,this.#t=i,this.#r=a,this.#o=o}get id(){return this.#e.sessionKey}async captureState(e){if(this.#r=e,isEveDevEnvironment())return this.#t!==void 0&&await this.writeMetadata(this.#t,e),{networkPolicy:this.#n,optionsHash:e,sandboxName:this.#a,stateSnapshotName:this.#o,version:MICROSANDBOX_METADATA_VERSION};let t=this.#o,n=createProviderName(`eve-sbx-state`,`${this.#e.sessionKey}:${randomUUID()}`);return await this.stopAndSnapshot(n),this.#o=n,this.#t!==void 0&&await this.writeMetadata(this.#t,e),t!==void 0&&await removeSnapshotIfExists(this.#e.module,t),{networkPolicy:this.#n,optionsHash:e,sandboxName:this.#a,stateSnapshotName:n,version:MICROSANDBOX_METADATA_VERSION}}async detach(){await this.#i.detach().catch(()=>{})}async readFileBytes(e){try{let t=this.#i.fs();return await t.exists(e)?Buffer.from(await t.read(e)):null}catch{return null}}async removePath(e){let t=`${e.force===!0?`f`:``}${e.recursive===!0?`r`:``}`,n=`${t.length>0?`rm -${t}`:`rm`} -- ${shellQuote(e.path)}`;await this.runInternalCommand({abortSignal:e.abortSignal,command:n,user:MICROSANDBOX_USER})}async removePersisted(){await removeSandboxIfExists(this.#e.module,this.#a),this.#o!==void 0&&await removeSnapshotIfExists(this.#e.module,this.#o)}async setNetworkPolicy(e){let t=this.#o,n=createProviderName(`eve-sbx-state`,`${this.#e.sessionKey}:${randomUUID()}`),i=this.#a;await this.stopAndSnapshot(n),await removeSandboxIfExists(this.#e.module,i);let a=createProviderName(`eve-sbx-ses`,`${this.#e.sessionKey}:${randomUUID()}`);this.#i=await createMicrosandbox({fromSnapshot:n,module:this.#e.module,name:a,networkPolicy:e,options:this.#e.options,tags:this.#e.tags,user:MICROSANDBOX_USER,workdir:WORKSPACE_ROOT}),this.#a=a,this.#n=e,this.#o=void 0,this.#t!==void 0&&this.#r!==void 0&&await this.writeMetadata(this.#t,this.#r),await removeSnapshotIfExists(this.#e.module,n),t!==void 0&&await removeSnapshotIfExists(this.#e.module,t)}async spawn(e){if(e.abortSignal?.aborted)throw new DOMException(`The operation was aborted.`,`AbortError`);let t={...this.#e.options.env,...createTransformBrokerEnvironment(createMicrosandboxNetworkPlan(this.#n)),...e.env},n=await this.#i.execStreamWith(`bash`,n=>n.args([`-lc`,e.command]).cwd(e.workingDirectory??WORKSPACE_ROOT).envs(t).user(MICROSANDBOX_USER));return e.abortSignal!==void 0&&e.abortSignal.addEventListener(`abort`,()=>{n.kill().catch(()=>{})},{once:!0}),adaptMicrosandboxExecToSandboxProcess(n)}async stopAndSnapshot(e){await this.#i.stop().catch(()=>{}),await stopAndSnapshotMicrosandboxSandbox(this.#e.module,this.#a,e)}async writeFiles(t){let n=this.#i.fs();for(let r of t){let t=posix.dirname(r.path);await this.runInternalCommand({command:`mkdir -p ${shellQuote(t)}`,user:MICROSANDBOX_USER}),await n.write(r.path,r.content),await this.runInternalCommand({command:`chown ${MICROSANDBOX_USER}:${MICROSANDBOX_USER} ${shellQuote(r.path)}`,user:`root`})}}async writeMetadata(e,t){this.#t=e,this.#r=t,await writeSessionMetadata(e,{networkPolicy:this.#n,optionsHash:t,sandboxName:this.#a,stateSnapshotName:this.#o,version:MICROSANDBOX_METADATA_VERSION})}async runInternalCommand(e){if(e.abortSignal?.aborted)throw new DOMException(`The operation was aborted.`,`AbortError`);let t=await this.#i.execWith(`bash`,t=>t.args([`-lc`,e.command]).cwd(WORKSPACE_ROOT).user(e.user));if(t.code!==0){let n=e.failureMessage??`Microsandbox command failed.`;throw Error(`${n} ${t.stderr()}`.trim())}}};async function createPreparedMicrosandbox(e){let t=e.setupBaseRuntime?`allow-all`:e.networkPolicy,n=await createMicrosandbox({fromSnapshot:e.fromSnapshot,module:e.module,name:e.name,networkPolicy:t,options:e.options,tags:e.tags,user:e.setupBaseRuntime?void 0:MICROSANDBOX_USER,workdir:e.setupBaseRuntime?`/`:WORKSPACE_ROOT}),r=new MicrosandboxVm({module:e.module,options:e.options,sessionKey:e.sessionKey,tags:e.tags},n,e.name,t);return e.setupBaseRuntime&&(await withProgressHeartbeat(`preparing base runtime inside VM`,e.log,async()=>{await ensureMicrosandboxBaseRuntime(n,{log:e.log})}),e.networkPolicy!==void 0&&e.networkPolicy!==`allow-all`&&(e.log?.(`applying network policy`),await r.setNetworkPolicy(e.networkPolicy))),r}async function connectMicrosandbox(e){let t;try{t=await e.module.Sandbox.get(e.metadata.sandboxName)}catch(t){if(!isMicrosandboxNotFoundError(t)||e.metadata.stateSnapshotName===void 0)throw t;return await restoreMicrosandboxSessionSnapshot(e)}if(t.status!==`running`&&t.status!==`draining`&&e.metadata.stateSnapshotName!==void 0)return await restoreMicrosandboxSessionSnapshot(e);let n=t.status===`running`||t.status===`draining`?await t.connectWithTimeout(1e4):await t.startDetached();return new MicrosandboxVm({module:e.module,options:e.options,sessionKey:e.sessionKey,tags:e.tags},n,e.metadata.sandboxName,e.metadata.networkPolicy,e.metadataPath,e.metadata.optionsHash,e.metadata.stateSnapshotName)}async function restoreMicrosandboxSessionSnapshot(e){if(e.metadata.stateSnapshotName===void 0||!await snapshotExists(e.module,e.metadata.stateSnapshotName))throw Error(`Microsandbox session snapshot is missing for sandbox "${e.metadata.sandboxName}".`);let t=createProviderName(`eve-sbx-ses`,`${e.sessionKey}:${randomUUID()}`),n=await createMicrosandbox({fromSnapshot:e.metadata.stateSnapshotName,module:e.module,name:t,networkPolicy:e.metadata.networkPolicy,options:e.options,tags:e.tags,user:MICROSANDBOX_USER,workdir:WORKSPACE_ROOT});await removeSandboxIfExists(e.module,e.metadata.sandboxName);let i=new MicrosandboxVm({module:e.module,options:e.options,sessionKey:e.sessionKey,tags:e.tags},n,t,e.metadata.networkPolicy,e.metadataPath,e.metadata.optionsHash,e.metadata.stateSnapshotName);return await i.writeMetadata(e.metadataPath,e.metadata.optionsHash),i}async function loadMicrosandboxModule(e){e.log?.(`checking microsandbox platform support`),await assertMicrosandboxPlatformCandidate();let t=await withProgressHeartbeat(`loading microsandbox npm package`,e.log,()=>loadOptionalEnginePackage({appRoot:e.appRoot,autoInstall:e.options.setup.autoInstall,importModule:async()=>await import(`microsandbox`),missingMessage:"The microsandbox sandbox backend requires the `microsandbox` package, which is not bundled with Eve. Install it in your application (for example `pnpm add -D microsandbox`), or use docker() / vercel() instead.",packageName:`microsandbox`}));if(e.log?.(`checking microsandbox VM runtime`),!t.isInstalled()){if(!e.options.setup.autoInstall||!isEveDevEnvironment())throw Error("The microsandbox VM runtime is not installed. Run `npx microsandbox install`, set MSB_PATH for a custom install, or let `eve dev` install it automatically with microsandbox({ setup: { autoInstall: true } }).");await withProgressHeartbeat(`installing microsandbox VM runtime`,e.log,async()=>{await t.setup().skipVerify(e.options.setup.skipVerify).install()})}return e.log?.(`microsandbox runtime ready`),t}async function withProgressHeartbeat(e,t,n){if(t?.(e),t===void 0)return await n();let r=Date.now(),i=setInterval(()=>{t(`${e} (${Math.round((Date.now()-r)/1e3)}s elapsed)`)},1e4);i.unref?.();try{return await n()}finally{clearInterval(i)}}async function loadMicrosandboxWithoutInstall(){try{let e=await import(`microsandbox`);return e.isInstalled()?e:null}catch{return null}}async function stopAndSnapshotMicrosandboxSandbox(e,t,n){for(let r=0;r<3;r+=1){let i=await e.Sandbox.get(t);await i.stopWithTimeout(r===0?MICROSANDBOX_STOP_TIMEOUT_MS:0).catch(()=>{});try{await i.snapshot(n);return}catch(e){if(!isMicrosandboxSnapshotSourceRunningError(e)||r===2)throw e;await i.kill().catch(()=>{}),await new Promise(e=>setTimeout(e,250))}}}async function snapshotExists(e,t){try{return await e.Snapshot.get(t),!0}catch(e){if(isMicrosandboxNotFoundError(e))return!1;throw e}}async function sandboxExists(e,t){try{return await e.Sandbox.get(t),!0}catch(e){if(isMicrosandboxNotFoundError(e))return!1;throw e}}async function removeSnapshotIfExists(e,t){try{await e.Snapshot.remove(t,{force:!0})}catch(e){if(!isMicrosandboxNotFoundError(e))throw e}}function createProviderName(e,t,n=``){return`${e}-${createStableHash(`${t}:${n}`).slice(0,32)}`}function createStableHash(e){return createHash(`sha256`).update(e).digest(`hex`)}async function doesPathExist(e){try{return await access(e),!0}catch{return!1}}async function createMicrosandbox(e){let t=e.module.Sandbox.builder(e.name).cpus(e.options.cpus).detached(!0).envs(e.options.env).labels(resolveMicrosandboxLabels(e.tags)).memory(e.options.memoryMiB).pullPolicy(e.options.pullPolicy).replace().workdir(e.workdir);return t=e.fromSnapshot===void 0?t.image(e.options.image):t.fromSnapshot(e.fromSnapshot),e.user!==void 0&&(t=t.user(e.user)),await applyMicrosandboxNetwork(t,e.networkPolicy).create()}async function removeSandboxIfExists(e,t){for(let n=0;n<3;n+=1)try{let r=await e.Sandbox.get(t);await r.stopWithTimeout(n===0?MICROSANDBOX_STOP_TIMEOUT_MS:0).catch(()=>{}),await r.remove();return}catch(r){if(isMicrosandboxNotFoundError(r))return;if(isMicrosandboxStillRunningError(r)&&n<2){await(await e.Sandbox.get(t).catch(()=>null))?.kill().catch(()=>{}),await new Promise(e=>setTimeout(e,250));continue}throw r}}function isMicrosandboxNotFoundError(e){return e instanceof Error?/not found|not exist|no such/i.test(e.message):!1}function isMicrosandboxStillRunningError(e){return e instanceof Error?/still running/i.test(e.message):!1}function isMicrosandboxSnapshotSourceRunningError(e){return e instanceof Error?/snapshot source sandbox .*not stopped|SnapshotSandboxRunning/i.test(e.message):!1}function resolveMicrosandboxLabels(e){return{"eve.backend":`microsandbox`,...withDevelopmentSandboxTags(e)}}export{MicrosandboxVm,connectMicrosandbox,createPreparedMicrosandbox,createProviderName,createStableHash,doesPathExist,loadMicrosandboxModule,loadMicrosandboxWithoutInstall,removeSnapshotIfExists,sandboxExists,snapshotExists,stopAndSnapshotMicrosandboxSandbox};
|
|
1
|
+
import{posix}from"node:path";import{access}from"node:fs/promises";import{createHash,randomUUID}from"node:crypto";import{isEveDevEnvironment,loadOptionalEnginePackage}from"#internal/application/optional-package-install.js";import{WORKSPACE_ROOT}from"#runtime/workspace/types.js";import{withDevelopmentSandboxTags}from"#execution/sandbox/development-run.js";import{shellQuote}from"#execution/sandbox/shell-quote.js";import{assertMicrosandboxPlatformCandidate,ensureMicrosandboxBaseRuntime}from"#execution/sandbox/bindings/microsandbox-platform.js";import{MICROSANDBOX_METADATA_VERSION,writeSessionMetadata}from"#execution/sandbox/bindings/microsandbox-metadata.js";import{MICROSANDBOX_USER}from"#execution/sandbox/bindings/microsandbox-options.js";import{applyMicrosandboxNetwork,createMicrosandboxNetworkPlan,createTransformBrokerEnvironment}from"#execution/sandbox/bindings/microsandbox-network.js";import{adaptMicrosandboxExecToSandboxProcess}from"#execution/sandbox/bindings/microsandbox-process.js";const MICROSANDBOX_STOP_TIMEOUT_MS=1e4;var MicrosandboxVm=class{#e;#t;#n;#r;#i;#a;#o;constructor(e,t,n,r,i,a,o){this.#e=e,this.#i=t,this.#a=n,this.#n=r,this.#t=i,this.#r=a,this.#o=o}get id(){return this.#e.sessionKey}async captureState(e){if(this.#r=e,isEveDevEnvironment())return this.#t!==void 0&&await this.writeMetadata(this.#t,e),{networkPolicy:this.#n,optionsHash:e,sandboxName:this.#a,stateSnapshotName:this.#o,version:MICROSANDBOX_METADATA_VERSION};let t=this.#o,n=createProviderName(`eve-sbx-state`,`${this.#e.sessionKey}:${randomUUID()}`);return await this.stopAndSnapshot(n),this.#o=n,this.#t!==void 0&&await this.writeMetadata(this.#t,e),t!==void 0&&await removeSnapshotIfExists(this.#e.module,t),{networkPolicy:this.#n,optionsHash:e,sandboxName:this.#a,stateSnapshotName:n,version:MICROSANDBOX_METADATA_VERSION}}async detach(){await this.#i.detach().catch(()=>{})}async readFileBytes(e){try{let t=this.#i.fs();return await t.exists(e)?Buffer.from(await t.read(e)):null}catch{return null}}async removePath(e){let t=`${e.force===!0?`f`:``}${e.recursive===!0?`r`:``}`,n=`${t.length>0?`rm -${t}`:`rm`} -- ${shellQuote(e.path)}`;await this.runInternalCommand({abortSignal:e.abortSignal,command:n,user:MICROSANDBOX_USER})}async removePersisted(){await removeSandboxIfExists(this.#e.module,this.#a),this.#o!==void 0&&await removeSnapshotIfExists(this.#e.module,this.#o)}async setNetworkPolicy(e){let t=this.#o,n=createProviderName(`eve-sbx-state`,`${this.#e.sessionKey}:${randomUUID()}`),i=this.#a;await this.stopAndSnapshot(n),await removeSandboxIfExists(this.#e.module,i);let a=createProviderName(`eve-sbx-ses`,`${this.#e.sessionKey}:${randomUUID()}`);this.#i=await createMicrosandbox({fromSnapshot:n,module:this.#e.module,name:a,networkPolicy:e,options:this.#e.options,tags:this.#e.tags,user:MICROSANDBOX_USER,workdir:WORKSPACE_ROOT}),this.#a=a,this.#n=e,this.#o=void 0,this.#t!==void 0&&this.#r!==void 0&&await this.writeMetadata(this.#t,this.#r),await removeSnapshotIfExists(this.#e.module,n),t!==void 0&&await removeSnapshotIfExists(this.#e.module,t)}async spawn(e){if(e.abortSignal?.aborted)throw new DOMException(`The operation was aborted.`,`AbortError`);let t={...this.#e.options.env,...createTransformBrokerEnvironment(createMicrosandboxNetworkPlan(this.#n)),...e.env},n=await this.#i.execStreamWith(`bash`,n=>n.args([`-lc`,e.command]).cwd(e.workingDirectory??WORKSPACE_ROOT).envs(t).user(MICROSANDBOX_USER));return e.abortSignal!==void 0&&e.abortSignal.addEventListener(`abort`,()=>{n.kill().catch(()=>{})},{once:!0}),adaptMicrosandboxExecToSandboxProcess(n)}async stopAndSnapshot(e){await this.#i.stop().catch(()=>{}),await stopAndSnapshotMicrosandboxSandbox(this.#e.module,this.#a,e)}async writeFiles(t){let n=this.#i.fs();for(let r of t){let t=posix.dirname(r.path);await this.runInternalCommand({command:`mkdir -p ${shellQuote(t)}`,user:MICROSANDBOX_USER}),await n.write(r.path,r.content),await this.runInternalCommand({command:`chown ${MICROSANDBOX_USER}:${MICROSANDBOX_USER} ${shellQuote(r.path)}`,user:`root`})}}async writeMetadata(e,t){this.#t=e,this.#r=t,await writeSessionMetadata(e,{networkPolicy:this.#n,optionsHash:t,sandboxName:this.#a,stateSnapshotName:this.#o,version:MICROSANDBOX_METADATA_VERSION})}async runInternalCommand(e){if(e.abortSignal?.aborted)throw new DOMException(`The operation was aborted.`,`AbortError`);let t=await this.#i.execWith(`bash`,t=>t.args([`-lc`,e.command]).cwd(WORKSPACE_ROOT).user(e.user));if(t.code!==0){let n=e.failureMessage??`Microsandbox command failed.`;throw Error(`${n} ${t.stderr()}`.trim())}}};async function createPreparedMicrosandbox(e){let t=e.setupBaseRuntime?`allow-all`:e.networkPolicy,n=await createMicrosandbox({fromSnapshot:e.fromSnapshot,module:e.module,name:e.name,networkPolicy:t,options:e.options,tags:e.tags,user:e.setupBaseRuntime?void 0:MICROSANDBOX_USER,workdir:e.setupBaseRuntime?`/`:WORKSPACE_ROOT}),r=new MicrosandboxVm({module:e.module,options:e.options,sessionKey:e.sessionKey,tags:e.tags},n,e.name,t);return e.setupBaseRuntime&&(await withProgressHeartbeat(`preparing base runtime inside VM`,e.log,async()=>{await ensureMicrosandboxBaseRuntime(n,{log:e.log})}),e.networkPolicy!==void 0&&e.networkPolicy!==`allow-all`&&(e.log?.(`applying network policy`),await r.setNetworkPolicy(e.networkPolicy))),r}async function connectMicrosandbox(e){let t;try{t=await e.module.Sandbox.get(e.metadata.sandboxName)}catch(t){if(!isMicrosandboxNotFoundError(t))throw t;return e.metadata.stateSnapshotName===void 0?null:await restoreMicrosandboxSessionSnapshot(e)}if(t.status!==`running`&&t.status!==`draining`&&e.metadata.stateSnapshotName!==void 0)return await restoreMicrosandboxSessionSnapshot(e);let n=t.status===`running`||t.status===`draining`?await t.connectWithTimeout(1e4):await t.startDetached();return new MicrosandboxVm({module:e.module,options:e.options,sessionKey:e.sessionKey,tags:e.tags},n,e.metadata.sandboxName,e.metadata.networkPolicy,e.metadataPath,e.metadata.optionsHash,e.metadata.stateSnapshotName)}async function restoreMicrosandboxSessionSnapshot(e){if(e.metadata.stateSnapshotName===void 0||!await snapshotExists(e.module,e.metadata.stateSnapshotName))return null;let t=createProviderName(`eve-sbx-ses`,`${e.sessionKey}:${randomUUID()}`),n=await createMicrosandbox({fromSnapshot:e.metadata.stateSnapshotName,module:e.module,name:t,networkPolicy:e.metadata.networkPolicy,options:e.options,tags:e.tags,user:MICROSANDBOX_USER,workdir:WORKSPACE_ROOT});await removeSandboxIfExists(e.module,e.metadata.sandboxName);let i=new MicrosandboxVm({module:e.module,options:e.options,sessionKey:e.sessionKey,tags:e.tags},n,t,e.metadata.networkPolicy,e.metadataPath,e.metadata.optionsHash,e.metadata.stateSnapshotName);return await i.writeMetadata(e.metadataPath,e.metadata.optionsHash),i}async function loadMicrosandboxModule(e){e.log?.(`checking microsandbox platform support`),await assertMicrosandboxPlatformCandidate();let t=await withProgressHeartbeat(`loading microsandbox npm package`,e.log,()=>loadOptionalEnginePackage({appRoot:e.appRoot,autoInstall:e.options.setup.autoInstall,importModule:async()=>await import(`microsandbox`),missingMessage:"The microsandbox sandbox backend requires the `microsandbox` package, which is not bundled with Eve. Install it in your application (for example `pnpm add -D microsandbox`), or use docker() / vercel() instead.",packageName:`microsandbox`}));if(e.log?.(`checking microsandbox VM runtime`),!t.isInstalled()){if(!e.options.setup.autoInstall||!isEveDevEnvironment())throw Error("The microsandbox VM runtime is not installed. Run `npx microsandbox install`, set MSB_PATH for a custom install, or let `eve dev` install it automatically with microsandbox({ setup: { autoInstall: true } }).");await withProgressHeartbeat(`installing microsandbox VM runtime`,e.log,async()=>{await t.setup().skipVerify(e.options.setup.skipVerify).install()})}return e.log?.(`microsandbox runtime ready`),t}async function withProgressHeartbeat(e,t,n){if(t?.(e),t===void 0)return await n();let r=Date.now(),i=setInterval(()=>{t(`${e} (${Math.round((Date.now()-r)/1e3)}s elapsed)`)},1e4);i.unref?.();try{return await n()}finally{clearInterval(i)}}async function loadMicrosandboxWithoutInstall(){try{let e=await import(`microsandbox`);return e.isInstalled()?e:null}catch{return null}}async function stopAndSnapshotMicrosandboxSandbox(e,t,n){for(let r=0;r<3;r+=1){let i=await e.Sandbox.get(t);await i.stopWithTimeout(r===0?MICROSANDBOX_STOP_TIMEOUT_MS:0).catch(()=>{});try{await i.snapshot(n);return}catch(e){if(!isMicrosandboxSnapshotSourceRunningError(e)||r===2)throw e;await i.kill().catch(()=>{}),await new Promise(e=>setTimeout(e,250))}}}async function snapshotExists(e,t){try{return await e.Snapshot.get(t),!0}catch{return!1}}async function sandboxExists(e,t){try{return await e.Sandbox.get(t),!0}catch(e){if(isMicrosandboxNotFoundError(e))return!1;throw e}}async function removeSnapshotIfExists(e,t){try{await e.Snapshot.remove(t,{force:!0})}catch(e){if(!isMicrosandboxNotFoundError(e))throw e}}function createProviderName(e,t,n=``){return`${e}-${createStableHash(`${t}:${n}`).slice(0,32)}`}function createStableHash(e){return createHash(`sha256`).update(e).digest(`hex`)}async function doesPathExist(e){try{return await access(e),!0}catch{return!1}}async function createMicrosandbox(e){let t=e.module.Sandbox.builder(e.name).cpus(e.options.cpus).detached(!0).envs(e.options.env).labels(resolveMicrosandboxLabels(e.tags)).memory(e.options.memoryMiB).pullPolicy(e.options.pullPolicy).replace().workdir(e.workdir);return t=e.fromSnapshot===void 0?t.image(e.options.image):t.fromSnapshot(e.fromSnapshot),e.user!==void 0&&(t=t.user(e.user)),await applyMicrosandboxNetwork(t,e.networkPolicy).create()}async function removeSandboxIfExists(e,t){for(let n=0;n<3;n+=1)try{let r=await e.Sandbox.get(t);await r.stopWithTimeout(n===0?MICROSANDBOX_STOP_TIMEOUT_MS:0).catch(()=>{}),await r.remove();return}catch(r){if(isMicrosandboxNotFoundError(r))return;if(isMicrosandboxStillRunningError(r)&&n<2){await(await e.Sandbox.get(t).catch(()=>null))?.kill().catch(()=>{}),await new Promise(e=>setTimeout(e,250));continue}throw r}}function isMicrosandboxNotFoundError(e){return e instanceof Error?/not found|not exist|no such/i.test(e.message):!1}function isMicrosandboxStillRunningError(e){return e instanceof Error?/still running/i.test(e.message):!1}function isMicrosandboxSnapshotSourceRunningError(e){return e instanceof Error?/snapshot source sandbox .*not stopped|SnapshotSandboxRunning/i.test(e.message):!1}function resolveMicrosandboxLabels(e){return{"eve.backend":`microsandbox`,...withDevelopmentSandboxTags(e)}}export{MicrosandboxVm,connectMicrosandbox,createPreparedMicrosandbox,createProviderName,createStableHash,doesPathExist,isMicrosandboxNotFoundError,loadMicrosandboxModule,loadMicrosandboxWithoutInstall,removeSnapshotIfExists,sandboxExists,snapshotExists,stopAndSnapshotMicrosandboxSandbox};
|
|
@@ -1,2 +1 @@
|
|
|
1
|
-
function isVercelSnapshotUnavailableError(e){for(let t of walkErrorChain(e))
|
|
2
|
-
`);if(/snapshot .*expired|expired .*snapshot|snapshot .*deleted|deleted .*snapshot/i.test(e))return!0}return!1}function*walkErrorChain(e){let t=e,n=new Set;for(;t!=null&&!n.has(t);)n.add(t),yield t,t=t.cause}export{isVercelSnapshotUnavailableError};
|
|
1
|
+
function isVercelSnapshotUnavailableError(e){for(let t of walkErrorChain(e))if((t.response?.status??t.status??t.statusCode)===410)return!0;return!1}function isVercelSandboxMissingError(e){for(let t of walkErrorChain(e))if((t.response?.status??t.status??t.statusCode)===404)return!0;return!1}function*walkErrorChain(e){let t=e,n=new Set;for(;t!=null&&!n.has(t);)n.add(t),yield t,t=t.cause}export{isVercelSandboxMissingError,isVercelSnapshotUnavailableError};
|
|
@@ -2,11 +2,6 @@ import type { SandboxCreateOptions } from "#compiled/@vercel/sandbox/index.js";
|
|
|
2
2
|
import type { SandboxBackend } from "#public/definitions/sandbox-backend.js";
|
|
3
3
|
import type { VercelSandboxBootstrapUseOptions, VercelSandboxSessionUseOptions } from "#public/sandbox/vercel-sandbox.js";
|
|
4
4
|
import { type CreateVercelSandbox, type VercelSandboxModule } from "#execution/sandbox/bindings/vercel-create-sdk.js";
|
|
5
|
-
/**
|
|
6
|
-
* Construction input for {@link createVercelSandbox}. Internal —
|
|
7
|
-
* the public surface is the `vercel()` factory under
|
|
8
|
-
* `eve/sandbox`.
|
|
9
|
-
*/
|
|
10
5
|
export interface CreateVercelSandboxInput {
|
|
11
6
|
readonly createSandbox?: CreateVercelSandbox;
|
|
12
7
|
readonly createOptions?: SandboxCreateOptions;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{WORKSPACE_ROOT}from"#runtime/workspace/types.js";import{streamToBuffer}from"#execution/sandbox/stream-utils.js";import{createLoggingSandboxSession}from"#execution/sandbox/logging-session.js";import{buildSandboxSession}from"#execution/sandbox/session.js";import{SandboxTemplateNotProvisionedError}from"#public/definitions/sandbox-backend.js";import{applyInitialVercelNetworkPolicy,ensureVercelSandboxBaseRuntime}from"#execution/sandbox/bindings/vercel-base-runtime.js";import{createVercelEveImageSandbox}from"#execution/sandbox/bindings/vercel-create-sdk.js";import{isVercelSnapshotUnavailableError}from"#execution/sandbox/bindings/vercel-errors.js";import{getNamedVercelSandbox}from"#execution/sandbox/bindings/vercel-lookup.js";function createVercelSandbox(e={}){let t=e.loadSandboxModule??(async()=>await import(`#compiled/@vercel/sandbox/index.js`)),n={timeout:DEFAULT_SANDBOX_TIMEOUT_MS,...e.createOptions},r=e.createSandbox??createVercelEveImageSandbox,
|
|
1
|
+
import{WORKSPACE_ROOT}from"#runtime/workspace/types.js";import{streamToBuffer}from"#execution/sandbox/stream-utils.js";import{createLoggingSandboxSession}from"#execution/sandbox/logging-session.js";import{buildSandboxSession}from"#execution/sandbox/session.js";import{SandboxTemplateNotProvisionedError}from"#public/definitions/sandbox-backend.js";import{applyInitialVercelNetworkPolicy,ensureVercelSandboxBaseRuntime}from"#execution/sandbox/bindings/vercel-base-runtime.js";import{createVercelEveImageSandbox}from"#execution/sandbox/bindings/vercel-create-sdk.js";import{isVercelSandboxMissingError,isVercelSnapshotUnavailableError}from"#execution/sandbox/bindings/vercel-errors.js";import{getNamedVercelSandbox}from"#execution/sandbox/bindings/vercel-lookup.js";function createVercelSandbox(e={}){let t=e.loadSandboxModule??(async()=>await import(`#compiled/@vercel/sandbox/index.js`)),n={timeout:DEFAULT_SANDBOX_TIMEOUT_MS,...e.createOptions},r=e.createSandbox??createVercelEveImageSandbox,d=new Map;return{name:`vercel`,async create(e){let s=resolveVercelSandboxTags(n.tags,e.tags),u=e.templateKey===null?null:await readTemplateForCreate({createOptions:n,loadSandboxModule:t,prewarmedTemplates:d,templateKey:e.templateKey}),f=await t(),p;try{p=await ensureSession({createOptions:n,createSandbox:r,existingMetadata:e.existingMetadata,sandboxModule:f,sessionKey:e.sessionKey,snapshotId:u?.snapshotId,tags:s})}catch(t){throw u!==null&&(isVercelSnapshotUnavailableError(t)||isVercelSandboxMissingError(t))?(d.delete(u.templateKey),await(await getNamedVercelSandbox({createOptions:n,sandboxModule:f,sandboxName:u.sandboxName}))?.delete(),new SandboxTemplateNotProvisionedError({backendName:`vercel`,templateKey:u.templateKey})):Error(`Failed to create sandbox session "${e.sessionKey}": ${errorMessage(t)}`,{cause:t})}return u===null&&p.created&&(await ensureVercelSandboxBaseRuntime(p.sandbox),await applyInitialVercelNetworkPolicy(p.sandbox,n.networkPolicy)),createHandle(p.sandbox,e.sessionKey)},async prewarm(e){let i;try{i=await ensureTemplateWithUnavailableRetry({bootstrap:e.bootstrap,createOptions:n,createSandbox:r,loadSandboxModule:t,log:e.log,seedFiles:e.seedFiles,templateKey:e.templateKey})}catch(t){throw Error(`Failed to prewarm Vercel sandbox template "${e.templateKey}": ${errorMessage(t)}`,{cause:t})}return d.set(e.templateKey,i.template),{reused:i.reused}}}}async function ensureTemplateWithUnavailableRetry(e){try{return await ensureTemplate(e)}catch(t){if(!isVercelSnapshotUnavailableError(t)&&!isVercelSandboxMissingError(t))throw t;return e.log?.(`cached template disappeared; rebuilding sandbox template`),await ensureTemplate(e)}}async function readTemplate(e){let t=e.prewarmedTemplates.get(e.templateKey);if(t!==void 0)return t;let n=await e.loadSandboxModule(),r=await getNamedVercelSandbox({createOptions:e.createOptions,sandboxModule:n,sandboxName:e.templateKey});if(r===null||typeof r.currentSnapshotId!=`string`)throw new SandboxTemplateNotProvisionedError({backendName:`vercel`,templateKey:e.templateKey});return{sandboxName:r.name,snapshotId:r.currentSnapshotId,templateKey:e.templateKey}}async function readTemplateForCreate(e){try{return await readTemplate(e)}catch(t){throw SandboxTemplateNotProvisionedError.is(t)?t:Error(`Failed to read sandbox template "${e.templateKey}": ${errorMessage(t)}`,{cause:t})}}async function ensureTemplate(e){let t=await e.loadSandboxModule(),i=await getNamedVercelSandbox({createOptions:e.createOptions,sandboxModule:t,sandboxName:e.templateKey}),s=resolveVercelSandboxTags(e.createOptions.tags,e.tags),c=extractAuthorSnapshotId(e.createOptions);if(i!==null&&isUnprovisionedTerminalTemplateSandbox(i,c)&&(await i.delete(),i=null),i===null?i=await e.createSandbox({sandboxModule:t,createOptions:withBaseSetupNetworkPolicy({...e.createOptions,name:e.templateKey,persistent:!1,tags:s})}):await ensureVercelSandboxTags(i,s),typeof i.currentSnapshotId==`string`&&i.currentSnapshotId.length>0&&i.currentSnapshotId!==c)return{reused:!0,template:{sandboxName:i.name,snapshotId:i.currentSnapshotId,templateKey:e.templateKey}};e.log?.(`preparing base runtime inside sandbox`),await ensureVercelSandboxBaseRuntime(i),await applyInitialVercelNetworkPolicy(i,e.createOptions.networkPolicy);let l=buildSandboxSession(createVercelInternalSandboxSession(i,e.templateKey),createVercelNetworkPolicySetter(i));e.bootstrap!==void 0&&(e.log?.(`running sandbox bootstrap`),await e.bootstrap({use:async t=>(t!==void 0&&await i.update(t),createLoggingSandboxSession({log:e.log,session:l}))}));for(let t of e.seedFiles)typeof t.content==`string`?await l.writeTextFile({content:t.content,path:t.path}):await l.writeBinaryFile({content:t.content,path:t.path});let u=await i.snapshot();return{reused:!1,template:{sandboxName:i.name,snapshotId:u.snapshotId,templateKey:e.templateKey}}}async function ensureSession(e){let t=getVercelSandboxName(e.existingMetadata)??e.sessionKey,n=await getNamedVercelSandbox({createOptions:e.createOptions,sandboxModule:e.sandboxModule,sandboxName:t});if(n!==null)return await ensureVercelSandboxTags(n,e.tags),{created:!1,sandbox:n};let r=createSessionCreateParams(e,t);return e.tags!==void 0&&(r.tags=e.tags),{created:!0,sandbox:await e.createSandbox({createOptions:r,sandboxModule:e.sandboxModule})}}function createSessionCreateParams(e,t){if(e.snapshotId===void 0)return withBaseSetupNetworkPolicy({...e.createOptions,name:t,persistent:!0});let{runtime:n,source:r,...i}=e.createOptions;return{...i,name:t,persistent:!0,source:{snapshotId:e.snapshotId,type:`snapshot`}}}function withBaseSetupNetworkPolicy(e){return{...e,networkPolicy:`allow-all`}}function createHandle(e,t){return{session:buildSandboxSession(createVercelInternalSandboxSession(e,t),createVercelNetworkPolicySetter(e)),useSessionFn:async n=>(n!==void 0&&await e.update(n),buildSandboxSession(createVercelInternalSandboxSession(e,t),createVercelNetworkPolicySetter(e))),async captureState(){return{backendName:`vercel`,metadata:{sandboxName:e.name},sessionKey:t}},async dispose(){}}}function createVercelNetworkPolicySetter(e){return async t=>{await e.update({networkPolicy:t})}}function createVercelInternalSandboxSession(n,r){return{id:r,resolvePath:resolveVercelSandboxPath,async spawn(t){return adaptVercelCommandToSandboxProcess(await n.runCommand({args:[`-lc`,t.command],cmd:`bash`,cwd:t.workingDirectory??WORKSPACE_ROOT,detached:!0,env:t.env,signal:t.abortSignal}))},async readFile(e){return await n.readFile({path:e.path})??null},async writeFile(e){let r=await streamToBuffer(e.content);await n.writeFiles([{content:r,path:e.path}])},async removePath(e){await n.fs.rm(e.path,{force:e.force,recursive:e.recursive,signal:e.abortSignal})}}}function adaptVercelCommandToSandboxProcess(e){let t=new TextEncoder,n,r,i=!1,a,o=new ReadableStream({start(e){n=e}}),s=new ReadableStream({start(e){r=e}});return(async()=>{try{for await(let i of e.logs()){let e=t.encode(i.data);i.stream===`stdout`?n?.enqueue(e):r?.enqueue(e)}}catch(e){a=e,n?.error(e),r?.error(e)}finally{i=!0,a===void 0&&(n?.close(),r?.close())}})(),{stdout:o,stderr:s,async wait(){let t=await e.wait();for(;!i;)await new Promise(e=>setTimeout(e,0));if(a!==void 0)throw a;return{exitCode:t.exitCode}},async kill(){await e.kill()}}}function resolveVercelSandboxPath(t){return t.startsWith(`/`)?t:`${WORKSPACE_ROOT}/${t}`}function isUnprovisionedTerminalTemplateSandbox(e,t){let n=e.currentSnapshotId;return typeof n==`string`&&n.length>0&&n!==t?!1:e.status===`aborted`||e.status===`failed`||e.status===`stopped`}function extractAuthorSnapshotId(e){let t=e.source;if(t?.type===`snapshot`&&typeof t.snapshotId==`string`)return t.snapshotId}function getVercelSandboxName(e){let t=e?.sandboxName;return typeof t==`string`?t:void 0}function resolveVercelSandboxTags(e,t){let n={};if(e!==void 0)for(let[t,r]of Object.entries(e))n[t]=r;if(t!==void 0)for(let[e,r]of Object.entries(t))n[e]=r;let r=Object.keys(n).length;if(r!==0){if(r>VERCEL_SANDBOX_TAG_LIMIT)throw Error(`Vercel Sandbox supports at most ${VERCEL_SANDBOX_TAG_LIMIT} tags. Eve reserves "agent", "channel", and "sessionId"; remove or consolidate custom tags passed to vercel().`);return n}}async function ensureVercelSandboxTags(e,t){t===void 0||areVercelSandboxTagsEqual(e.tags,t)||await e.update({tags:t})}function areVercelSandboxTagsEqual(e,t){let n=e??{},r=Object.entries(n),i=Object.entries(t);return r.length===i.length?i.every(([e,t])=>n[e]===t):!1}function errorMessage(e){if(e instanceof Error){let t=e.json,n=e.text,r=typeof n==`string`&&n.length>0?n:t===void 0?void 0:JSON.stringify(t);return r===void 0?e.message:`${e.message}: ${r}`}return String(e)}const DEFAULT_SANDBOX_TIMEOUT_MS=1800*1e3,VERCEL_SANDBOX_TAG_LIMIT=5;export{createVercelSandbox};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{contextStorage}from"#context/container.js";import"ai";import{CODE_MODE_RUNTIME_ACTION_INTERRUPT_KIND}from"#harness/code-mode-runtime-action-state.js";import{loadCodeModeModule}from"#shared/code-mode.js";import{isAuthorizationSignal}from"#harness/authorization.js";import{buildDynamicTools}from"#context/build-dynamic-tools.js";import{CODE_MODE_CONNECTION_AUTH_INTERRUPT_KIND,markCodeModeToolExecutionOptions,toCodeModeConnectionAuthArgs}from"#runtime/framework-tools/code-mode-connection-auth.js";import{ALL_SANDBOX_SURFACES}from"#harness/sandbox-surface.js";import{LOAD_SKILL_TOOL_NAME}from"#runtime/skills/fragment-context.js";import{buildToolSet}from"#harness/tools.js";const NEVER_SANDBOXED_TOOL_NAMES=new Set([LOAD_SKILL_TOOL_NAME]);function createEveCodeModeOptions(e={}){let t={approval:{mode:`interrupt`}};return e.lifecycle!==void 0&&(t.lifecycle=e.lifecycle),t}async function applySandboxToolSet(e){let t={},n=new Map(e.surfaces.map(e=>[e,{}]));for(let[r,i]of Object.entries(e.tools)){if(NEVER_SANDBOXED_TOOL_NAMES.has(r)){t[r]=i;continue}let a=e.harnessTools.get(r),o=a?.runtimeAction!==void 0;o&&(t[r]=i);let s=e.surfaces.filter(e=>e.claims(a,i));if(s.length===0){o||(t[r]=i);continue}let c=o&&a!==void 0?createRuntimeActionHostTool(a):wrapHostToolForCodeMode(i);for(let e of s)n.get(e)[r]=c}let r={};for(let i of e.surfaces){let a=n.get(i);Object.keys(a).length!==0&&(Object.assign(r,a),t[i.toolName]=await createSandboxTool(i,a,e.lifecycle))}return{hostTools:r,modelTools:t}}async function createSandboxTool(e,t,r){let{createCodeModeTool:i}=await loadCodeModeModule(),a=i(t,createEveCodeModeOptions({lifecycle:r}));if(e.describe===void 0)return a;let o=typeof a.description==`string`?a.description:``;return{...a,description:e.describe({generated:o,toolNames:Object.keys(t)})}}async function buildSandboxHostTools(t){let n=buildToolSet({approvedTools:t.approvedTools,capabilities:t.capabilities,tools:t.tools}),r=contextStorage.getStore();if(r!==void 0){let e=buildDynamicTools(r)
|
|
1
|
+
import{contextStorage}from"#context/container.js";import"ai";import{CODE_MODE_RUNTIME_ACTION_INTERRUPT_KIND}from"#harness/code-mode-runtime-action-state.js";import{loadCodeModeModule}from"#shared/code-mode.js";import{isAuthorizationSignal}from"#harness/authorization.js";import{buildDynamicTools}from"#context/build-dynamic-tools.js";import{CODE_MODE_CONNECTION_AUTH_INTERRUPT_KIND,markCodeModeToolExecutionOptions,toCodeModeConnectionAuthArgs}from"#runtime/framework-tools/code-mode-connection-auth.js";import{ALL_SANDBOX_SURFACES}from"#harness/sandbox-surface.js";import{LOAD_SKILL_TOOL_NAME}from"#runtime/skills/fragment-context.js";import{buildToolSet,buildToolSetFromDefinitions}from"#harness/tools.js";const NEVER_SANDBOXED_TOOL_NAMES=new Set([LOAD_SKILL_TOOL_NAME]);function createEveCodeModeOptions(e={}){let t={approval:{mode:`interrupt`}};return e.lifecycle!==void 0&&(t.lifecycle=e.lifecycle),t}async function applySandboxToolSet(e){let t={},n=new Map(e.surfaces.map(e=>[e,{}]));for(let[r,i]of Object.entries(e.tools)){if(NEVER_SANDBOXED_TOOL_NAMES.has(r)){t[r]=i;continue}let a=e.harnessTools.get(r),o=a?.runtimeAction!==void 0;o&&(t[r]=i);let s=e.surfaces.filter(e=>e.claims(a,i));if(s.length===0){o||(t[r]=i);continue}let c=o&&a!==void 0?createRuntimeActionHostTool(a):wrapHostToolForCodeMode(i);for(let e of s)n.get(e)[r]=c}let r={};for(let i of e.surfaces){let a=n.get(i);Object.keys(a).length!==0&&(Object.assign(r,a),t[i.toolName]=await createSandboxTool(i,a,e.lifecycle))}return{hostTools:r,modelTools:t}}async function createSandboxTool(e,t,r){let{createCodeModeTool:i}=await loadCodeModeModule(),a=i(t,createEveCodeModeOptions({lifecycle:r}));if(e.describe===void 0)return a;let o=typeof a.description==`string`?a.description:``;return{...a,description:e.describe({generated:o,toolNames:Object.keys(t)})}}async function buildSandboxHostTools(t){let n=buildToolSet({approvedTools:t.approvedTools,capabilities:t.capabilities,tools:t.tools}),r=contextStorage.getStore();if(r!==void 0){let e=buildDynamicTools(r),a=buildToolSetFromDefinitions({approvedTools:t.approvedTools,capabilities:t.capabilities,tools:e});for(let[e,t]of Object.entries(a))n[e]??=t}return(await applySandboxToolSet({harnessTools:t.tools,tools:n,surfaces:ALL_SANDBOX_SURFACES})).hostTools}function createRuntimeActionHostTool(e){return{description:e.description,inputSchema:e.inputSchema,execute:async(r,i)=>{let a=i?.codeModeInterrupt;if(a?.resolution!==void 0)return a.resolution;let{requestCodeModeInterrupt:o}=await loadCodeModeModule();return o({kind:CODE_MODE_RUNTIME_ACTION_INTERRUPT_KIND,runtimeAction:e.runtimeAction,toolInput:r,toolName:e.name})}}}function wrapHostToolForCodeMode(e){let t=e.execute;if(t===void 0)return e;let invoke=async(e,i)=>{let c=await resolveExecuteOutput(t(e,markCodeModeToolExecutionOptions(i)));if(isAuthorizationSignal(c)){let{requestCodeModeInterrupt:t}=await loadCodeModeModule(),r=c.challenges[0]?.name;r&&t({args:toCodeModeConnectionAuthArgs(e),challenges:c.challenges,connectionName:r,kind:CODE_MODE_CONNECTION_AUTH_INTERRUPT_KIND,toolName:``})}return c};return{...e,execute:(e,t)=>invoke(e,t)}}async function resolveExecuteOutput(e){if(isAsyncIterable(e)){let t;for await(let n of e)t=n;return t}return await e}function isAsyncIterable(e){return typeof e==`object`&&!!e&&Symbol.asyncIterator in e&&typeof e[Symbol.asyncIterator]==`function`}export{applySandboxToolSet,buildSandboxHostTools,createEveCodeModeOptions};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createErrorId,createLogger,formatError,logError,recordErrorOnSpan}from"#internal/logging.js";import{resolveInstalledPackageInfo}from"#internal/application/package.js";import{EmptyModelResponseError,classifyModelCallError,extractModelCallErrorDetails,extractUnsupportedProviderToolTypes,isNoOutputGeneratedError,summarizeKnownModelCallConfigError,summarizeKnownModelCallRequestError}from"#harness/model-call-error.js";import{toErrorMessage}from"#shared/errors.js";import{createActionResultEvent,createAuthorizationRequiredEvent,createCompactionCompletedEvent,createCompactionRequestedEvent,createInputRequestedEvent,createResultCompletedEvent}from"#protocol/message.js";import{formatLanguageModelGatewayId}from"#internal/runtime-model.js";import{contextStorage}from"#context/container.js";import{ToolLoopAgent,isStepCount}from"ai";import{advanceStep,emitFailedStep,emitRecoverableFailedTurn,emitStepStarted,emitStreamContent,emitTurnEpilogue,emitTurnPreamble,getHarnessEmissionState,setHarnessEmissionState}from"#harness/emission.js";import{setEveAttributes}from"#runtime/attributes/emit.js";import{isCodeModeRuntimeActionInterrupt}from"#harness/code-mode-runtime-action-state.js";import{clearPendingCodeModeInterrupt,getPendingCodeModeInterrupt,setPendingCodeModeInterrupt}from"#harness/code-mode-interrupt-state.js";import{createRuntimeActionRequestFromToolCall,resolvePendingRuntimeActions,setPendingRuntimeActionBatch}from"#harness/runtime-actions.js";import{CODE_MODE_TOOL_NAME,loadCodeModeModule}from"#shared/code-mode.js";import{resolveAssistantStepText}from"#harness/messages.js";import{buildDynamicInstructionMessages}from"#context/dynamic-instruction-lifecycle.js";import{PendingSkillAnnouncementKey}from"#context/dynamic-skill-lifecycle.js";import{consumeDeferredStepInput,getApprovedTools,hasDeferredStepInput,hasStepInput,resolvePendingInput,setPendingInputBatch}from"#harness/input-requests.js";import{isAuthorizationSignal,setPendingAuthorization}from"#harness/authorization.js";import{buildDynamicTools}from"#context/build-dynamic-tools.js";import{isCodeModeConnectionAuthInterrupt}from"#runtime/framework-tools/code-mode-connection-auth.js";import{isSandboxEnabled,selectSandboxSurfaces}from"#harness/sandbox-surface.js";import{buildToolSetWithProviderTools}from"#harness/tools.js";import{readToolInterrupt}from"#harness/tool-interrupts.js";import{ASK_QUESTION_TOOL_NAME}from"#runtime/framework-tools/ask-question.js";import{WEB_SEARCH_TOOL_DEFINITION}from"#runtime/framework-tools/web-search.js";import{extractQuestionInputRequests,extractToolApprovalInputRequests}from"#harness/input-extraction.js";import{applyLastToolCacheBreakpoint,applySystemCacheBreakpoint,detectPromptCachePath,getAnthropicCacheMarker}from"#harness/prompt-cache.js";import{resolveFrameworkToolFromUpstreamType,resolveGatewayPinForWebSearchBackend,resolveWebSearchBackend}from"#harness/provider-tools.js";import{context,trace}from"#compiled/@opentelemetry/api/index.js";import{hydrateSandboxAttachments,stageAttachmentsToSandbox}from"#harness/attachment-staging.js";import{applySandboxToolSet,buildSandboxHostTools,createEveCodeModeOptions}from"#harness/code-mode.js";import{createCodeModeLifecycle}from"#harness/code-mode-lifecycle.js";import{compactMessages,getInputTokenCount,resolveCompactionModel,shouldCompact}from"#harness/compaction.js";import{accumulateTurnUsage,getTurnUsageState,setTurnUsageState}from"#harness/turn-tag-state.js";import{buildTelemetryRuntimeContext}from"#harness/instrumentation-runtime-context.js";import{getInstrumentationConfig}from"#harness/instrumentation-config.js";import{extractWorkflowStreamWriteErrorDetails}from"#harness/workflow-stream-error.js";import{ensureOtelIntegration}from"#harness/otel-integration.js";import{buildStepHooks,emitStepActions,isInvalidToolCall}from"#harness/step-hooks.js";import{pruneToolResults}from"#harness/tool-result-pruning.js";import{FINAL_OUTPUT_TOOL_NAME,buildFinalOutputTool}from"#runtime/framework-tools/final-output.js";const environment=process.env.NODE_ENV??`unknown`,eveVersion=resolveInstalledPackageInfo().version,log=createLogger(`harness.tool-loop`);function logToolExecutionError(e){e.toolOutput.type===`tool-error`&&logError(log,`tool execution failed`,e.toolOutput.error,{toolName:e.toolCall.toolName,toolCallId:e.toolCall.toolCallId})}function enrichTelemetry(e,t,n){if(e===void 0)return;let r={};for(let e of Object.keys(n??{}))r[e]=!0;return{functionId:e.functionId??t,includeRuntimeContext:r,isEnabled:!0,recordInputs:e.recordInputs??!0,recordOutputs:e.recordOutputs??!0}}function resolveGatewayPinForStep(e){if(e.cachePath.kind!==`gateway-auto`||e.tools[WEB_SEARCH_TOOL_DEFINITION.name]===void 0)return;let t=resolveWebSearchBackend(e.modelReference);return t===null?void 0:resolveGatewayPinForWebSearchBackend(t)??void 0}function buildGatewayAttributionHeaders(e,t){if(typeof e!=`string`)return;let n=t?.agentName??t?.agentId,r=process.env.VERCEL_PROJECT_PRODUCTION_URL||process.env.VERCEL_URL,i=r?`https://${r}`:void 0;if(!n&&!i)return;let a={};return n&&(a[`x-title`]=n),i&&(a[`http-referer`]=i),a}const TURN_TRACE_STATE_KEY=`eve.harness.turnTrace`;function getTurnTraceState(e){return e.state?.[TURN_TRACE_STATE_KEY]}function setTurnTraceState(e,t){let n={traceId:t.traceId,spanId:t.spanId,traceFlags:t.traceFlags};return{...e,state:{...e.state,[TURN_TRACE_STATE_KEY]:n}}}function resolveStepOtelContext(e,t,n){if(t)return trace.setSpan(context.active(),t);if(e){let e=getTurnTraceState(n);if(e){let t=trace.wrapSpanContext({traceId:e.traceId,spanId:e.spanId,traceFlags:e.traceFlags});return trace.setSpan(context.active(),t)}}}function createToolLoopHarness(t){let n=t.handleEvent,a=getInstrumentationConfig();a!==void 0&&ensureOtelIntegration();let l=a===void 0?void 0:trace.getTracer(`eve`),u=t.runtimeIdentity?.agentName;async function runStep(e,t){let n;if(l&&hasStepInput(t)){let t=a?.functionId??u,r={"eve.version":eveVersion,"eve.environment":environment,"eve.session.id":e.sessionId};t&&(r[`ai.telemetry.functionId`]=t),n=l.startSpan(`ai.eve.turn`,{attributes:r})}let r=resolveStepOtelContext(l,n,e),executeStep=()=>executeStepBody(e,t,n);try{return r?await context.with(r,executeStep):await executeStep()}finally{n?.end()}}async function executeStepBody(l,h,g){let _=l;g&&(_=setTurnTraceState(_,g.spanContext()));let v=getHarnessEmissionState(_.state),y=consumeDeferredStepInput({input:h,session:_});_=y.session;let w=await resolvePendingRuntimeActions({emit:n,session:_,stepInput:y.input});if(w.outcome===`unresolved`)return{next:null,session:w.session};_=w.session;let T=resolvePendingInput({history:w.messages,resolveApprovalKey:resolveApprovalKeyFromTools(t.tools),session:_,stepInput:y.input});if(T.outcome===`unresolved`)return{next:null,session:T.session};if(n&&T.rejectedActions)for(let e of T.rejectedActions.results)await n(createActionResultEvent({rejected:!0,result:e,sequence:T.rejectedActions.event.sequence,stepIndex:T.rejectedActions.event.stepIndex,turnId:T.rejectedActions.event.turnId}));n&&hasStepInput(h)&&(v=await emitTurnPreamble(n,h??{},v,t.runtimeIdentity),_=setHarnessEmissionState(_,v),g&&g.setAttribute(`eve.turn.id`,v.turnId)),_=T.session;let E=T.messages;if(y.input?.context!==void 0)for(let e of y.input.context)E.push({content:e,role:`user`});if(y.input?.message!==void 0&&!T.deferredMessage){let e=await stageAttachmentsToSandbox(y.input.message);E.push({content:e,role:`user`})}let D=await t.resolveModel(_.agent.modelReference),O=detectPromptCachePath(D),k=O.kind===`anthropic-direct`?getAnthropicCacheMarker():void 0,A=buildGatewayAttributionHeaders(D,t.runtimeIdentity);({messages:E,session:_}=await maybeCompact({emit:n,emissionState:v,headers:A,messages:E,model:D,onCompaction:t.onCompaction,resolveModel:t.resolveModel,session:_,telemetry:enrichTelemetry(a,u)??void 0}));let j=getApprovedTools(_),M=contextStorage.getStore(),N=await hydrateSandboxAttachments(E),P=[],F=[];for(let e of N)e.role===`system`?P.push(e):F.push(e);if(M!==void 0){P.push(...buildDynamicInstructionMessages(M));let e=M.get(PendingSkillAnnouncementKey);e!==void 0&&e.length>0&&P.push({role:`system`,content:e})}let I=F,prepareModelCallInput=e=>{let t=e?[{role:`system`,content:e}]:[],n=_.agent.system?[{role:`system`,content:_.agent.system}]:[],r=P.length>0||t.length>0?[...t,...n,...P]:void 0,i=r!==void 0&&k?applySystemCacheBreakpoint(r,k):r??_.agent.system??void 0;return{instructions:i,telemetryRuntimeContext:buildTelemetryRuntimeContext({eveVersion,authored:a,emissionState:v,environment,modelInput:{instructions:i,messages:I},session:_})}},runOneModelCall=async e=>{let{instructions:i,telemetryRuntimeContext:s={}}=e.preparedInput??prepareModelCallInput(e.extraSystemNote);e.retryReason&&(s[`eve.retry.reason`]=e.retryReason);let c=e.trailingUserNote?[...I,{role:`user`,content:e.trailingUserNote}]:I,l=selectSandboxSurfaces(t),f=await buildToolSetWithProviderTools({approvedTools:j,capabilities:t.capabilities,disabledProviderTools:e.disabledProviderTools,modelReference:_.agent.modelReference,tools:t.tools});if(M!==void 0){let e=buildDynamicTools(M);for(let t of e)f[t.name]??={description:t.description,inputSchema:t.inputSchema,execute:t.execute,outputSchema:t.outputSchema}}_.outputSchema!==void 0&&(f[FINAL_OUTPUT_TOOL_NAME]=buildFinalOutputTool(_.outputSchema));let p=l.length>0?(await applySandboxToolSet({harnessTools:t.tools,lifecycle:n===void 0?void 0:createCodeModeLifecycle({emit:n,emissionState:v,tools:t.tools}),tools:f,surfaces:l})).modelTools:f,m=k?applyLastToolCacheBreakpoint(p,k):p,h=resolveGatewayPinForStep({cachePath:O,modelReference:_.agent.modelReference,tools:m}),g=buildStepHooks({cachePath:O,emit:n,emissionState:v,emitStepStarted:e.suppressStepStartedEmission!==!0,gatewayPinProvider:h,marker:k,session:_}),y=new ToolLoopAgent({headers:A,instructions:i,model:D,onToolExecutionEnd:logToolExecutionError,onError(e){summarizeKnownModelCallConfigError(e.error)===null&&logError(log,`tool-loop stream error`,e.error)},onStepFinish:g.onStepFinish,prepareStep:g.prepareStep,runtimeContext:s,stopWhen:isStepCount(1),telemetry:enrichTelemetry(a,u,s),tools:m}),executeModelCall=async()=>{if(n){let e=await y.stream({messages:c}),{handledInlineToolResultCallIds:r,inlineAuthorizationResults:i,inlineToolResultParts:a}=await emitStreamContent(n,v,e.fullStream),s=await g.stepResult;if(isEmptyModelResponse(s))throw new EmptyModelResponseError;if(await emitStepActions(n,v,s,{excludedActionToolNames:new Set([ASK_QUESTION_TOOL_NAME,CODE_MODE_TOOL_NAME,FINAL_OUTPUT_TOOL_NAME]),handledInlineToolResultCallIds:r,tools:t.tools}),a.length>0||i.length>0){let e=s.toolResults,t=new Map(e.map(e=>[e.toolCallId,e]));for(let e of i)t.set(e.toolCallId,e);return{content:s.content,finishReason:s.finishReason,response:{...s.response,...a.length>0?{messages:[{role:`tool`,content:[...a]},...s.response.messages]}:{}},text:s.text,toolCalls:s.toolCalls,toolResults:[...t.values()],usage:s.usage}}return s}await y.generate({messages:c});let e=await g.stepResult;if(isEmptyModelResponse(e))throw new EmptyModelResponseError;return e};return runModelCallWithRetries(()=>executeModelCall().catch(rethrowNoOutputAsEmptyResponse),{sessionId:_.sessionId,turnId:v.turnId})},L=prepareModelCallInput();n&&await emitStepStarted(n,v,E);let R=await continuePendingCodeModeInterrupt({capabilities:t.capabilities,childResults:y.input?.runtimeActionResults,config:t,emit:n,emissionState:v,messages:E,runStep,session:_});if(R!==null)return R;let z;try{z=await runOneModelCall({preparedInput:L,suppressStepStartedEmission:!0})}catch(r){let a=await runModelCallRecoveryPipeline({error:r,stages:[e=>attemptUnsupportedProviderToolRecovery({error:e.error,runOneModelCall,sessionId:_.sessionId,turnId:v.turnId}),e=>attemptEmptyResponseRecovery({error:e.error,retryCallOptions:e.retryCallOptions,runOneModelCall,sessionId:_.sessionId,turnId:v.turnId})]});if(a.outcome===`recovered`)z=a.result;else{let r=a.error;if(g&&recordErrorOnSpan(g,r),!n)throw r;let o=extractWorkflowStreamWriteErrorDetails(r);if(o!==null){let t=createErrorId();return log.error(`workflow stream write failed — parking session for retry by the user`,{...o,errorId:t,error:r,sessionId:_.sessionId,turnId:v.turnId}),v=await emitRecoverableFailedTurn(n,v,{code:`WORKFLOW_STREAM_WRITE_FAILED`,details:{...o,errorId:t},message:toErrorMessage(r)}),{next:null,session:setHarnessEmissionState(_,v)}}let l=classifyModelCallError(r),u=createErrorId(),m=l===`terminal`?summarizeKnownModelCallConfigError(r):null,h=m===null?summarizeKnownModelCallRequestError(r):null,y=m?.message??h?.message??toErrorMessage(r),b=extractModelCallErrorDetails(r),x=buildModelCallFailureDetails({configSummary:m,error:r,errorId:u,modelCallDetails:b,requestSummary:h}),S=buildModelCallFailureLogFields({error:r,errorId:u,modelCallDetails:b,requestSummary:h,sessionId:_.sessionId,turnId:v.turnId});return l===`terminal`?(m===null?log.error(h?.message??`model call failed terminally`,S):log.error(`${m.name}: ${m.message}`,{errorId:u,sessionId:_.sessionId,turnId:v.turnId}),await emitFailedStep(n,v,{code:`MODEL_CALL_FAILED`,details:x,message:y,sessionId:_.sessionId}),{next:{done:!0,output:``},session:_}):t.mode===`task`?(log.error(h?.message??`model call failed; failing the task run`,S),await emitFailedStep(n,v,{code:`MODEL_CALL_FAILED`,details:x,message:y,sessionId:_.sessionId}),{next:{done:!0,isError:!0,output:y},session:_}):(log.error(h?.message??`model call failed — parking session for retry by the user`,S),v=await emitRecoverableFailedTurn(n,v,{code:`MODEL_CALL_FAILED`,details:x,message:y}),{next:null,session:setHarnessEmissionState(_,v)})}}let B=accumulateTurnUsage({previous:getTurnUsageState(_.state),turnId:v.turnId,usage:z.usage??{}});_=setTurnUsageState(_,B);let V;try{V=formatLanguageModelGatewayId(D)}catch{V=void 0}return await setEveAttributes({"$eve.model":V,"$eve.input_tokens":B.inputTokens,"$eve.output_tokens":B.outputTokens,"$eve.cache_read_tokens":B.cacheReadTokens,"$eve.cache_write_tokens":B.cacheWriteTokens,"$eve.tool_count":t.tools.size}),handleStepResult({config:t,emit:n,emissionState:v,promptMessages:E,result:z,runStep,session:_})}return runStep}function buildModelCallFailureDetails(e){let{configSummary:t,error:r,errorId:i,modelCallDetails:a,requestSummary:o}=e;return t===null?o===null?{...formatError(r,i),...a}:{errorId:i,message:toErrorMessage(r),name:o.name,...a}:{errorId:i,message:t.message,name:t.name,...a}}function buildModelCallFailureLogFields(e){let t={errorId:e.errorId,sessionId:e.sessionId,turnId:e.turnId};return e.requestSummary===null?{...t,error:e.error}:{...t,details:e.modelCallDetails}}async function runModelCallRecoveryPipeline(e){let t=e.error,n;for(let r of e.stages){let e=await r({error:t,retryCallOptions:n});if(e.outcome===`recovered`)return e;e.outcome===`failed`&&(t=e.error,n=e.retryCallOptions)}return{outcome:`failed`,error:t}}async function attemptUnsupportedProviderToolRecovery(e){let t=extractUnsupportedProviderToolTypes(e.error);if(t.length===0)return{outcome:`skipped`};let n=[];for(let e of t){let t=resolveFrameworkToolFromUpstreamType(e);t!==null&&!n.includes(t)&&n.push(t)}if(n.length===0)return{outcome:`skipped`};log.warn(`disabling unsupported provider tool(s); retrying step once`,{disabled:n,sessionId:e.sessionId,turnId:e.turnId,upstreamTypes:t});let r={disabledProviderTools:new Set(n),extraSystemNote:buildDisabledToolNote(n)};try{return{outcome:`recovered`,result:await e.runOneModelCall({...r,suppressStepStartedEmission:!0})}}catch(e){return{outcome:`failed`,error:e,retryCallOptions:r}}}function buildDisabledToolNote(e){let t=e.join(`, `);return`The following ${e.length===1?`tool is`:`tools are`} not available with the current model and has been removed: ${t}. Proceed using the remaining tools or your training knowledge.`}function isEmptyModelResponse(e){return e.finishReason===`other`&&e.toolCalls.length===0&&resolveAssistantStepText(e.response.messages,e.text)===null}function rethrowNoOutputAsEmptyResponse(e){throw isNoOutputGeneratedError(e)?new EmptyModelResponseError({cause:e}):e}async function attemptEmptyResponseRecovery(e){if(!(e.error instanceof EmptyModelResponseError))return{outcome:`skipped`};log.warn(`empty model response; reissuing the model call once`,{sessionId:e.sessionId,turnId:e.turnId});try{return{outcome:`recovered`,result:await e.runOneModelCall({...e.retryCallOptions,retryReason:`empty-response`,suppressStepStartedEmission:!0,trailingUserNote:`Your previous reply was not delivered. Answer now from the tool results above; do not re-run tools or mention this notice.`})}}catch(t){return{outcome:`failed`,error:t,retryCallOptions:e.retryCallOptions}}}async function handleStepResult(e){let{config:t,emit:n,promptMessages:r,result:i,runStep:a}=e,{emissionState:o,session:s}=e,c=i.response.messages,l=resolveAssistantStepText(c,i.text),u={...s,compaction:createNextCompactionConfig(s.compaction,r,i)};if(isSandboxEnabled(t)){let{getCodeModeInterrupt:e}=await loadCodeModeModule(),a=e(i);if(a!==void 0)return parkOnCodeModeInterrupt({baseSession:u,config:t,emit:n,emissionState:o,interrupt:a,promptMessages:r,responseMessages:c})}let d=extractToolApprovalInputRequests({content:i.content??[]}),f=new Set(d.map(e=>e.action.callId)),p=extractQuestionInputRequests({toolCalls:i.toolCalls,excludedCallIds:f}),m=[...d,...p],g=(i.toolCalls??[]).filter(e=>!isInvalidToolCall(e)).filter(e=>t.tools.get(e.toolName)?.runtimeAction!==void 0).map(e=>createRuntimeActionRequestFromToolCall({toolCall:e,tools:t.tools}));if(g.length>0)return{next:null,session:setHarnessEmissionState(setPendingRuntimeActionBatch({actions:g,event:{sequence:o.sequence,stepIndex:o.stepIndex,turnId:o.turnId},responseMessages:c,session:{...u,history:[...r]}}),o)};if(m.length>0){let e=setPendingInputBatch({event:{sequence:o.sequence,stepIndex:o.stepIndex,turnId:o.turnId},requests:m,responseMessages:c,session:{...u,history:[...r]}});return n&&(await n(createInputRequestedEvent({requests:m,sequence:o.sequence,stepIndex:o.stepIndex,turnId:o.turnId})),t.mode===`conversation`&&(o=await emitTurnEpilogue(n,o,t.mode),e=setHarnessEmissionState(e,o))),{next:null,session:e}}let _=findAuthorizationSignalFromToolResults(i.toolResults);if(_){let{challenges:e}=_;if(n)for(let t of e)await n(createAuthorizationRequiredEvent({authorization:t.challenge,name:t.name,description:t.challenge.instructions??`Authorization required for ${t.name}`,webhookUrl:t.hookUrl,sequence:o.sequence,stepIndex:o.stepIndex,turnId:o.turnId}));return{next:null,session:setHarnessEmissionState({...u,history:[...r],state:setPendingAuthorization(u.state,{challenges:e})},o)}}let y=pruneToolResults(r),b=y!==r,x=u.compaction;b&&x.lastKnownInputTokens!==void 0&&(x={recentWindowSize:x.recentWindowSize,threshold:x.threshold});let S=[...y,...c],C={...u,compaction:x,history:S};return!(C.outputSchema!==void 0&&extractFinalOutput(i)!==void 0)&&(c.at(-1)?.role===`tool`||hasDeferredStepInput(C))?(n&&(o=advanceStep(o),C=setHarnessEmissionState(C,o)),{next:a,session:C}):t.mode===`task`?finishTaskTurn({emissionState:o,emit:n,prunedHistory:y,result:i,schema:C.outputSchema,session:C,stepOutput:l}):finishConversationTurn({emissionState:o,emit:n,prunedHistory:y,result:i,schema:C.outputSchema,session:C})}const OUTPUT_SCHEMA_NOT_FULFILLED={code:`OUTPUT_SCHEMA_NOT_FULFILLED`,message:`The agent could not produce a result matching the requested schema.`};function extractFinalOutput(e){return(e.toolCalls??[]).find(e=>e.toolName===FINAL_OUTPUT_TOOL_NAME)?.input}function persistStructuredAssistantTurn(e,t,n){return{...e,history:[...t,{content:JSON.stringify(n),role:`assistant`}],outputSchema:void 0}}async function emitStructuredResult(e,t,n,r){return await e(createResultCompletedEvent({result:n,sequence:t.sequence,stepIndex:t.stepIndex,turnId:t.turnId})),emitTurnEpilogue(e,t,r)}async function finishTaskTurn(e){let{emit:t,prunedHistory:n,result:r,schema:i,stepOutput:a}=e,{emissionState:o,session:s}=e;if(i===void 0)return t&&(o=await emitTurnEpilogue(t,o,`task`),s=setHarnessEmissionState(s,o)),{next:{done:!0,output:a??``},session:s};let c=extractFinalOutput(r);return c===void 0?(t&&await emitFailedStep(t,o,{...OUTPUT_SCHEMA_NOT_FULFILLED,sessionId:s.sessionId}),{next:{done:!0,isError:!0,output:OUTPUT_SCHEMA_NOT_FULFILLED.message},session:s}):(s=persistStructuredAssistantTurn(s,n,c),t&&(o=await emitStructuredResult(t,o,c,`task`),s=setHarnessEmissionState(s,o)),{next:{done:!0,output:c},session:s})}async function finishConversationTurn(e){let{emit:t,prunedHistory:n,result:r,schema:i}=e,{emissionState:a,session:o}=e;if(i===void 0)return t&&(a=await emitTurnEpilogue(t,a,`conversation`),o=setHarnessEmissionState(o,a)),{next:null,session:o};let s=extractFinalOutput(r);return s===void 0?(t&&(a=await emitRecoverableFailedTurn(t,a,OUTPUT_SCHEMA_NOT_FULFILLED),o=setHarnessEmissionState(o,a)),{next:null,session:o}):(o=persistStructuredAssistantTurn(o,n,s),t&&(a=await emitStructuredResult(t,a,s,`conversation`),o=setHarnessEmissionState(o,a)),{next:null,session:o})}async function continuePendingCodeModeInterrupt(e){let t=getPendingCodeModeInterrupt(e.session.state);if(t===void 0)return null;let{continueCodeModeApproval:n,continueCodeModeInterrupt:i,getCodeModeApprovalResponse:a,isCodeModeApprovalInterrupt:o,replaceCodeModeInterruptResult:s,unwrapCodeModeResult:c}=await loadCodeModeModule(),l=t.interrupt,u=o(l)?a([...e.messages],l):void 0;if(o(l)&&u===void 0)return{next:null,session:e.session};let d=createEveCodeModeOptions({lifecycle:e.emit===void 0?void 0:createCodeModeLifecycle({emit:e.emit,emissionState:e.emissionState,skipReplayed:!0,tools:e.config.tools})}),f;try{let t=await buildSandboxHostTools({approvedTools:getApprovedTools(e.session),capabilities:e.capabilities,tools:e.config.tools});if(o(l)&&u!==void 0)f=await n({approvalResponse:u,interrupt:l,options:d,tools:t});else if(isCodeModeConnectionAuthInterrupt(l))f=await i({interrupt:l,resolution:{status:`authorized`},tools:t,options:d});else if(isCodeModeRuntimeActionInterrupt(l)){let n=e.childResults??[],r=l,a=0;for(;;){f=await i({interrupt:r,resolution:n[a]?.output,tools:t,options:d});let e=c(f);if(e.status!==`interrupted`||!isCodeModeRuntimeActionInterrupt(e.interrupt)||a+1>=n.length)break;a++,r=e.interrupt}}else throw Error(`Unsupported code-mode interrupt kind "${l.payload.kind}".`)}catch(e){logError(log,`code-mode interrupt continuation failed`,e),f={error:`code_mode_continuation_failed`,message:toErrorMessage(e),retryable:!1}}let m=c(f),h=m.status===`interrupted`?m.interrupt:m.output,g=[...e.session.history,...t.responseMessages],_=isCodeModeRuntimeActionInterrupt(l)?replaceCodeModeToolResult(g,l.outerToolCallId,h):s(g,l,h),v=clearPendingCodeModeInterrupt({...e.session,history:_});if(m.status===`interrupted`){let t=e.session.history.length,n=_.slice(0,t),r=_.slice(t);return v={...v,history:n},parkOnCodeModeInterrupt({baseSession:v,config:e.config,emit:e.emit,emissionState:e.emissionState,interrupt:m.interrupt,promptMessages:n,responseMessages:r})}return{next:e.runStep,session:v}}function replaceCodeModeToolResult(e,t,n){if(t===void 0)return[...e];let r=typeof n==`string`?{type:`text`,value:n}:{type:`json`,value:n};return e.map(e=>{if(e.role!==`tool`)return e;let n=e.content.map(e=>e.type!==`tool-result`||e.toolCallId!==t?e:{...e,output:r});return{...e,content:n}})}async function parkOnCodeModeInterrupt(e){let{isCodeModeApprovalInterrupt:t,toCodeModeApprovalMessages:n}=await loadCodeModeModule(),r=e.interrupt,i={...e.baseSession,history:[...e.promptMessages]};if(isCodeModeConnectionAuthInterrupt(r)){let t=[...r.payload.challenges??[]];if(e.emit)for(let n of t)await e.emit(createAuthorizationRequiredEvent({authorization:n.challenge,name:n.name,description:n.challenge.instructions??`Authorization required for ${n.name}`,webhookUrl:n.hookUrl,sequence:e.emissionState.sequence,stepIndex:e.emissionState.stepIndex,turnId:e.emissionState.turnId}));return{next:null,session:setPendingCodeModeInterrupt({interrupt:r,responseMessages:e.responseMessages,session:{...i,state:setPendingAuthorization(i.state,{challenges:t})}})}}if(t(r)){let t=n(r),a=extractToolApprovalInputRequests({content:extractAssistantContent(t)}),o=setPendingInputBatch({event:{sequence:e.emissionState.sequence,stepIndex:e.emissionState.stepIndex,turnId:e.emissionState.turnId},requests:a,responseMessages:t,session:setPendingCodeModeInterrupt({interrupt:r,responseMessages:e.responseMessages,session:i})});if(e.emit&&(await e.emit(createInputRequestedEvent({requests:a,sequence:e.emissionState.sequence,stepIndex:e.emissionState.stepIndex,turnId:e.emissionState.turnId})),e.config.mode===`conversation`)){let t=await emitTurnEpilogue(e.emit,e.emissionState,e.config.mode);o=setHarnessEmissionState(o,t)}return{next:null,session:o}}return{next:null,session:setHarnessEmissionState(setPendingCodeModeInterrupt({interrupt:r,responseMessages:e.responseMessages,session:i}),e.emissionState)}}function extractAssistantContent(e){let t=[];for(let n of e)n.role===`assistant`&&Array.isArray(n.content)&&t.push(...n.content);return t}function createNextCompactionConfig(e,t,n){let r={recentWindowSize:e.recentWindowSize,threshold:e.threshold};return n.usage?.inputTokens!==void 0&&(r.lastKnownInputTokens=n.usage.inputTokens,r.lastKnownPromptMessageCount=t.length),r}async function maybeCompact(e){let{emit:t,emissionState:n}=e,r=e.messages,i=e.session;if(!shouldCompact(r,i.compaction))return{messages:r,session:i};let a=await resolveCompactionModel({compactionModelReference:i.agent.compactionModelReference,model:e.model,modelReference:i.agent.modelReference,resolveModel:e.resolveModel});if(t&&await t(createCompactionRequestedEvent({modelId:formatLanguageModelGatewayId(a.model),sequence:n.sequence,sessionId:i.sessionId,turnId:n.turnId,usageInputTokens:getInputTokenCount(r,i.compaction)})),r=await compactMessages(r,a.model,i.compaction,a.providerOptions,e.telemetry,e.headers),e.onCompaction)for(let t of e.onCompaction())r.push(t);return t&&await t(createCompactionCompletedEvent({modelId:formatLanguageModelGatewayId(a.model),sequence:n.sequence,sessionId:i.sessionId,turnId:n.turnId})),{messages:r,session:i}}function resolveApprovalKeyFromTools(e){return t=>{let n=e.get(t.action.toolName);if(n?.approvalKey!==void 0)return n.approvalKey(t.action.input)}}async function runModelCallWithRetries(e,t){for(let n=1;;n++)try{return await e()}catch(e){if(n===3||classifyModelCallError(e)!==`retry`)throw e;let r=500*2**(n-1)+Math.floor(Math.random()*250);log.warn(`model call failed transiently — retrying`,{attempt:n,delayMs:r,sessionId:t.sessionId,turnId:t.turnId,error:e}),await new Promise(e=>setTimeout(e,r))}}function findAuthorizationSignalFromToolResults(e){let t=contextStorage.getStore();if(t!==void 0)for(let n of e??[]){let e=readToolInterrupt(t,n.toolCallId);if(e!==void 0&&isAuthorizationSignal(e))return e}for(let t of e??[])if(isAuthorizationSignal(t.output))return t.output}export{createToolLoopHarness};
|
|
1
|
+
import{createErrorId,createLogger,formatError,logError,recordErrorOnSpan}from"#internal/logging.js";import{resolveInstalledPackageInfo}from"#internal/application/package.js";import{EmptyModelResponseError,classifyModelCallError,extractModelCallErrorDetails,extractUnsupportedProviderToolTypes,isNoOutputGeneratedError,summarizeKnownModelCallConfigError,summarizeKnownModelCallRequestError}from"#harness/model-call-error.js";import{toErrorMessage}from"#shared/errors.js";import{createActionResultEvent,createAuthorizationRequiredEvent,createCompactionCompletedEvent,createCompactionRequestedEvent,createInputRequestedEvent,createResultCompletedEvent}from"#protocol/message.js";import{formatLanguageModelGatewayId}from"#internal/runtime-model.js";import{contextStorage}from"#context/container.js";import{ToolLoopAgent,isStepCount}from"ai";import{advanceStep,emitFailedStep,emitRecoverableFailedTurn,emitStepStarted,emitStreamContent,emitTurnEpilogue,emitTurnPreamble,getHarnessEmissionState,setHarnessEmissionState}from"#harness/emission.js";import{setEveAttributes}from"#runtime/attributes/emit.js";import{isCodeModeRuntimeActionInterrupt}from"#harness/code-mode-runtime-action-state.js";import{clearPendingCodeModeInterrupt,getPendingCodeModeInterrupt,setPendingCodeModeInterrupt}from"#harness/code-mode-interrupt-state.js";import{createRuntimeActionRequestFromToolCall,resolvePendingRuntimeActions,setPendingRuntimeActionBatch}from"#harness/runtime-actions.js";import{CODE_MODE_TOOL_NAME,loadCodeModeModule}from"#shared/code-mode.js";import{resolveAssistantStepText}from"#harness/messages.js";import{buildDynamicInstructionMessages}from"#context/dynamic-instruction-lifecycle.js";import{PendingSkillAnnouncementKey}from"#context/dynamic-skill-lifecycle.js";import{consumeDeferredStepInput,getApprovedTools,hasDeferredStepInput,hasStepInput,resolvePendingInput,setPendingInputBatch}from"#harness/input-requests.js";import{isAuthorizationSignal,setPendingAuthorization}from"#harness/authorization.js";import{buildDynamicTools}from"#context/build-dynamic-tools.js";import{isCodeModeConnectionAuthInterrupt}from"#runtime/framework-tools/code-mode-connection-auth.js";import{isSandboxEnabled,selectSandboxSurfaces}from"#harness/sandbox-surface.js";import{buildToolSetFromDefinitions,buildToolSetWithProviderTools}from"#harness/tools.js";import{readToolInterrupt}from"#harness/tool-interrupts.js";import{ASK_QUESTION_TOOL_NAME}from"#runtime/framework-tools/ask-question.js";import{WEB_SEARCH_TOOL_DEFINITION}from"#runtime/framework-tools/web-search.js";import{extractQuestionInputRequests,extractToolApprovalInputRequests}from"#harness/input-extraction.js";import{applyLastToolCacheBreakpoint,applySystemCacheBreakpoint,detectPromptCachePath,getAnthropicCacheMarker}from"#harness/prompt-cache.js";import{resolveFrameworkToolFromUpstreamType,resolveGatewayPinForWebSearchBackend,resolveWebSearchBackend}from"#harness/provider-tools.js";import{context,trace}from"#compiled/@opentelemetry/api/index.js";import{hydrateSandboxAttachments,stageAttachmentsToSandbox}from"#harness/attachment-staging.js";import{applySandboxToolSet,buildSandboxHostTools,createEveCodeModeOptions}from"#harness/code-mode.js";import{createCodeModeLifecycle}from"#harness/code-mode-lifecycle.js";import{compactMessages,getInputTokenCount,resolveCompactionModel,shouldCompact}from"#harness/compaction.js";import{accumulateTurnUsage,getTurnUsageState,setTurnUsageState}from"#harness/turn-tag-state.js";import{buildTelemetryRuntimeContext}from"#harness/instrumentation-runtime-context.js";import{getInstrumentationConfig}from"#harness/instrumentation-config.js";import{extractWorkflowStreamWriteErrorDetails}from"#harness/workflow-stream-error.js";import{ensureOtelIntegration}from"#harness/otel-integration.js";import{buildStepHooks,emitStepActions,isInvalidToolCall}from"#harness/step-hooks.js";import{pruneToolResults}from"#harness/tool-result-pruning.js";import{FINAL_OUTPUT_TOOL_NAME,buildFinalOutputTool}from"#runtime/framework-tools/final-output.js";const environment=process.env.NODE_ENV??`unknown`,eveVersion=resolveInstalledPackageInfo().version,log=createLogger(`harness.tool-loop`);function logToolExecutionError(e){e.toolOutput.type===`tool-error`&&logError(log,`tool execution failed`,e.toolOutput.error,{toolName:e.toolCall.toolName,toolCallId:e.toolCall.toolCallId})}function enrichTelemetry(e,t,n){if(e===void 0)return;let r={};for(let e of Object.keys(n??{}))r[e]=!0;return{functionId:e.functionId??t,includeRuntimeContext:r,isEnabled:!0,recordInputs:e.recordInputs??!0,recordOutputs:e.recordOutputs??!0}}function resolveGatewayPinForStep(e){if(e.cachePath.kind!==`gateway-auto`||e.tools[WEB_SEARCH_TOOL_DEFINITION.name]===void 0)return;let t=resolveWebSearchBackend(e.modelReference);return t===null?void 0:resolveGatewayPinForWebSearchBackend(t)??void 0}function buildGatewayAttributionHeaders(e,t){if(typeof e!=`string`)return;let n=t?.agentName??t?.agentId,r=process.env.VERCEL_PROJECT_PRODUCTION_URL||process.env.VERCEL_URL,i=r?`https://${r}`:void 0;if(!n&&!i)return;let a={};return n&&(a[`x-title`]=n),i&&(a[`http-referer`]=i),a}const TURN_TRACE_STATE_KEY=`eve.harness.turnTrace`;function getTurnTraceState(e){return e.state?.[TURN_TRACE_STATE_KEY]}function setTurnTraceState(e,t){let n={traceId:t.traceId,spanId:t.spanId,traceFlags:t.traceFlags};return{...e,state:{...e.state,[TURN_TRACE_STATE_KEY]:n}}}function resolveStepOtelContext(e,t,n){if(t)return trace.setSpan(context.active(),t);if(e){let e=getTurnTraceState(n);if(e){let t=trace.wrapSpanContext({traceId:e.traceId,spanId:e.spanId,traceFlags:e.traceFlags});return trace.setSpan(context.active(),t)}}}function createToolLoopHarness(t){let n=t.handleEvent,a=getInstrumentationConfig();a!==void 0&&ensureOtelIntegration();let l=a===void 0?void 0:trace.getTracer(`eve`),u=t.runtimeIdentity?.agentName;async function runStep(e,t){let n;if(l&&hasStepInput(t)){let t=a?.functionId??u,r={"eve.version":eveVersion,"eve.environment":environment,"eve.session.id":e.sessionId};t&&(r[`ai.telemetry.functionId`]=t),n=l.startSpan(`ai.eve.turn`,{attributes:r})}let r=resolveStepOtelContext(l,n,e),executeStep=()=>executeStepBody(e,t,n);try{return r?await context.with(r,executeStep):await executeStep()}finally{n?.end()}}async function executeStepBody(l,h,g){let _=l;g&&(_=setTurnTraceState(_,g.spanContext()));let v=getHarnessEmissionState(_.state),y=consumeDeferredStepInput({input:h,session:_});_=y.session;let w=await resolvePendingRuntimeActions({emit:n,session:_,stepInput:y.input});if(w.outcome===`unresolved`)return{next:null,session:w.session};_=w.session;let T=resolvePendingInput({history:w.messages,resolveApprovalKey:resolveApprovalKeyFromTools(t.tools),session:_,stepInput:y.input});if(T.outcome===`unresolved`)return{next:null,session:T.session};if(n&&T.rejectedActions)for(let e of T.rejectedActions.results)await n(createActionResultEvent({rejected:!0,result:e,sequence:T.rejectedActions.event.sequence,stepIndex:T.rejectedActions.event.stepIndex,turnId:T.rejectedActions.event.turnId}));n&&hasStepInput(h)&&(v=await emitTurnPreamble(n,h??{},v,t.runtimeIdentity),_=setHarnessEmissionState(_,v),g&&g.setAttribute(`eve.turn.id`,v.turnId)),_=T.session;let E=T.messages;if(y.input?.context!==void 0)for(let e of y.input.context)E.push({content:e,role:`user`});if(y.input?.message!==void 0&&!T.deferredMessage){let e=await stageAttachmentsToSandbox(y.input.message);E.push({content:e,role:`user`})}let D=await t.resolveModel(_.agent.modelReference),O=detectPromptCachePath(D),k=O.kind===`anthropic-direct`?getAnthropicCacheMarker():void 0,A=buildGatewayAttributionHeaders(D,t.runtimeIdentity);({messages:E,session:_}=await maybeCompact({emit:n,emissionState:v,headers:A,messages:E,model:D,onCompaction:t.onCompaction,resolveModel:t.resolveModel,session:_,telemetry:enrichTelemetry(a,u)??void 0}));let j=getApprovedTools(_),M=contextStorage.getStore(),N=await hydrateSandboxAttachments(E),P=[],F=[];for(let e of N)e.role===`system`?P.push(e):F.push(e);if(M!==void 0){P.push(...buildDynamicInstructionMessages(M));let e=M.get(PendingSkillAnnouncementKey);e!==void 0&&e.length>0&&P.push({role:`system`,content:e})}let I=F,prepareModelCallInput=e=>{let t=e?[{role:`system`,content:e}]:[],n=_.agent.system?[{role:`system`,content:_.agent.system}]:[],r=P.length>0||t.length>0?[...t,...n,...P]:void 0,i=r!==void 0&&k?applySystemCacheBreakpoint(r,k):r??_.agent.system??void 0;return{instructions:i,telemetryRuntimeContext:buildTelemetryRuntimeContext({eveVersion,authored:a,emissionState:v,environment,modelInput:{instructions:i,messages:I},session:_})}},runOneModelCall=async e=>{let{instructions:i,telemetryRuntimeContext:s={}}=e.preparedInput??prepareModelCallInput(e.extraSystemNote);e.retryReason&&(s[`eve.retry.reason`]=e.retryReason);let c=e.trailingUserNote?[...I,{role:`user`,content:e.trailingUserNote}]:I,l=selectSandboxSurfaces(t),f=await buildToolSetWithProviderTools({approvedTools:j,capabilities:t.capabilities,disabledProviderTools:e.disabledProviderTools,modelReference:_.agent.modelReference,tools:t.tools});if(M!==void 0){let n=buildDynamicTools(M),r=buildToolSetFromDefinitions({approvedTools:j,capabilities:t.capabilities,disabledProviderTools:e.disabledProviderTools,tools:n});for(let[e,t]of Object.entries(r))f[e]??=t}_.outputSchema!==void 0&&(f[FINAL_OUTPUT_TOOL_NAME]=buildFinalOutputTool(_.outputSchema));let p=l.length>0?(await applySandboxToolSet({harnessTools:t.tools,lifecycle:n===void 0?void 0:createCodeModeLifecycle({emit:n,emissionState:v,tools:t.tools}),tools:f,surfaces:l})).modelTools:f,m=k?applyLastToolCacheBreakpoint(p,k):p,h=resolveGatewayPinForStep({cachePath:O,modelReference:_.agent.modelReference,tools:m}),g=buildStepHooks({cachePath:O,emit:n,emissionState:v,emitStepStarted:e.suppressStepStartedEmission!==!0,gatewayPinProvider:h,marker:k,session:_}),y=new ToolLoopAgent({headers:A,instructions:i,model:D,onToolExecutionEnd:logToolExecutionError,onError(e){summarizeKnownModelCallConfigError(e.error)===null&&logError(log,`tool-loop stream error`,e.error)},onStepFinish:g.onStepFinish,prepareStep:g.prepareStep,runtimeContext:s,stopWhen:isStepCount(1),telemetry:enrichTelemetry(a,u,s),tools:m}),executeModelCall=async()=>{if(n){let e=await y.stream({messages:c}),{handledInlineToolResultCallIds:r,inlineAuthorizationResults:i,inlineToolResultParts:a}=await emitStreamContent(n,v,e.fullStream),s=await g.stepResult;if(isEmptyModelResponse(s))throw new EmptyModelResponseError;if(await emitStepActions(n,v,s,{excludedActionToolNames:new Set([ASK_QUESTION_TOOL_NAME,CODE_MODE_TOOL_NAME,FINAL_OUTPUT_TOOL_NAME]),handledInlineToolResultCallIds:r,tools:t.tools}),a.length>0||i.length>0){let e=s.toolResults,t=new Map(e.map(e=>[e.toolCallId,e]));for(let e of i)t.set(e.toolCallId,e);return{content:s.content,finishReason:s.finishReason,response:{...s.response,...a.length>0?{messages:[{role:`tool`,content:[...a]},...s.response.messages]}:{}},text:s.text,toolCalls:s.toolCalls,toolResults:[...t.values()],usage:s.usage}}return s}await y.generate({messages:c});let e=await g.stepResult;if(isEmptyModelResponse(e))throw new EmptyModelResponseError;return e};return runModelCallWithRetries(()=>executeModelCall().catch(rethrowNoOutputAsEmptyResponse),{sessionId:_.sessionId,turnId:v.turnId})},L=prepareModelCallInput();n&&await emitStepStarted(n,v,E);let R=await continuePendingCodeModeInterrupt({capabilities:t.capabilities,childResults:y.input?.runtimeActionResults,config:t,emit:n,emissionState:v,messages:E,runStep,session:_});if(R!==null)return R;let z;try{z=await runOneModelCall({preparedInput:L,suppressStepStartedEmission:!0})}catch(r){let a=await runModelCallRecoveryPipeline({error:r,stages:[e=>attemptUnsupportedProviderToolRecovery({error:e.error,runOneModelCall,sessionId:_.sessionId,turnId:v.turnId}),e=>attemptEmptyResponseRecovery({error:e.error,retryCallOptions:e.retryCallOptions,runOneModelCall,sessionId:_.sessionId,turnId:v.turnId})]});if(a.outcome===`recovered`)z=a.result;else{let r=a.error;if(g&&recordErrorOnSpan(g,r),!n)throw r;let o=extractWorkflowStreamWriteErrorDetails(r);if(o!==null){let t=createErrorId();return log.error(`workflow stream write failed — parking session for retry by the user`,{...o,errorId:t,error:r,sessionId:_.sessionId,turnId:v.turnId}),v=await emitRecoverableFailedTurn(n,v,{code:`WORKFLOW_STREAM_WRITE_FAILED`,details:{...o,errorId:t},message:toErrorMessage(r)}),{next:null,session:setHarnessEmissionState(_,v)}}let l=classifyModelCallError(r),u=createErrorId(),m=l===`terminal`?summarizeKnownModelCallConfigError(r):null,h=m===null?summarizeKnownModelCallRequestError(r):null,y=m?.message??h?.message??toErrorMessage(r),b=extractModelCallErrorDetails(r),x=buildModelCallFailureDetails({configSummary:m,error:r,errorId:u,modelCallDetails:b,requestSummary:h}),S=buildModelCallFailureLogFields({error:r,errorId:u,modelCallDetails:b,requestSummary:h,sessionId:_.sessionId,turnId:v.turnId});return l===`terminal`?(m===null?log.error(h?.message??`model call failed terminally`,S):log.error(`${m.name}: ${m.message}`,{errorId:u,sessionId:_.sessionId,turnId:v.turnId}),await emitFailedStep(n,v,{code:`MODEL_CALL_FAILED`,details:x,message:y,sessionId:_.sessionId}),{next:{done:!0,output:``},session:_}):t.mode===`task`?(log.error(h?.message??`model call failed; failing the task run`,S),await emitFailedStep(n,v,{code:`MODEL_CALL_FAILED`,details:x,message:y,sessionId:_.sessionId}),{next:{done:!0,isError:!0,output:y},session:_}):(log.error(h?.message??`model call failed — parking session for retry by the user`,S),v=await emitRecoverableFailedTurn(n,v,{code:`MODEL_CALL_FAILED`,details:x,message:y}),{next:null,session:setHarnessEmissionState(_,v)})}}let B=accumulateTurnUsage({previous:getTurnUsageState(_.state),turnId:v.turnId,usage:z.usage??{}});_=setTurnUsageState(_,B);let V;try{V=formatLanguageModelGatewayId(D)}catch{V=void 0}return await setEveAttributes({"$eve.model":V,"$eve.input_tokens":B.inputTokens,"$eve.output_tokens":B.outputTokens,"$eve.cache_read_tokens":B.cacheReadTokens,"$eve.cache_write_tokens":B.cacheWriteTokens,"$eve.tool_count":t.tools.size}),handleStepResult({config:t,emit:n,emissionState:v,promptMessages:E,result:z,runStep,session:_})}return runStep}function buildModelCallFailureDetails(e){let{configSummary:t,error:r,errorId:i,modelCallDetails:a,requestSummary:o}=e;return t===null?o===null?{...formatError(r,i),...a}:{errorId:i,message:toErrorMessage(r),name:o.name,...a}:{errorId:i,message:t.message,name:t.name,...a}}function buildModelCallFailureLogFields(e){let t={errorId:e.errorId,sessionId:e.sessionId,turnId:e.turnId};return e.requestSummary===null?{...t,error:e.error}:{...t,details:e.modelCallDetails}}async function runModelCallRecoveryPipeline(e){let t=e.error,n;for(let r of e.stages){let e=await r({error:t,retryCallOptions:n});if(e.outcome===`recovered`)return e;e.outcome===`failed`&&(t=e.error,n=e.retryCallOptions)}return{outcome:`failed`,error:t}}async function attemptUnsupportedProviderToolRecovery(e){let t=extractUnsupportedProviderToolTypes(e.error);if(t.length===0)return{outcome:`skipped`};let n=[];for(let e of t){let t=resolveFrameworkToolFromUpstreamType(e);t!==null&&!n.includes(t)&&n.push(t)}if(n.length===0)return{outcome:`skipped`};log.warn(`disabling unsupported provider tool(s); retrying step once`,{disabled:n,sessionId:e.sessionId,turnId:e.turnId,upstreamTypes:t});let r={disabledProviderTools:new Set(n),extraSystemNote:buildDisabledToolNote(n)};try{return{outcome:`recovered`,result:await e.runOneModelCall({...r,suppressStepStartedEmission:!0})}}catch(e){return{outcome:`failed`,error:e,retryCallOptions:r}}}function buildDisabledToolNote(e){let t=e.join(`, `);return`The following ${e.length===1?`tool is`:`tools are`} not available with the current model and has been removed: ${t}. Proceed using the remaining tools or your training knowledge.`}function isEmptyModelResponse(e){return e.finishReason===`other`&&e.toolCalls.length===0&&resolveAssistantStepText(e.response.messages,e.text)===null}function rethrowNoOutputAsEmptyResponse(e){throw isNoOutputGeneratedError(e)?new EmptyModelResponseError({cause:e}):e}async function attemptEmptyResponseRecovery(e){if(!(e.error instanceof EmptyModelResponseError))return{outcome:`skipped`};log.warn(`empty model response; reissuing the model call once`,{sessionId:e.sessionId,turnId:e.turnId});try{return{outcome:`recovered`,result:await e.runOneModelCall({...e.retryCallOptions,retryReason:`empty-response`,suppressStepStartedEmission:!0,trailingUserNote:`Your previous reply was not delivered. Answer now from the tool results above; do not re-run tools or mention this notice.`})}}catch(t){return{outcome:`failed`,error:t,retryCallOptions:e.retryCallOptions}}}async function handleStepResult(e){let{config:t,emit:n,promptMessages:r,result:i,runStep:a}=e,{emissionState:o,session:s}=e,c=i.response.messages,l=resolveAssistantStepText(c,i.text),u={...s,compaction:createNextCompactionConfig(s.compaction,r,i)};if(isSandboxEnabled(t)){let{getCodeModeInterrupt:e}=await loadCodeModeModule(),a=e(i);if(a!==void 0)return parkOnCodeModeInterrupt({baseSession:u,config:t,emit:n,emissionState:o,interrupt:a,promptMessages:r,responseMessages:c})}let d=extractToolApprovalInputRequests({content:i.content??[]}),f=new Set(d.map(e=>e.action.callId)),p=extractQuestionInputRequests({toolCalls:i.toolCalls,excludedCallIds:f}),m=[...d,...p],g=(i.toolCalls??[]).filter(e=>!isInvalidToolCall(e)).filter(e=>t.tools.get(e.toolName)?.runtimeAction!==void 0).map(e=>createRuntimeActionRequestFromToolCall({toolCall:e,tools:t.tools}));if(g.length>0)return{next:null,session:setHarnessEmissionState(setPendingRuntimeActionBatch({actions:g,event:{sequence:o.sequence,stepIndex:o.stepIndex,turnId:o.turnId},responseMessages:c,session:{...u,history:[...r]}}),o)};if(m.length>0){let e=setPendingInputBatch({event:{sequence:o.sequence,stepIndex:o.stepIndex,turnId:o.turnId},requests:m,responseMessages:c,session:{...u,history:[...r]}});return n&&(await n(createInputRequestedEvent({requests:m,sequence:o.sequence,stepIndex:o.stepIndex,turnId:o.turnId})),t.mode===`conversation`&&(o=await emitTurnEpilogue(n,o,t.mode),e=setHarnessEmissionState(e,o))),{next:null,session:e}}let _=findAuthorizationSignalFromToolResults(i.toolResults);if(_){let{challenges:e}=_;if(n)for(let t of e)await n(createAuthorizationRequiredEvent({authorization:t.challenge,name:t.name,description:t.challenge.instructions??`Authorization required for ${t.name}`,webhookUrl:t.hookUrl,sequence:o.sequence,stepIndex:o.stepIndex,turnId:o.turnId}));return{next:null,session:setHarnessEmissionState({...u,history:[...r],state:setPendingAuthorization(u.state,{challenges:e})},o)}}let y=pruneToolResults(r),b=y!==r,x=u.compaction;b&&x.lastKnownInputTokens!==void 0&&(x={recentWindowSize:x.recentWindowSize,threshold:x.threshold});let S=[...y,...c],C={...u,compaction:x,history:S};return!(C.outputSchema!==void 0&&extractFinalOutput(i)!==void 0)&&(c.at(-1)?.role===`tool`||hasDeferredStepInput(C))?(n&&(o=advanceStep(o),C=setHarnessEmissionState(C,o)),{next:a,session:C}):t.mode===`task`?finishTaskTurn({emissionState:o,emit:n,prunedHistory:y,result:i,schema:C.outputSchema,session:C,stepOutput:l}):finishConversationTurn({emissionState:o,emit:n,prunedHistory:y,result:i,schema:C.outputSchema,session:C})}const OUTPUT_SCHEMA_NOT_FULFILLED={code:`OUTPUT_SCHEMA_NOT_FULFILLED`,message:`The agent could not produce a result matching the requested schema.`};function extractFinalOutput(e){return(e.toolCalls??[]).find(e=>e.toolName===FINAL_OUTPUT_TOOL_NAME)?.input}function persistStructuredAssistantTurn(e,t,n){return{...e,history:[...t,{content:JSON.stringify(n),role:`assistant`}],outputSchema:void 0}}async function emitStructuredResult(e,t,n,r){return await e(createResultCompletedEvent({result:n,sequence:t.sequence,stepIndex:t.stepIndex,turnId:t.turnId})),emitTurnEpilogue(e,t,r)}async function finishTaskTurn(e){let{emit:t,prunedHistory:n,result:r,schema:i,stepOutput:a}=e,{emissionState:o,session:s}=e;if(i===void 0)return t&&(o=await emitTurnEpilogue(t,o,`task`),s=setHarnessEmissionState(s,o)),{next:{done:!0,output:a??``},session:s};let c=extractFinalOutput(r);return c===void 0?(t&&await emitFailedStep(t,o,{...OUTPUT_SCHEMA_NOT_FULFILLED,sessionId:s.sessionId}),{next:{done:!0,isError:!0,output:OUTPUT_SCHEMA_NOT_FULFILLED.message},session:s}):(s=persistStructuredAssistantTurn(s,n,c),t&&(o=await emitStructuredResult(t,o,c,`task`),s=setHarnessEmissionState(s,o)),{next:{done:!0,output:c},session:s})}async function finishConversationTurn(e){let{emit:t,prunedHistory:n,result:r,schema:i}=e,{emissionState:a,session:o}=e;if(i===void 0)return t&&(a=await emitTurnEpilogue(t,a,`conversation`),o=setHarnessEmissionState(o,a)),{next:null,session:o};let s=extractFinalOutput(r);return s===void 0?(t&&(a=await emitRecoverableFailedTurn(t,a,OUTPUT_SCHEMA_NOT_FULFILLED),o=setHarnessEmissionState(o,a)),{next:null,session:o}):(o=persistStructuredAssistantTurn(o,n,s),t&&(a=await emitStructuredResult(t,a,s,`conversation`),o=setHarnessEmissionState(o,a)),{next:null,session:o})}async function continuePendingCodeModeInterrupt(e){let t=getPendingCodeModeInterrupt(e.session.state);if(t===void 0)return null;let{continueCodeModeApproval:n,continueCodeModeInterrupt:i,getCodeModeApprovalResponse:a,isCodeModeApprovalInterrupt:o,replaceCodeModeInterruptResult:s,unwrapCodeModeResult:c}=await loadCodeModeModule(),l=t.interrupt,u=o(l)?a([...e.messages],l):void 0;if(o(l)&&u===void 0)return{next:null,session:e.session};let d=createEveCodeModeOptions({lifecycle:e.emit===void 0?void 0:createCodeModeLifecycle({emit:e.emit,emissionState:e.emissionState,skipReplayed:!0,tools:e.config.tools})}),f;try{let t=await buildSandboxHostTools({approvedTools:getApprovedTools(e.session),capabilities:e.capabilities,tools:e.config.tools});if(o(l)&&u!==void 0)f=await n({approvalResponse:u,interrupt:l,options:d,tools:t});else if(isCodeModeConnectionAuthInterrupt(l))f=await i({interrupt:l,resolution:{status:`authorized`},tools:t,options:d});else if(isCodeModeRuntimeActionInterrupt(l)){let n=e.childResults??[],r=l,a=0;for(;;){f=await i({interrupt:r,resolution:n[a]?.output,tools:t,options:d});let e=c(f);if(e.status!==`interrupted`||!isCodeModeRuntimeActionInterrupt(e.interrupt)||a+1>=n.length)break;a++,r=e.interrupt}}else throw Error(`Unsupported code-mode interrupt kind "${l.payload.kind}".`)}catch(e){logError(log,`code-mode interrupt continuation failed`,e),f={error:`code_mode_continuation_failed`,message:toErrorMessage(e),retryable:!1}}let m=c(f),h=m.status===`interrupted`?m.interrupt:m.output,g=[...e.session.history,...t.responseMessages],_=isCodeModeRuntimeActionInterrupt(l)?replaceCodeModeToolResult(g,l.outerToolCallId,h):s(g,l,h),v=clearPendingCodeModeInterrupt({...e.session,history:_});if(m.status===`interrupted`){let t=e.session.history.length,n=_.slice(0,t),r=_.slice(t);return v={...v,history:n},parkOnCodeModeInterrupt({baseSession:v,config:e.config,emit:e.emit,emissionState:e.emissionState,interrupt:m.interrupt,promptMessages:n,responseMessages:r})}return{next:e.runStep,session:v}}function replaceCodeModeToolResult(e,t,n){if(t===void 0)return[...e];let r=typeof n==`string`?{type:`text`,value:n}:{type:`json`,value:n};return e.map(e=>{if(e.role!==`tool`)return e;let n=e.content.map(e=>e.type!==`tool-result`||e.toolCallId!==t?e:{...e,output:r});return{...e,content:n}})}async function parkOnCodeModeInterrupt(e){let{isCodeModeApprovalInterrupt:t,toCodeModeApprovalMessages:n}=await loadCodeModeModule(),r=e.interrupt,i={...e.baseSession,history:[...e.promptMessages]};if(isCodeModeConnectionAuthInterrupt(r)){let t=[...r.payload.challenges??[]];if(e.emit)for(let n of t)await e.emit(createAuthorizationRequiredEvent({authorization:n.challenge,name:n.name,description:n.challenge.instructions??`Authorization required for ${n.name}`,webhookUrl:n.hookUrl,sequence:e.emissionState.sequence,stepIndex:e.emissionState.stepIndex,turnId:e.emissionState.turnId}));return{next:null,session:setPendingCodeModeInterrupt({interrupt:r,responseMessages:e.responseMessages,session:{...i,state:setPendingAuthorization(i.state,{challenges:t})}})}}if(t(r)){let t=n(r),a=extractToolApprovalInputRequests({content:extractAssistantContent(t)}),o=setPendingInputBatch({event:{sequence:e.emissionState.sequence,stepIndex:e.emissionState.stepIndex,turnId:e.emissionState.turnId},requests:a,responseMessages:t,session:setPendingCodeModeInterrupt({interrupt:r,responseMessages:e.responseMessages,session:i})});if(e.emit&&(await e.emit(createInputRequestedEvent({requests:a,sequence:e.emissionState.sequence,stepIndex:e.emissionState.stepIndex,turnId:e.emissionState.turnId})),e.config.mode===`conversation`)){let t=await emitTurnEpilogue(e.emit,e.emissionState,e.config.mode);o=setHarnessEmissionState(o,t)}return{next:null,session:o}}return{next:null,session:setHarnessEmissionState(setPendingCodeModeInterrupt({interrupt:r,responseMessages:e.responseMessages,session:i}),e.emissionState)}}function extractAssistantContent(e){let t=[];for(let n of e)n.role===`assistant`&&Array.isArray(n.content)&&t.push(...n.content);return t}function createNextCompactionConfig(e,t,n){let r={recentWindowSize:e.recentWindowSize,threshold:e.threshold};return n.usage?.inputTokens!==void 0&&(r.lastKnownInputTokens=n.usage.inputTokens,r.lastKnownPromptMessageCount=t.length),r}async function maybeCompact(e){let{emit:t,emissionState:n}=e,r=e.messages,i=e.session;if(!shouldCompact(r,i.compaction))return{messages:r,session:i};let a=await resolveCompactionModel({compactionModelReference:i.agent.compactionModelReference,model:e.model,modelReference:i.agent.modelReference,resolveModel:e.resolveModel});if(t&&await t(createCompactionRequestedEvent({modelId:formatLanguageModelGatewayId(a.model),sequence:n.sequence,sessionId:i.sessionId,turnId:n.turnId,usageInputTokens:getInputTokenCount(r,i.compaction)})),r=await compactMessages(r,a.model,i.compaction,a.providerOptions,e.telemetry,e.headers),e.onCompaction)for(let t of e.onCompaction())r.push(t);return t&&await t(createCompactionCompletedEvent({modelId:formatLanguageModelGatewayId(a.model),sequence:n.sequence,sessionId:i.sessionId,turnId:n.turnId})),{messages:r,session:i}}function resolveApprovalKeyFromTools(e){return t=>{let n=e.get(t.action.toolName);if(n?.approvalKey!==void 0)return n.approvalKey(t.action.input)}}async function runModelCallWithRetries(e,t){for(let n=1;;n++)try{return await e()}catch(e){if(n===3||classifyModelCallError(e)!==`retry`)throw e;let r=500*2**(n-1)+Math.floor(Math.random()*250);log.warn(`model call failed transiently — retrying`,{attempt:n,delayMs:r,sessionId:t.sessionId,turnId:t.turnId,error:e}),await new Promise(e=>setTimeout(e,r))}}function findAuthorizationSignalFromToolResults(e){let t=contextStorage.getStore();if(t!==void 0)for(let n of e??[]){let e=readToolInterrupt(t,n.toolCallId);if(e!==void 0&&isAuthorizationSignal(e))return e}for(let t of e??[])if(isAuthorizationSignal(t.output))return t.output}export{createToolLoopHarness};
|
|
@@ -25,6 +25,18 @@ export declare function buildToolSet(input: {
|
|
|
25
25
|
readonly disabledProviderTools?: ReadonlySet<string>;
|
|
26
26
|
readonly tools: HarnessToolMap;
|
|
27
27
|
}): ToolSet;
|
|
28
|
+
/**
|
|
29
|
+
* Builds a ToolSet from an ordered list of harness definitions.
|
|
30
|
+
*
|
|
31
|
+
* The first definition for a name wins, matching the dynamic-tool scope
|
|
32
|
+
* ordering where step tools override turn/session tools.
|
|
33
|
+
*/
|
|
34
|
+
export declare function buildToolSetFromDefinitions(input: {
|
|
35
|
+
readonly approvedTools?: ReadonlySet<string>;
|
|
36
|
+
readonly capabilities?: SessionCapabilities;
|
|
37
|
+
readonly disabledProviderTools?: ReadonlySet<string>;
|
|
38
|
+
readonly tools: readonly HarnessToolDefinition[];
|
|
39
|
+
}): ToolSet;
|
|
28
40
|
/**
|
|
29
41
|
* Wraps a tool's `execute` so a returned {@link AuthorizationSignal} is
|
|
30
42
|
* stashed out-of-band ({@link stashToolInterrupt}) for the park detector while
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{isObject}from"#shared/guards.js";import{loadContext}from"#context/container.js";import{tool}from"ai";import{authorizationPendingModelText,isAuthorizationPendingModelOutput,isAuthorizationSignal,modelFacingAuthorizationOutput}from"#harness/authorization.js";import{isCodeModeToolExecutionOptions}from"#runtime/framework-tools/code-mode-connection-auth.js";import{stashToolInterrupt}from"#harness/tool-interrupts.js";import{ASK_QUESTION_TOOL_NAME}from"#runtime/framework-tools/ask-question.js";import{WEB_SEARCH_TOOL_DEFINITION}from"#runtime/framework-tools/web-search.js";import{resolveWebSearchBackend,resolveWebSearchProviderTool}from"#harness/provider-tools.js";function buildToolSet(e){let t={},a=e.capabilities?.requestInput===!0,o=e.disabledProviderTools;for(let s of e.tools.values()){if(s.name===ASK_QUESTION_TOOL_NAME&&!a||o?.has(s.name))continue;let c=s.toModelOutput;t[s.name]=tool({description:s.description,execute:wrapToolExecute(s),inputSchema:s.inputSchema,needsApproval:buildNeedsApprovalFn(s,e),outputSchema:s.outputSchema,...s.execute===void 0?c===void 0?{}:{toModelOutput:({output:e})=>c(e)}:{toModelOutput:({output:e})=>isAuthorizationPendingModelOutput(e)?{type:`text`,value:authorizationPendingModelText(e.connections)}:c===void 0?typeof e==`string`?{type:`text`,value:e}:{type:`json`,value:e??null}:c(e)}})}return t}function wrapToolExecute(e){let n=e.execute;if(n!==void 0)return async(e,r)=>{let i=await n(e);return!isAuthorizationSignal(i)||isCodeModeToolExecutionOptions(r)?i:(stashToolInterrupt(loadContext(),r.toolCallId,i),modelFacingAuthorizationOutput(i))}}async function buildToolSetWithProviderTools(e){let t=e.disabledProviderTools,n={...buildToolSet({approvedTools:e.approvedTools,capabilities:e.capabilities,disabledProviderTools:t,tools:e.tools})};if(!t?.has(WEB_SEARCH_TOOL_DEFINITION.name)){let t=e.tools.get(WEB_SEARCH_TOOL_DEFINITION.name);if(t!==void 0&&t.execute===void 0){let t=resolveWebSearchBackend(e.modelReference);t===null?delete n[WEB_SEARCH_TOOL_DEFINITION.name]:n[WEB_SEARCH_TOOL_DEFINITION.name]=await resolveWebSearchProviderTool(t)}}return n}function buildNeedsApprovalFn(t,n){return async r=>{if(t.needsApproval===void 0)return!1;let i=isObject(r)?r:void 0;return t.needsApproval({approvedTools:n.approvedTools??new Set,toolInput:i,toolName:t.name})}}export{buildToolSet,buildToolSetWithProviderTools,wrapToolExecute};
|
|
1
|
+
import{isObject}from"#shared/guards.js";import{loadContext}from"#context/container.js";import{tool}from"ai";import{authorizationPendingModelText,isAuthorizationPendingModelOutput,isAuthorizationSignal,modelFacingAuthorizationOutput}from"#harness/authorization.js";import{isCodeModeToolExecutionOptions}from"#runtime/framework-tools/code-mode-connection-auth.js";import{stashToolInterrupt}from"#harness/tool-interrupts.js";import{ASK_QUESTION_TOOL_NAME}from"#runtime/framework-tools/ask-question.js";import{WEB_SEARCH_TOOL_DEFINITION}from"#runtime/framework-tools/web-search.js";import{resolveWebSearchBackend,resolveWebSearchProviderTool}from"#harness/provider-tools.js";function buildToolSet(e){let t={},a=e.capabilities?.requestInput===!0,o=e.disabledProviderTools;for(let s of e.tools.values()){if(s.name===ASK_QUESTION_TOOL_NAME&&!a||o?.has(s.name))continue;let c=s.toModelOutput;t[s.name]=tool({description:s.description,execute:wrapToolExecute(s),inputSchema:s.inputSchema,needsApproval:buildNeedsApprovalFn(s,e),outputSchema:s.outputSchema,...s.execute===void 0?c===void 0?{}:{toModelOutput:({output:e})=>c(e)}:{toModelOutput:({output:e})=>isAuthorizationPendingModelOutput(e)?{type:`text`,value:authorizationPendingModelText(e.connections)}:c===void 0?typeof e==`string`?{type:`text`,value:e}:{type:`json`,value:e??null}:c(e)}})}return t}function buildToolSetFromDefinitions(e){let t=new Map;for(let n of e.tools)t.has(n.name)||t.set(n.name,n);return buildToolSet({approvedTools:e.approvedTools,capabilities:e.capabilities,disabledProviderTools:e.disabledProviderTools,tools:t})}function wrapToolExecute(e){let n=e.execute;if(n!==void 0)return async(e,r)=>{let i=await n(e);return!isAuthorizationSignal(i)||isCodeModeToolExecutionOptions(r)?i:(stashToolInterrupt(loadContext(),r.toolCallId,i),modelFacingAuthorizationOutput(i))}}async function buildToolSetWithProviderTools(e){let t=e.disabledProviderTools,n={...buildToolSet({approvedTools:e.approvedTools,capabilities:e.capabilities,disabledProviderTools:t,tools:e.tools})};if(!t?.has(WEB_SEARCH_TOOL_DEFINITION.name)){let t=e.tools.get(WEB_SEARCH_TOOL_DEFINITION.name);if(t!==void 0&&t.execute===void 0){let t=resolveWebSearchBackend(e.modelReference);t===null?delete n[WEB_SEARCH_TOOL_DEFINITION.name]:n[WEB_SEARCH_TOOL_DEFINITION.name]=await resolveWebSearchProviderTool(t)}}return n}function buildNeedsApprovalFn(t,n){return async r=>{if(t.needsApproval===void 0)return!1;let i=isObject(r)?r:void 0;return t.needsApproval({approvedTools:n.approvedTools??new Set,toolInput:i,toolName:t.name})}}export{buildToolSet,buildToolSetFromDefinitions,buildToolSetWithProviderTools,wrapToolExecute};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createRequire}from"node:module";import{existsSync,readFileSync,realpathSync}from"node:fs";import{basename,dirname,join}from"node:path";import{EVE_PACKAGE_NAME}from"#internal/package-name.js";import{fileURLToPath}from"node:url";let cachedPackageInfo;const BUNDLED_FALLBACK_PACKAGE_VERSION=`0.11.
|
|
1
|
+
import{createRequire}from"node:module";import{existsSync,readFileSync,realpathSync}from"node:fs";import{basename,dirname,join}from"node:path";import{EVE_PACKAGE_NAME}from"#internal/package-name.js";import{fileURLToPath}from"node:url";let cachedPackageInfo;const BUNDLED_FALLBACK_PACKAGE_VERSION=`0.11.2`,WORKFLOW_MODULE_ALIASES={"workflow/api":`src/compiled/@workflow/core/runtime.js`,"workflow/errors":`src/compiled/@workflow/errors/index.js`,"workflow/internal/private":`src/compiled/@workflow/core/private.js`,"workflow/runtime":`src/compiled/@workflow/core/runtime.js`};function resolveFallbackPackageVersion(){return BUNDLED_FALLBACK_PACKAGE_VERSION.startsWith(`__`)?`0.0.0`:BUNDLED_FALLBACK_PACKAGE_VERSION}const FALLBACK_PACKAGE_INFO={name:EVE_PACKAGE_NAME,version:resolveFallbackPackageVersion()};function resolveCurrentModulePath(){return typeof __filename==`string`?__filename:resolveCurrentModulePathFromStack()}function resolveCurrentModulePathFromStack(){let e=Error.prepareStackTrace;try{Error.prepareStackTrace=(e,t)=>t;let e=Error().stack?.[0]?.getFileName();if(typeof e!=`string`||e.length===0)throw Error(`Failed to resolve the current module path from the stack trace.`);return e.startsWith(`file:`)?fileURLToPath(e):e}finally{Error.prepareStackTrace=e}}const require=createRequire(resolveCurrentModulePath());function isBuildOutputPackageRoot(e){return basename(e)===`dist`&&existsSync(join(dirname(e),`package.json`))}function resolvePackageBuildRoot(){let e=dirname(realpathSync(resolveCurrentModulePath()));for(;;){if(isBuildOutputPackageRoot(e))return e;let t=dirname(e);if(t===e)return null;e=t}}function findNearestPackageRoot(e){let n=e;for(;;){if(existsSync(join(n,`package.json`))&&!isBuildOutputPackageRoot(n))return n;let r=dirname(n);if(r===n)throw Error(`Failed to resolve package root from "${e}".`);n=r}}function resolvePackageRoot(){return findNearestPackageRoot(dirname(realpathSync(resolveCurrentModulePath())))}function tryResolvePackageRoot(){try{return resolvePackageRoot()}catch{return}}function rewriteSourceFilePathForBuild(e){return e.replace(/\.[cm]?tsx?$/,`.js`)}function resolvePackageSourceFilePath(e){let t=resolvePackageBuildRoot();return t===null?join(resolvePackageRoot(),e):join(t,rewriteSourceFilePathForBuild(e))}function resolvePackageSourceDirectoryPath(e){let t=resolvePackageBuildRoot();return join(t===null?resolvePackageRoot():t,e)}function resolvePackageDependencyPath(e){return require.resolve(e)}function resolvePackageCompiledFilePath(e){let t=resolvePackageBuildRoot();return t===null?join(resolvePackageRoot(),`.generated`,`compiled`,e.replace(/^src\/compiled\//,``)):join(t,e)}function normalizeInstalledPackageInfo(e){let t=e;if(!(typeof t.name!=`string`||typeof t.version!=`string`))return{name:t.name,version:t.version}}function tryReadInstalledPackageInfo(e,t){let r=normalizeInstalledPackageInfo(JSON.parse(readFileSync(e,`utf8`)));if(r?.name===t)return r}function resolveInstalledPackageInfo(){if(cachedPackageInfo)return cachedPackageInfo;let e=tryResolvePackageRoot(),t=e===void 0?void 0:tryReadInstalledPackageInfo(join(e,`package.json`),EVE_PACKAGE_NAME);if(t)return cachedPackageInfo=t,cachedPackageInfo;try{let e=tryReadInstalledPackageInfo(require.resolve(`${EVE_PACKAGE_NAME}/package.json`),EVE_PACKAGE_NAME);if(e)return cachedPackageInfo=e,cachedPackageInfo}catch{}return cachedPackageInfo={...FALLBACK_PACKAGE_INFO},cachedPackageInfo}function resolveWorkflowModulePath(e){if(e===`workflow`)return resolvePackageSourceFilePath(`src/internal/workflow/index.ts`);if(e===`workflow/internal/builtins`)return resolvePackageSourceFilePath(`src/internal/workflow/builtins.ts`);let t=WORKFLOW_MODULE_ALIASES[e];return t===void 0?require.resolve(e):resolvePackageCompiledFilePath(t)}export{resolveInstalledPackageInfo,resolvePackageDependencyPath,resolvePackageRoot,resolvePackageSourceDirectoryPath,resolvePackageSourceFilePath,resolveWorkflowModulePath};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{getPackageManagerStrategy}from"../../primitives/pm/index.js";import{pinnedNodeEngineMajor}from"../../node-engine.js";import{pathExists,writeTextFile}from"../files.js";import{resolveVersionToken}from"../version-tokens.js";import{SUPPORTED_AUTHORED_MODULE_FILE_EXTENSIONS}from"../update/module-files.js";import{WEB_APP_TEMPLATE_FILES}from"./web-template.js";import{basename,join,resolve}from"node:path";import{mkdir,readdir,stat}from"node:fs/promises";const CURRENT_DIRECTORY_PROJECT_NAME=`.`,ALLOWED_CREATE_IN_PLACE_ENTRIES=new Set([`.DS_Store`,`.git`,`.gitkeep`,`.hg`]),DEFAULT_AI_PACKAGE_VERSION=`7.0.0-beta.178`,DEFAULT_CONNECT_PACKAGE_VERSION=`0.2.2`,DEFAULT_ZOD_PACKAGE_VERSION=`4.4.3`,DEFAULT_EVE_PACKAGE_CONTRACT={version:`0.11.
|
|
1
|
+
import{getPackageManagerStrategy}from"../../primitives/pm/index.js";import{pinnedNodeEngineMajor}from"../../node-engine.js";import{pathExists,writeTextFile}from"../files.js";import{resolveVersionToken}from"../version-tokens.js";import{SUPPORTED_AUTHORED_MODULE_FILE_EXTENSIONS}from"../update/module-files.js";import{WEB_APP_TEMPLATE_FILES}from"./web-template.js";import{basename,join,resolve}from"node:path";import{mkdir,readdir,stat}from"node:fs/promises";const CURRENT_DIRECTORY_PROJECT_NAME=`.`,ALLOWED_CREATE_IN_PLACE_ENTRIES=new Set([`.DS_Store`,`.git`,`.gitkeep`,`.hg`]),DEFAULT_AI_PACKAGE_VERSION=`7.0.0-beta.178`,DEFAULT_CONNECT_PACKAGE_VERSION=`0.2.2`,DEFAULT_ZOD_PACKAGE_VERSION=`4.4.3`,DEFAULT_EVE_PACKAGE_CONTRACT={version:`0.11.2`,nodeEngine:`>=24`};function resolveEvePackageContract(e=DEFAULT_EVE_PACKAGE_CONTRACT){return{version:resolveVersionToken(`evePackage.version`,e.version),nodeEngine:resolveVersionToken(`evePackage.nodeEngine`,e.nodeEngine)}}function modelProviderSlug(e){let t=(e.split(`/`)[0]??``).replaceAll(/[^A-Za-z0-9._-]/gu,``);return t.length>0?t:`anthropic`}function byokProviderEnvVar(e){let t=modelProviderSlug(e).toUpperCase().replaceAll(/[^A-Z0-9]/gu,`_`);return`${/^[0-9]/.test(t)?`_`:``}${t}_API_KEY`}function agentTemplateFiles(e){return{"agent/agent.ts":BASE_AGENT_TEMPLATE.replaceAll(`__EVE_INIT_MODEL__`,e),"agent/channels/eve.ts":WEB_APP_TEMPLATE_FILES[`agent/channels/eve.ts`],"agent/instructions.md":AGENT_INSTRUCTIONS_TEMPLATE}}function renderTemplate(e,t){return e.replaceAll(`__EVE_INIT_APP_NAME__`,t.appName).replaceAll(`__EVE_INIT_MODEL__`,t.model).replaceAll(`__EVE_INIT_BYOK_PROVIDER__`,modelProviderSlug(t.model)).replaceAll(`__EVE_INIT_BYOK_ENV_VAR__`,byokProviderEnvVar(t.model)).replaceAll(`__EVE_INIT_PACKAGE_VERSION__`,formatEveDependencySpecifier(t.eveVersion)).replaceAll(`__EVE_INIT_AI_SDK_VERSION__`,t.aiPackageVersion).replaceAll(`__EVE_INIT_CONNECT_VERSION__`,t.connectPackageVersion).replaceAll(`__EVE_INIT_ZOD_VERSION__`,t.zodPackageVersion).replaceAll(`__EVE_INIT_TSGO_VERSION__`,t.tsgoPackageVersion).replaceAll(`__EVE_INIT_TYPES_NODE_VERSION__`,t.nodeTypesVersion).replaceAll(`__EVE_INIT_NODE_ENGINE__`,t.nodeEngine)}function formatEveDependencySpecifier(e){return/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z-.]+)?$/.test(e)?`^${e}`:e}const BASE_AGENT_TEMPLATE=`import { defineAgent } from "eve";
|
|
2
2
|
|
|
3
3
|
export default defineAgent({
|
|
4
4
|
model: "__EVE_INIT_MODEL__",
|
package/docs/README.md
CHANGED
|
@@ -40,7 +40,7 @@ Read in this order:
|
|
|
40
40
|
5. [TypeScript API](./reference/typescript-api.md)
|
|
41
41
|
6. [Context Control](./concepts/context-control.md)
|
|
42
42
|
7. [Skills](./skills.mdx)
|
|
43
|
-
8. [Tools](./tools.mdx)
|
|
43
|
+
8. [Tools](./tools/overview.mdx)
|
|
44
44
|
9. [Connections](./connections.mdx)
|
|
45
45
|
10. [Sandboxes](./sandbox.mdx)
|
|
46
46
|
11. [Channels](./channels/overview.mdx)
|
|
@@ -24,20 +24,20 @@ Compaction also preserves the framework's own tool state automatically. It reset
|
|
|
24
24
|
|
|
25
25
|
These ship with every agent, no imports. The harness shows the model the tool descriptors first, then executes only what the model actually calls; discovery never runs them. The shell and file tools (`bash`, `read_file`, `write_file`, `glob`, `grep`) live in the app runtime and proxy their work into the agent's single [sandbox](../sandbox); the rest run in the app runtime. The "Where it runs" column below names where each tool's effect lands.
|
|
26
26
|
|
|
27
|
-
| Tool | Does
|
|
28
|
-
| ------------------- |
|
|
29
|
-
| `bash` | Run a shell command.
|
|
30
|
-
| `read_file` | Read a text file with line-numbered output (enables read-before-write).
|
|
31
|
-
| `write_file` | Write a complete file; enforces read-before-write and stale-read detection.
|
|
32
|
-
| `glob` | Find files by glob pattern.
|
|
33
|
-
| `grep` | Search file contents by regex.
|
|
34
|
-
| `web_fetch` | Fetch a URL.
|
|
35
|
-
| `web_search` | Search the web (provider-managed; resolved from the model provider).
|
|
36
|
-
| `todo` | Maintain a durable per-session todo list.
|
|
37
|
-
| `ask_question` | Ask the user a clarifying question or a choice mid-turn and park until they answer. No `execute`; the model calls it with `{ prompt, options?, allowFreeform? }`. | App runtime |
|
|
38
|
-
| `agent` | Delegate a subtask to a copy of itself (shares the parent sandbox + tools, fresh history/state).
|
|
39
|
-
| `load_skill` | Pull an on-demand [skill](../skills)'s instructions into the current turn. Present only when the agent declares skills.
|
|
40
|
-
| `connection_search` | Discover tools across declared [connections](../connections); matched tools become directly callable. Present only when the agent declares connections.
|
|
27
|
+
| Tool | Does | Where it runs |
|
|
28
|
+
| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- |
|
|
29
|
+
| `bash` | Run a shell command. | Sandbox |
|
|
30
|
+
| `read_file` | Read a text file with line-numbered output (enables read-before-write). | Sandbox FS |
|
|
31
|
+
| `write_file` | Write a complete file; enforces read-before-write and stale-read detection. | Sandbox FS |
|
|
32
|
+
| `glob` | Find files by glob pattern. | Sandbox FS |
|
|
33
|
+
| `grep` | Search file contents by regex. | Sandbox FS |
|
|
34
|
+
| `web_fetch` | Fetch a URL. | App runtime |
|
|
35
|
+
| `web_search` | Search the web (provider-managed; resolved from the model provider). | Provider |
|
|
36
|
+
| `todo` | Maintain a durable per-session todo list. | App runtime |
|
|
37
|
+
| `ask_question` | Ask the user a clarifying question or a choice mid-turn and park until they answer. No `execute`; the model calls it with `{ prompt, options?, allowFreeform? }`. See [Human-in-the-loop](../tools/human-in-the-loop). | App runtime |
|
|
38
|
+
| `agent` | Delegate a subtask to a copy of itself (shares the parent sandbox + tools, fresh history/state). | App runtime |
|
|
39
|
+
| `load_skill` | Pull an on-demand [skill](../skills)'s instructions into the current turn. Present only when the agent declares skills. | App runtime |
|
|
40
|
+
| `connection_search` | Discover tools across declared [connections](../connections); matched tools become directly callable. Present only when the agent declares connections. | App runtime |
|
|
41
41
|
|
|
42
42
|
Notes:
|
|
43
43
|
|
|
@@ -34,33 +34,33 @@ curl http://127.0.0.1:3000/eve/v1/session/<sessionId>/stream
|
|
|
34
34
|
|
|
35
35
|
The stream is newline-delimited JSON (NDJSON), one event per line:
|
|
36
36
|
|
|
37
|
-
| Event | Meaning
|
|
38
|
-
| ------------------------- |
|
|
39
|
-
| `session.started` | A durable session was created.
|
|
40
|
-
| `turn.started` | A new turn began.
|
|
41
|
-
| `message.received` | An inbound user message was accepted.
|
|
42
|
-
| `step.started` | A model step began.
|
|
43
|
-
| `actions.requested` | The model requested tool calls.
|
|
44
|
-
| `action.result` | A tool call returned.
|
|
45
|
-
| `input.requested` | The run paused for human input (HITL approval or `ask_question`); carries `requests`.
|
|
46
|
-
| `subagent.called` | A subagent was delegated; carries `childSessionId` to attach to.
|
|
47
|
-
| `subagent.completed` | A delegated subagent finished.
|
|
48
|
-
| `reasoning.appended` | A reasoning delta (incremental, with cumulative text so far).
|
|
49
|
-
| `reasoning.completed` | The finalized reasoning block.
|
|
50
|
-
| `message.appended` | An assistant text delta (incremental, with cumulative text so far).
|
|
51
|
-
| `message.completed` | A finalized assistant text block.
|
|
52
|
-
| `result.completed` | The finalized structured result for a turn that requested an output schema; carries `result`.
|
|
53
|
-
| `compaction.requested` | Context-window compaction began; carries `modelId`, `sessionId`, `turnId`, `usageInputTokens`.
|
|
54
|
-
| `compaction.completed` | A compaction checkpoint was written to durable history.
|
|
55
|
-
| `authorization.required` | A connection needs OAuth; carries `name`, `description`, and an `authorization` challenge.
|
|
56
|
-
| `authorization.completed` | A connection's authorization resolved; carries `outcome`.
|
|
57
|
-
| `step.completed` | A model step finished; carries `finishReason` and usage.
|
|
58
|
-
| `step.failed` | A model step failed; carries `{ code, message, details? }`.
|
|
59
|
-
| `turn.completed` | The turn finished.
|
|
60
|
-
| `turn.failed` | The turn failed; carries `{ code, message, details? }`.
|
|
61
|
-
| `session.waiting` | The session parked, waiting for the next input (a message, an answer).
|
|
62
|
-
| `session.failed` | The session failed.
|
|
63
|
-
| `session.completed` | The session reached a terminal end.
|
|
37
|
+
| Event | Meaning |
|
|
38
|
+
| ------------------------- | ------------------------------------------------------------------------------------------------------------------- |
|
|
39
|
+
| `session.started` | A durable session was created. |
|
|
40
|
+
| `turn.started` | A new turn began. |
|
|
41
|
+
| `message.received` | An inbound user message was accepted. |
|
|
42
|
+
| `step.started` | A model step began. |
|
|
43
|
+
| `actions.requested` | The model requested tool calls. |
|
|
44
|
+
| `action.result` | A tool call returned. |
|
|
45
|
+
| `input.requested` | The run paused for human input ([HITL](../tools/human-in-the-loop) approval or `ask_question`); carries `requests`. |
|
|
46
|
+
| `subagent.called` | A subagent was delegated; carries `childSessionId` to attach to. |
|
|
47
|
+
| `subagent.completed` | A delegated subagent finished. |
|
|
48
|
+
| `reasoning.appended` | A reasoning delta (incremental, with cumulative text so far). |
|
|
49
|
+
| `reasoning.completed` | The finalized reasoning block. |
|
|
50
|
+
| `message.appended` | An assistant text delta (incremental, with cumulative text so far). |
|
|
51
|
+
| `message.completed` | A finalized assistant text block. |
|
|
52
|
+
| `result.completed` | The finalized structured result for a turn that requested an output schema; carries `result`. |
|
|
53
|
+
| `compaction.requested` | Context-window compaction began; carries `modelId`, `sessionId`, `turnId`, `usageInputTokens`. |
|
|
54
|
+
| `compaction.completed` | A compaction checkpoint was written to durable history. |
|
|
55
|
+
| `authorization.required` | A connection needs OAuth; carries `name`, `description`, and an `authorization` challenge. |
|
|
56
|
+
| `authorization.completed` | A connection's authorization resolved; carries `outcome`. |
|
|
57
|
+
| `step.completed` | A model step finished; carries `finishReason` and usage. |
|
|
58
|
+
| `step.failed` | A model step failed; carries `{ code, message, details? }`. |
|
|
59
|
+
| `turn.completed` | The turn finished. |
|
|
60
|
+
| `turn.failed` | The turn failed; carries `{ code, message, details? }`. |
|
|
61
|
+
| `session.waiting` | The session parked, waiting for the next input (a message, an answer). |
|
|
62
|
+
| `session.failed` | The session failed. |
|
|
63
|
+
| `session.completed` | The session reached a terminal end. |
|
|
64
64
|
|
|
65
65
|
`reasoning.appended` and `message.appended` stream deltas as they arrive, and each one carries both the new delta and the cumulative text for the current block. The finalized block shows up on `message.completed` and `reasoning.completed`, which is the compatibility path for clients that don't render incremental streaming.
|
|
66
66
|
|
package/docs/getting-started.mdx
CHANGED
|
@@ -5,7 +5,11 @@ description: "Install Eve, scaffold your first agent, give it a tool, and run it
|
|
|
5
5
|
|
|
6
6
|
Eve is a filesystem-first framework for durable agents. You write capabilities under `agent/`, and Eve runs the model loop, persists every session, and serves the agent over HTTP and platform channels. You'll scaffold an app, add a tool, run it locally, then create, stream, and continue a session over HTTP.
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
<Callout>
|
|
9
|
+
Eve is currently in beta and subject to the [Vercel beta
|
|
10
|
+
terms](https://vercel.com/docs/release-phases/public-beta-agreement); the framework, APIs,
|
|
11
|
+
documentation, and behavior may change before general availability.
|
|
12
|
+
</Callout>
|
|
9
13
|
|
|
10
14
|
## Prerequisites
|
|
11
15
|
|
|
@@ -100,7 +100,7 @@ Assistant text, reasoning, tool calls, and tool results stream into `data` as th
|
|
|
100
100
|
|
|
101
101
|
## Human-in-the-loop prompts
|
|
102
102
|
|
|
103
|
-
Tools opt into approval with `needsApproval
|
|
103
|
+
Tools opt into approval with `needsApproval`, and the model can also ask a question with `ask_question` — see [Human-in-the-loop](../../tools/human-in-the-loop) for the server-side model. Either way the stream emits an `input.requested` event, and the pending request rides on a `dynamic-tool` part of the latest message at `part.toolMetadata?.eve?.inputRequest`. Read it, then answer through the same session with `send()`:
|
|
104
104
|
|
|
105
105
|
```tsx
|
|
106
106
|
const request = agent.data.messages
|
|
@@ -7,7 +7,11 @@ Eve is a framework for building durable agents as ordinary files in a TypeScript
|
|
|
7
7
|
|
|
8
8
|
Instead of one large configuration object, each part of your agent gets a clear home. Instructions go in one file, tools in one folder, channels in another. Eve discovers that structure and turns it into an agent that runs locally, serves HTTP, connects to other platforms, and keeps working across many turns.
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
<Callout>
|
|
11
|
+
Eve is currently in beta and subject to the [Vercel beta
|
|
12
|
+
terms](https://vercel.com/docs/release-phases/public-beta-agreement); the framework, APIs,
|
|
13
|
+
documentation, and behavior may change before general availability.
|
|
14
|
+
</Callout>
|
|
11
15
|
|
|
12
16
|
## An Eve project at a glance
|
|
13
17
|
|
package/docs/meta.json
CHANGED
package/docs/reference/cli.md
CHANGED
|
@@ -5,8 +5,6 @@ description: "Reference for every eve CLI command: init, info, build, start, dev
|
|
|
5
5
|
|
|
6
6
|
The `eve` binary (`bin: eve`) runs from your app root, and every command first loads `.env`/`.env.local` from that root. Running `eve` with no command runs `eve dev`.
|
|
7
7
|
|
|
8
|
-
Eve is currently a preview and subject to the Vercel beta terms; the framework, APIs, documentation, and behavior may change before general availability.
|
|
9
|
-
|
|
10
8
|
## Commands
|
|
11
9
|
|
|
12
10
|
| Command | Description |
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: "
|
|
3
|
-
description: "
|
|
2
|
+
title: "Responsible Use"
|
|
3
|
+
description: "Deployer responsibility and safeguards to review before using Eve with sensitive, regulated, or production data."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
Eve is currently a preview and subject to the Vercel beta terms; the framework, APIs, documentation, and behavior may change before general availability.
|
|
7
|
-
|
|
8
6
|
As the deployer, it is your responsibility to ensure your agent complies with applicable laws.
|
|
9
7
|
|
|
10
8
|
You are responsible for configuring approval policies, tool restrictions, connection scopes, route/session authorization, sandbox controls, telemetry exports, and other safeguards appropriate for your use case.
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Human-in-the-loop"
|
|
3
|
+
description: "Pause a run for a person — gate a tool on approval or have the agent ask a question — and resume durably when they answer."
|
|
4
|
+
url: /human-in-the-loop
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Human-in-the-loop (HITL) is any point where the agent durably pauses and waits for a person. Two things trigger it, and both ride the same pause-and-resume protocol:
|
|
8
|
+
|
|
9
|
+
- **Approvals** — a tool requires a person to sign off before (or instead of) running. The agent decides to call the tool; a human decides whether it does.
|
|
10
|
+
- **Questions** — the agent itself asks the user a clarifying question or a choice mid-turn, and parks until they answer.
|
|
11
|
+
|
|
12
|
+
Either way the run parks at `session.waiting`, durably, for as long as it takes — seconds or days — and picks back up exactly where it left off once the answer arrives. Channels render the request for you.
|
|
13
|
+
|
|
14
|
+
## Approvals
|
|
15
|
+
|
|
16
|
+
Approval is a property of a [tool](./overview) that pauses for a person before it runs. Gate a tool with `needsApproval` and the helpers from `eve/tools/approval`:
|
|
17
|
+
|
|
18
|
+
```ts title="agent/tools/refund_charge.ts"
|
|
19
|
+
import { defineTool } from "eve/tools";
|
|
20
|
+
import { always } from "eve/tools/approval";
|
|
21
|
+
import { z } from "zod";
|
|
22
|
+
|
|
23
|
+
export default defineTool({
|
|
24
|
+
description: "Refund a charge.",
|
|
25
|
+
inputSchema: z.object({ chargeId: z.string(), amount: z.number() }),
|
|
26
|
+
needsApproval: always(), // or once() / never() / a predicate
|
|
27
|
+
async execute(input) {
|
|
28
|
+
return refund(input);
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
| Helper | Behavior |
|
|
34
|
+
| ---------- | ---------------------------------------------------------------------------------- |
|
|
35
|
+
| `never()` | Never require approval (the default when omitted). |
|
|
36
|
+
| `once()` | Require approval only the first time the tool runs in a session; auto-allow after. |
|
|
37
|
+
| `always()` | Require approval before every call. |
|
|
38
|
+
|
|
39
|
+
By default, omitted `needsApproval` behaves like `never()`, so tool calls may execute without human approval. Require human approval or other safeguards for sensitive, irreversible, regulated, financial, healthcare, employment, housing, legal, safety-impacting, user-impacting, or external side-effecting actions.
|
|
40
|
+
|
|
41
|
+
When the decision depends on the input, pass your own predicate instead of a helper. It receives `{ toolName, toolInput, approvedTools }` and returns a boolean. `toolInput` can be undefined, so guard the access. To require approval only when an amount crosses a threshold:
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
needsApproval: ({ toolInput }) => (toolInput?.amount ?? 0) > 1000,
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Gating a side effect on approval is also how you make non-idempotent work safe across replays: a charge or email that sits behind `always()` can't fire from a re-run step without a fresh human decision.
|
|
48
|
+
|
|
49
|
+
## Questions
|
|
50
|
+
|
|
51
|
+
The built-in `ask_question` tool lets the model pause and ask the user, rather than guessing. It has no `execute` — the model calls it with `{ prompt, options?, allowFreeform? }`:
|
|
52
|
+
|
|
53
|
+
- `prompt`: the question to put to the user.
|
|
54
|
+
- `options`: an optional list of choices to offer. Channels render these as buttons or a select menu.
|
|
55
|
+
- `allowFreeform`: whether the user may answer with free text instead of picking an option.
|
|
56
|
+
|
|
57
|
+
`ask_question` is part of the [default harness](../concepts/default-harness), so it is available without you defining anything. It produces the same `input.requested` pause as an approval, and resumes the same way.
|
|
58
|
+
|
|
59
|
+
## How pause and resume works
|
|
60
|
+
|
|
61
|
+
Approvals and questions share one protocol:
|
|
62
|
+
|
|
63
|
+
1. The model requests input (an approval, or an `ask_question`).
|
|
64
|
+
2. Eve emits an `input.requested` stream event carrying the pending requests.
|
|
65
|
+
3. The turn parks at `session.waiting`, durably, for as long as it takes.
|
|
66
|
+
4. The client answers with `inputResponses` (structured, keyed by `requestId`) or a normal follow-up `message`. A follow-up whose text matches an option label (case-insensitive) resolves automatically.
|
|
67
|
+
|
|
68
|
+
The run picks back up exactly where it parked. Because the pause is durable, nothing is held in memory while it waits — the process can restart and the parked turn survives.
|
|
69
|
+
|
|
70
|
+
See [Sessions, runs & streaming](../concepts/sessions-runs-and-streaming) for the full event and resume contract that this builds on.
|
|
71
|
+
|
|
72
|
+
## Answering from a client or channel
|
|
73
|
+
|
|
74
|
+
Channels turn requests into native UI: the Slack adapter renders approvals as buttons and questions as select menus, and writes the user's choice back as the answer. You get this for free on every [channel](../channels).
|
|
75
|
+
|
|
76
|
+
From your own frontend, read the pending request off the latest message and answer through the same session — see [Building a frontend](../guides/frontend/overview#human-in-the-loop-prompts) for the client-side reducer and `inputResponses` shape.
|
|
77
|
+
|
|
78
|
+
## What to read next
|
|
79
|
+
|
|
80
|
+
- [Tools](./overview): define the typed actions an approval gates
|
|
81
|
+
- [Default harness](../concepts/default-harness): the built-in tools, including `ask_question`
|
|
82
|
+
- [Sessions, runs & streaming](../concepts/sessions-runs-and-streaming): the event and resume contract behind the pause
|
|
83
|
+
- [Building a frontend](../guides/frontend/overview): render and answer requests from your own UI
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: "Tools"
|
|
3
3
|
description: "Define typed actions the agent can call, and gate sensitive ones on human approval."
|
|
4
|
+
url: /tools
|
|
4
5
|
---
|
|
5
6
|
|
|
6
|
-
A tool is a typed action the agent can call, such as hitting an API, running a query, or writing a file. The action stays in code you control. Tools run in your app runtime with full access to `process.env`, not in the [sandbox](
|
|
7
|
+
A tool is a typed action the agent can call, such as hitting an API, running a query, or writing a file. The action stays in code you control. Tools run in your app runtime with full access to `process.env`, not in the [sandbox](../sandbox).
|
|
7
8
|
|
|
8
9
|
## Define a tool
|
|
9
10
|
|
|
@@ -36,16 +37,16 @@ When a tool returns structured data, add an optional `outputSchema`. With Zod or
|
|
|
36
37
|
`execute` gets a `ctx` carrying the runtime accessors:
|
|
37
38
|
|
|
38
39
|
- `ctx.session`: session metadata, turn, auth, parent lineage.
|
|
39
|
-
- `ctx.getSandbox()`: the live [sandbox](
|
|
40
|
-
- `ctx.getSkill(id)`: read a packaged [skill](
|
|
40
|
+
- `ctx.getSandbox()`: the live [sandbox](../sandbox) handle.
|
|
41
|
+
- `ctx.getSkill(id)`: read a packaged [skill](../skills)'s metadata and files.
|
|
41
42
|
|
|
42
43
|
Running in the app runtime is what lets a tool import shared code from `lib/`, read `process.env`, and take part in Eve’s durable pause/resume model.
|
|
43
44
|
|
|
44
45
|
Eve never runs authored tools during discovery. The model sees descriptors first, and only what it actually calls gets executed. Completed steps never re-run; Eve replays the recorded result. A step interrupted mid-execution re-runs, so make non-idempotent side effects like charges or emails idempotent, or gate them with approval.
|
|
45
46
|
|
|
46
|
-
##
|
|
47
|
+
## Gate a tool on human approval
|
|
47
48
|
|
|
48
|
-
|
|
49
|
+
A tool can require a person to sign off before it runs. Set `needsApproval` with the helpers from `eve/tools/approval`:
|
|
49
50
|
|
|
50
51
|
```ts title="agent/tools/refund_charge.ts"
|
|
51
52
|
import { defineTool } from "eve/tools";
|
|
@@ -62,32 +63,7 @@ export default defineTool({
|
|
|
62
63
|
});
|
|
63
64
|
```
|
|
64
65
|
|
|
65
|
-
|
|
66
|
-
| ---------- | ---------------------------------------------------------------------------------- |
|
|
67
|
-
| `never()` | Never require approval (the default when omitted). |
|
|
68
|
-
| `once()` | Require approval only the first time the tool runs in a session; auto-allow after. |
|
|
69
|
-
| `always()` | Require approval before every call. |
|
|
70
|
-
|
|
71
|
-
By default, omitted `needsApproval` behaves like `never()`, so tool calls may execute without human approval. Require human approval or other safeguards for sensitive, irreversible, regulated, financial, healthcare, employment, housing, legal, safety-impacting, user-impacting, or external side-effecting actions.
|
|
72
|
-
|
|
73
|
-
When the decision depends on the input, pass your own predicate instead of a helper. It receives `{ toolName, toolInput, approvedTools }` and returns a boolean. `toolInput` can be undefined, so guard the access. To require approval only when an amount crosses a threshold:
|
|
74
|
-
|
|
75
|
-
```ts
|
|
76
|
-
needsApproval: ({ toolInput }) => (toolInput?.amount ?? 0) > 1000,
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
### How approval pause and resume works
|
|
80
|
-
|
|
81
|
-
Approvals and questions share one protocol:
|
|
82
|
-
|
|
83
|
-
1. The model requests input (an approval or an `ask_question`).
|
|
84
|
-
2. Eve emits an `input.requested` stream event carrying the pending requests.
|
|
85
|
-
3. The turn parks at `session.waiting`, durably, for as long as it takes.
|
|
86
|
-
4. The client answers with `inputResponses` (structured, keyed by `requestId`) or a normal follow-up `message`. A follow-up whose text matches an option label (case-insensitive) resolves automatically.
|
|
87
|
-
|
|
88
|
-
The run picks back up exactly where it parked, and channels render the request for you. The Slack adapter, for example, turns approvals into buttons and questions into select menus.
|
|
89
|
-
|
|
90
|
-
See [Sessions, runs & streaming](./concepts/sessions-runs-and-streaming) for the event and resume contract.
|
|
66
|
+
Approval is one half of Eve's [human-in-the-loop](./human-in-the-loop) model — the page covers the `always/once/never` helpers, input-dependent predicates, and how a gated call pauses and resumes durably.
|
|
91
67
|
|
|
92
68
|
## Shape what the model sees with `toModelOutput`
|
|
93
69
|
|
|
@@ -105,7 +81,8 @@ Do not return secrets, credentials, unnecessary personal data, or unbounded sens
|
|
|
105
81
|
|
|
106
82
|
## What to read next
|
|
107
83
|
|
|
108
|
-
- [
|
|
109
|
-
- [
|
|
110
|
-
- [
|
|
111
|
-
- [
|
|
84
|
+
- [Human-in-the-loop](./human-in-the-loop): gate a tool on approval, or have the agent ask a question
|
|
85
|
+
- [Skills](../skills): on-demand procedures the model loads when relevant
|
|
86
|
+
- [Default harness](../concepts/default-harness): the built-in tools and how to override or disable them
|
|
87
|
+
- [Dynamic capabilities](../guides/dynamic-capabilities): tools whose set is resolved per session with `defineDynamic`
|
|
88
|
+
- [Auth & route protection](../guides/auth-and-route-protection): authenticate a tool to an external service
|