eve 0.11.8 → 0.11.10

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.
Files changed (87) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/src/cli/banner.js +1 -1
  3. package/dist/src/cli/commands/init-git.d.ts +1 -1
  4. package/dist/src/cli/commands/init-git.js +1 -1
  5. package/dist/src/cli/commands/init.d.ts +1 -0
  6. package/dist/src/cli/commands/init.js +1 -1
  7. package/dist/src/cli/dev/tui/line-editor.d.ts +53 -19
  8. package/dist/src/cli/dev/tui/line-editor.js +6 -1
  9. package/dist/src/cli/dev/tui/live-region.d.ts +2 -0
  10. package/dist/src/cli/dev/tui/live-region.js +1 -1
  11. package/dist/src/cli/dev/tui/runner.d.ts +3 -0
  12. package/dist/src/cli/dev/tui/runner.js +1 -1
  13. package/dist/src/cli/dev/tui/setup-panel.js +3 -3
  14. package/dist/src/cli/dev/tui/setup-selection-input.js +2 -1
  15. package/dist/src/cli/dev/tui/stream-format.d.ts +21 -2
  16. package/dist/src/cli/dev/tui/stream-format.js +4 -2
  17. package/dist/src/cli/dev/tui/terminal-renderer.js +10 -10
  18. package/dist/src/cli/dev/tui/terminal-text.d.ts +11 -0
  19. package/dist/src/cli/dev/tui/terminal-text.js +1 -1
  20. package/dist/src/cli/dev/tui/theme.d.ts +2 -0
  21. package/dist/src/cli/dev/tui/theme.js +1 -1
  22. package/dist/src/cli/dev/tui/tui.d.ts +3 -0
  23. package/dist/src/cli/dev/tui/tui.js +1 -1
  24. package/dist/src/cli/format-elapsed.d.ts +2 -0
  25. package/dist/src/cli/format-elapsed.js +1 -0
  26. package/dist/src/cli/run.d.ts +4 -18
  27. package/dist/src/cli/run.js +2 -2
  28. package/dist/src/cli/ui/live-row.d.ts +19 -0
  29. package/dist/src/cli/ui/live-row.js +1 -0
  30. package/dist/src/compiler/manifest.d.ts +2 -2
  31. package/dist/src/compiler/manifest.js +1 -1
  32. package/dist/src/compiler/normalize-agent-config.js +1 -1
  33. package/dist/src/compiler/normalize-manifest.js +1 -1
  34. package/dist/src/compiler/normalize-subagent.d.ts +1 -0
  35. package/dist/src/compiler/normalize-subagent.js +1 -1
  36. package/dist/src/context/dynamic-skill-lifecycle.js +1 -1
  37. package/dist/src/evals/assertions/run.d.ts +7 -1
  38. package/dist/src/evals/assertions/run.js +2 -2
  39. package/dist/src/evals/context.js +1 -1
  40. package/dist/src/evals/index.d.ts +1 -1
  41. package/dist/src/evals/match.d.ts +6 -0
  42. package/dist/src/evals/types.d.ts +3 -1
  43. package/dist/src/harness/code-mode.js +1 -1
  44. package/dist/src/internal/application/compiled-artifacts.d.ts +7 -0
  45. package/dist/src/internal/application/compiled-artifacts.js +4 -4
  46. package/dist/src/internal/application/package.js +1 -1
  47. package/dist/src/internal/authored-definition/core.js +1 -1
  48. package/dist/src/internal/dev-boot-progress.d.ts +13 -0
  49. package/dist/src/internal/dev-boot-progress.js +1 -0
  50. package/dist/src/internal/logging.d.ts +9 -0
  51. package/dist/src/internal/logging.js +1 -1
  52. package/dist/src/internal/nitro/host/start-development-server.d.ts +2 -5
  53. package/dist/src/internal/nitro/host/start-development-server.js +2 -2
  54. package/dist/src/internal/nitro/host/types.d.ts +6 -0
  55. package/dist/src/internal/nitro/host.d.ts +1 -1
  56. package/dist/src/internal/nitro/routes/agent-info/build-agent-info-response-from-manifest.js +1 -1
  57. package/dist/src/internal/nitro/routes/agent-info/build-agent-info-response.js +1 -1
  58. package/dist/src/internal/workflow/configure-world.d.ts +12 -0
  59. package/dist/src/internal/workflow/configure-world.js +1 -0
  60. package/dist/src/public/definitions/agent.d.ts +1 -1
  61. package/dist/src/public/index.d.ts +1 -1
  62. package/dist/src/runtime/agent/mock-model-adapter.js +2 -2
  63. package/dist/src/runtime/framework-tools/skill.js +1 -1
  64. package/dist/src/runtime/resolve-agent-graph.js +1 -1
  65. package/dist/src/runtime/skills/sandbox-access.d.ts +3 -2
  66. package/dist/src/runtime/skills/sandbox-access.js +1 -1
  67. package/dist/src/setup/cli/select-state.js +1 -1
  68. package/dist/src/setup/primitives/pm/npm.js +1 -1
  69. package/dist/src/setup/primitives/pm/run.js +1 -1
  70. package/dist/src/setup/primitives/pm/types.d.ts +2 -0
  71. package/dist/src/setup/scaffold/create/project.js +1 -1
  72. package/dist/src/setup/slack-connect-create.js +1 -1
  73. package/dist/src/setup/slackbot.js +1 -1
  74. package/dist/src/shared/agent-definition.d.ts +23 -0
  75. package/dist/src/shared/text-boundaries.d.ts +19 -0
  76. package/dist/src/shared/text-boundaries.js +1 -0
  77. package/docs/agent-config.md +33 -6
  78. package/docs/concepts/default-harness.md +14 -14
  79. package/docs/concepts/execution-model-and-durability.md +16 -1
  80. package/docs/concepts/sessions-runs-and-streaming.md +27 -27
  81. package/docs/evals/assertions.mdx +1 -0
  82. package/docs/guides/deployment.md +17 -2
  83. package/docs/guides/dev-tui.md +10 -7
  84. package/docs/guides/dynamic-capabilities.md +2 -0
  85. package/docs/guides/frontend/overview.mdx +1 -1
  86. package/docs/reference/typescript-api.md +2 -2
  87. package/package.json +1 -1
@@ -11,9 +11,10 @@ export declare function assertSafeSkillId(id: string): asserts id is string;
11
11
  * Returns the SKILL.md body with any YAML frontmatter stripped, so the
12
12
  * model receives plain markdown as the tool result. Throws when the id
13
13
  * is unsafe or the file does not exist; the AI SDK forwards the error
14
- * to the model as a tool-error result.
14
+ * to the model as a tool-error result. `availableNames`, when given, is
15
+ * listed in the not-found error so the model can correct a wrong id.
15
16
  */
