eve 0.38.1 → 0.38.3
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 +17 -0
- package/dist/src/cli/commands/registry.js +1 -1
- package/dist/src/cli/dev/tui/tool-presentation.js +1 -1
- package/dist/src/compiled/.vendor-stamp.json +5 -5
- package/dist/src/compiled/@workflow/core/events-consumer.d.ts +97 -0
- package/dist/src/compiled/@workflow/core/index.js +2 -2
- package/dist/src/compiled/@workflow/core/private.js +1 -1
- package/dist/src/compiled/@workflow/core/runtime/constants.d.ts +35 -0
- package/dist/src/compiled/@workflow/core/runtime/helpers.d.ts +127 -93
- package/dist/src/compiled/@workflow/core/runtime/quickjs-entrypoint.d.ts +8 -9
- package/dist/src/compiled/@workflow/core/runtime/replay-budget.d.ts +8 -0
- package/dist/src/compiled/@workflow/core/runtime/resume-latency.d.ts +143 -0
- package/dist/src/compiled/@workflow/core/runtime/step-executor.d.ts +27 -12
- package/dist/src/compiled/@workflow/core/runtime/suspension-handler.d.ts +39 -8
- package/dist/src/compiled/@workflow/core/runtime/world-compatibility.d.ts +6 -5
- package/dist/src/compiled/@workflow/core/runtime.js +3 -3
- package/dist/src/compiled/@workflow/core/telemetry/semantic-conventions.d.ts +81 -0
- package/dist/src/compiled/@workflow/core/test-support/orchestrator-context.d.ts +25 -0
- package/dist/src/compiled/@workflow/core/version.d.ts +1 -1
- package/dist/src/compiled/@workflow/core/workflow.js +1 -1
- package/dist/src/compiled/@workflow/errors/index.d.ts +18 -2
- package/dist/src/compiled/@workflow/errors/index.js +1 -1
- package/dist/src/compiled/@workflow/world/analytics.d.ts +9 -0
- package/dist/src/compiled/@workflow/world/events.d.ts +76 -87
- package/dist/src/compiled/@workflow/world/index.d.ts +3 -3
- package/dist/src/compiled/@workflow/world/interfaces.d.ts +45 -45
- package/dist/src/compiled/@workflow/world/queue.d.ts +86 -0
- package/dist/src/compiled/@workflow/world/slot-identity.d.ts +15 -0
- package/dist/src/compiled/@workflow/world/spec-version.d.ts +30 -18
- package/dist/src/compiled/@workflow/world/test-support/duplicate-event-fixtures.d.ts +41 -0
- package/dist/src/compiled/@workflow/world-local/index.js +5 -5
- package/dist/src/compiled/@workflow/world-vercel/index.js +12 -12
- package/dist/src/compiled/_chunks/workflow/{attribute-changes-DZtQkszH.js → attribute-changes-B8K0pwpH.js} +1 -1
- package/dist/src/compiled/_chunks/workflow/{dist-CPlyFOVo.js → dist-BRGbNjhD.js} +1 -1
- package/dist/src/compiled/_chunks/workflow/{dist-xQXN70ED.js → dist-CqvWKNQv.js} +1 -1
- package/dist/src/compiled/_chunks/workflow/frames-DoexhtxJ.js +3 -0
- package/dist/src/compiled/_chunks/workflow/private-z_2Kcwee.js +1 -0
- package/dist/src/compiled/_chunks/workflow/resume-hook-DO_MuJlY.js +1 -0
- package/dist/src/compiled/_chunks/workflow/run-lq7-eG0l.js +1 -0
- package/dist/src/compiled/_chunks/workflow/sleep-7TgF3i-e.js +1 -0
- package/dist/src/compiled/_chunks/workflow/wait-until-BPSgt1Wg.js +15 -0
- package/dist/src/compiled/_chunks/workflow/ws-transport-TYZ9zwoZ.js +1 -0
- package/dist/src/compiler/model-catalog.js +1 -1
- package/dist/src/compiler/normalize-agent-config.js +1 -1
- package/dist/src/execution/create-session-step.d.ts +1 -0
- package/dist/src/execution/create-session-step.js +1 -1
- package/dist/src/execution/dispatch-runtime-actions-shared.d.ts +1 -1
- package/dist/src/execution/node-step.js +1 -1
- package/dist/src/execution/session-callback-step.d.ts +8 -0
- package/dist/src/execution/session-callback-step.js +1 -1
- package/dist/src/execution/session.d.ts +2 -0
- package/dist/src/execution/session.js +1 -1
- package/dist/src/execution/task-event-callback.d.ts +5 -6
- package/dist/src/execution/task-event-callback.js +1 -1
- package/dist/src/execution/tasks/child/instructions.d.ts +3 -0
- package/dist/src/execution/tasks/child/instructions.js +1 -0
- package/dist/src/execution/tasks/child/steps.d.ts +7 -1
- package/dist/src/execution/tasks/child/steps.js +1 -1
- package/dist/src/execution/tasks/child/update.d.ts +10 -0
- package/dist/src/execution/tasks/child/update.js +1 -0
- package/dist/src/execution/tasks/child/workflow.d.ts +3 -3
- package/dist/src/execution/tasks/child/workflow.js +1 -1
- package/dist/src/execution/tasks/parent/agent-views.js +1 -1
- package/dist/src/execution/tasks/parent/control-shared.d.ts +1 -1
- package/dist/src/execution/tasks/parent/dispatch-task-step.d.ts +1 -1
- package/dist/src/execution/tasks/parent/dispatch-task-step.js +1 -1
- package/dist/src/execution/tasks/parent/dispatch.d.ts +5 -3
- package/dist/src/execution/tasks/parent/dispatch.js +1 -1
- package/dist/src/execution/workflow-entry.js +1 -1
- package/dist/src/execution/workflow-steps.js +1 -1
- package/dist/src/harness/advertised-tools.d.ts +3 -0
- package/dist/src/harness/advertised-tools.js +1 -1
- package/dist/src/harness/ai-sdk-hook-bridge.d.ts +1 -1
- package/dist/src/harness/ai-sdk-hook-bridge.js +1 -1
- package/dist/src/harness/instrumentation/lifecycle.d.ts +2 -0
- package/dist/src/harness/tool-loop.js +2 -2
- package/dist/src/internal/application/package.js +1 -1
- package/dist/src/internal/workflow/development-world-client.js +1 -1
- package/dist/src/public/channels/slack/index.d.ts +1 -1
- package/dist/src/public/channels/slack/input-response.d.ts +11 -0
- package/dist/src/public/channels/slack/input-response.js +1 -0
- package/dist/src/public/channels/slack/interactions.d.ts +10 -14
- package/dist/src/public/channels/slack/interactions.js +1 -1
- package/dist/src/public/channels/slack/slackChannel.d.ts +35 -0
- package/dist/src/public/channels/slack/slackChannel.js +1 -1
- package/dist/src/runtime/framework-tools/index.js +1 -1
- package/dist/src/runtime/framework-tools/tasks.d.ts +6 -7
- package/dist/src/runtime/framework-tools/tasks.js +1 -1
- package/dist/src/runtime/prompt/compose.js +1 -1
- package/dist/src/runtime/session-callback-route.js +1 -1
- package/dist/src/setup/scaffold/create/project.js +1 -1
- package/dist/src/tasks/task-id.d.ts +1 -2
- package/dist/src/tasks/task-id.js +1 -1
- package/dist/src/tasks/task-inbox-token.d.ts +2 -0
- package/dist/src/tasks/task-inbox-token.js +1 -0
- package/dist/src/tasks/types.d.ts +9 -1
- package/dist/src/tasks/wire.d.ts +2 -3
- package/dist/src/tracing/agent-otel-provider.js +2 -2
- package/docs/channels/custom.mdx +1 -0
- package/docs/channels/mcp.mdx +185 -0
- package/docs/channels/meta.json +1 -0
- package/docs/channels/overview.mdx +6 -1
- package/docs/channels/slack.mdx +19 -9
- package/docs/guides/auth-and-route-protection.md +1 -0
- package/docs/guides/dev-tui.md +1 -0
- package/docs/instructions.mdx +1 -0
- package/package.json +6 -6
- package/dist/src/compiled/_chunks/workflow/frames-BBs-9k1d.js +0 -3
- package/dist/src/compiled/_chunks/workflow/private-UQtz8ZcW.js +0 -1
- package/dist/src/compiled/_chunks/workflow/resume-hook-CYtSorp6.js +0 -1
- package/dist/src/compiled/_chunks/workflow/run-Cjo0A3NO.js +0 -1
- package/dist/src/compiled/_chunks/workflow/sleep-C1P0OWBm.js +0 -1
- package/dist/src/compiled/_chunks/workflow/wait-until-CT1ve8qB.js +0 -15
- package/dist/src/compiled/_chunks/workflow/ws-transport-beXxD0s_.js +0 -1
- package/dist/src/execution/tasks/parent/send.d.ts +0 -25
- package/dist/src/execution/tasks/parent/send.js +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# eve
|
|
2
2
|
|
|
3
|
+
## 0.38.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 8b2a914: Authorize Slack HITL answers with `onInputResponse` before they resume a parked session. Omitting the hook preserves the existing submitting-user authorization behavior regardless of other Slack handlers.
|
|
8
|
+
- c2b9bbf: Slack `onEvent` handlers can now pass `title` to `ctx.send()` to set the run title without changing the message sent to the model.
|
|
9
|
+
|
|
10
|
+
## 0.38.2
|
|
11
|
+
|
|
12
|
+
### Patch Changes
|
|
13
|
+
|
|
14
|
+
- 250d67a: Bump `@workflow/*` packages to the latest 5.0.0 betas (`core` beta.42, `errors` beta.17, `world` beta.27, `world-local` beta.36, `world-vercel` beta.38, `world-postgres` beta.34). The development Workflow world now advertises spec version 6 (slot-numbered event ids), matching the updated local world.
|
|
15
|
+
- f9f29d3: Surface AI Gateway model catalog request failures during compilation instead of reporting unavailable models as missing metadata. Models with eve-owned metadata continue to compile without the catalog.
|
|
16
|
+
- 77de320: Let background task children send intermediate progress to their parent with `task_update`, using the existing local and remote child-to-parent transports. Remote task HITL is now presented only by the parent channel, finished agents continue through their original subagent tool with `agentId`, and the redundant `task_send` tool has been removed.
|
|
17
|
+
- 88f6ca9: Configure MCP channel endpoints with `route`, default them to `/eve/v1/mcp`, and derive OAuth protected-resource metadata paths from the MCP resource identifier.
|
|
18
|
+
- fe1ad3b: Search by item name when suggesting registry matches, so path typos such as `channels/slack` can still suggest `channel/slack`.
|
|
19
|
+
|
|
3
20
|
## 0.38.1
|
|
4
21
|
|
|
5
22
|
### Patch Changes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{hasInteractiveTerminal}from"./preconditions.js";import{serializeHeadlessSetupEvent}from"./setup-headless.js";import{runDeclaredSetups}from"./registry-declared-setups.js";import{runRegistryPackage}from"./registry-package.js";import{eveMetadataFromRegistryItem,parseOfficialRegistrySearchMetadata}from"./registry-metadata.js";import{printRegistrySearchResults,registryViewText}from"./registry-presentation.js";import{addRegistryMappings,readRegistryConfig}from"./registry-project.js";import{errorMessage,resolveRegistryItemForAdd,runRegistryAction,setupReminder,setupResumeCommand}from"./registry-recovery.js";import{resolveInstalledPackageInfo}from"#internal/application/package.js";import{createPrompter}from"#setup/prompter.js";import{WizardCancelledError}from"#setup/step.js";import{addRegistryItems,getRegistryItems,searchRegistries}from"#compiled/shadcn-registry/index.js";import semver from"#compiled/semver/index.js";const defaultAddCommandDependencies={createPrompter,hasInteractiveTerminal,loadSetupCommandRunner:async()=>(await import(`./registry-setup-command.js`)).runRegistrySetupCommand};function resolveOfficialRegistryUrl(e=process.env.EVE_DEV_OFFICIAL_REGISTRY_URL){if(e===void 0)return`https://eve.dev/r`;let t;try{t=new URL(e)}catch{throw Error(`EVE_DEV_OFFICIAL_REGISTRY_URL must be an HTTP(S) URL.`)}if(t.protocol!==`http:`&&t.protocol!==`https:`)throw Error(`EVE_DEV_OFFICIAL_REGISTRY_URL must be an HTTP(S) URL.`);if(t.username!==``||t.password!==``)throw Error(`EVE_DEV_OFFICIAL_REGISTRY_URL must not include credentials.`);if(t.search!==``||t.hash!==``)throw Error(`EVE_DEV_OFFICIAL_REGISTRY_URL must not include a query or fragment.`);return t.pathname=t.pathname.replace(/\/+$/,``),t.toString().replace(/\/$/,``)}const OFFICIAL_REGISTRY=resolveOfficialRegistryUrl(),OFFICIAL_CATALOG=`${OFFICIAL_REGISTRY}/registry.json`,SKILLS_REGISTRY=`@skills`;function isRegistryAddress(e){return e.startsWith(`@`)||/^https?:\/\//.test(e)}function itemAddress(e){return isRegistryAddress(e)?e:`${OFFICIAL_REGISTRY}/${e}.json`}async function installOfficialRegistryItem(e,t,n={}){let r=await readEveRegistryConfig(e);await addRegistryItems([itemAddress(t)],{config:r,cwd:e,overwrite:n.overwrite})}function assertCompatibleEveVersion(e){if(e===void 0)return;let t=resolveInstalledPackageInfo().version;if(semver.validRange(e)===null)throw Error(`Registry item has an invalid eve version requirement: ${e}.`);if(!semver.subset(t,e))throw Error(`This registry item requires eve ${e}, but this project is using eve ${t}. Upgrade eve and run the command again.`)}function isOfficialItemAddress(e){return e.startsWith(`${OFFICIAL_REGISTRY}/`)}function withBuiltInRegistries(e){return{...e,registries:{[SKILLS_REGISTRY]:`https://www.skills.sh/r/{name}?agent=eve`,...e.registries}}}async function readEveRegistryConfig(e){return withBuiltInRegistries(await readRegistryConfig(e))}function configuredRegistrySources(e){return Object.keys(e.registries??{})}function validateRegistrySource(e){if(e!==void 0&&!isRegistryAddress(e))throw Error(`Registry sources must be a namespace or URL: ${e}`)}async function loadOfficialSearchMetadata(){let e=await fetch(OFFICIAL_CATALOG);if(!e.ok)throw Error(`Could not read the eve registry (${e.status}).`);return parseOfficialRegistrySearchMetadata(await e.json())}function searchItemAddress(e){return e.registry===OFFICIAL_CATALOG?e.name:e.addCommandArgument}function enrichSearchItem(e,t){let n=searchItemAddress(e);return{item:e,address:n,...t.get(n)}}function registrySourceLabel(e){return e===OFFICIAL_CATALOG?`eve`:e===SKILLS_REGISTRY?`skills.sh`:e}function searchPresentationSections(e,t,n){return e.flatMap(e=>{let r=t.get(e);return r===void 0?[]:[{label:registrySourceLabel(e),items:r.items.map(e=>enrichSearchItem(e,n)),total:r.pagination.total}]})}async function printAddSuggestions(e,t,n){try{let{resultsBySource:r,sources:i,metadataByAddress:a}=await searchRegistryCatalog(t,{limit:5,query:n}),s=searchPresentationSections(i,r,a);if(s.every(e=>e.items.length===0))return;e.log(`Did you mean?`),printRegistrySearchResults(e,{query:n,sections:s})}catch{}}async function searchRegistryCatalog(e,t){validateRegistrySource(t.source);let n=await readEveRegistryConfig(e),r=t.source?[t.source]:[OFFICIAL_CATALOG,...configuredRegistrySources(n).filter(e=>t.query!==void 0||e!==SKILLS_REGISTRY)],i=await Promise.all(r.map(async e=>{try{return{result:await searchRegistries([e],{config:n,limit:t.limit??100,query:t.query}),source:e}}catch(t){return{error:t,source:e}}})),a=[],o=new Map;for(let e of i)if(`error`in e)a.push({message:errorMessage(e.error),registry:e.source});else{let t=e.result.errors??[];if(a.push(...t),t.length===0){let t=e.result.items.filter(t=>t.registry===e.source);o.set(e.source,{...e.result,items:t,pagination:{...e.result.pagination,total:e.result.items.length===t.length?e.result.pagination.total:0}})}}let s=new Map(a.map(e=>[`${e.registry}\0${e.message}`,e])),c=o.has(OFFICIAL_CATALOG)?await loadOfficialSearchMetadata():new Map,l=o.get(OFFICIAL_CATALOG);l!==void 0&&l.items.sort((e,t)=>{let rank=e=>c.get(searchItemAddress(e))?.implementation===`native`?0:1;return rank(e)-rank(t)});let d=[...o.values()];return{config:n,result:{items:d.flatMap(e=>e.items),pagination:{hasMore:d.some(e=>e.pagination.hasMore),limit:t.limit??100,offset:0,total:d.reduce((e,t)=>e+t.pagination.total,0)},...s.size>0?{errors:[...s.values()]}:{}},resultsBySource:o,sources:r,metadataByAddress:c}}function registryManifestTitle(e){if(typeof e!=`object`||!e||Array.isArray(e))return;let t=e.title;return typeof t==`string`?t:void 0}async function browseRegistryCatalog(e,t={}){let{config:n,result:r}=await searchRegistryCatalog(e,t),i=await Promise.all(r.items.map(async e=>{let[t]=await getRegistryItems([e.addCommandArgument],{config:n});return t}));return{items:r.items.map((e,t)=>{let n={address:e.registry===OFFICIAL_CATALOG?e.name:e.addCommandArgument,name:e.name,source:e.registry===OFFICIAL_CATALOG?`Vercel`:e.registry},r=registryManifestTitle(i[t]);return r!==void 0&&(n.title=r),e.type!==void 0&&(n.type=e.type),e.description!==void 0&&(n.description=e.description),n}),total:r.pagination.total,errors:r.errors??[]}}async function browseRegistryItems(e,t,n,r,i={}){let{result:a,resultsBySource:s,sources:c,metadataByAddress:l}=await searchRegistryCatalog(t,{limit:i.limit,query:n,source:r}),u=a.errors??[];if(i.json||s.size>0){let t=a.items.map(e=>({...e,...l.get(searchItemAddress(e))})),r={query:n,sections:searchPresentationSections(c,s,l)};i.json&&(r.json={...a,items:t}),printRegistrySearchResults(e,r)}for(let t of u)e.error(`${t.registry}: ${t.message}`);u.length>0&&(process.exitCode=1)}async function getRegistryItemManifest(e,t){let n=await readEveRegistryConfig(e),r=await getRegistryItems([itemAddress(t)],{config:n});return r.length===1?r[0]:r}async function installRegistryItem(e,t,n={},r=defaultAddCommandDependencies){let i,a=[],o={error:e=>{i=e},log:e=>a.push(e)},s=process.exitCode,c=await runAddCommand(o,e,t,{...n,yes:n.prompter===void 0||n.yes,setupAuthorized:n.prompter!==void 0},r);if(process.exitCode=s,i!==void 0)throw Error(i);let l={output:a};return c!==void 0&&(l.setup=c),l}async function runAddCommand(e,a,o,s,c=defaultAddCommandDependencies){return runRegistryAction(e,a,async()=>{let l=await readEveRegistryConfig(a),u=itemAddress(o);if(s.skipInstall===!0){if(s.overwrite===!0)throw Error(`--overwrite cannot be used with --skip-install.`);if(s.skipSetup===!0)throw Error(`--skip-install cannot be used with --skip-setup.`);if(!isOfficialItemAddress(u))throw Error(`Setup flows are currently supported only for official eve registry items.`)}let f=await resolveRegistryItemForAdd(e,async()=>(await getRegistryItems([u],{config:l}))[0],()=>printAddSuggestions(e,a,o));if(!f.found)return;let p=f.item,m=isOfficialItemAddress(u)?eveMetadataFromRegistryItem(p):void 0;if(assertCompatibleEveVersion(m?.requires),m?.components!==void 0){if(!isOfficialItemAddress(u))throw Error(`Registry packages require the official eve registry.`);let t=await runRegistryPackage({logger:e,appRoot:a,item:o,components:m.components,config:l,options:s,dependencies:c,operations:{itemAddress,metadata:eveMetadataFromRegistryItem,assertCompatibleVersion:assertCompatibleEveVersion,runSetups:({item:t,setups:r,prompter:i})=>runDeclaredSetups({logger:e,appRoot:a,item:t,setups:r,options:{yes:s.yes,nonInteractive:s.nonInteractive,answers:s.answers,prompter:i,signal:s.signal},dependencies:c,cancelledReminder:setupReminder(t,`cancelled`),resumeCommand:setupResumeCommand(t)}),setupReminder:e=>setupReminder(e,`skipped`)}});return t===!1?void 0:t}if(s.skipInstall===!0){if(m?.setup===void 0)throw Error(`Registry item "${o}" does not declare a setup flow.`);let t=await runDeclaredSetups({logger:e,appRoot:a,item:o,setups:m.setup,options:s,dependencies:c,cancelledReminder:setupReminder(o,`cancelled`),resumeCommand:setupResumeCommand(o)});return t===!1?void 0:t}if(await addRegistryItems([u],{config:l,cwd:a,overwrite:s.overwrite,silent:s.silent}),m?.setup===void 0)return;let h=c.hasInteractiveTerminal?.()??defaultAddCommandDependencies.hasInteractiveTerminal();if(s.nonInteractive&&e.log(serializeHeadlessSetupEvent({version:1,type:`progress`,message:s.skipInstall?`Setup ${o}`:`Installed ${o}`})),s.skipSetup===!0||!s.nonInteractive&&!s.yes&&!h&&s.setupAuthorized!==!0){e.log(setupReminder(o,`skipped`));return}if(!s.nonInteractive&&!s.yes&&s.setupAuthorized!==!0)try{if(await(s.prompter??c.createPrompter?.()??defaultAddCommandDependencies.createPrompter()).select({message:`Set up ${o} now?`,initialValue:`yes`,options:[{value:`yes`,label:`Yes`},{value:`no`,label:`No`}]})===`no`){e.log(setupReminder(o,`skipped`));return}}catch(t){if(!(t instanceof WizardCancelledError))throw t;e.log(setupReminder(o,`cancelled`));return}let g=await runDeclaredSetups({logger:e,appRoot:a,item:o,setups:m.setup,options:s,dependencies:c,cancelledReminder:setupReminder(o,`cancelled`),resumeCommand:setupResumeCommand(o)});return g!==!1&&s.nonInteractive&&e.log(serializeHeadlessSetupEvent({version:1,type:`completed`,item:o,completedItems:[o],...g.deploymentRequired===!0?{deploymentRequired:!0,next:{command:`eve`,args:[`deploy`]}}:{}})),g===!1?void 0:g})}async function runRegistryAddCommand(e,t,n){await runRegistryAction(e,t,async()=>{let r=await addRegistryMappings(t,n);for(let t of r.skippedBuiltIn)e.log(`Skipped ${t} because it is built in.`);for(let t of r.skippedExisting)e.log(`Skipped ${t} because it is already configured.`);r.added.length>0&&e.log(`Added ${r.added.join(`, `)} to package.json.`)})}async function runRegistryListCommand(e,t,n,r={}){await runRegistryAction(e,t,()=>browseRegistryItems(e,t,void 0,n,r))}async function runRegistrySearchCommand(e,t,n,r,i={}){await runRegistryAction(e,t,()=>browseRegistryItems(e,t,n,r,{...i,limit:i.limit??10}))}async function runRegistryViewCommand(e,t,n,r={}){await runRegistryAction(e,t,async()=>{let i=await readEveRegistryConfig(t),a=await getRegistryItems([itemAddress(n)],{config:i}),o=a.length===1?a[0]:a;e.log(r.json?JSON.stringify(o,null,2):registryViewText(n,o))})}export{browseRegistryCatalog,getRegistryItemManifest,installOfficialRegistryItem,installRegistryItem,resolveOfficialRegistryUrl,runAddCommand,runRegistryAddCommand,runRegistryListCommand,runRegistrySearchCommand,runRegistryViewCommand};
|
|
1
|
+
import{hasInteractiveTerminal}from"./preconditions.js";import{serializeHeadlessSetupEvent}from"./setup-headless.js";import{runDeclaredSetups}from"./registry-declared-setups.js";import{runRegistryPackage}from"./registry-package.js";import{eveMetadataFromRegistryItem,parseOfficialRegistrySearchMetadata}from"./registry-metadata.js";import{printRegistrySearchResults,registryViewText}from"./registry-presentation.js";import{addRegistryMappings,readRegistryConfig}from"./registry-project.js";import{errorMessage,resolveRegistryItemForAdd,runRegistryAction,setupReminder,setupResumeCommand}from"./registry-recovery.js";import{resolveInstalledPackageInfo}from"#internal/application/package.js";import{createPrompter}from"#setup/prompter.js";import{WizardCancelledError}from"#setup/step.js";import{addRegistryItems,getRegistryItems,searchRegistries}from"#compiled/shadcn-registry/index.js";import semver from"#compiled/semver/index.js";const defaultAddCommandDependencies={createPrompter,hasInteractiveTerminal,loadSetupCommandRunner:async()=>(await import(`./registry-setup-command.js`)).runRegistrySetupCommand};function resolveOfficialRegistryUrl(e=process.env.EVE_DEV_OFFICIAL_REGISTRY_URL){if(e===void 0)return`https://eve.dev/r`;let t;try{t=new URL(e)}catch{throw Error(`EVE_DEV_OFFICIAL_REGISTRY_URL must be an HTTP(S) URL.`)}if(t.protocol!==`http:`&&t.protocol!==`https:`)throw Error(`EVE_DEV_OFFICIAL_REGISTRY_URL must be an HTTP(S) URL.`);if(t.username!==``||t.password!==``)throw Error(`EVE_DEV_OFFICIAL_REGISTRY_URL must not include credentials.`);if(t.search!==``||t.hash!==``)throw Error(`EVE_DEV_OFFICIAL_REGISTRY_URL must not include a query or fragment.`);return t.pathname=t.pathname.replace(/\/+$/,``),t.toString().replace(/\/$/,``)}const OFFICIAL_REGISTRY=resolveOfficialRegistryUrl(),OFFICIAL_CATALOG=`${OFFICIAL_REGISTRY}/registry.json`,SKILLS_REGISTRY=`@skills`;function isRegistryAddress(e){return e.startsWith(`@`)||/^https?:\/\//.test(e)}function itemAddress(e){return isRegistryAddress(e)?e:`${OFFICIAL_REGISTRY}/${e}.json`}async function installOfficialRegistryItem(e,t,n={}){let r=await readEveRegistryConfig(e);await addRegistryItems([itemAddress(t)],{config:r,cwd:e,overwrite:n.overwrite})}function assertCompatibleEveVersion(e){if(e===void 0)return;let t=resolveInstalledPackageInfo().version;if(semver.validRange(e)===null)throw Error(`Registry item has an invalid eve version requirement: ${e}.`);if(!semver.subset(t,e))throw Error(`This registry item requires eve ${e}, but this project is using eve ${t}. Upgrade eve and run the command again.`)}function isOfficialItemAddress(e){return e.startsWith(`${OFFICIAL_REGISTRY}/`)}function withBuiltInRegistries(e){return{...e,registries:{[SKILLS_REGISTRY]:`https://www.skills.sh/r/{name}?agent=eve`,...e.registries}}}async function readEveRegistryConfig(e){return withBuiltInRegistries(await readRegistryConfig(e))}function configuredRegistrySources(e){return Object.keys(e.registries??{})}function validateRegistrySource(e){if(e!==void 0&&!isRegistryAddress(e))throw Error(`Registry sources must be a namespace or URL: ${e}`)}async function loadOfficialSearchMetadata(){let e=await fetch(OFFICIAL_CATALOG);if(!e.ok)throw Error(`Could not read the eve registry (${e.status}).`);return parseOfficialRegistrySearchMetadata(await e.json())}function searchItemAddress(e){return e.registry===OFFICIAL_CATALOG?e.name:e.addCommandArgument}function enrichSearchItem(e,t){let n=searchItemAddress(e);return{item:e,address:n,...t.get(n)}}function registrySourceLabel(e){return e===OFFICIAL_CATALOG?`eve`:e===SKILLS_REGISTRY?`skills.sh`:e}function searchPresentationSections(e,t,n){return e.flatMap(e=>{let r=t.get(e);return r===void 0?[]:[{label:registrySourceLabel(e),items:r.items.map(e=>enrichSearchItem(e,n)),total:r.pagination.total}]})}async function printAddSuggestions(e,t,n){try{let r=n.split(`/`).at(-1)||n,{resultsBySource:i,sources:a,metadataByAddress:s}=await searchRegistryCatalog(t,{limit:5,query:r}),c=searchPresentationSections(a,i,s);if(c.every(e=>e.items.length===0))return;e.log(`Did you mean?`),printRegistrySearchResults(e,{query:r,sections:c})}catch{}}async function searchRegistryCatalog(e,t){validateRegistrySource(t.source);let n=await readEveRegistryConfig(e),r=t.source?[t.source]:[OFFICIAL_CATALOG,...configuredRegistrySources(n).filter(e=>t.query!==void 0||e!==SKILLS_REGISTRY)],i=await Promise.all(r.map(async e=>{try{return{result:await searchRegistries([e],{config:n,limit:t.limit??100,query:t.query}),source:e}}catch(t){return{error:t,source:e}}})),a=[],o=new Map;for(let e of i)if(`error`in e)a.push({message:errorMessage(e.error),registry:e.source});else{let t=e.result.errors??[];if(a.push(...t),t.length===0){let t=e.result.items.filter(t=>t.registry===e.source);o.set(e.source,{...e.result,items:t,pagination:{...e.result.pagination,total:e.result.items.length===t.length?e.result.pagination.total:0}})}}let s=new Map(a.map(e=>[`${e.registry}\0${e.message}`,e])),c=o.has(OFFICIAL_CATALOG)?await loadOfficialSearchMetadata():new Map,l=o.get(OFFICIAL_CATALOG);l!==void 0&&l.items.sort((e,t)=>{let rank=e=>c.get(searchItemAddress(e))?.implementation===`native`?0:1;return rank(e)-rank(t)});let d=[...o.values()];return{config:n,result:{items:d.flatMap(e=>e.items),pagination:{hasMore:d.some(e=>e.pagination.hasMore),limit:t.limit??100,offset:0,total:d.reduce((e,t)=>e+t.pagination.total,0)},...s.size>0?{errors:[...s.values()]}:{}},resultsBySource:o,sources:r,metadataByAddress:c}}function registryManifestTitle(e){if(typeof e!=`object`||!e||Array.isArray(e))return;let t=e.title;return typeof t==`string`?t:void 0}async function browseRegistryCatalog(e,t={}){let{config:n,result:r}=await searchRegistryCatalog(e,t),i=await Promise.all(r.items.map(async e=>{let[t]=await getRegistryItems([e.addCommandArgument],{config:n});return t}));return{items:r.items.map((e,t)=>{let n={address:e.registry===OFFICIAL_CATALOG?e.name:e.addCommandArgument,name:e.name,source:e.registry===OFFICIAL_CATALOG?`Vercel`:e.registry},r=registryManifestTitle(i[t]);return r!==void 0&&(n.title=r),e.type!==void 0&&(n.type=e.type),e.description!==void 0&&(n.description=e.description),n}),total:r.pagination.total,errors:r.errors??[]}}async function browseRegistryItems(e,t,n,r,i={}){let{result:a,resultsBySource:s,sources:c,metadataByAddress:l}=await searchRegistryCatalog(t,{limit:i.limit,query:n,source:r}),u=a.errors??[];if(i.json||s.size>0){let t=a.items.map(e=>({...e,...l.get(searchItemAddress(e))})),r={query:n,sections:searchPresentationSections(c,s,l)};i.json&&(r.json={...a,items:t}),printRegistrySearchResults(e,r)}for(let t of u)e.error(`${t.registry}: ${t.message}`);u.length>0&&(process.exitCode=1)}async function getRegistryItemManifest(e,t){let n=await readEveRegistryConfig(e),r=await getRegistryItems([itemAddress(t)],{config:n});return r.length===1?r[0]:r}async function installRegistryItem(e,t,n={},r=defaultAddCommandDependencies){let i,a=[],o={error:e=>{i=e},log:e=>a.push(e)},s=process.exitCode,c=await runAddCommand(o,e,t,{...n,yes:n.prompter===void 0||n.yes,setupAuthorized:n.prompter!==void 0},r);if(process.exitCode=s,i!==void 0)throw Error(i);let l={output:a};return c!==void 0&&(l.setup=c),l}async function runAddCommand(e,a,o,s,c=defaultAddCommandDependencies){return runRegistryAction(e,a,async()=>{let l=await readEveRegistryConfig(a),u=itemAddress(o);if(s.skipInstall===!0){if(s.overwrite===!0)throw Error(`--overwrite cannot be used with --skip-install.`);if(s.skipSetup===!0)throw Error(`--skip-install cannot be used with --skip-setup.`);if(!isOfficialItemAddress(u))throw Error(`Setup flows are currently supported only for official eve registry items.`)}let f=await resolveRegistryItemForAdd(e,async()=>(await getRegistryItems([u],{config:l}))[0],()=>printAddSuggestions(e,a,o));if(!f.found)return;let p=f.item,m=isOfficialItemAddress(u)?eveMetadataFromRegistryItem(p):void 0;if(assertCompatibleEveVersion(m?.requires),m?.components!==void 0){if(!isOfficialItemAddress(u))throw Error(`Registry packages require the official eve registry.`);let t=await runRegistryPackage({logger:e,appRoot:a,item:o,components:m.components,config:l,options:s,dependencies:c,operations:{itemAddress,metadata:eveMetadataFromRegistryItem,assertCompatibleVersion:assertCompatibleEveVersion,runSetups:({item:t,setups:r,prompter:i})=>runDeclaredSetups({logger:e,appRoot:a,item:t,setups:r,options:{yes:s.yes,nonInteractive:s.nonInteractive,answers:s.answers,prompter:i,signal:s.signal},dependencies:c,cancelledReminder:setupReminder(t,`cancelled`),resumeCommand:setupResumeCommand(t)}),setupReminder:e=>setupReminder(e,`skipped`)}});return t===!1?void 0:t}if(s.skipInstall===!0){if(m?.setup===void 0)throw Error(`Registry item "${o}" does not declare a setup flow.`);let t=await runDeclaredSetups({logger:e,appRoot:a,item:o,setups:m.setup,options:s,dependencies:c,cancelledReminder:setupReminder(o,`cancelled`),resumeCommand:setupResumeCommand(o)});return t===!1?void 0:t}if(await addRegistryItems([u],{config:l,cwd:a,overwrite:s.overwrite,silent:s.silent}),m?.setup===void 0)return;let h=c.hasInteractiveTerminal?.()??defaultAddCommandDependencies.hasInteractiveTerminal();if(s.nonInteractive&&e.log(serializeHeadlessSetupEvent({version:1,type:`progress`,message:s.skipInstall?`Setup ${o}`:`Installed ${o}`})),s.skipSetup===!0||!s.nonInteractive&&!s.yes&&!h&&s.setupAuthorized!==!0){e.log(setupReminder(o,`skipped`));return}if(!s.nonInteractive&&!s.yes&&s.setupAuthorized!==!0)try{if(await(s.prompter??c.createPrompter?.()??defaultAddCommandDependencies.createPrompter()).select({message:`Set up ${o} now?`,initialValue:`yes`,options:[{value:`yes`,label:`Yes`},{value:`no`,label:`No`}]})===`no`){e.log(setupReminder(o,`skipped`));return}}catch(t){if(!(t instanceof WizardCancelledError))throw t;e.log(setupReminder(o,`cancelled`));return}let g=await runDeclaredSetups({logger:e,appRoot:a,item:o,setups:m.setup,options:s,dependencies:c,cancelledReminder:setupReminder(o,`cancelled`),resumeCommand:setupResumeCommand(o)});return g!==!1&&s.nonInteractive&&e.log(serializeHeadlessSetupEvent({version:1,type:`completed`,item:o,completedItems:[o],...g.deploymentRequired===!0?{deploymentRequired:!0,next:{command:`eve`,args:[`deploy`]}}:{}})),g===!1?void 0:g})}async function runRegistryAddCommand(e,t,n){await runRegistryAction(e,t,async()=>{let r=await addRegistryMappings(t,n);for(let t of r.skippedBuiltIn)e.log(`Skipped ${t} because it is built in.`);for(let t of r.skippedExisting)e.log(`Skipped ${t} because it is already configured.`);r.added.length>0&&e.log(`Added ${r.added.join(`, `)} to package.json.`)})}async function runRegistryListCommand(e,t,n,r={}){await runRegistryAction(e,t,()=>browseRegistryItems(e,t,void 0,n,r))}async function runRegistrySearchCommand(e,t,n,r,i={}){await runRegistryAction(e,t,()=>browseRegistryItems(e,t,n,r,{...i,limit:i.limit??10}))}async function runRegistryViewCommand(e,t,n,r={}){await runRegistryAction(e,t,async()=>{let i=await readEveRegistryConfig(t),a=await getRegistryItems([itemAddress(n)],{config:i}),o=a.length===1?a[0]:a;e.log(r.json?JSON.stringify(o,null,2):registryViewText(n,o))})}export{browseRegistryCatalog,getRegistryItemManifest,installOfficialRegistryItem,installRegistryItem,resolveOfficialRegistryUrl,runAddCommand,runRegistryAddCommand,runRegistryListCommand,runRegistrySearchCommand,runRegistryViewCommand};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{summarizeToolArgs,summarizeToolResult}from"./tool-format.js";import{diffWriteDetail}from"./line-diff.js";import{stripTerminalControls}from"#cli/ui/terminal-text.js";const WRITE_FILE_VERB=`Write`,DELEGATE_VERB=`Delegate`,FINAL_OUTPUT_TITLE=`Return final output`,BUILTIN_TOOL_COPY={agent:{verb:`Delegate`,pastVerb:`Delegated`,argKey:`message`,singularNoun:`task`,pluralNoun:`tasks`},ask_question:{verb:`Ask`,pastVerb:`Asked`,argKey:`prompt`,singularNoun:`question`,pluralNoun:`questions`},bash:{verb:`Run`,pastVerb:`Ran`,argKey:`command`,singularNoun:`command`,pluralNoun:`commands`},connection_search:{verb:`Discover`,pastVerb:`Discovered`,argKey:`keywords`,singularNoun:`tool search`,pluralNoun:`tool searches`},glob:{verb:`Glob`,pastVerb:`Globbed`,argKey:`pattern`,singularNoun:`pattern`,pluralNoun:`patterns`},grep:{verb:`Grep`,pastVerb:`Grepped`,argKey:`pattern`,singularNoun:`pattern`,pluralNoun:`patterns`},load_skill:{verb:`Load`,pastVerb:`Loaded`,argKey:`skill`,singularNoun:`skill`,pluralNoun:`skills`},read_file:{verb:`Read`,pastVerb:`Read`,argKey:`filePath`,singularNoun:`file`,pluralNoun:`files`},task_cancel:{verb:`Cancel`,pastVerb:`Cancelled`,argKey:`taskIds`,extractItem:taskIdsArg,singularNoun:`task`,pluralNoun:`tasks`},task_peek:{verb:`Check`,pastVerb:`Checked`,argKey:`taskIds`,extractItem:taskIdsArg,singularNoun:`task`,pluralNoun:`tasks`},
|
|
1
|
+
import{summarizeToolArgs,summarizeToolResult}from"./tool-format.js";import{diffWriteDetail}from"./line-diff.js";import{stripTerminalControls}from"#cli/ui/terminal-text.js";const WRITE_FILE_VERB=`Write`,DELEGATE_VERB=`Delegate`,FINAL_OUTPUT_TITLE=`Return final output`,BUILTIN_TOOL_COPY={agent:{verb:`Delegate`,pastVerb:`Delegated`,argKey:`message`,singularNoun:`task`,pluralNoun:`tasks`},ask_question:{verb:`Ask`,pastVerb:`Asked`,argKey:`prompt`,singularNoun:`question`,pluralNoun:`questions`},bash:{verb:`Run`,pastVerb:`Ran`,argKey:`command`,singularNoun:`command`,pluralNoun:`commands`},connection_search:{verb:`Discover`,pastVerb:`Discovered`,argKey:`keywords`,singularNoun:`tool search`,pluralNoun:`tool searches`},glob:{verb:`Glob`,pastVerb:`Globbed`,argKey:`pattern`,singularNoun:`pattern`,pluralNoun:`patterns`},grep:{verb:`Grep`,pastVerb:`Grepped`,argKey:`pattern`,singularNoun:`pattern`,pluralNoun:`patterns`},load_skill:{verb:`Load`,pastVerb:`Loaded`,argKey:`skill`,singularNoun:`skill`,pluralNoun:`skills`},read_file:{verb:`Read`,pastVerb:`Read`,argKey:`filePath`,singularNoun:`file`,pluralNoun:`files`},task_cancel:{verb:`Cancel`,pastVerb:`Cancelled`,argKey:`taskIds`,extractItem:taskIdsArg,singularNoun:`task`,pluralNoun:`tasks`},task_peek:{verb:`Check`,pastVerb:`Checked`,argKey:`taskIds`,extractItem:taskIdsArg,singularNoun:`task`,pluralNoun:`tasks`},task_sleep:{verb:`Pause`,pastVerb:`Paused`,argKey:`seconds`,extractItem:sleepSecondsArg,singularNoun:`pause`,pluralNoun:`pauses`},task_update:{verb:`Update`,pastVerb:`Updated`,argKey:`message`,singularNoun:`parent`,pluralNoun:`parents`},web_fetch:{verb:`Fetch`,pastVerb:`Fetched`,argKey:`url`,singularNoun:`URL`,pluralNoun:`URLs`},web_search:{verb:`Search`,pastVerb:`Searched`,argKey:`query`,extractItem:e=>salientArg(e,`objective`)??salientArg(e,`search_query`)??salientArg(e,`searchQuery`)??webSearchActionArg(e),singularNoun:`query`,pluralNoun:`queries`}};function presentWriteFileTool(a,o,s){let c=readWriteFileInput(a,o);if(c===void 0)return{title:a,subtitle:summarizeToolArgs(o),summarizeResult:summarizeToolResult};let l=diffWriteDetail(s?.previousContent,c.content,s?.existed).map(e=>e.kind===`gap`?e:{...e,text:stripTerminalControls(e.text)});return{title:`${WRITE_FILE_VERB} ${salientLine(c.path)??c.path}`,doneTitle:`Wrote ${salientLine(c.path)??c.path}`,subtitle:``,summarizeResult:()=>void 0,detail:l,keepDetailWhenDone:!0}}function readWriteFileInput(e,t){if(toolBaseName(e)!==`write_file`||typeof t!=`object`||!t||Array.isArray(t))return;let n=t,r=n.filePath,i=n.content;if(!(typeof r!=`string`||r.trim().length===0)&&typeof i==`string`)return{path:r,content:i}}function presentTool(n,r,i){let c=toolBaseName(n);if(c===`todo`)return presentTodoTool(r);if(c===`write_file`)return presentWriteFileTool(n,r,i);if(i?.isSubagent===!0)return{title:`${DELEGATE_VERB} ${c}`,doneTitle:`Delegated ${c}`,subtitle:salientArg(r,`message`)??``,summarizeResult:()=>void 0};if(c===`final_output`)return{title:FINAL_OUTPUT_TITLE,subtitle:``,summarizeResult:()=>void 0};let l=BUILTIN_TOOL_COPY[c];if(l!==void 0){let e=salientArg(r,l.argKey)??l.extractItem?.(r);if(e!==void 0)return{title:`${l.verb} ${e}`,doneTitle:`${l.pastVerb} ${e}`,subtitle:``,summarizeResult:()=>void 0,group:{verb:l.verb,pastVerb:l.pastVerb,singularNoun:l.singularNoun,pluralNoun:l.pluralNoun,item:e}}}return{title:n,subtitle:summarizeToolArgs(r),summarizeResult:summarizeToolResult}}function presentPreparingTool(e,t){let n=toolBaseName(e);if(n===`final_output`)return{title:FINAL_OUTPUT_TITLE,subtitle:``,summarizeResult:()=>void 0};if(t?.isSubagent===!0)return{title:`${DELEGATE_VERB} ${n} …`,subtitle:``,summarizeResult:()=>void 0};let r=n===`write_file`?WRITE_FILE_VERB:BUILTIN_TOOL_COPY[n]?.verb;return{title:r===void 0?e:`${r} …`,subtitle:r===void 0?`preparing…`:``,summarizeResult:()=>void 0}}function isPanelRoutedTool(e){let t=toolBaseName(e);return t===`todo`||t===`ask_question`}function toolBaseName(e){return e.split(/[.:/]/u).at(-1)??e}function presentTodoTool(e){let t=typeof e==`object`&&e&&!Array.isArray(e)?e.todos:void 0;return Array.isArray(t)?{title:`Update todo list`,subtitle:`${t.length} task${t.length===1?``:`s`}`,summarizeResult:()=>void 0}:{title:`Read todo list`,subtitle:``,summarizeResult:()=>void 0}}function webSearchActionArg(e){if(typeof e!=`object`||!e||Array.isArray(e))return;let t=e.action;if(typeof t!=`object`||!t||Array.isArray(t))return;let n=t,r=n.queries;if(Array.isArray(r)){let e=r.filter(e=>typeof e==`string`).map(salientLine).filter(e=>e!==void 0).join(`, `);if(e.length>0)return e}for(let e of[`query`,`url`,`pattern`]){let t=n[e];if(typeof t==`string`){let e=salientLine(t);if(e!==void 0)return e}}}function taskIdsArg(e){if(typeof e!=`object`||!e||Array.isArray(e))return;let t=e.taskIds;if(!Array.isArray(t))return;let n=t.filter(e=>typeof e==`string`);if(n.length!==0)return salientLine(n.join(`, `))}function sleepSecondsArg(e){if(typeof e!=`object`||!e||Array.isArray(e))return;let t=e.seconds;if(!(typeof t!=`number`||!Number.isFinite(t)))return`${String(t)}s`}function salientArg(e,t){if(typeof e!=`object`||!e||Array.isArray(e))return;let n=e[t];if(typeof n==`string`)return salientLine(n)}function salientLine(e){let t=stripTerminalControls(e).split(/\r?\n/u).find(e=>e.trim().length>0)?.trim()??``;return t.length>0?t:void 0}export{isPanelRoutedTool,presentPreparingTool,presentTool,readWriteFileInput,toolBaseName};
|
|
@@ -36,12 +36,12 @@
|
|
|
36
36
|
"@vercel/otel": "2.1.3",
|
|
37
37
|
"@vercel/sandbox-drives": "2.10.0-beta.0",
|
|
38
38
|
"@vercel/sandbox": "2.8.0",
|
|
39
|
-
"@workflow/core": "5.0.0-beta.
|
|
40
|
-
"@workflow/errors": "5.0.0-beta.
|
|
39
|
+
"@workflow/core": "5.0.0-beta.42",
|
|
40
|
+
"@workflow/errors": "5.0.0-beta.17",
|
|
41
41
|
"@workflow/serde": "5.0.0-beta.2",
|
|
42
|
-
"@workflow/world": "5.0.0-beta.
|
|
43
|
-
"@workflow/world-local": "5.0.0-beta.
|
|
44
|
-
"@workflow/world-vercel": "5.0.0-beta.
|
|
42
|
+
"@workflow/world": "5.0.0-beta.27",
|
|
43
|
+
"@workflow/world-local": "5.0.0-beta.36",
|
|
44
|
+
"@workflow/world-vercel": "5.0.0-beta.38",
|
|
45
45
|
"zod": "4.4.3",
|
|
46
46
|
"zod-validation-error": "5.0.0"
|
|
47
47
|
},
|
|
@@ -45,6 +45,16 @@ export interface EventsConsumerOptions {
|
|
|
45
45
|
* downstream subscribe() calls have a chance to cancel the check first.
|
|
46
46
|
*/
|
|
47
47
|
onUnconsumedEvent: (event: Event) => void;
|
|
48
|
+
/**
|
|
49
|
+
* Callback invoked when an event is skipped because it repeats an event
|
|
50
|
+
* class the walk already consumed for the same entity. `firstEventType` is
|
|
51
|
+
* the type that recorded the class, which is the one the workflow observed.
|
|
52
|
+
* Diagnostics only: skipping is a normal outcome, not an error — though a
|
|
53
|
+
* `firstEventType` differing from `event.eventType` says the two writers
|
|
54
|
+
* decided the entity's outcome differently, which is worth more than an
|
|
55
|
+
* info log.
|
|
56
|
+
*/
|
|
57
|
+
onDuplicateEvent?: (event: Event, firstEventType: Event['eventType']) => void;
|
|
48
58
|
/**
|
|
49
59
|
* Returns the current promise queue. The unconsumed event check is chained
|
|
50
60
|
* onto this queue so it only fires after all pending async work (e.g.,
|
|
@@ -87,8 +97,17 @@ export declare class EventsConsumer {
|
|
|
87
97
|
* rather than parked for a consumer that cannot exist.
|
|
88
98
|
*/
|
|
89
99
|
private readonly resolved;
|
|
100
|
+
/**
|
|
101
|
+
* `<class>:<correlationId>` for every event class the walk has already
|
|
102
|
+
* consumed, mapped to the event type that recorded it. The type is kept so a
|
|
103
|
+
* repeat that decided the same class *differently* (a `step_failed` behind a
|
|
104
|
+
* `step_completed`) can be reported as more than a re-commit. See
|
|
105
|
+
* {@link EventsConsumer.firstEventTypeOfClass}.
|
|
106
|
+
*/
|
|
107
|
+
private readonly seenEventClasses;
|
|
90
108
|
private onConsumedEvent?;
|
|
91
109
|
private onUnconsumedEvent;
|
|
110
|
+
private onDuplicateEvent?;
|
|
92
111
|
private getPromiseQueue;
|
|
93
112
|
private isDeliveryIdle;
|
|
94
113
|
private pendingUnconsumedCheck;
|
|
@@ -153,8 +172,86 @@ export declare class EventsConsumer {
|
|
|
153
172
|
* replay diverged.
|
|
154
173
|
*/
|
|
155
174
|
private park;
|
|
175
|
+
/**
|
|
176
|
+
* The key `event`'s class is tracked under, or `undefined` for the event
|
|
177
|
+
* types that belong to no class (`hook_received`, `hook_conflict`,
|
|
178
|
+
* `attr_set`, `run_created`) and are therefore never skipped.
|
|
179
|
+
*
|
|
180
|
+
* Run events carry no correlation id. They are classes of the run itself, so
|
|
181
|
+
* they all key off the same bucket.
|
|
182
|
+
*/
|
|
183
|
+
private eventClassKey;
|
|
184
|
+
/**
|
|
185
|
+
* Remembers that `event`'s class is now decided for its entity, if the type
|
|
186
|
+
* belongs to a class. First writer wins: the recorded type is the one the
|
|
187
|
+
* workflow observed, and a later repeat is measured against it.
|
|
188
|
+
*/
|
|
189
|
+
private recordEventClass;
|
|
190
|
+
/**
|
|
191
|
+
* The type that already decided `event`'s class for the same entity, or
|
|
192
|
+
* `undefined` when nothing has: a second `step_created` for one step, a
|
|
193
|
+
* second terminal outcome, a second `step_started` after the step's result is
|
|
194
|
+
* already in the log.
|
|
195
|
+
*
|
|
196
|
+
* Such an event is committed but inert. Concurrent replays write into one
|
|
197
|
+
* log without a currency guard, so a replay working from a prefix that
|
|
198
|
+
* predates another replay's write can commit its own copy of work the log
|
|
199
|
+
* already records. That copy cannot change what the workflow observed: the
|
|
200
|
+
* outcome was decided by the first event of the class and every later replay
|
|
201
|
+
* reads that same event at the same log position, so ignoring the straggler
|
|
202
|
+
* is deterministic across replays.
|
|
203
|
+
*
|
|
204
|
+
* Classes are tracked separately, so passing one does not suppress another.
|
|
205
|
+
* A step whose result is in the log still reaches its `step_created` and
|
|
206
|
+
* `step_started` consumers if it has yet to see those classes.
|
|
207
|
+
*
|
|
208
|
+
* Consulted only after every registered callback has declined the event, so
|
|
209
|
+
* it can never take an event a consumer wanted. A retry's `step_started` is
|
|
210
|
+
* claimed by the step's live consumer and counts as an attempt exactly as
|
|
211
|
+
* before, and a second `step_created` reaching a step that has not finished
|
|
212
|
+
* is likewise consumed rather than skipped; only the copies nobody claims are
|
|
213
|
+
* skipped.
|
|
214
|
+
*
|
|
215
|
+
* Unlike the divergence report, this does *not* wait out the deferred window
|
|
216
|
+
* first, and it does not need to. The window buys time for a consumer that
|
|
217
|
+
* has yet to register, and no such consumer can want this event: the class
|
|
218
|
+
* was recorded by a consumption in this same replay, which means the entity's
|
|
219
|
+
* consumer was registered and took an event of this class, and correlation
|
|
220
|
+
* ids are minted from a monotonic ULID per body position, so nothing later in
|
|
221
|
+
* the body registers a second consumer under this id. Waiting would cost
|
|
222
|
+
* `getDeferredCheckDelayMs()` per straggler per replay for information that
|
|
223
|
+
* cannot arrive — 0.75% of production runs carry at least one straggler, and
|
|
224
|
+
* the p99 among those carries 155.
|
|
225
|
+
*
|
|
226
|
+
* The invariant to preserve if hook identity ever becomes caller-supplied
|
|
227
|
+
* (an idempotency key rather than a minted id): two `createHook` calls in one
|
|
228
|
+
* body could then share a correlation id, and the second consumer's
|
|
229
|
+
* `hook_created` would be a repeat of a class this replay already recorded.
|
|
230
|
+
* That would make skipping wrong for `hook_created`, and is the reason the
|
|
231
|
+
* class map lives next to the event types rather than being inferred.
|
|
232
|
+
*/
|
|
233
|
+
private firstEventTypeOfClass;
|
|
234
|
+
/** Steps the walk over a repeat of an already-consumed class. */
|
|
235
|
+
private skipDuplicateEvent;
|
|
156
236
|
private handleEndOfLog;
|
|
157
237
|
private scheduleUnconsumedCheck;
|
|
238
|
+
/**
|
|
239
|
+
* Decide what a still-unconsumed event is, now that the promise queue has
|
|
240
|
+
* drained and the delivery gate says the VM is not mid-reaction.
|
|
241
|
+
*
|
|
242
|
+
* `mayPark` is false only for the end-of-log recheck of an event {@link park}
|
|
243
|
+
* already holds. Nothing in the first branch applies to one of those: it is
|
|
244
|
+
* not the event at the cursor, so the identity guard is not meaningful, and
|
|
245
|
+
* it is parked already.
|
|
246
|
+
*
|
|
247
|
+
* A duplicate class never arrives here. {@link consume} steps over one in the
|
|
248
|
+
* pass that offered it, before this check is ever scheduled, and a class
|
|
249
|
+
* recorded while the check was in flight can only have been recorded by a
|
|
250
|
+
* consumption inside {@link consume}, whose next pass re-offers this event
|
|
251
|
+
* and steps over it there — leaving the identity guard above to drop the
|
|
252
|
+
* in-flight check.
|
|
253
|
+
*/
|
|
254
|
+
private resolveUnconsumedEvent;
|
|
158
255
|
/**
|
|
159
256
|
* Run `fn` once no data delivery is in flight, polling the way
|
|
160
257
|
* `scheduleWhenIdle` does: let the promise queue drain, re-check a timer
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
2
|
-
`)}`)}return await x(t,r.value)}}}async function E(e,t={}){let n=
|
|
1
|
+
import{E as e,I as t,P as n,X as r,Z as i,ar as a,cr as o,fr as s,hr as c,lr as l,mr as u,nr as d,pr as f,qn as p,sr as m}from"../../_chunks/workflow/wait-until-BPSgt1Wg.js";import{i as h,u as g}from"../../_chunks/workflow/dist-CqvWKNQv.js";import{a as _,i as v,r as y,t as b}from"../../_chunks/workflow/attribute-changes-B8K0pwpH.js";import"../../_chunks/workflow/dist-BRGbNjhD.js";import{n as x}from"../../_chunks/workflow/resume-hook-DO_MuJlY.js";import{t as S}from"../../_chunks/workflow/sleep-7TgF3i-e.js";function C(e){v(`createHook()`,`https://workflow-sdk.dev/docs/api-reference/workflow/create-hook`,C)}function w(e){v(`createWebhook()`,`https://workflow-sdk.dev/docs/api-reference/workflow/create-webhook`,w)}function T({schema:e}={}){function t(e){v(`defineHook().create()`,`https://workflow-sdk.dev/docs/api-reference/workflow/define-hook`,t)}return{create:t,async resume(t,n){if(!e?.[`~standard`])return await x(t,n);let r=e[`~standard`].validate(n);if(r instanceof Promise&&(r=await r),r.issues){let e=r.issues.map(e=>{let t=e.path?.map(e=>String(typeof e==`object`&&e?e.key:e)).join(`.`);return t?` at "${t}": ${e.message}`:` ${e.message}`});throw Error(`Hook payload did not match the defined schema:\n${e.join(`
|
|
2
|
+
`)}`)}return await x(t,r.value)}}}async function E(e,t={}){let n=i.getStore(),r=n?.workflowMetadata?.workflowRunId;if(!r)throw new h(`setAttributes() must be called from a 'use workflow' or 'use step' function. Calling it from plain host code is not supported.`);let o=b(e,t);if(o.length!==0){if(n.runReadyBarrier)try{await n.runReadyBarrier}catch{}await(await a()).events.create(r,{eventType:`attr_set`,specVersion:6,eventData:{changes:o,writer:{type:`step`,stepId:n.stepMetadata.stepId,attempt:n.stepMetadata.attempt},...t.allowReservedAttributes===!0?{allowReservedAttributes:!0}:{}}})}}function D(){let e=i.getStore();return e||y(`getStepMetadata()`,`https://workflow-sdk.dev/docs/api-reference/workflow/get-step-metadata`,D),e.stepMetadata}function O(){let e=i.getStore();return e||_(`getWorkflowMetadata()`,`https://workflow-sdk.dev/docs/api-reference/workflow/get-workflow-metadata`,O),e.workflowMetadata}function k(a={}){let h=i.getStore();h||_(`getWritable()`,`https://workflow-sdk.dev/docs/api-reference/workflow/get-writable`,k);let{namespace:g}=a,v=h.workflowMetadata.workflowRunId,y=r(v,g);h.writables??=new Map;let b=h.writables,x=b.get(y);if(x)return x.writable;let S=t(n(globalThis,h.ops,v,h.encryptionKey,!0,h.runReadyBarrier),h.encryptionKey),C=new e(v,y,h.runReadyBarrier),w=m();return h.ops.push(w.promise),o(S.readable,C,w).catch(()=>{}),l(S.writable,w),Object.defineProperty(S.writable,s,{value:y,writable:!1}),Object.defineProperty(S.writable,c,{value:v,writable:!1}),h.workflowDeploymentId&&Object.defineProperty(S.writable,f,{value:h.workflowDeploymentId,writable:!1}),p(h.encryptionKey)&&Object.defineProperty(S.writable,u,{value:d(h.encryptionKey.keyPair.publicKey),writable:!1}),b.set(y,{writable:S.writable,state:w}),S.writable}export{h as FatalError,g as RetryableError,C as createHook,w as createWebhook,T as defineHook,D as getStepMetadata,O as getWorkflowMetadata,k as getWritable,E as setAttributes,S as sleep};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{a as e,i as t,n,o as r,r as i,s as a,t as o}from"../../_chunks/workflow/private-
|
|
1
|
+
import{a as e,i as t,n,o as r,r as i,s as a,t as o}from"../../_chunks/workflow/private-z_2Kcwee.js";export{o as awaitEarlierDeliveries,n as getStepFunction,i as hasParkedCommittedDelivery,t as isDeliveryIdle,e as registerDeliveryBarrier,r as registerStepFunction,a as scheduleWhenIdle};
|
|
@@ -85,6 +85,41 @@ export declare const MAX_MAX_INLINE_STEPS = 16;
|
|
|
85
85
|
* one-time warning so misconfiguration is observable.
|
|
86
86
|
*/
|
|
87
87
|
export declare function getMaxInlineSteps(): number;
|
|
88
|
+
/**
|
|
89
|
+
* Upper bound on the serialized step input that resilient step dispatch will
|
|
90
|
+
* inline into the queue message's `stepInput`.
|
|
91
|
+
*
|
|
92
|
+
* Vercel Queues has no hard message-size cap (bodies above its ~256 KB
|
|
93
|
+
* inline threshold transparently spill to S3-backed storage), so this bound
|
|
94
|
+
* is a cost/latency choice, not a rejection guard: the message also carries
|
|
95
|
+
* the runId, stepId, stepName, and trace carrier alongside CBOR framing
|
|
96
|
+
* overhead, and staying under the queue's inline threshold keeps step
|
|
97
|
+
* messages on its fast inline path instead of paying an S3 store+fetch
|
|
98
|
+
* double-hop for bytes that already live in the event log. Above this size
|
|
99
|
+
* the dispatch falls back to the sequential path (`step_created` write, then
|
|
100
|
+
* a payload-less queue message). Matches `MAX_INLINE_RESUME_PAYLOAD_BYTES`
|
|
101
|
+
* on the resilient hook resume path.
|
|
102
|
+
*/
|
|
103
|
+
export declare const MAX_RESILIENT_STEP_INPUT_BYTES: number;
|
|
104
|
+
/**
|
|
105
|
+
* Whether resilient step dispatch is enabled: the suspension handler
|
|
106
|
+
* parallelizes each newly created step's `step_created` event write with its
|
|
107
|
+
* step-execution queue publish, carrying the serialized step input in the
|
|
108
|
+
* queue message (`stepInput`) so the consumer can idempotently re-ensure the
|
|
109
|
+
* event if the direct write failed transiently. Mirrors the resilient start
|
|
110
|
+
* (`runInput`) and resilient hook resume (`hookInput`) patterns.
|
|
111
|
+
*
|
|
112
|
+
* **Off by default.** Enable via `WORKFLOW_RESILIENT_STEP_DISPATCH=1`.
|
|
113
|
+
*
|
|
114
|
+
* The queue publish races the create's verdict, and a create can come back
|
|
115
|
+
* refused — as a duplicate this replay should stop pursuing, or as a stale
|
|
116
|
+
* write on a World that refuses rather than reports. Either way the message
|
|
117
|
+
* carrying the payload is already out, so the consumer can materialize a step
|
|
118
|
+
* whose create was refused, and nothing orders the verdict before the
|
|
119
|
+
* consumer's redelivery re-ensure. Enabling this trades that window for the
|
|
120
|
+
* latency the parallel publish saves.
|
|
121
|
+
*/
|
|
122
|
+
export declare function isResilientStepDispatchEnabled(): boolean;
|
|
88
123
|
/**
|
|
89
124
|
* Optional client-side override for the server-supplied per-run event ceiling.
|
|
90
125
|
* When set to a positive integer, the runtime clamps the server's limit *down*
|
|
@@ -90,13 +90,11 @@ export declare function healthCheck(world: World, options?: HealthCheckOptions):
|
|
|
90
90
|
* same array. The set is updated alongside `target`.
|
|
91
91
|
*
|
|
92
92
|
* Events are appended in the order the World returned them, and are not
|
|
93
|
-
* re-sorted
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
* receipt order is the order to keep. Nothing downstream may assume the tail is
|
|
99
|
-
* the newest event — see {@link latestEventStateUpdatedAt}.
|
|
93
|
+
* re-sorted. Every append source is already in canonical order relative to the
|
|
94
|
+
* tail (a cursor-delimited page, or a write-response delta), so receipt order is
|
|
95
|
+
* the order to keep, and re-sorting here would only cost a pass over the log.
|
|
96
|
+
* Nothing downstream may assume the tail is the newest event — see
|
|
97
|
+
* {@link maxEventSlot}.
|
|
100
98
|
*/
|
|
101
99
|
export declare function appendUniqueEvents(target: Event[], events: readonly Event[], targetIds?: Set<string>): void;
|
|
102
100
|
/**
|
|
@@ -107,8 +105,12 @@ export declare function appendUniqueEvents(target: Event[], events: readonly Eve
|
|
|
107
105
|
* `preloadedEvents` is loaded `sortOrder: 'asc'` and is never re-sorted
|
|
108
106
|
* client-side, so a `hook_received` spliced in by the lazy-resume consumer must
|
|
109
107
|
* land in `eventId` order — a plain `push` would place a late-committing
|
|
110
|
-
* earlier event after events that sort before it, corrupting replay.
|
|
111
|
-
*
|
|
108
|
+
* earlier event after events that sort before it, corrupting replay.
|
|
109
|
+
*
|
|
110
|
+
* Lexicographic string order is the log's order: a slot id is a fixed-width
|
|
111
|
+
* zero-padded position, so comparing the strings compares the positions. This
|
|
112
|
+
* needs no parse of its own for that reason, and the comparison is exact rather
|
|
113
|
+
* than a reconstruction.
|
|
112
114
|
*/
|
|
113
115
|
export declare function insertEventByEventId(target: Event[], event: Event): void;
|
|
114
116
|
/**
|
|
@@ -132,70 +134,76 @@ export interface LoadedEventLog {
|
|
|
132
134
|
events: Event[];
|
|
133
135
|
cursor: string | null;
|
|
134
136
|
}
|
|
135
|
-
/**
|
|
136
|
-
* Whether the optimistic-concurrency guard for event creation is enabled.
|
|
137
|
-
* **On by default** where the runtime executes: replay-context creates send a
|
|
138
|
-
* `stateUpdatedAt` snapshot (and can be rejected with 412 by a supporting
|
|
139
|
-
* backend) unless `WORKFLOW_PRECONDITION_GUARD` is set to `0`. Backends without
|
|
140
|
-
* guard support ignore the snapshot, so enabling by default is
|
|
141
|
-
* backward-compatible.
|
|
142
|
-
*/
|
|
143
|
-
export declare function isPreconditionGuardEnabled(): boolean;
|
|
144
137
|
/**
|
|
145
138
|
* Whether a replay refuses to run over a log with a hole in it (see
|
|
146
139
|
* {@link findEventSlotGap}). **On by default**; set
|
|
147
140
|
* `WORKFLOW_SLOT_GAP_CHECK=0` to replay across holes instead.
|
|
148
141
|
*
|
|
149
|
-
*
|
|
150
|
-
*
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
-
*
|
|
155
|
-
*
|
|
142
|
+
* A World that allocates a position at the moment it commits leaves no hole
|
|
143
|
+
* behind when a write fails, so density is a property the log has by
|
|
144
|
+
* construction rather than one this check maintains. What the check is for is
|
|
145
|
+
* the reads and the Worlds where that does not hold, and by the time it runs
|
|
146
|
+
* the benign explanations are spent: a position missing because a concurrent
|
|
147
|
+
* commit is not visible yet clears on a re-read, which is what
|
|
148
|
+
* {@link settleEventSlotGap} does first.
|
|
149
|
+
*
|
|
150
|
+
* What is left is a hole that persists, and its two causes are
|
|
151
|
+
* indistinguishable from the log. Either a World allocated the position outside
|
|
152
|
+
* the commit and lost the write, in which case nothing happened there and
|
|
153
|
+
* replaying past it is correct, or an event that did happen is missing, in
|
|
154
|
+
* which case replaying past it decides a branch on absence and produces a wrong
|
|
155
|
+
* result with nothing to show for it. Failing is the recoverable side of that
|
|
156
|
+
* trade, and this is the way back out if a fleet turns out to carry holes of
|
|
157
|
+
* the first kind.
|
|
156
158
|
*/
|
|
157
159
|
export declare function isSlotGapCheckEnabled(): boolean;
|
|
158
|
-
/**
|
|
159
|
-
* The `stateUpdatedAt` value to send with a replay-context event creation: the
|
|
160
|
-
* *maximum* ULID time (epoch ms) over the events the runtime has loaded. Returns
|
|
161
|
-
* `undefined` when there are no events or that id is not a decodable ULID.
|
|
162
|
-
*
|
|
163
|
-
* It is the maximum rather than the tail's because the loaded log is in the
|
|
164
|
-
* World's canonical order, which is not necessarily event-id order (see
|
|
165
|
-
* {@link appendUniqueEvents}). The maximum is what lets the count sent alongside
|
|
166
|
-
* it be read as "events at or below this watermark": every loaded event is at or
|
|
167
|
-
* below it, so the count is exactly `events.length`. Reading the tail instead
|
|
168
|
-
* would understate the watermark on a World whose order is not id-ordered, which
|
|
169
|
-
* is safe (it can only weaken detection) but needlessly imprecise.
|
|
170
|
-
*
|
|
171
|
-
* The maximum is found by lexicographic id comparison, decoding only once: the
|
|
172
|
-
* 26-character Crockford ULID encodes its timestamp in the leading 10
|
|
173
|
-
* characters, so the greatest id also carries the greatest time.
|
|
174
|
-
*
|
|
175
|
-
* Granularity: snapshots are epoch-milliseconds, and the backend allows an
|
|
176
|
-
* equal-timestamp snapshot (an up-to-date client must not be rejected). Two
|
|
177
|
-
* out-of-band events landing in the same millisecond where only the first was
|
|
178
|
-
* loaded therefore pass this half of the guard undetected — that is exactly
|
|
179
|
-
* the hole `stateEventCount` closes, since the count of events at or below the
|
|
180
|
-
* watermark differs even when the watermarks are equal.
|
|
181
|
-
*/
|
|
182
|
-
export declare function latestEventStateUpdatedAt(events: Event[]): number | undefined;
|
|
183
160
|
/**
|
|
184
161
|
* Merge the events a bump-and-report write handed back into the log it was
|
|
185
162
|
* derived from, and answer how many of them were new.
|
|
186
163
|
*
|
|
187
164
|
* Unlike {@link appendUniqueEvents}, this re-sorts. The reported events occupy
|
|
188
165
|
* slots *below* the write that reported them, so appending them would put them
|
|
189
|
-
* after events they precede
|
|
190
|
-
*
|
|
191
|
-
* A run that is not slot-numbered cannot produce this report in the first
|
|
192
|
-
* place; the sort is skipped rather than applied to ids it cannot order.
|
|
166
|
+
* after events they precede. Sorting by id restores the World's canonical order
|
|
167
|
+
* rather than guessing at it: a slot id is that order, written down.
|
|
193
168
|
*/
|
|
194
169
|
export declare function mergeReportedEvents(target: Event[], events: readonly Event[]): number;
|
|
170
|
+
/** What {@link absorbSkippedSlotReport} did with a write's report. */
|
|
171
|
+
export interface SkippedSlotReport {
|
|
172
|
+
/** How many of the reported events were new to the log. Zero if dropped. */
|
|
173
|
+
added: number;
|
|
174
|
+
/** How many events the report offered, whether or not they were taken. */
|
|
175
|
+
offered: number;
|
|
176
|
+
/** The report was truncated, so it was dropped whole instead of merged. */
|
|
177
|
+
truncated: boolean;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Apply a write's skipped-slot report to the log it was derived from, deciding
|
|
181
|
+
* whether the report may be taken at all.
|
|
182
|
+
*
|
|
183
|
+
* Every replay-context write can come back carrying the events on the slots it
|
|
184
|
+
* skipped over, and every caller wants the same thing from them: fold them in
|
|
185
|
+
* so the writes that follow name a position above them, and so the replay
|
|
186
|
+
* resuming from this log sees them without a reload.
|
|
187
|
+
*
|
|
188
|
+
* The one policy is that a **truncated report is dropped whole**. It covers a
|
|
189
|
+
* span of positions but carries only some of the events on them, so merging it
|
|
190
|
+
* would raise the log's highest position past a position whose event is
|
|
191
|
+
* missing. Later writes read that maximum to say what they have seen, so each
|
|
192
|
+
* would claim a position it never saw, and the World only reports the span a
|
|
193
|
+
* write skips — it would never send the missing one. Dropping costs one more
|
|
194
|
+
* round of the same events on the next write and keeps the log a prefix of the
|
|
195
|
+
* truth, which the note above {@link mergeReportedEvents} explains is always an
|
|
196
|
+
* available answer.
|
|
197
|
+
*
|
|
198
|
+
* Callers that log do so from the returned counts; the decision is not theirs
|
|
199
|
+
* to re-derive.
|
|
200
|
+
*/
|
|
201
|
+
export declare function absorbSkippedSlotReport(target: Event[], result: {
|
|
202
|
+
events?: readonly Event[];
|
|
203
|
+
hasMore?: boolean;
|
|
204
|
+
}): SkippedSlotReport;
|
|
195
205
|
/**
|
|
196
|
-
* The highest slot the loaded log occupies, or `undefined`
|
|
197
|
-
* slot-numbered. A run keeps the id scheme it was created under, so one event
|
|
198
|
-
* settles it for the whole log.
|
|
206
|
+
* The highest slot the loaded log occupies, or `undefined` for an empty log.
|
|
199
207
|
*
|
|
200
208
|
* The maximum, not the count, and the two are not interchangeable even though
|
|
201
209
|
* a healthy log makes them equal. A World hands a position to the insert that
|
|
@@ -208,8 +216,13 @@ export declare function mergeReportedEvents(target: Event[], events: readonly Ev
|
|
|
208
216
|
*
|
|
209
217
|
* A hole below the maximum is therefore a property of the read, not of the log,
|
|
210
218
|
* which is what lets {@link settleEventSlotGap} re-read instead of giving up.
|
|
219
|
+
*
|
|
220
|
+
* @throws if any event id carries no slot. Every World the runtime replays
|
|
221
|
+
* against numbers events by slot, so an id that does not is a broken log rather
|
|
222
|
+
* than an older one, and a maximum derived by skipping it would understate the
|
|
223
|
+
* log to every write that reads it.
|
|
211
224
|
*/
|
|
212
|
-
export declare function maxEventSlot(events: Event[]): number | undefined;
|
|
225
|
+
export declare function maxEventSlot(events: readonly Event[]): number | undefined;
|
|
213
226
|
/** A position the log skips over, described well enough to name in an error. */
|
|
214
227
|
export interface EventSlotGap {
|
|
215
228
|
/** The lowest slot below the log's maximum that no event occupies. */
|
|
@@ -222,8 +235,8 @@ export interface EventSlotGap {
|
|
|
222
235
|
/**
|
|
223
236
|
* The hole in a loaded log, or `undefined` when there is none to find.
|
|
224
237
|
*
|
|
225
|
-
*
|
|
226
|
-
*
|
|
238
|
+
* The World allocates every position, so a log that holds `n` events below slot
|
|
239
|
+
* `n` is missing one. That matters before a replay
|
|
227
240
|
* and nowhere else: the replay reads the log as the complete record of what has
|
|
228
241
|
* happened, and an absent position is indistinguishable from an event that
|
|
229
242
|
* never occurred. The branch it would have decided gets decided the other way,
|
|
@@ -240,8 +253,10 @@ export interface EventSlotGap {
|
|
|
240
253
|
* legitimately begins at the second slot and fills in on its own. Every replay
|
|
241
254
|
* that races a run's own start would otherwise report a hole.
|
|
242
255
|
*
|
|
243
|
-
* Returns `undefined` for
|
|
244
|
-
*
|
|
256
|
+
* Returns `undefined` for an empty log, which has no density to check.
|
|
257
|
+
*
|
|
258
|
+
* @throws if any event id carries no slot, for the reason {@link maxEventSlot}
|
|
259
|
+
* gives.
|
|
245
260
|
*/
|
|
246
261
|
export declare function findEventSlotGap(events: readonly Event[]): EventSlotGap | undefined;
|
|
247
262
|
/**
|
|
@@ -274,42 +289,39 @@ export declare function settleEventSlotGap(runId: string, loaded: LoadedEventLog
|
|
|
274
289
|
gap: EventSlotGap | undefined;
|
|
275
290
|
}>;
|
|
276
291
|
/**
|
|
277
|
-
*
|
|
278
|
-
*
|
|
279
|
-
*
|
|
280
|
-
*
|
|
281
|
-
*
|
|
282
|
-
*
|
|
283
|
-
*
|
|
284
|
-
*
|
|
285
|
-
*
|
|
286
|
-
*
|
|
292
|
+
* How much of its run's log a replay-context event creation had loaded when it
|
|
293
|
+
* decided to write, as the highest slot that log occupies.
|
|
294
|
+
*
|
|
295
|
+
* One integer says it because the World keeps its positions dense: a writer
|
|
296
|
+
* that names slot N is claiming to hold every event from 1 to N and nothing
|
|
297
|
+
* above. The World answers by numbering the write above whatever the log has
|
|
298
|
+
* actually reached and handing back the events on the slots in between — the
|
|
299
|
+
* ones this writer decided without.
|
|
300
|
+
*
|
|
301
|
+
* Density is the World's invariant, not a claim about this particular read. A
|
|
302
|
+
* reader that is short of a position it holds no event for names a lower N,
|
|
303
|
+
* which understates what it has seen and only costs it a wider report. Naming
|
|
304
|
+
* a position it cannot account for is the direction that is unsafe, which is
|
|
305
|
+
* why {@link slotSnapshotParams} takes the maximum rather than the count.
|
|
306
|
+
*
|
|
307
|
+
* Its own object rather than a bare number so a call site cannot half-send it,
|
|
308
|
+
* and so the empty case spreads to nothing.
|
|
287
309
|
*/
|
|
288
|
-
export interface
|
|
289
|
-
stateUpdatedAt?: number;
|
|
290
|
-
stateEventCount?: number;
|
|
291
|
-
stateCursor?: string;
|
|
310
|
+
export interface SlotSnapshotParams {
|
|
292
311
|
eventCount?: number;
|
|
293
312
|
}
|
|
294
313
|
/**
|
|
295
|
-
* Build the
|
|
296
|
-
*
|
|
297
|
-
*
|
|
298
|
-
*
|
|
299
|
-
*
|
|
300
|
-
*
|
|
301
|
-
*
|
|
302
|
-
*
|
|
303
|
-
*
|
|
304
|
-
* order the World returned them in.
|
|
305
|
-
*
|
|
306
|
-
* Both fields are therefore invariant under permutation of the log: a maximum is
|
|
307
|
-
* order-independent, and the length is set cardinality once `appendUniqueEvents`
|
|
308
|
-
* has deduped by event id. Two replays that consume the same events in different
|
|
309
|
-
* orders send an identical snapshot, so this guard detects that a log is missing
|
|
310
|
-
* an event and can never detect that a replay consumed one in a different order.
|
|
314
|
+
* Build the slot snapshot to attach to a replay-context event creation.
|
|
315
|
+
*
|
|
316
|
+
* Empty for an empty log, which is the state a `run_created` write is issued
|
|
317
|
+
* from: there is no position held yet to name.
|
|
318
|
+
*
|
|
319
|
+
* The maximum rather than the length, for the reason {@link maxEventSlot}
|
|
320
|
+
* gives: a partially-read log holds fewer events than its highest position, and
|
|
321
|
+
* counting those would make the write claim to have seen less than it has, so
|
|
322
|
+
* the World would report the same events back on every attempt.
|
|
311
323
|
*/
|
|
312
|
-
export declare function
|
|
324
|
+
export declare function slotSnapshotParams(events: readonly Event[]): SlotSnapshotParams;
|
|
313
325
|
/**
|
|
314
326
|
* The events a rejecting World attached to a `PreconditionFailedError`, when it
|
|
315
327
|
* returned the ones the client's snapshot was missing inline.
|
|
@@ -335,6 +347,28 @@ export type EventCreator = (data: CreateEventRequest, params?: CreateEventParams
|
|
|
335
347
|
* based on the presence of a `__health` query parameter.
|
|
336
348
|
*/
|
|
337
349
|
export declare function withHealthCheck(handler: (req: Request) => Promise<Response>, worldSpecVersion?: number): (req: Request) => Promise<Response>;
|
|
350
|
+
/**
|
|
351
|
+
* Idempotency key for a step's background-dispatch queue message, scoped to
|
|
352
|
+
* the step's IDENTITY — correlation id plus (hashed) step name — rather than
|
|
353
|
+
* the bare correlation id.
|
|
354
|
+
*
|
|
355
|
+
* The scoping matters for resilient step dispatch under the precondition
|
|
356
|
+
* guard: a guard-rejected `step_created` leaves its (revoked) step message in
|
|
357
|
+
* flight, and the corrected replay may re-derive the same correlation id for
|
|
358
|
+
* a DIFFERENT step. Under a bare-correlationId key the corrected replay's
|
|
359
|
+
* dispatch would silently dedupe against the revoked in-flight message —
|
|
360
|
+
* which then resolves `skipped` against the re-created entity (the server's
|
|
361
|
+
* stepName fence rejects its bare start) — and the legitimate step would
|
|
362
|
+
* never be executed. Scoping by step name keeps every dedup property that
|
|
363
|
+
* matters (crash recovery re-dispatch, concurrent handlers, the delayed
|
|
364
|
+
* retry sharing the suspension re-dispatch's key — all name the same step)
|
|
365
|
+
* while letting the corrected schedule's dispatch through.
|
|
366
|
+
*
|
|
367
|
+
* Every producer of a step-dispatch (or step-retry) message must use this
|
|
368
|
+
* key. Cross-version mixing is not a concern: queue messages are pinned to
|
|
369
|
+
* the deployment that produced them, so one run never sees two key schemes.
|
|
370
|
+
*/
|
|
371
|
+
export declare function stepDispatchIdempotencyKey(correlationId: string, stepName: string): string;
|
|
338
372
|
/**
|
|
339
373
|
* Queues a message to the specified queue with tracing.
|
|
340
374
|
*/
|