16
- export declare function loadSkillFromSandbox(access: SandboxAccess, id: string): Promise<string>;
17
+ export declare function loadSkillFromSandbox(access: SandboxAccess, id: string, availableNames?: readonly string[]): Promise<string>;
17
18
  /**
18
19
  * Creates the public runtime skill handle. Existence is checked lazily by
19
20
  * each file read against the sandbox.
@@ -1 +1 @@
1
- import{WORKSPACE_ROOT}from"#runtime/workspace/types.js";const FRONTMATTER_PATTERN=/^---\r?\n[\s\S]*?\r?\n---\r?\n?/;function assertSafeSkillId(e){if(e.length===0||e.trim()!==e||e.startsWith(`.`)||e.includes(`/`)||e.includes(`\\`)||e.includes(`..`)||/^[A-Za-z]:/.test(e))throw Error(`Expected skill id to be a non-empty safe path segment without whitespace, separators, "." prefix, or "..".`)}async function loadSkillFromSandbox(e,n){assertSafeSkillId(n);let r=await requireSandboxSession(e),i=skillFilePath(n,`SKILL.md`),a=await r.readTextFile({path:i});if(a===null)throw Error(`No skill named "${n}" at ${i}.`);return a.replace(FRONTMATTER_PATTERN,``)}function createSandboxSkillHandle(e,t){return assertSafeSkillId(t),{name:t,file(n){assertSafeSkillRelativePath(n);let r=skillFilePath(t,n);return{async bytes(){let t=await(await requireSandboxSession(e)).readBinaryFile({path:r});if(t===null)throw Error(`Skill file not found: ${r}`);return t},async text(){let t=await(await requireSandboxSession(e)).readTextFile({path:r});if(t===null)throw Error(`Skill file not found: ${r}`);return t}}}}}function assertSafeSkillRelativePath(e){if(e.length===0||e.startsWith(`/`)||e.includes(`\\`)||e.split(`/`).some(e=>e.length===0||e===`.`||e===`..`))throw Error(`Expected skill file path to be a relative path inside the skill directory.`)}async function requireSandboxSession(e){let t=await e.get();if(t===null)throw Error(`The sandbox is not available in the current authored runtime context.`);return t}function skillFilePath(t,n){return`${WORKSPACE_ROOT}/skills/${t}/${n}`}export{assertSafeSkillId,createSandboxSkillHandle,loadSkillFromSandbox};
1
+ import{WORKSPACE_ROOT}from"#runtime/workspace/types.js";const FRONTMATTER_PATTERN=/^---\r?\n[\s\S]*?\r?\n---\r?\n?/;function assertSafeSkillId(e){if(e.length===0||e.trim()!==e||e.startsWith(`.`)||e.includes(`/`)||e.includes(`\\`)||e.includes(`..`)||/^[A-Za-z]:/.test(e))throw Error(`Expected skill id to be a non-empty safe path segment without whitespace, separators, "." prefix, or "..".`)}async function loadSkillFromSandbox(e,n,r=[]){assertSafeSkillId(n);let i=await requireSandboxSession(e),a=skillFilePath(n,`SKILL.md`),o=await i.readTextFile({path:a});if(o===null){let e=r.length>0?` Available skills: ${r.join(`, `)}.`:``;throw Error(`No skill named "${n}" at ${a}.${e}`)}return o.replace(FRONTMATTER_PATTERN,``)}function createSandboxSkillHandle(e,t){return assertSafeSkillId(t),{name:t,file(n){assertSafeSkillRelativePath(n);let r=skillFilePath(t,n);return{async bytes(){let t=await(await requireSandboxSession(e)).readBinaryFile({path:r});if(t===null)throw Error(`Skill file not found: ${r}`);return t},async text(){let t=await(await requireSandboxSession(e)).readTextFile({path:r});if(t===null)throw Error(`Skill file not found: ${r}`);return t}}}}}function assertSafeSkillRelativePath(e){if(e.length===0||e.startsWith(`/`)||e.includes(`\\`)||e.split(`/`).some(e=>e.length===0||e===`.`||e===`..`))throw Error(`Expected skill file path to be a relative path inside the skill directory.`)}async function requireSandboxSession(e){let t=await e.get();if(t===null)throw Error(`The sandbox is not available in the current authored runtime context.`);return t}function skillFilePath(t,n){return`${WORKSPACE_ROOT}/skills/${t}/${n}`}export{assertSafeSkillId,createSandboxSkillHandle,loadSkillFromSandbox};
@@ -1 +1 @@
1
- function submitRowIndex(e){return e.length}function filterOptions(e,t){let n=t.trim().toLowerCase();return n===``?[...e]:e.filter(e=>e.label.toLowerCase().includes(n)||e.value.toLowerCase().includes(n)||(e.hint?.toLowerCase().includes(n)??!1)||(e.focusHint?.toLowerCase().includes(n)??!1))}function isFocusable(e){return!e.disabled&&!e.locked}function isActionable(e){return isFocusable(e)&&!e.completed}function firstFocusableIndex(e,t){let n=e.findIndex(isFocusable);return n>=0?n:t?submitRowIndex(e):0}function stepCursor(e,t,n,r){let i=e.length+ +!!r;if(i===0)return t;let a=t;for(let t=0;t<i;t+=1){if(a=(a+n+i)%i,r&&a===submitRowIndex(e))return a;let t=e[a];if(t&&isFocusable(t))return a}return t}function reduceSelect(e,t,n){let r=n.submitRow===!0;switch(t.type){case`char`:{let i=e.filter+t.char;return{...e,filter:i,cursor:firstFocusableIndex(filterOptions(n.options,i),r)}}case`backspace`:{if(e.filter.length===0)return e;let t=e.filter.slice(0,-1);return{...e,filter:t,cursor:firstFocusableIndex(filterOptions(n.options,t),r)}}case`up`:case`down`:{let i=filterOptions(n.options,e.filter),a=t.type===`up`?-1:1,o=stepCursor(i,e.cursor,a,r);return o===e.cursor?e:{...e,cursor:o}}case`toggle`:{let t=filterOptions(n.options,e.filter)[e.cursor];if(t===void 0||!isActionable(t))return e;let r=new Set(e.selected);return r.has(t.value)?r.delete(t.value):r.add(t.value),{...e,selected:r}}}}function initialSelectState(e){let t=e.filter??``,n=filterOptions(e.options,t),r=firstFocusableIndex(n,e.submitRow===!0);if(e.defaultValue!==void 0){let t=n.findIndex(t=>isFocusable(t)&&t.value===e.defaultValue);t>=0&&(r=t)}let i=e.options.filter(e=>e.locked).map(e=>e.value);return{filter:t,cursor:r,selected:new Set([...e.initialValues??[],...i])}}function selectValueAtCursor(e,t){let n=e[t];return n&&isActionable(n)?n.value:void 0}function orderedSelection(e,t){return e.filter(e=>t.has(e.value)).map(e=>e.value)}export{filterOptions,initialSelectState,orderedSelection,reduceSelect,selectValueAtCursor,submitRowIndex};
1
+ import{previousGraphemeBoundary}from"#shared/text-boundaries.js";function submitRowIndex(e){return e.length}function filterOptions(e,t){let n=t.trim().toLowerCase();return n===``?[...e]:e.filter(e=>e.label.toLowerCase().includes(n)||e.value.toLowerCase().includes(n)||(e.hint?.toLowerCase().includes(n)??!1)||(e.focusHint?.toLowerCase().includes(n)??!1))}function isFocusable(e){return!e.disabled&&!e.locked}function isActionable(e){return isFocusable(e)&&!e.completed}function firstFocusableIndex(e,t){let n=e.findIndex(isFocusable);return n>=0?n:t?submitRowIndex(e):0}function stepCursor(e,t,n,r){let i=e.length+ +!!r;if(i===0)return t;let a=t;for(let t=0;t<i;t+=1){if(a=(a+n+i)%i,r&&a===submitRowIndex(e))return a;let t=e[a];if(t&&isFocusable(t))return a}return t}function reduceSelect(t,n,r){let i=r.submitRow===!0;switch(n.type){case`char`:{let e=t.filter+n.char;return{...t,filter:e,cursor:firstFocusableIndex(filterOptions(r.options,e),i)}}case`backspace`:{if(t.filter.length===0)return t;let n=t.filter.slice(0,previousGraphemeBoundary(t.filter,t.filter.length));return{...t,filter:n,cursor:firstFocusableIndex(filterOptions(r.options,n),i)}}case`up`:case`down`:{let e=filterOptions(r.options,t.filter),a=n.type===`up`?-1:1,o=stepCursor(e,t.cursor,a,i);return o===t.cursor?t:{...t,cursor:o}}case`toggle`:{let e=filterOptions(r.options,t.filter)[t.cursor];if(e===void 0||!isActionable(e))return t;let n=new Set(t.selected);return n.has(e.value)?n.delete(e.value):n.add(e.value),{...t,selected:n}}}}function initialSelectState(e){let t=e.filter??``,n=filterOptions(e.options,t),r=firstFocusableIndex(n,e.submitRow===!0);if(e.defaultValue!==void 0){let t=n.findIndex(t=>isFocusable(t)&&t.value===e.defaultValue);t>=0&&(r=t)}let i=e.options.filter(e=>e.locked).map(e=>e.value);return{filter:t,cursor:r,selected:new Set([...e.initialValues??[],...i])}}function selectValueAtCursor(e,t){let n=e[t];return n&&isActionable(n)?n.value:void 0}function orderedSelection(e,t){return e.filter(e=>t.has(e.value)).map(e=>e.value)}export{filterOptions,initialSelectState,orderedSelection,reduceSelect,selectValueAtCursor,submitRowIndex};
@@ -1 +1 @@
1
- import{applyNoProjectConfiguration,resolveStandardInvocation}from"./shared.js";const npmPackageManager={kind:`npm`,scaffoldFiles:{},applyProjectConfiguration:applyNoProjectConfiguration,devArguments:()=>[`exec`,`--`,`eve`,`dev`],installArguments:e=>[`install`,...e.bypassMinimumReleaseAge===!0?[`--min-release-age=0`]:[]],prepareArguments:(e,t)=>t,resolveInvocation:e=>resolveStandardInvocation(`npm`,e)};export{npmPackageManager};
1
+ import{applyNoProjectConfiguration,resolveStandardInvocation}from"./shared.js";const npmPackageManager={kind:`npm`,scaffoldFiles:{},applyProjectConfiguration:applyNoProjectConfiguration,devArguments:()=>[`exec`,`--`,`eve`,`dev`],installArguments:e=>[`install`,...e.bypassMinimumReleaseAge===!0?[`--min-release-age=0`]:[],...e.progressDetails===!0?[`--loglevel=silly`]:[]],prepareArguments:(e,t)=>t,resolveInvocation:e=>resolveStandardInvocation(`npm`,e)};export{npmPackageManager};
@@ -1 +1 @@
1
- import{createProcessOutputBuffer}from"../process-output.js";import{armProcessAbort}from"../process-abort.js";import{PNPM_WORKSPACE_MEMBERSHIP_ARGUMENTS,hasAncestorPnpmWorkspace,pnpmWorkspaceClaimsProject}from"./pnpm.js";import{getPackageManagerStrategy}from"./index.js";import{spawn}from"node:child_process";function stdioForRun(e){return e.onOutput?[e.nonInteractive?`ignore`:`inherit`,`pipe`,`pipe`]:e.nonInteractive?[`ignore`,`inherit`,`inherit`]:`inherit`}function spawnPackageManager(n,r,i,s={}){return s.signal?.aborted===!0?Promise.resolve(!1):new Promise(c=>{let l=getPackageManagerStrategy(n),u=l.prepareArguments(r,i),d=l.resolveInvocation(u),f=s.onOutput&&createProcessOutputBuffer(s.onOutput),p=spawn(d.command,[...d.args],{cwd:r,stdio:stdioForRun(s),shell:d.shell,signal:s.signal}),m=armProcessAbort(p,s.signal);p.stdout?.on(`data`,e=>f?.write(`stdout`,e)),p.stderr?.on(`data`,e=>f?.write(`stderr`,e));let h=!1;function settle(e){h||(h=!0,f?.flush(),c(e))}function reportFailure(e){s.onOutput?s.onOutput({stream:`stderr`,text:e}):process.stderr.write(`\n${e}\n`)}p.on(`error`,e=>{s.signal?.aborted===!0||e.name===`AbortError`||(e.code===`ENOENT`?(m(),reportFailure(`${n} not found. Install it before running this step.`),settle(!1)):(m(),reportFailure(`${n} ${i.join(` `)} failed: ${e.message}`),settle(!1)))}),p.on(`close`,e=>{m(),settle(s.signal?.aborted===!0?!1:e===0)})})}function capturePackageManager(n,r,i,s){return s.signal?.aborted===!0?Promise.resolve({ok:!1,stdout:``}):new Promise(c=>{let l=getPackageManagerStrategy(n),u=l.prepareArguments(r,i),d=l.resolveInvocation(u),f=spawn(d.command,[...d.args],{cwd:r,stdio:[`ignore`,`pipe`,`pipe`],shell:d.shell,signal:s.signal}),p=armProcessAbort(f,s.signal),m=[],h=s.onOutput&&createProcessOutputBuffer(s.onOutput);f.stdout?.on(`data`,e=>m.push(e)),f.stderr?.on(`data`,e=>h?.write(`stderr`,e));let g=!1;function settle(e){g||(g=!0,h?.flush(),c({ok:e,stdout:Buffer.concat(m).toString(`utf8`)}))}function reportFailure(e){s.onOutput?s.onOutput({stream:`stderr`,text:e}):process.stderr.write(`\n${e}\n`)}f.on(`error`,e=>{s.signal?.aborted===!0||e.name===`AbortError`||(p(),reportFailure(e.code===`ENOENT`?`${n} not found. Install it before running this step.`:`${n} ${i.join(` `)} failed: ${e.message}`),settle(!1))}),f.on(`close`,e=>{p(),!g&&e!==0&&s.signal?.aborted!==!0&&reportFailure(`${n} ${i.join(` `)} exited with code ${e??`unknown`}.`),settle(s.signal?.aborted===!0?!1:e===0)})})}async function runPackageManagerInstall(e,t,o={}){let s=getPackageManagerStrategy(e),c=o;if(e===`pnpm`&&o.ignoreWorkspace!==!0&&hasAncestorPnpmWorkspace(t)){let r=await capturePackageManager(e,t,PNPM_WORKSPACE_MEMBERSHIP_ARGUMENTS,o);if(!r.ok)return!1;let a=pnpmWorkspaceClaimsProject(r.stdout,t);if(a===void 0)return o.onOutput?.({stream:`stderr`,text:`Could not determine whether the ancestor pnpm workspace includes this project.`}),!1;a||(c={...o,ignoreWorkspace:!0})}return spawnPackageManager(e,t,s.installArguments(c),o)}function eveDevArguments(e){return getPackageManagerStrategy(e).devArguments()}function spawnPnpm(e,t,n={}){return spawnPackageManager(`pnpm`,e,t,n)}function runPnpmInstall(e,t={}){return runPackageManagerInstall(`pnpm`,e,t)}export{eveDevArguments,runPackageManagerInstall,runPnpmInstall,spawnPackageManager,spawnPnpm};
1
+ import{createProcessOutputBuffer}from"../process-output.js";import{armProcessAbort}from"../process-abort.js";import{PNPM_WORKSPACE_MEMBERSHIP_ARGUMENTS,hasAncestorPnpmWorkspace,pnpmWorkspaceClaimsProject}from"./pnpm.js";import{getPackageManagerStrategy}from"./index.js";import{spawn}from"node:child_process";function stdioForRun(e){return e.onOutput?[e.nonInteractive?`ignore`:`inherit`,`pipe`,`pipe`]:e.nonInteractive?[`ignore`,`inherit`,`inherit`]:`inherit`}function spawnPackageManager(n,r,i,s={}){return s.signal?.aborted===!0?Promise.resolve(!1):new Promise(c=>{let l=getPackageManagerStrategy(n),u=l.prepareArguments(r,i),d=l.resolveInvocation(u),f=s.onOutput&&createProcessOutputBuffer(s.onOutput),p=spawn(d.command,[...d.args],{cwd:r,stdio:stdioForRun(s),shell:d.shell,signal:s.signal}),m=armProcessAbort(p,s.signal);p.stdout?.on(`data`,e=>f?.write(`stdout`,e)),p.stderr?.on(`data`,e=>f?.write(`stderr`,e));let h=!1;function settle(e){h||(h=!0,f?.flush(),c(e))}function reportFailure(e){s.onOutput?s.onOutput({stream:`stderr`,text:e}):process.stderr.write(`\n${e}\n`)}p.on(`error`,e=>{s.signal?.aborted===!0||e.name===`AbortError`||(e.code===`ENOENT`?(m(),reportFailure(`${n} not found. Install it before running this step.`),settle(!1)):(m(),reportFailure(`${n} ${i.join(` `)} failed: ${e.message}`),settle(!1)))}),p.on(`close`,e=>{m(),settle(s.signal?.aborted===!0?!1:e===0)})})}function capturePackageManager(n,r,i,s){return s.signal?.aborted===!0?Promise.resolve({ok:!1,stdout:``}):new Promise(c=>{let l=getPackageManagerStrategy(n),u=l.prepareArguments(r,i),d=l.resolveInvocation(u),f=spawn(d.command,[...d.args],{cwd:r,stdio:[`ignore`,`pipe`,`pipe`],shell:d.shell,signal:s.signal}),p=armProcessAbort(f,s.signal),m=[],h=s.onOutput&&createProcessOutputBuffer(s.onOutput);f.stdout?.on(`data`,e=>m.push(e)),f.stderr?.on(`data`,e=>h?.write(`stderr`,e));let g=!1;function settle(e){g||(g=!0,h?.flush(),c({ok:e,stdout:Buffer.concat(m).toString(`utf8`)}))}function reportFailure(e){s.onOutput?s.onOutput({stream:`stderr`,text:e}):process.stderr.write(`\n${e}\n`)}function replayCapturedStdout(){let e=Buffer.concat(m);e.length!==0&&(h?(h.write(`stdout`,e),h.flush()):process.stdout.write(e))}f.on(`error`,e=>{s.signal?.aborted===!0||e.name===`AbortError`||(p(),replayCapturedStdout(),reportFailure(e.code===`ENOENT`?`${n} not found. Install it before running this step.`:`${n} ${i.join(` `)} failed: ${e.message}`),settle(!1))}),f.on(`close`,e=>{p(),!g&&e!==0&&s.signal?.aborted!==!0&&(replayCapturedStdout(),reportFailure(`${n} ${i.join(` `)} exited with code ${e??`unknown`}.`)),settle(s.signal?.aborted===!0?!1:e===0)})})}async function runPackageManagerInstall(e,t,o={}){let s=getPackageManagerStrategy(e),c=o;if(e===`pnpm`&&o.ignoreWorkspace!==!0&&hasAncestorPnpmWorkspace(t)){let r=await capturePackageManager(e,t,PNPM_WORKSPACE_MEMBERSHIP_ARGUMENTS,o);if(!r.ok)return!1;let a=pnpmWorkspaceClaimsProject(r.stdout,t);if(a===void 0)return o.onOutput?.({stream:`stderr`,text:`Could not determine whether the ancestor pnpm workspace includes this project.`}),!1;a||(c={...o,ignoreWorkspace:!0})}return spawnPackageManager(e,t,s.installArguments(c),o)}function eveDevArguments(e){return getPackageManagerStrategy(e).devArguments()}function spawnPnpm(e,t,n={}){return spawnPackageManager(`pnpm`,e,t,n)}function runPnpmInstall(e,t={}){return runPackageManagerInstall(`pnpm`,e,t)}export{eveDevArguments,runPackageManagerInstall,runPnpmInstall,spawnPackageManager,spawnPnpm};
@@ -15,6 +15,8 @@ export interface PackageManagerInstallOptions {
15
15
  readonly bypassMinimumReleaseAge?: boolean;
16
16
  /** Resolves the project standalone even when an ancestor workspace exists. */
17
17
  readonly ignoreWorkspace?: boolean;
18
+ /** Requests verbose package-manager output for a live progress display. */
19
+ readonly progressDetails?: boolean;
18
20
  }
19
21
  /**
20
22
  * Package-manager-specific command and generated-project behavior.
@@ -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.8`,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_TYPESCRIPT_VERSION__`,t.typescriptPackageVersion).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";
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.10`,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_TYPESCRIPT_VERSION__`,t.typescriptPackageVersion).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__",
@@ -1 +1 @@
1
- import{parseCreatedSlackConnector}from"./slack-connect.js";import{CONNECT_LOOKUP_TIMEOUT_MS,fetchSlackConnectorDetails}from"./slack-connect-lifecycle.js";import{setTimeout}from"node:timers/promises";import{performance}from"node:perf_hooks";const CREATED_CONNECTOR_PROGRESS=/\bConnector created:\s*(scl_[A-Za-z0-9]+)\b/,realDelay=(e,t)=>setTimeout(e,void 0,{signal:t}),monotonicNow=()=>performance.now();async function pollCreatedSlackConnector(e,r,i,a,o,s){let c=e.delay??realDelay,l=e.now??monotonicNow,u=l()+CONNECT_LOOKUP_TIMEOUT_MS;for(;;){s.throwIfAborted();let t=u-l();if(t<=0)return;let d=await fetchSlackConnectorDetails({deps:e,projectRoot:r,connectorId:i,orgId:a,onOutput:o,timeoutMs:Math.min(1e4,t),signal:s});if(s.throwIfAborted(),d.state===`found`)return d.details.ref;let f=u-l();if(f<=0)return;await c(Math.min(3e3,f),s)}}async function createSlackConnector(t){let n=new AbortController,r=new AbortController,i=new AbortController,o=t.signal?AbortSignal.any([t.signal,n.signal]):n.signal,s=t.signal?AbortSignal.any([t.signal,r.signal]):r.signal,c=t.signal?AbortSignal.any([t.signal,i.signal]):i.signal,l=!1,reportCreated=e=>{l||(l=!0,t.onCreated(e))},u,d,f=new Promise((e,t)=>{u=e,d=t}),p,m,startWorkspaceLookup=e=>{m===void 0&&(m=t.waitForWorkspace(e,c).catch(e=>{if(!(i.signal.aborted&&t.signal?.aborted!==!0))throw e}),m.then(t=>{t!==void 0&&u({ref:e,workspace:t})},d))},createOutput=e=>{t.onOutput(e);let n=e.text.match(CREATED_CONNECTOR_PROGRESS)?.[1];n===void 0||p!==void 0||(p=pollCreatedSlackConnector(t.deps,t.projectRoot,n,t.orgId,t.onOutput,s).catch(e=>{if(!(r.signal.aborted&&t.signal?.aborted!==!0))throw e}),p.then(e=>{e!==void 0&&(reportCreated(e),startWorkspaceLookup(e))},d))},h=t.phase(`Waiting for Slack setup to finish...`,()=>t.deps.runVercelCaptureStdout([`connect`,`create`,`slack`,`--triggers`,`--name`,t.slug,`-F`,`json`],{cwd:t.projectRoot,nonInteractive:!0,onOutput:createOutput,timeoutMs:6e5,signal:o})),g;try{g=await Promise.race([h.then(e=>({via:`cli`,result:e})),f.then(e=>({via:`workspace`,...e}))])}catch(e){throw r.abort(),i.abort(),await Promise.allSettled([p,m].filter(e=>e!==void 0)),e}if(g.via===`workspace`)return n.abort(),await h,t.signal?.throwIfAborted(),{state:`created`,via:`workspace`,ref:g.ref,workspace:g.workspace};if(!g.result.ok)return p!==void 0&&await p,i.abort(),m!==void 0&&await m,t.signal?.throwIfAborted(),{state:`failed`};let _=parseCreatedSlackConnector(g.result.stdout);if(_!==void 0)return reportCreated(_),r.abort(),i.abort(),p!==void 0&&await p,m!==void 0&&await m,t.signal?.throwIfAborted(),{state:`created`,via:`cli`,ref:_};let v=p===void 0?void 0:await p;return i.abort(),m!==void 0&&await m,t.signal?.throwIfAborted(),v===void 0?{state:`unresolved`}:{state:`created`,via:`cli`,ref:v}}export{createSlackConnector};
1
+ import{parseCreatedSlackConnector}from"./slack-connect.js";import{CONNECT_LOOKUP_TIMEOUT_MS,fetchSlackConnectorDetails}from"./slack-connect-lifecycle.js";import{performance}from"node:perf_hooks";import{setTimeout}from"node:timers/promises";const CREATED_CONNECTOR_PROGRESS=/\bConnector created:\s*(scl_[A-Za-z0-9]+)\b/,realDelay=(e,t)=>setTimeout(e,void 0,{signal:t}),monotonicNow=()=>performance.now();async function pollCreatedSlackConnector(e,r,i,a,o,s){let c=e.delay??realDelay,l=e.now??monotonicNow,u=l()+CONNECT_LOOKUP_TIMEOUT_MS;for(;;){s.throwIfAborted();let t=u-l();if(t<=0)return;let d=await fetchSlackConnectorDetails({deps:e,projectRoot:r,connectorId:i,orgId:a,onOutput:o,timeoutMs:Math.min(1e4,t),signal:s});if(s.throwIfAborted(),d.state===`found`)return d.details.ref;let f=u-l();if(f<=0)return;await c(Math.min(3e3,f),s)}}async function createSlackConnector(t){let n=new AbortController,r=new AbortController,i=new AbortController,o=t.signal?AbortSignal.any([t.signal,n.signal]):n.signal,s=t.signal?AbortSignal.any([t.signal,r.signal]):r.signal,c=t.signal?AbortSignal.any([t.signal,i.signal]):i.signal,l=!1,reportCreated=e=>{l||(l=!0,t.onCreated(e))},u,d,f=new Promise((e,t)=>{u=e,d=t}),p,m,startWorkspaceLookup=e=>{m===void 0&&(m=t.waitForWorkspace(e,c).catch(e=>{if(!(i.signal.aborted&&t.signal?.aborted!==!0))throw e}),m.then(t=>{t!==void 0&&u({ref:e,workspace:t})},d))},createOutput=e=>{t.onOutput(e);let n=e.text.match(CREATED_CONNECTOR_PROGRESS)?.[1];n===void 0||p!==void 0||(p=pollCreatedSlackConnector(t.deps,t.projectRoot,n,t.orgId,t.onOutput,s).catch(e=>{if(!(r.signal.aborted&&t.signal?.aborted!==!0))throw e}),p.then(e=>{e!==void 0&&(reportCreated(e),startWorkspaceLookup(e))},d))},h=t.phase(`Waiting for Slack setup to finish...`,()=>t.deps.runVercelCaptureStdout([`connect`,`create`,`slack`,`--triggers`,`--name`,t.slug,`-F`,`json`],{cwd:t.projectRoot,nonInteractive:!0,onOutput:createOutput,timeoutMs:6e5,signal:o})),g;try{g=await Promise.race([h.then(e=>({via:`cli`,result:e})),f.then(e=>({via:`workspace`,...e}))])}catch(e){throw r.abort(),i.abort(),await Promise.allSettled([p,m].filter(e=>e!==void 0)),e}if(g.via===`workspace`)return n.abort(),await h,t.signal?.throwIfAborted(),{state:`created`,via:`workspace`,ref:g.ref,workspace:g.workspace};if(!g.result.ok)return p!==void 0&&await p,i.abort(),m!==void 0&&await m,t.signal?.throwIfAborted(),{state:`failed`};let _=parseCreatedSlackConnector(g.result.stdout);if(_!==void 0)return reportCreated(_),r.abort(),i.abort(),p!==void 0&&await p,m!==void 0&&await m,t.signal?.throwIfAborted(),{state:`created`,via:`cli`,ref:_};let v=p===void 0?void 0:await p;return i.abort(),m!==void 0&&await m,t.signal?.throwIfAborted(),v===void 0?{state:`unresolved`}:{state:`created`,via:`cli`,ref:v}}export{createSlackConnector};
@@ -1 +1 @@
1
- import{parseCreatedSlackConnector,parseSlackConnectorDetails,pickSlackConnector}from"./slack-connect.js";import{CONNECT_LOOKUP_TIMEOUT_MS,attachSlackConnector,cleanupCreatedAttempt,fetchSlackWorkspace,findSlackConnector,listSlackConnectors,readProjectLink}from"./slack-connect-lifecycle.js";import{createSlackConnector}from"./slack-connect-create.js";import{join}from"node:path";import{SLACK_CHANNEL_DEFAULT_ROUTE}from"#setup/scaffold/index.js";import{setTimeout}from"node:timers/promises";import{createPromptCommandOutput,withPhase}from"#setup/cli/index.js";import{captureVercel,runVercel,runVercelCaptureStdout}from"#setup/primitives/run-vercel.js";import{performance}from"node:perf_hooks";import{updateSlackChannelConnectorUid}from"#setup/scaffold/update/update-slack-channel.js";const defaultDeps={captureVercel,runVercel,runVercelCaptureStdout},realDelay=(e,t)=>setTimeout(e,void 0,{signal:t}),monotonicNow=()=>performance.now();async function pollSlackWorkspace(e,t,n,i,a,s){let c=e.delay??realDelay,l=e.now??monotonicNow,u=l()+3e5;for(;;){s?.throwIfAborted();let d=u-l();if(d<=0)return{state:`timed-out`};let f=await fetchSlackWorkspace({deps:e,projectRoot:t,connectorId:n,orgId:i,onOutput:a,timeoutMs:Math.min(CONNECT_LOOKUP_TIMEOUT_MS,d),signal:s});if(s?.throwIfAborted(),f.state!==`pending`)return f;let p=u-l();if(p<=0)return{state:`timed-out`};await c(Math.min(3e3,p),s)}}function isAbortFromSignal(e,t){return t?.aborted===!0&&(e===t.reason||e instanceof Error&&e.name===`AbortError`)}function cleanupFailureResult(e){return{state:`cleanup-failed`,connectorUids:e.connectorUids}}async function runAttempt(e){let{log:t,deps:n,projectRoot:r,orgId:a,onOutput:o,signal:s,phase:c}=e,l,d;if(e.source.state===`existing`)l=e.source.ref;else{let i=await createSlackConnector({deps:n,projectRoot:r,orgId:a,slug:e.slug,onOutput:o,signal:s,phase:c,onCreated:e.onCreated,waitForWorkspace:async(e,t)=>{let i=await pollSlackWorkspace(n,r,e.id,a,o,t);return i.state===`connected`?i.workspace:void 0}});if(i.state===`failed`)return{state:`create-failed`};if(i.state===`unresolved`)return t.warning(`Vercel did not return an exact Slack connector UID for this request, so eve cannot attach or remove it safely.`),{state:`unresolved`};l=i.ref,i.via===`workspace`&&(d=i.workspace)}if(e.source.state===`existing`&&d===void 0){let e=await c(`Waiting for the Slack workspace connection...`,()=>pollSlackWorkspace(n,r,l.id,a,o,s));if(e.state===`timed-out`)return{state:`timed-out`,ref:l};if(e.state===`failed`)return{state:`failed`,ref:l,message:e.message};d=e.workspace}let f=await c(`Configuring Slack event delivery for this agent...`,()=>attachSlackConnector(n,r,l,o,s));return s?.throwIfAborted(),f.state===`attached`?d===void 0?{state:`attached`,ref:l}:{state:`attached`,ref:l,workspace:d}:{state:f.state,ref:l}}async function raceAttemptAgainstChoice(e){let t=new AbortController,n=e.outerSignal?AbortSignal.any([e.outerSignal,t.signal]):t.signal,r=e.run(n);try{let i=await Promise.race([r.then(e=>({via:`work`,outcome:e})),e.prompt.choice.then(e=>({via:`choice`,choice:e}))]);if(i.via===`work`)return i;t.abort();let a;try{a=await r}catch(e){if(!isAbortFromSignal(e,n))throw e}return{via:`choice`,choice:i.choice,settled:a}}catch(e){t.abort();try{await r}catch{}throw e}finally{e.prompt.close()}}async function provisionSlackbot(e,t,n,r=defaultDeps,i={}){i.signal?.throwIfAborted();let o=createPromptCommandOutput(e),u={log:e,deps:r,projectRoot:t,onOutput:o},d=await(r.readProjectLink??readProjectLink)(t),p=d?.projectId,m=d?.orgId,h=`slack/${n}`,g=await withPhase(e,`Checking for an existing Slackbot...`,()=>findSlackConnector(r,t,p,h,o,i.signal));if(i.signal?.throwIfAborted(),g.state===`failed`)return e.warning(`Could not inspect existing Slack connectors, so eve did not create another one. ${g.message}`),{state:`connector-lookup-failed`};if(p===void 0&&g.connectorUids.size>0)return e.warning("Could not verify which Slack connectors belong to this Vercel project, so eve did not create another one. Restore `.vercel/project.json`, then try again."),{state:`connector-lookup-failed`};let finishOutcome=async(t,n,r)=>{let i=n.state===`new`;switch(t.state){case`create-failed`:if(i){let e=await r();if(e.state===`failed`)return cleanupFailureResult(e)}return{state:`create-failed`};case`unresolved`:if(i){let e=await r();if(e.state===`failed`)return cleanupFailureResult(e)}return{state:`create-failed`};case`detach-failed`:return e.warning(`Could not remove the connector's existing trigger destination. Run \`vercel connect detach ${t.ref.uid} --yes\`, then \`vercel connect attach ${t.ref.uid} --triggers --trigger-path ${SLACK_CHANNEL_DEFAULT_ROUTE} --yes\`.`),{state:`detach-failed`,connectorUid:t.ref.uid};case`attach-failed`:return e.warning(`Could not register this project as a trigger destination. Run \`vercel connect attach ${t.ref.uid} --triggers --trigger-path ${SLACK_CHANNEL_DEFAULT_ROUTE} --yes\` to enable event delivery.`),{state:`attach-failed`,connectorUid:t.ref.uid};case`attached`:return t.workspace===void 0?{state:`attached`,connectorUid:t.ref.uid}:{state:`attached`,connectorUid:t.ref.uid,chatUrl:t.workspace.workspaceUrl,workspaceName:t.workspace.workspaceName};case`failed`:return e.warning(`Could not verify the Slack workspace connection. ${t.message}`),{state:`installation-check-failed`,connectorUid:t.ref.uid};case`timed-out`:if(e.warning(`The Slackbot did not connect to a Slack workspace in time.`),i){let e=await r();if(e.state===`failed`)return cleanupFailureResult(e)}return{state:`not-installed`}}},cleanupNewAttempt=async(e,t)=>cleanupCreatedAttempt(u,{expectedUid:h,baselineConnectorUids:e.baselineConnectorUids,createdRef:t});function attemptInput(i,a){return{log:e,deps:r,projectRoot:t,orgId:m,slug:n,source:i,onOutput:o,onCreated:a}}async function runExistingConnector(t){let cleanupCurrentAttempt=async()=>({state:`clean`}),notInstalled=()=>(e.warning(`The existing Slack connector \`${t.ref.uid}\` is not connected to a Slack workspace. eve did not remove it because this run did not create it. If its original browser request is still open, complete it; otherwise run \`vercel connect remove ${t.ref.uid} --disconnect-all --yes\` before trying again.`),{state:`existing-not-installed`,connectorUid:t.ref.uid}),finishExistingOutcome=async e=>e.state===`timed-out`?notInstalled():finishOutcome(e,t,cleanupCurrentAttempt);if(i.awaitChoice!==void 0){let e=await raceAttemptAgainstChoice({prompt:i.awaitChoice({status:`Waiting for the existing Slack workspace connection...`,context:`Complete the original setup in the browser`,actions:[{value:`cancel`,label:`Stop waiting`}]}),outerSignal:i.signal,run:e=>runAttempt({...attemptInput(t,()=>{}),signal:e,phase:(e,t)=>t()})});return e.via===`work`?finishExistingOutcome(e.outcome):e.settled?.state===`attached`?finishExistingOutcome(e.settled):notInstalled()}return finishExistingOutcome(await runAttempt({...attemptInput(t,()=>{}),signal:i.signal,phase:(t,n)=>withPhase(e,t,n)}))}async function runUncontrolledAttempt(t){let n,cleanupCurrentAttempt=()=>cleanupNewAttempt(t,n),r=runAttempt({...attemptInput(t,e=>{n=e}),signal:i.signal,phase:(t,n)=>withPhase(e,t,n)});try{return await finishOutcome(await r,t,cleanupCurrentAttempt)}catch(e){throw i.signal?.aborted===!0&&await cleanupCurrentAttempt(),e}}async function runInteractiveAttempt(n,a){let s,cleanupCurrentAttempt=()=>cleanupNewAttempt(n,s),l=a({status:`Waiting for Slack setup to finish...`,context:`Complete setup in the browser, then wait while eve verifies the connection`,actions:[{value:`retry`,label:`Did your browser not open? Try again`},{value:`cancel`,label:`Cancel`}]}),u;try{u=await raceAttemptAgainstChoice({prompt:l,outerSignal:i.signal,run:t=>runAttempt({...attemptInput(n,e=>{s=e}),signal:t,phase:e.spinner===void 0?(e,t)=>t():(t,n)=>withPhase(e,t,n)})})}catch(e){throw await cleanupCurrentAttempt(),e}if(u.via===`work`)return{state:`finished`,result:await finishOutcome(u.outcome,n,cleanupCurrentAttempt)};if(u.settled?.state===`attached`)return{state:`finished`,result:await finishOutcome(u.settled,n,cleanupCurrentAttempt)};let d=await cleanupCurrentAttempt();if(d.state===`failed`)return{state:`finished`,result:cleanupFailureResult(d)};if(u.choice===`retry`){let n=await withPhase(e,`Checking existing Slack connectors...`,()=>listSlackConnectors(r,t,o,i.signal));return i.signal?.throwIfAborted(),n.state===`failed`?(e.warning(`Could not inspect existing Slack connectors, so eve did not create another one. ${n.message}`),{state:`finished`,result:{state:`connector-lookup-failed`}}):{state:`retry`,source:{state:`new`,baselineConnectorUids:new Set(n.connectors.map(e=>e.uid))}}}return{state:`finished`,result:{state:`cancelled`}}}if(g.state===`found`)return runExistingConnector({state:`existing`,ref:g.connector});let _={state:`new`,baselineConnectorUids:g.connectorUids};if(i.awaitChoice===void 0)return runUncontrolledAttempt(_);for(;;){let e=await runInteractiveAttempt(_,i.awaitChoice);if(e.state===`finished`)return e.result;_=e.source}}async function reconcileSlackUid(e,t,n,r){if(n.state!==`attached`||n.connectorUid===r)return!0;let{patched:i}=await updateSlackChannelConnectorUid(join(t,`agent/channels/slack.ts`),n.connectorUid);return i?!0:(e.warning(`Could not patch agent/channels/slack.ts automatically. Update \`connectSlackCredentials("...")\` to \`"${n.connectorUid}"\` and run \`vercel deploy --prod\`.`),!1)}export{parseCreatedSlackConnector,parseSlackConnectorDetails,pickSlackConnector,provisionSlackbot,reconcileSlackUid};
1
+ import{parseCreatedSlackConnector,parseSlackConnectorDetails,pickSlackConnector}from"./slack-connect.js";import{CONNECT_LOOKUP_TIMEOUT_MS,attachSlackConnector,cleanupCreatedAttempt,fetchSlackWorkspace,findSlackConnector,listSlackConnectors,readProjectLink}from"./slack-connect-lifecycle.js";import{createSlackConnector}from"./slack-connect-create.js";import{join}from"node:path";import{SLACK_CHANNEL_DEFAULT_ROUTE}from"#setup/scaffold/index.js";import{performance}from"node:perf_hooks";import{setTimeout}from"node:timers/promises";import{createPromptCommandOutput,withPhase}from"#setup/cli/index.js";import{captureVercel,runVercel,runVercelCaptureStdout}from"#setup/primitives/run-vercel.js";import{updateSlackChannelConnectorUid}from"#setup/scaffold/update/update-slack-channel.js";const defaultDeps={captureVercel,runVercel,runVercelCaptureStdout},realDelay=(e,t)=>setTimeout(e,void 0,{signal:t}),monotonicNow=()=>performance.now();async function pollSlackWorkspace(e,t,n,i,a,s){let c=e.delay??realDelay,l=e.now??monotonicNow,u=l()+3e5;for(;;){s?.throwIfAborted();let d=u-l();if(d<=0)return{state:`timed-out`};let f=await fetchSlackWorkspace({deps:e,projectRoot:t,connectorId:n,orgId:i,onOutput:a,timeoutMs:Math.min(CONNECT_LOOKUP_TIMEOUT_MS,d),signal:s});if(s?.throwIfAborted(),f.state!==`pending`)return f;let p=u-l();if(p<=0)return{state:`timed-out`};await c(Math.min(3e3,p),s)}}function isAbortFromSignal(e,t){return t?.aborted===!0&&(e===t.reason||e instanceof Error&&e.name===`AbortError`)}function cleanupFailureResult(e){return{state:`cleanup-failed`,connectorUids:e.connectorUids}}async function runAttempt(e){let{log:t,deps:n,projectRoot:r,orgId:a,onOutput:o,signal:s,phase:c}=e,l,d;if(e.source.state===`existing`)l=e.source.ref;else{let i=await createSlackConnector({deps:n,projectRoot:r,orgId:a,slug:e.slug,onOutput:o,signal:s,phase:c,onCreated:e.onCreated,waitForWorkspace:async(e,t)=>{let i=await pollSlackWorkspace(n,r,e.id,a,o,t);return i.state===`connected`?i.workspace:void 0}});if(i.state===`failed`)return{state:`create-failed`};if(i.state===`unresolved`)return t.warning(`Vercel did not return an exact Slack connector UID for this request, so eve cannot attach or remove it safely.`),{state:`unresolved`};l=i.ref,i.via===`workspace`&&(d=i.workspace)}if(e.source.state===`existing`&&d===void 0){let e=await c(`Waiting for the Slack workspace connection...`,()=>pollSlackWorkspace(n,r,l.id,a,o,s));if(e.state===`timed-out`)return{state:`timed-out`,ref:l};if(e.state===`failed`)return{state:`failed`,ref:l,message:e.message};d=e.workspace}let f=await c(`Configuring Slack event delivery for this agent...`,()=>attachSlackConnector(n,r,l,o,s));return s?.throwIfAborted(),f.state===`attached`?d===void 0?{state:`attached`,ref:l}:{state:`attached`,ref:l,workspace:d}:{state:f.state,ref:l}}async function raceAttemptAgainstChoice(e){let t=new AbortController,n=e.outerSignal?AbortSignal.any([e.outerSignal,t.signal]):t.signal,r=e.run(n);try{let i=await Promise.race([r.then(e=>({via:`work`,outcome:e})),e.prompt.choice.then(e=>({via:`choice`,choice:e}))]);if(i.via===`work`)return i;t.abort();let a;try{a=await r}catch(e){if(!isAbortFromSignal(e,n))throw e}return{via:`choice`,choice:i.choice,settled:a}}catch(e){t.abort();try{await r}catch{}throw e}finally{e.prompt.close()}}async function provisionSlackbot(e,t,n,r=defaultDeps,i={}){i.signal?.throwIfAborted();let o=createPromptCommandOutput(e),u={log:e,deps:r,projectRoot:t,onOutput:o},d=await(r.readProjectLink??readProjectLink)(t),p=d?.projectId,m=d?.orgId,h=`slack/${n}`,g=await withPhase(e,`Checking for an existing Slackbot...`,()=>findSlackConnector(r,t,p,h,o,i.signal));if(i.signal?.throwIfAborted(),g.state===`failed`)return e.warning(`Could not inspect existing Slack connectors, so eve did not create another one. ${g.message}`),{state:`connector-lookup-failed`};if(p===void 0&&g.connectorUids.size>0)return e.warning("Could not verify which Slack connectors belong to this Vercel project, so eve did not create another one. Restore `.vercel/project.json`, then try again."),{state:`connector-lookup-failed`};let finishOutcome=async(t,n,r)=>{let i=n.state===`new`;switch(t.state){case`create-failed`:if(i){let e=await r();if(e.state===`failed`)return cleanupFailureResult(e)}return{state:`create-failed`};case`unresolved`:if(i){let e=await r();if(e.state===`failed`)return cleanupFailureResult(e)}return{state:`create-failed`};case`detach-failed`:return e.warning(`Could not remove the connector's existing trigger destination. Run \`vercel connect detach ${t.ref.uid} --yes\`, then \`vercel connect attach ${t.ref.uid} --triggers --trigger-path ${SLACK_CHANNEL_DEFAULT_ROUTE} --yes\`.`),{state:`detach-failed`,connectorUid:t.ref.uid};case`attach-failed`:return e.warning(`Could not register this project as a trigger destination. Run \`vercel connect attach ${t.ref.uid} --triggers --trigger-path ${SLACK_CHANNEL_DEFAULT_ROUTE} --yes\` to enable event delivery.`),{state:`attach-failed`,connectorUid:t.ref.uid};case`attached`:return t.workspace===void 0?{state:`attached`,connectorUid:t.ref.uid}:{state:`attached`,connectorUid:t.ref.uid,chatUrl:t.workspace.workspaceUrl,workspaceName:t.workspace.workspaceName};case`failed`:return e.warning(`Could not verify the Slack workspace connection. ${t.message}`),{state:`installation-check-failed`,connectorUid:t.ref.uid};case`timed-out`:if(e.warning(`The Slackbot did not connect to a Slack workspace in time.`),i){let e=await r();if(e.state===`failed`)return cleanupFailureResult(e)}return{state:`not-installed`}}},cleanupNewAttempt=async(e,t)=>cleanupCreatedAttempt(u,{expectedUid:h,baselineConnectorUids:e.baselineConnectorUids,createdRef:t});function attemptInput(i,a){return{log:e,deps:r,projectRoot:t,orgId:m,slug:n,source:i,onOutput:o,onCreated:a}}async function runExistingConnector(t){let cleanupCurrentAttempt=async()=>({state:`clean`}),notInstalled=()=>(e.warning(`The existing Slack connector \`${t.ref.uid}\` is not connected to a Slack workspace. eve did not remove it because this run did not create it. If its original browser request is still open, complete it; otherwise run \`vercel connect remove ${t.ref.uid} --disconnect-all --yes\` before trying again.`),{state:`existing-not-installed`,connectorUid:t.ref.uid}),finishExistingOutcome=async e=>e.state===`timed-out`?notInstalled():finishOutcome(e,t,cleanupCurrentAttempt);if(i.awaitChoice!==void 0){let e=await raceAttemptAgainstChoice({prompt:i.awaitChoice({status:`Waiting for the existing Slack workspace connection...`,context:`Complete the original setup in the browser`,actions:[{value:`cancel`,label:`Stop waiting`}]}),outerSignal:i.signal,run:e=>runAttempt({...attemptInput(t,()=>{}),signal:e,phase:(e,t)=>t()})});return e.via===`work`?finishExistingOutcome(e.outcome):e.settled?.state===`attached`?finishExistingOutcome(e.settled):notInstalled()}return finishExistingOutcome(await runAttempt({...attemptInput(t,()=>{}),signal:i.signal,phase:(t,n)=>withPhase(e,t,n)}))}async function runUncontrolledAttempt(t){let n,cleanupCurrentAttempt=()=>cleanupNewAttempt(t,n),r=runAttempt({...attemptInput(t,e=>{n=e}),signal:i.signal,phase:(t,n)=>withPhase(e,t,n)});try{return await finishOutcome(await r,t,cleanupCurrentAttempt)}catch(e){throw i.signal?.aborted===!0&&await cleanupCurrentAttempt(),e}}async function runInteractiveAttempt(n,a){let s,cleanupCurrentAttempt=()=>cleanupNewAttempt(n,s),l=a({status:`Waiting for Slack setup to finish...`,context:`Complete setup in the browser, then wait while eve verifies the connection`,actions:[{value:`retry`,label:`Did your browser not open? Try again`},{value:`cancel`,label:`Cancel`}]}),u;try{u=await raceAttemptAgainstChoice({prompt:l,outerSignal:i.signal,run:t=>runAttempt({...attemptInput(n,e=>{s=e}),signal:t,phase:e.spinner===void 0?(e,t)=>t():(t,n)=>withPhase(e,t,n)})})}catch(e){throw await cleanupCurrentAttempt(),e}if(u.via===`work`)return{state:`finished`,result:await finishOutcome(u.outcome,n,cleanupCurrentAttempt)};if(u.settled?.state===`attached`)return{state:`finished`,result:await finishOutcome(u.settled,n,cleanupCurrentAttempt)};let d=await cleanupCurrentAttempt();if(d.state===`failed`)return{state:`finished`,result:cleanupFailureResult(d)};if(u.choice===`retry`){let n=await withPhase(e,`Checking existing Slack connectors...`,()=>listSlackConnectors(r,t,o,i.signal));return i.signal?.throwIfAborted(),n.state===`failed`?(e.warning(`Could not inspect existing Slack connectors, so eve did not create another one. ${n.message}`),{state:`finished`,result:{state:`connector-lookup-failed`}}):{state:`retry`,source:{state:`new`,baselineConnectorUids:new Set(n.connectors.map(e=>e.uid))}}}return{state:`finished`,result:{state:`cancelled`}}}if(g.state===`found`)return runExistingConnector({state:`existing`,ref:g.connector});let _={state:`new`,baselineConnectorUids:g.connectorUids};if(i.awaitChoice===void 0)return runUncontrolledAttempt(_);for(;;){let e=await runInteractiveAttempt(_,i.awaitChoice);if(e.state===`finished`)return e.result;_=e.source}}async function reconcileSlackUid(e,t,n,r){if(n.state!==`attached`||n.connectorUid===r)return!0;let{patched:i}=await updateSlackChannelConnectorUid(join(t,`agent/channels/slack.ts`),n.connectorUid);return i?!0:(e.warning(`Could not patch agent/channels/slack.ts automatically. Update \`connectSlackCredentials("...")\` to \`"${n.connectorUid}"\` and run \`vercel deploy --prod\`.`),!1)}export{parseCreatedSlackConnector,parseSlackConnectorDetails,pickSlackConnector,provisionSlackbot,reconcileSlackUid};
@@ -107,6 +107,11 @@ export interface AgentExperimentalDefinition {
107
107
  * environment variable (`"1"` enables it) for backwards compatibility.
108
108
  */
109
109
  readonly codeMode?: boolean;
110
+ /**
111
+ * Durable Workflow runtime configuration. Root agents may use this to select
112
+ * the Workflow world backing sessions and runs.
113
+ */
114
+ readonly workflow?: AgentWorkflowDefinition;
110
115
  }
111
116
  /**
112
117
  * Advanced hosted-build controls authored in `agent.ts`.
@@ -126,6 +131,24 @@ export interface AgentBuildDefinition {
126
131
  */
127
132
  readonly externalDependencies?: string[];
128
133
  }
134
+ /**
135
+ * Package name for a Workflow world module.
136
+ *
137
+ * The package must export either a default factory or a `createWorld` factory.
138
+ * The factory is called at runtime so credentials and deployment-specific
139
+ * options can come from environment variables instead of the compiled manifest.
140
+ */
141
+ export type AgentWorkflowWorldDefinition = string;
142
+ /**
143
+ * Advanced durable-runtime configuration for eve's Workflow SDK integration.
144
+ */
145
+ export interface AgentWorkflowDefinition {
146
+ /**
147
+ * Workflow world module used for durable workflow storage, queueing, hooks,
148
+ * and streaming.
149
+ */
150
+ readonly world?: AgentWorkflowWorldDefinition;
151
+ }
129
152
  /**
130
153
  * Compiled-side agent definition. Carries a `name` because the compiler
131
154
  * stamps the path-derived `agentId` onto every compiled agent node.
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Shared grapheme-cluster boundaries for editable text. JavaScript offsets
3
+ * stay UTF-16 offsets; segmentation defines which offsets are legal caret
4
+ * positions and which spans editing operations may remove.
5
+ */
6
+ interface GraphemeSpan {
7
+ readonly text: string;
8
+ readonly start: number;
9
+ readonly end: number;
10
+ }
11
+ /** Splits text into grapheme clusters while preserving `String.slice` offsets. */
12
+ export declare function graphemes(text: string): GraphemeSpan[];
13
+ /** The nearest grapheme boundary strictly before `offset`. */
14
+ export declare function previousGraphemeBoundary(text: string, offset: number): number;
15
+ /** The nearest grapheme boundary strictly after `offset`. */
16
+ export declare function nextGraphemeBoundary(text: string, offset: number): number;
17
+ /** The nearest grapheme boundary at or after `offset`. */
18
+ export declare function graphemeBoundaryAtOrAfter(text: string, offset: number): number;
19
+ export {};
@@ -0,0 +1 @@
1
+ const graphemeSegmenter=new Intl.Segmenter(void 0,{granularity:`grapheme`});function graphemes(t){return Array.from(graphemeSegmenter.segment(t),({index:e,segment:t})=>({text:t,start:e,end:e+t.length}))}function previousGraphemeBoundary(t,n){let r=Math.max(0,Math.min(n,t.length));return r===0?0:graphemeSegmenter.segment(t).containing(r-1)?.index??0}function nextGraphemeBoundary(t,n){let r=Math.max(0,Math.min(n,t.length));if(r===t.length)return r;let i=graphemeSegmenter.segment(t).containing(r);return i===void 0?t.length:i.index+i.segment.length}function graphemeBoundaryAtOrAfter(t,n){let r=Math.max(0,Math.min(n,t.length));if(r===0||r===t.length)return r;let i=graphemeSegmenter.segment(t).containing(r);return i===void 0||i.index===r?r:i.index+i.segment.length}export{graphemeBoundaryAtOrAfter,graphemes,nextGraphemeBoundary,previousGraphemeBoundary};
@@ -54,16 +54,43 @@ export default defineAgent({
54
54
 
55
55
  See [Default harness](./concepts/default-harness#compaction) for how the loop applies it.
56
56
 
57
+ ## Workflow world
58
+
59
+ By default, eve selects the Workflow SDK world for the host: Vercel Workflow on
60
+ Vercel, and the SDK's local world in local development or `eve start`. Advanced
61
+ self-hosted deployments can select the Workflow world package to use from the
62
+ root `agent.ts`:
63
+
64
+ ```ts title="agent/agent.ts"
65
+ import { defineAgent } from "eve";
66
+
67
+ export default defineAgent({
68
+ model: "anthropic/claude-opus-4.8",
69
+ experimental: {
70
+ workflow: {
71
+ world: "@workflow/world-postgres",
72
+ },
73
+ },
74
+ });
75
+ ```
76
+
77
+ Install that package in your app. It should export a default factory or
78
+ `createWorld()` function.
79
+
80
+ Put credentials and host-specific options in runtime environment variables read
81
+ by the world package, not in `agent.ts`. If the installed package must stay
82
+ external in hosted output, list it in `build.externalDependencies`.
83
+
57
84
  ## Other defineAgent fields
58
85
 
59
86
  `defineAgent` takes a few more fields, all optional. For the exported types, see the [TypeScript API](./reference/typescript-api).
60
87
 
61
- | Field | Type | Default | Description |
62
- | -------------- | --------------------------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
63
- | `modelOptions` | `AgentModelOptionsDefinition` | none | Provider option overrides forwarded to the model call. |
64
- | `experimental` | `{ codeMode?: boolean }` | flags unset | Opt-in flags that can change or disappear in any release. Treat them as unstable. `codeMode` routes executable tools through a sandboxed code-execution wrapper, where the model writes JavaScript that calls the tools inside the [sandbox](./sandbox). |
65
- | `outputSchema` | Standard Schema or a JSON Schema object | none | Structured return type for task-mode runs (a subagent, schedule, or remote job). Interactive conversation turns ignore it unless the client supplies a per-message schema. |
66
- | `build` | `{ externalDependencies?: string[] }` | none | Hosted-build packaging controls. `externalDependencies` keeps listed packages external while eve compiles authored modules such as tools and channels, and traces those packages into the hosted output. |
88
+ | Field | Type | Default | Description |
89
+ | -------------- | ------------------------------------------------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
90
+ | `modelOptions` | `AgentModelOptionsDefinition` | none | Provider option overrides forwarded to the model call. |
91
+ | `experimental` | `{ codeMode?: boolean; workflow?: { world?: string } }` | flags unset | Opt-in flags that can change or disappear in any release. Treat them as unstable. `codeMode` routes executable tools through a sandboxed code-execution wrapper, where the model writes JavaScript that calls the tools inside the [sandbox](./sandbox). `workflow.world` selects the Workflow world package backing session state, queues, hooks, and streams on the root agent. |
92
+ | `outputSchema` | Standard Schema or a JSON Schema object | none | Structured return type for task-mode runs (a subagent, schedule, or remote job). Interactive conversation turns ignore it unless the client supplies a per-message schema. |
93
+ | `build` | `{ externalDependencies?: string[] }` | none | Hosted-build packaging controls. `externalDependencies` keeps listed packages external while eve compiles authored modules such as tools and channels, and traces those packages into the hosted output. |
67
94
 
68
95
  `codeMode` is experimental and may change or be removed.
69
96
 
@@ -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 | 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 |
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](/docs/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
 
@@ -19,7 +19,22 @@ The Workflow SDK is not inherently tied to Vercel. In local development and in a
19
19
 
20
20
  Nitro hosts the HTTP routes and workflow entrypoints. It does not supply the workflow state store or the sandbox runtime. Those are separate adapters: Workflow uses the active world implementation, and Sandbox uses the backend from `agent/sandbox` or `defaultBackend()`.
21
21
 
22
- Today, eve owns Workflow world selection. In the future, eve will expose a supported way to provide a different Workflow world, so advanced self-hosted deployments can swap the state, queue, auth, and streaming backend behind the same agent runtime. The underlying [Workflow Worlds](https://workflow-sdk.dev/worlds) abstraction is what makes that possible, but it is not an eve application API yet.
22
+ For advanced self-hosted deployments, the root `agent.ts` can select the installed Workflow world package to use with `experimental.workflow.world`:
23
+
24
+ ```ts title="agent/agent.ts"
25
+ import { defineAgent } from "eve";
26
+
27
+ export default defineAgent({
28
+ model: "anthropic/claude-opus-4.8",
29
+ experimental: {
30
+ workflow: {
31
+ world: "@workflow/world-postgres",
32
+ },
33
+ },
34
+ });
35
+ ```
36
+
37
+ The world package backs workflow state, queues, hooks, and streams. Keep secrets and deployment-specific options in runtime environment variables read by that package, not in `agent.ts`. See [agent.ts](../agent-config#workflow-world) and [Workflow Worlds](https://workflow-sdk.dev/worlds).
23
38
 
24
39
  ## Resuming after a crash
25
40
 
@@ -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](../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. |
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](/docs/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
 
@@ -17,6 +17,7 @@ Run-level assertions read the whole run, so they take no value. They are methods
17
17
  | `t.messageIncludes(token)` | Joined assistant text contains `token` (string or RegExp) |
18
18
  | `t.outputEquals(value)` / `t.outputMatches(schema)` | Deep equality or Standard Schema (e.g. Zod) validation of the agent's structured output |
19
19
  | `t.calledTool(name, opts?)` | A matching tool call happened (`input`, `output`, `isError`, `times` constraints) |
20
+ | `t.loadedSkill(skill, opts?)` | Sugar for `t.calledTool("load_skill", { input: { skill }, ...opts })` |
20
21
  | `t.notCalledTool(name)` | No call to `name` |
21
22
  | `t.toolOrder([...names])` | Tool names appear in order (other calls may interleave) |
22
23
  | `t.usedNoTools()` | No tool calls at all |
@@ -21,7 +21,22 @@ Nitro is the HTTP host layer. It gives eve a build artifact that can serve the h
21
21
 
22
22
  On Vercel, eve emits Vercel Build Output, the Workflow SDK runs on Vercel Workflow, and `defaultBackend()` selects Vercel Sandbox. Outside Vercel, `eve start` serves the standard Nitro Node output, the Workflow SDK uses its local world by default, and `defaultBackend()` selects a local sandbox backend in availability order. That local workflow world persists run state on disk and has no direct coupling to Vercel; Vercel-only behavior such as latest-deployment routing and dashboard run attributes is additive.
23
23
 
24
- Eve does not expose Workflow world selection as a public app API today. Future releases will let advanced deployments provide a different Workflow world, the SDK abstraction for workflow state, queues, auth, and streaming; see [Workflow Worlds](https://workflow-sdk.dev/worlds) for the underlying concept.
24
+ Advanced self-hosted deployments can select a different installed Workflow world package in the root `agent.ts`:
25
+
26
+ ```ts title="agent/agent.ts"
27
+ import { defineAgent } from "eve";
28
+
29
+ export default defineAgent({
30
+ model: "anthropic/claude-opus-4.8",
31
+ experimental: {
32
+ workflow: {
33
+ world: "@acme/eve-workflow-world",
34
+ },
35
+ },
36
+ });
37
+ ```
38
+
39
+ The world package should read credentials and host-specific options from runtime environment variables. It should export a default factory or `createWorld()` function. See [Workflow Worlds](https://workflow-sdk.dev/worlds) for the underlying SDK abstraction.
25
40
 
26
41
  ## 2. Environment variables and secrets
27
42
 
@@ -125,7 +140,7 @@ Eve writes the standard Nitro output under `.output/` instead of Vercel Build Ou
125
140
 
126
141
  Self-deployed agents should make the Vercel-specific choices explicit:
127
142
 
128
- - Let the Workflow SDK use its default local world, which stores workflow state under `.workflow-data`, or configure your host so that directory is on persistent storage.
143
+ - Let the Workflow SDK use its default local world, which stores workflow state under `.workflow-data`, configure your host so that directory is on persistent storage, or select another world with `experimental.workflow.world` in the root `agent.ts`.
129
144
  - Install the AI SDK package for your provider, then use a direct provider model object and `OPENAI_API_KEY` / `ANTHROPIC_API_KEY` when you want no Gateway dependency.
130
145
  - Use `AI_GATEWAY_API_KEY` if you still want Gateway routing from a non-Vercel host.
131
146
  - Replace `vercelOidc()` with auth that your host can verify.
@@ -58,18 +58,21 @@ The provider row demands attention (a bold yellow "Configure provider" with "Req
58
58
 
59
59
  ## Keyboard shortcuts
60
60
 
61
- The prompt input behaves like a shell line editor.
61
+ Chat and freeform `ask_question` inputs behave like a shell line editor.
62
62
 
63
63
  | Key | Action |
64
64
  | ---------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
65
- | `Enter` | Send the message. |
66
- | `Ctrl+C` | Interrupt a running turn, or quit at the prompt. |
67
- | `↑` / `↓` | Cycle through the messages you have sent this session. |
68
- | `←` / `→`, `Home` / `End`, `Ctrl+A` / `Ctrl+E` | Move the caret. |
69
- | `Ctrl+U` / `Ctrl+K` / `Ctrl+W` | Kill the whole line, the rest of the line, or the previous word. |
65
+ | `Enter` | Submit the message or question response. |
66
+ | `Shift+Enter` | Insert a newline without sending (needs a terminal that reports modified keys). |
67
+ | `Ctrl+C` | Interrupt a running turn. At the chat or freeform-question prompt, clear non-empty input; when empty, quit. |
68
+ | `↑` / `↓` | Move between input lines; at a chat-buffer edge, navigate messages you have sent this session. |
69
+ | `←` / `→`, `Home` / `End`, `Ctrl+A` / `Ctrl+E` | Move the caret; Home/End stay within the current line. |
70
+ | `Ctrl+U` / `Ctrl+K` / `Ctrl+W` | Delete to the start of the line, to its end, or through the previous word. |
70
71
  | `Ctrl+L` | Cycle the log display mode (`none → all → stderr → sandbox → none`) and briefly show the mode in the status line. |
71
72
  | `Ctrl+R` | Redraw the screen. |
72
73
 
74
+ In terminals that support bracketed paste, pasting multi-line text into chat or a freeform question inserts it intact and renders one row per line rather than submitting at the first line. `Shift+Enter` adds a line by hand. The input grows down to the available terminal height, then scrolls to keep the caret visible; `Enter` submits the whole response.
75
+
73
76
  If a turn fails terminally (the server session dies or the connection drops), the TUI starts a fresh session and notes it inline so you can keep going. Server-side context resets with the old session.
74
77
 
75
78
  ## Answer the agent inline
@@ -77,7 +80,7 @@ If a turn fails terminally (the server session dies or the connection drops), th
77
80
  When the agent needs something from you, the TUI asks inline.
78
81
 
79
82
  - Tool approvals are a `y` or `n`.
80
- - Option questions let you pick with `↑` / `↓` and `Enter`, or you can type a freeform answer.
83
+ - Option questions let you pick with `↑` / `↓` and `Enter`, or you can compose a multi-line freeform answer.
81
84
  - If a tool needs an authorized [connection](../connections), the URL shows up right in the transcript, and the turn picks back up once you finish the flow.
82
85
 
83
86
  ## Control what logs show
@@ -116,6 +116,8 @@ export default defineDynamic({
116
116
 
117
117
  The caller's team gets its own playbook advertised as a loadable skill; everyone else gets nothing.
118
118
 
119
+ Skills follow the same naming rule as tools: a single `defineSkill(...)` is named after the file slug, while a map return names each entry `slug__key` — even when the map holds one entry, so adding a second skill later never renames the first.
120
+
119
121
  ## Dynamic instructions
120
122
 
121
123
  A dynamic instructions file resolves the per-session system prompt the same way, returning `defineInstructions(...)` built from the principal, tenant, or external data:
@@ -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`, 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()`:
103
+ Tools opt into approval with `needsApproval`, and the model can also ask a question with `ask_question` — see [Human-in-the-loop](/docs/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
@@ -50,7 +50,7 @@ export default defineTool({
50
50
  | `defineEvalConfig` | `eve/evals` | `evals/evals.config.ts` | [Evals](../evals/overview) |
51
51
  | `useEveAgent` | `eve/react`, `eve/vue`, `eve/svelte` | frontend | [Frontend](../guides/frontend/overview) |
52
52
 
53
- A few non-`define*` helpers round out the set: `disableTool` and `ExperimentalWorkflow` from `eve/tools` (see [Default harness](../concepts/default-harness)), the route verbs `GET`/`POST`/`PUT`/`PATCH`/`DELETE`/`WS` from `eve/channels`, the approval predicates `always`/`once`/`never` from `eve/tools/approval`, and the channel auth helpers `localDev`/`vercelOidc`/`placeholderAuth` from `eve/channels/auth`. To wrap a built-in tool, import its default value from `eve/tools/defaults` (`bash`, `readFile`, `writeFile`, `glob`, `grep`, `webFetch`, `webSearch`, `todo`, `loadSkill`).
53
+ A few non-`define*` helpers round out the set: `disableTool` and `ExperimentalWorkflow` from `eve/tools` (see [Default harness](../concepts/default-harness)), the route verbs `GET`/`POST`/`PUT`/`PATCH`/`DELETE`/`WS` from `eve/channels`, the approval predicates `always`/`once`/`never` from `eve/tools/approval`, and the channel auth helpers `localDev`/`vercelOidc`/`placeholderAuth` from `eve/channels/auth`. To wrap a built-in tool, import its default value from `eve/tools/defaults` (`bash`, `readFile`, `writeFile`, `glob`, `grep`, `webFetch`, `webSearch`, `todo`, `loadSkill`). `AgentWorkflowDefinition` and `AgentWorkflowWorldDefinition` are exported from `eve` for the `defineAgent({ experimental: { workflow } })` config shape.
54
54
 
55
55
  ## Runtime context (`ctx`)
56
56
 
@@ -68,7 +68,7 @@ A few non-`define*` helpers round out the set: `disableTool` and `ExperimentalWo
68
68
 
69
69
  | Import | Holds |
70
70
  | ----------------------------------------------------------- | -------------------------------------------------------------------- |
71
- | `eve` | `defineAgent`, `defineRemoteAgent` |
71
+ | `eve` | `defineAgent`, `defineRemoteAgent`, agent config types |
72
72
  | `eve/tools` | `defineTool`, `defineDynamic`, `disableTool`, `ExperimentalWorkflow` |
73
73
  | `eve/tools/defaults` | the built-in tools as plain values |
74
74
  | `eve/tools/approval` | `always`, `once`, `never` |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eve",
3
- "version": "0.11.8",
3
+ "version": "0.11.10",
4
4
  "private": false,
5
5
  "description": "Filesystem-first framework for durable backend AI agents that run anywhere.",
6
6
  "keywords": [