experimental-ash 0.7.1 → 0.7.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.
Files changed (61) hide show
  1. package/dist/docs/external-agent-protocol.md +5 -5
  2. package/dist/docs/internals/README.md +8 -8
  3. package/dist/docs/internals/context.md +1 -1
  4. package/dist/docs/internals/hooks.md +5 -5
  5. package/dist/docs/internals/message-runtime.md +6 -6
  6. package/dist/docs/public/README.md +22 -22
  7. package/dist/docs/public/agent-ts.md +21 -17
  8. package/dist/docs/public/auth-and-route-protection.md +13 -13
  9. package/dist/docs/public/channels/README.md +16 -13
  10. package/dist/docs/public/cli-build-and-debugging.md +3 -3
  11. package/dist/docs/public/connections.md +5 -5
  12. package/dist/docs/public/context-control.md +8 -8
  13. package/dist/docs/public/evals.md +3 -3
  14. package/dist/docs/public/getting-started.md +5 -5
  15. package/dist/docs/public/hooks.md +4 -4
  16. package/dist/docs/public/human-in-the-loop.md +3 -3
  17. package/dist/docs/public/instrumentation.md +4 -3
  18. package/dist/docs/public/project-layout.md +11 -11
  19. package/dist/docs/public/runs-and-streaming.md +3 -3
  20. package/dist/docs/public/sandbox.md +3 -3
  21. package/dist/docs/public/session-context.md +10 -6
  22. package/dist/docs/public/skills.md +5 -3
  23. package/dist/docs/public/subagents.md +2 -2
  24. package/dist/docs/public/tools.md +4 -5
  25. package/dist/docs/public/typescript-api.md +12 -12
  26. package/dist/docs/public/vercel-deployment.md +4 -4
  27. package/dist/docs/public/workspace.md +3 -3
  28. package/dist/src/channel/compiled-channel.d.ts +4 -2
  29. package/dist/src/channel/schedule.d.ts +11 -23
  30. package/dist/src/channel/schedule.js +18 -19
  31. package/dist/src/chunks/{dev-authored-source-watcher-CYsfBiYM.js → dev-authored-source-watcher-Druw92QN.js} +1 -1
  32. package/dist/src/chunks/{host-CsF9KDv8.js → host-CQ7AZID3.js} +2 -2
  33. package/dist/src/chunks/{paths-DvimrhJF.js → paths-DQbfjCIS.js} +25 -25
  34. package/dist/src/chunks/{prewarm-DdHk68ib.js → prewarm-CcphIXc0.js} +1 -1
  35. package/dist/src/cli/commands/info.js +1 -1
  36. package/dist/src/cli/run.js +1 -1
  37. package/dist/src/compiler/normalize-channel.d.ts +4 -4
  38. package/dist/src/compiler/normalize-channel.js +9 -22
  39. package/dist/src/evals/cli/eval.js +1 -1
  40. package/dist/src/internal/application/package.js +1 -1
  41. package/dist/src/internal/authored-definition/channel.d.ts +7 -9
  42. package/dist/src/internal/authored-definition/channel.js +9 -33
  43. package/dist/src/internal/nitro/routes/channel-dispatch.d.ts +5 -3
  44. package/dist/src/internal/nitro/routes/channel-dispatch.js +8 -5
  45. package/dist/src/internal/nitro/routes/runtime-stack.js +1 -1
  46. package/dist/src/internal/nitro/routes/schedule-task.d.ts +3 -4
  47. package/dist/src/internal/nitro/routes/schedule-task.js +5 -7
  48. package/dist/src/public/channels/{http.d.ts → ash.d.ts} +2 -2
  49. package/dist/src/public/channels/{http.js → ash.js} +2 -2
  50. package/dist/src/public/channels/slack/slackChannel.js +1 -1
  51. package/dist/src/public/definitions/channel.d.ts +1 -63
  52. package/dist/src/public/definitions/channel.js +0 -16
  53. package/dist/src/public/definitions/defineChannel.d.ts +7 -4
  54. package/dist/src/runtime/framework-channels/index.d.ts +1 -1
  55. package/dist/src/runtime/framework-channels/index.js +7 -7
  56. package/dist/src/runtime/resolve-channel.d.ts +9 -4
  57. package/dist/src/runtime/resolve-channel.js +21 -28
  58. package/dist/src/runtime/types.d.ts +13 -8
  59. package/package.json +5 -5
  60. package/dist/src/compiler/channel-url.d.ts +0 -5
  61. package/dist/src/compiler/channel-url.js +0 -14
@@ -1,4 +1,4 @@
1
- import{E as e,S as t,T as n,_ as r,a as i,c as a,d as o,g as s,h as c,l,m as u,p as ee,s as te,u as d,x as f,y as p}from"./paths-DvimrhJF.js";import{t as m}from"./authored-module-loader-CqZSviWm.js";import{t as h}from"./errors-DsO9xmQL.js";import{i as g,t as _}from"./package-DmsQgn4v.js";import{join as v,posix as y}from"node:path";import{mkdir as ne,readFile as re,readdir as ie,realpath as b,writeFile as x}from"node:fs/promises";import{createHash as S}from"node:crypto";import{existsSync as C}from"node:fs";function w(e){return e.dev?{appRoot:e.appRoot,dev:e.dev,moduleMapLoaderPath:g(`src/internal/authored-module-map-loader.ts`)}:{appRoot:e.appRoot,dev:e.dev}}const T=`#ash-channel/`;function E(e){let t=e.compileResult.manifest.channels,n=new Set,r=[],i=new Set,o=a();for(let e of t){if(e.kind===`disabled`){if(!o.has(e.name))throw Error(`agent/channels/${e.name}.ts exports disableRoute() but "${e.name}" is not a framework channel. Rename the file to one of: ${[...o].sort().join(`, `)}.`);i.add(e.name);continue}n.add(e.name),r.push({method:e.method,route:e.urlPath})}let s=l().filter(e=>!n.has(e.name)&&!i.has(e.name)).map(e=>({method:e.method,route:e.urlPath})),c=new Set,u=[];for(let e of[...s,...r]){let t=k(e);c.has(t)||(c.add(t),u.push(e))}return u}function D(e,t){for(let n of t.registrations)A(e,{artifactsConfig:t.artifactsConfig,method:n.method,route:n.route})}function O(e,t){return N(t.previous,t.next)?!1:(j(e),D(e,{artifactsConfig:t.artifactsConfig,registrations:t.next}),e.routing.sync(),!0)}function k(e){return`${e.method.toUpperCase()} ${e.route}`}function A(e,t){let r=k(t),i=`${T}${r}`,a=n(g(`src/internal/nitro/routes/channel-dispatch.ts`));e.options.handlers.push({handler:i,method:t.method,route:t.route}),e.options.virtual[i]=[`import { dispatchChannelRequest } from ${a};`,`const config = ${JSON.stringify(t.artifactsConfig)};`,`export default (event) => dispatchChannelRequest(event, ${JSON.stringify(r)}, config);`].join(`
1
+ import{E as e,S as t,T as n,_ as r,a as i,c as a,d as o,g as s,h as c,l,m as u,p as ee,s as te,u as d,x as f,y as p}from"./paths-DQbfjCIS.js";import{t as m}from"./authored-module-loader-CqZSviWm.js";import{t as h}from"./errors-DsO9xmQL.js";import{i as g,t as _}from"./package-DmsQgn4v.js";import{join as v,posix as y}from"node:path";import{mkdir as ne,readFile as re,readdir as ie,realpath as b,writeFile as x}from"node:fs/promises";import{createHash as S}from"node:crypto";import{existsSync as C}from"node:fs";function w(e){return e.dev?{appRoot:e.appRoot,dev:e.dev,moduleMapLoaderPath:g(`src/internal/authored-module-map-loader.ts`)}:{appRoot:e.appRoot,dev:e.dev}}const T=`#ash-channel/`;function E(e){let t=e.compileResult.manifest.channels,n=new Set,r=[],i=new Set,o=a();for(let e of t){if(e.kind===`disabled`){if(!o.has(e.name))throw Error(`agent/channels/${e.name}.ts exports disableRoute() but "${e.name}" is not a framework channel. Rename the file to one of: ${[...o].sort().join(`, `)}.`);i.add(e.name);continue}n.add(e.name),r.push({method:e.method,route:e.urlPath})}let s=l().filter(e=>!n.has(e.name)&&!i.has(e.name)).map(e=>({method:e.method,route:e.urlPath})),c=new Set,u=[];for(let e of[...s,...r]){let t=k(e);c.has(t)||(c.add(t),u.push(e))}return u}function D(e,t){for(let n of t.registrations)A(e,{artifactsConfig:t.artifactsConfig,method:n.method,route:n.route})}function O(e,t){return N(t.previous,t.next)?!1:(j(e),D(e,{artifactsConfig:t.artifactsConfig,registrations:t.next}),e.routing.sync(),!0)}function k(e){return`${e.method.toUpperCase()} ${e.route}`}function A(e,t){let r=k(t),i=`${T}${r}`,a=n(g(`src/internal/nitro/routes/channel-dispatch.ts`));e.options.handlers.push({handler:i,method:t.method,route:t.route}),e.options.virtual[i]=[`import { dispatchChannelRequest } from ${a};`,`const config = ${JSON.stringify(t.artifactsConfig)};`,`export default (event) => dispatchChannelRequest(event, ${JSON.stringify(r)}, config);`].join(`
2
2
  `)}function j(e){for(let t=e.options.handlers.length-1;t>=0;--t){let n=e.options.handlers[t];n!==void 0&&M(n)&&e.options.handlers.splice(t,1)}for(let t of Object.keys(e.options.virtual))t.startsWith(T)&&delete e.options.virtual[t]}function M(e){return e.handler.startsWith(T)}function N(e,t){if(e.length!==t.length)return!1;for(let n=0;n<e.length;n+=1){let r=e[n],i=t[n];if(r===void 0||i===void 0||r.method!==i.method||r.route!==i.route)return!1}return!0}const P=`ash.schedule.`;var F=class extends Error{scheduleId;sourceId;taskName;constructor(e,t={}){super(e),this.name=`ScheduleRegistrationError`,t.scheduleId!==void 0&&(this.scheduleId=t.scheduleId),t.sourceId!==void 0&&(this.sourceId=t.sourceId),t.taskName!==void 0&&(this.taskName=t.taskName)}};function I(e){let t=e.map(e=>({cron:e.cron,description:`Run Ash schedule "${e.name}" from "${e.logicalPath}".`,logicalPath:e.logicalPath,scheduleId:e.name,sourceId:e.sourceId,taskName:R(e.sourceId)})).sort((e,t)=>e.sourceId.localeCompare(t.sourceId));return L(t),t}function L(e){let t=new Map;for(let n of e){let e=t.get(n.scheduleId);if(e===void 0){t.set(n.scheduleId,n);continue}throw new F(`Duplicate authored schedule id "${n.scheduleId}" found in "${e.logicalPath}" and "${n.logicalPath}".`,{scheduleId:n.scheduleId,sourceId:n.sourceId,taskName:n.taskName})}}function R(e){return`${P}${Buffer.from(e,`utf8`).toString(`base64url`)}`}const z=`#ash-schedule-task/`;function B(e,t){if(t.registrations.length!==0){e.options.experimental.tasks=!0;for(let n of t.registrations)U(e,{artifactsConfig:t.artifactsConfig,dispatchModulePath:t.dispatchModulePath,registration:n})}}function V(e,t){let n=!G(t.previous,t.next);return H(e),B(e,{artifactsConfig:t.artifactsConfig,dispatchModulePath:t.dispatchModulePath,registrations:t.next}),n}function H(e){for(let t of Object.keys(e.options.tasks))t.startsWith(`ash.schedule.`)&&delete e.options.tasks[t];for(let t of Object.keys(e.options.virtual))t.startsWith(z)&&delete e.options.virtual[t];for(let[t,n]of Object.entries(e.options.scheduledTasks)){let r=W(n).filter(e=>!e.startsWith(P));if(r.length===0){delete e.options.scheduledTasks[t];continue}if(r.length===1){let[n]=r;n!==void 0&&(e.options.scheduledTasks[t]=n);continue}e.options.scheduledTasks[t]=r}}function U(e,t){let r=`${z}${t.registration.taskName}`,i=n(t.dispatchModulePath);e.options.tasks[t.registration.taskName]={description:t.registration.description,handler:r},e.options.virtual[r]=[`import { dispatchScheduleTask } from ${i};`,`const config = ${JSON.stringify(t.artifactsConfig)};`,`export default {`,` meta: { description: ${JSON.stringify(t.registration.description)} },`,` async run(event) {`,` return { result: await dispatchScheduleTask(event.name, config) };`,` },`,`};`].join(`
3
3
  `),ae(e,t.registration.cron,t.registration.taskName)}function ae(e,t,n){let r=e.options.scheduledTasks[t];if(r===void 0){e.options.scheduledTasks[t]=n;return}if(typeof r==`string`){e.options.scheduledTasks[t]=[r,n];return}r.includes(n)||r.push(n)}function W(e){return typeof e==`string`?[e]:[...e]}function G(e,t){if(e.length!==t.length)return!1;for(let n=0;n<e.length;n+=1){let r=e[n],i=t[n];if(r===void 0||i===void 0||r.cron!==i.cron||r.description!==i.description||r.logicalPath!==i.logicalPath||r.scheduleId!==i.scheduleId||r.sourceId!==i.sourceId||r.taskName!==i.taskName)return!1}return!0}async function K(e){return[...e.manifest.schedules].map(e=>{let t={cron:e.cron,logicalPath:e.logicalPath,markdown:e.markdown,name:e.name,sourceId:e.sourceId,sourceKind:e.sourceKind};return e.channel===void 0?t:{...t,channel:e.channel}})}async function q(e){return await K({manifest:await o({compiledArtifactsSource:e.compiledArtifactsSource})})}async function J(e){let t=v(e.outDir,`compiled-artifacts-bootstrap.mjs`),n=v(e.outDir,`compiled-artifacts-instrumentation.mjs`),r=se(e.compileResult.manifest.agentRoot);await ne(e.outDir,{recursive:!0}),await x(t,await le({compileResult:e.compileResult,installModulePath:g(`src/runtime/loaders/bundled-artifacts.ts`),moduleMapPath:t,metadata:e.compileResult.metadata})),r!==void 0&&await x(n,ue({agentName:e.compileResult.manifest.config.name,instrumentationPath:r,registerConfigPath:g(`src/harness/instrumentation-config.ts`)}));let i={bootstrapPath:t};return r!==void 0&&(i.instrumentationPluginPath=n,i.instrumentationSourcePath=r),i}const oe=[`.ts`,`.mts`,`.js`,`.mjs`];function se(e){for(let t of oe){let n=v(e,`instrumentation${t}`);if(C(n))return n}}function ce(e){return e.replace(/^export const moduleMap = /m,`const moduleMap = `).replace(/\nexport default moduleMap;\n?$/,`
4
4
  `)}async function le(e){let r=ce(t({importSpecifierStyle:`absolute`,manifest:e.compileResult.manifest,moduleMapPath:e.moduleMapPath})).trim();return[`// Generated by Ash. Do not edit by hand.`,`import { installBundledCompiledArtifacts } from ${n(e.installModulePath)};`,``,r,``,`const metadata = ${JSON.stringify(e.metadata,null,2)};`,``,`const manifest = ${JSON.stringify(e.compileResult.manifest,null,2)};`,``,`export function installCompiledArtifactsBootstrap() {`,` installBundledCompiledArtifacts({`,` manifest,`,` metadata,`,` moduleMap,`,` });`,`}`,``,`installCompiledArtifactsBootstrap();`,``,`// Default export satisfies the Nitro plugin contract so this file`,`// can be used directly as a Nitro plugin without a separate wrapper.`,`export default function installCompiledArtifactsPlugin() {`,` // Already installed on import above.`,`}`,``,`export async function __ashInstallCompiledArtifactsStep() {`,` "use step";`,` return null;`,`}`,``].join(`
@@ -1,2 +1,2 @@
1
- import{D as e,b as t,t as n,v as r,y as i}from"../../chunks/paths-DvimrhJF.js";import{d as a,f as o,g as s}from"../../chunks/types-D9Uv7nU4.js";import{createCliTheme as c,renderCliBanner as l,renderCliSection as u}from"../ui/output.js";async function d(e){let t=await f(e);return{application:n(t?.project.appRoot??e),compiledState:t,messaging:{createSessionRoutePath:o,continueSessionRoutePattern:a,streamRoutePattern:s}}}async function f(n){try{return await i({startPath:n})}catch(n){if(n instanceof r)return n.result;if(n instanceof e||n instanceof t)return null;throw n}}function p(e,t){return`${e} ${t}${e===1?``:`s`}`}function m(e,t){return`${`${e} error${e===1?``:`s`}`}, ${`${t} warning${t===1?``:`s`}`}`}function h(e){switch(e){case`ready`:return`success`;case`failed`:return`danger`;default:return`warning`}}async function g(e,t){let n=await d(t),r=n.compiledState,i=n.application,a=c(),o=[{label:`App Root`,value:i.appRoot}],s=[{label:`Workflow Build`,value:i.workflowBuildDir},{label:`Output`,value:i.outputDir}],f=[];r===null?o.push({label:`Compile`,tone:`warning`,value:`unavailable`}):(o.push({label:`Agent Root`,value:r.project.agentRoot},{label:`Layout`,value:r.project.layout},{label:`Compile`,tone:h(r.metadata.status),value:r.metadata.status},{label:`Diagnostics`,tone:r.metadata.discovery.summary.errors>0?`danger`:r.metadata.discovery.summary.warnings>0?`warning`:`success`,value:m(r.metadata.discovery.summary.errors,r.metadata.discovery.summary.warnings)},{label:`Instructions`,value:r.manifest.instructions?.logicalPath??`none`},{label:`Skills`,value:p(r.manifest.skills.length,`skill`)}),s.unshift({label:`Compiled Manifest`,value:r.paths.compiledManifestPath},{label:`Discovery Manifest`,value:r.paths.discoveryManifestPath},{label:`Diagnostics`,value:r.paths.diagnosticsPath},{label:`Module Map`,value:r.paths.moduleMapPath},{label:`Metadata`,value:r.paths.compileMetadataPath}),f.push(r.manifest.instructions===void 0?{label:`Instructions`,value:`No instructions prompt discovered.`}:{label:`Instructions`,value:r.manifest.instructions.logicalPath})),e.log([l(a,{subtitle:`Resolved application paths and the active message contract.`,title:`Ash Info`}),``,u(a,{rows:o,title:`Application`}),``,u(a,{rows:s,title:`Artifacts`}),...r===null?[]:[``,u(a,{rows:f,title:`Instructions`})],``,u(a,{rows:[{label:`Workflow ID`,value:i.workflowId},{label:`Source Dir`,value:i.workflowSourceDir},{label:`Create`,tone:`info`,value:`POST ${n.messaging.createSessionRoutePath}`},{label:`Continue`,tone:`info`,value:`POST ${n.messaging.continueSessionRoutePattern}`},{label:`Stream`,tone:`info`,value:`GET ${n.messaging.streamRoutePattern}`}],title:`Messaging`})].join(`
1
+ import{D as e,b as t,t as n,v as r,y as i}from"../../chunks/paths-DQbfjCIS.js";import{d as a,f as o,g as s}from"../../chunks/types-D9Uv7nU4.js";import{createCliTheme as c,renderCliBanner as l,renderCliSection as u}from"../ui/output.js";async function d(e){let t=await f(e);return{application:n(t?.project.appRoot??e),compiledState:t,messaging:{createSessionRoutePath:o,continueSessionRoutePattern:a,streamRoutePattern:s}}}async function f(n){try{return await i({startPath:n})}catch(n){if(n instanceof r)return n.result;if(n instanceof e||n instanceof t)return null;throw n}}function p(e,t){return`${e} ${t}${e===1?``:`s`}`}function m(e,t){return`${`${e} error${e===1?``:`s`}`}, ${`${t} warning${t===1?``:`s`}`}`}function h(e){switch(e){case`ready`:return`success`;case`failed`:return`danger`;default:return`warning`}}async function g(e,t){let n=await d(t),r=n.compiledState,i=n.application,a=c(),o=[{label:`App Root`,value:i.appRoot}],s=[{label:`Workflow Build`,value:i.workflowBuildDir},{label:`Output`,value:i.outputDir}],f=[];r===null?o.push({label:`Compile`,tone:`warning`,value:`unavailable`}):(o.push({label:`Agent Root`,value:r.project.agentRoot},{label:`Layout`,value:r.project.layout},{label:`Compile`,tone:h(r.metadata.status),value:r.metadata.status},{label:`Diagnostics`,tone:r.metadata.discovery.summary.errors>0?`danger`:r.metadata.discovery.summary.warnings>0?`warning`:`success`,value:m(r.metadata.discovery.summary.errors,r.metadata.discovery.summary.warnings)},{label:`Instructions`,value:r.manifest.instructions?.logicalPath??`none`},{label:`Skills`,value:p(r.manifest.skills.length,`skill`)}),s.unshift({label:`Compiled Manifest`,value:r.paths.compiledManifestPath},{label:`Discovery Manifest`,value:r.paths.discoveryManifestPath},{label:`Diagnostics`,value:r.paths.diagnosticsPath},{label:`Module Map`,value:r.paths.moduleMapPath},{label:`Metadata`,value:r.paths.compileMetadataPath}),f.push(r.manifest.instructions===void 0?{label:`Instructions`,value:`No instructions prompt discovered.`}:{label:`Instructions`,value:r.manifest.instructions.logicalPath})),e.log([l(a,{subtitle:`Resolved application paths and the active message contract.`,title:`Ash Info`}),``,u(a,{rows:o,title:`Application`}),``,u(a,{rows:s,title:`Artifacts`}),...r===null?[]:[``,u(a,{rows:f,title:`Instructions`})],``,u(a,{rows:[{label:`Workflow ID`,value:i.workflowId},{label:`Source Dir`,value:i.workflowSourceDir},{label:`Create`,tone:`info`,value:`POST ${n.messaging.createSessionRoutePath}`},{label:`Continue`,tone:`info`,value:`POST ${n.messaging.continueSessionRoutePattern}`},{label:`Stream`,tone:`info`,value:`GET ${n.messaging.streamRoutePattern}`}],title:`Messaging`})].join(`
2
2
  `))}export{g as printApplicationInfo};
@@ -1,3 +1,3 @@
1
- import{t as e}from"../chunks/package-DmsQgn4v.js";import{createCliTheme as t,renderCliTaggedLine as n}from"./ui/output.js";import{i as r,n as i,r as a,t as o}from"../chunks/url-BVRhVE2O.js";import{resolve as s}from"node:path";async function c(){return(await import(`../chunks/host-CsF9KDv8.js`).then(e=>e.t)).buildHost}async function l(){return(await import(`./commands/info.js`)).printApplicationInfo}async function u(){return(await import(`./dev/repl.js`)).runDevelopmentRepl}async function d(){return(await import(`../evals/cli/eval.js`)).runEvalCommand}async function f(){return(await import(`../chunks/host-CsF9KDv8.js`).then(e=>e.t)).startHost}function p(e=process.cwd()){return s(e)}function m(e){return`Ash (v${e})`}function h(e){return e.name()===`info`||e.name()===`dev`}async function g(e){await new Promise((t,n)=>{let r=!1,i=()=>{process.off(`SIGINT`,a),process.off(`SIGTERM`,a)},a=()=>{r||(r=!0,i(),e.close().then(t,n))};process.once(`SIGINT`,a),process.once(`SIGTERM`,a)})}function _(e){if(!/^-?\d+$/.test(e))throw new r(`Expected a numeric port, received "${e}".`);let t=Number(e);if(!Number.isInteger(t))throw new r(`Expected a numeric port, received "${e}".`);if(t<0||t>65535)throw new r(`Expected a port between 0 and 65535, received "${e}".`);return t}function v(){return!!(process.stdin.isTTY&&process.stdout.isTTY)}function y(e){let t=e[1];return e[0]!==`dev`||e.length!==2||t===void 0||t.startsWith(`-`)?[...e]:[`dev`,`--url`,t]}function b(e){if(e.url){if(e.host!==void 0)throw new r(`The --host option cannot be used with --url.`);if(e.port!==void 0)throw new r(`The --port option cannot be used with --url.`);if(e.repl===!1)throw new r(`The --no-repl option cannot be used with --url.`);return e.url}}function x(r,a){let s=p(),y=e().version,x=new i,S=t();return x.name(`ash`).description(`Build and run an Ash application.`).version(y).showHelpAfterError().exitOverride().hook(`preAction`,(e,t)=>{h(t)&&r.log(m(y))}).configureOutput({writeErr:e=>{r.error(e.trimEnd())},writeOut:e=>{r.log(e.trimEnd())}}),x.command(`build`).description(`Build the current Ash application.`).action(async()=>{let e=await(a.buildHost??await c())(s);r.log(n(S,{message:`built output at ${e}`,tag:`build`,tone:`success`}))}),x.command(`dev`).description(`Start the Ash development server or connect the REPL to an existing URL.`).option(`--host <host>`,`Host interface to bind`).option(`--no-repl`,`Start the server without the interactive REPL`).option(`--port <port>`,`Port to listen on (defaults to $PORT, then 3000)`,_).option(`--schedules`,`Run scheduled tasks during development (off by default)`).option(`-u, --url <url>`,`Connect the REPL to an existing server URL`,o).addHelpText(`after`,`
1
+ import{t as e}from"../chunks/package-DmsQgn4v.js";import{createCliTheme as t,renderCliTaggedLine as n}from"./ui/output.js";import{i as r,n as i,r as a,t as o}from"../chunks/url-BVRhVE2O.js";import{resolve as s}from"node:path";async function c(){return(await import(`../chunks/host-CQ7AZID3.js`).then(e=>e.t)).buildHost}async function l(){return(await import(`./commands/info.js`)).printApplicationInfo}async function u(){return(await import(`./dev/repl.js`)).runDevelopmentRepl}async function d(){return(await import(`../evals/cli/eval.js`)).runEvalCommand}async function f(){return(await import(`../chunks/host-CQ7AZID3.js`).then(e=>e.t)).startHost}function p(e=process.cwd()){return s(e)}function m(e){return`Ash (v${e})`}function h(e){return e.name()===`info`||e.name()===`dev`}async function g(e){await new Promise((t,n)=>{let r=!1,i=()=>{process.off(`SIGINT`,a),process.off(`SIGTERM`,a)},a=()=>{r||(r=!0,i(),e.close().then(t,n))};process.once(`SIGINT`,a),process.once(`SIGTERM`,a)})}function _(e){if(!/^-?\d+$/.test(e))throw new r(`Expected a numeric port, received "${e}".`);let t=Number(e);if(!Number.isInteger(t))throw new r(`Expected a numeric port, received "${e}".`);if(t<0||t>65535)throw new r(`Expected a port between 0 and 65535, received "${e}".`);return t}function v(){return!!(process.stdin.isTTY&&process.stdout.isTTY)}function y(e){let t=e[1];return e[0]!==`dev`||e.length!==2||t===void 0||t.startsWith(`-`)?[...e]:[`dev`,`--url`,t]}function b(e){if(e.url){if(e.host!==void 0)throw new r(`The --host option cannot be used with --url.`);if(e.port!==void 0)throw new r(`The --port option cannot be used with --url.`);if(e.repl===!1)throw new r(`The --no-repl option cannot be used with --url.`);return e.url}}function x(r,a){let s=p(),y=e().version,x=new i,S=t();return x.name(`ash`).description(`Build and run an Ash application.`).version(y).showHelpAfterError().exitOverride().hook(`preAction`,(e,t)=>{h(t)&&r.log(m(y))}).configureOutput({writeErr:e=>{r.error(e.trimEnd())},writeOut:e=>{r.log(e.trimEnd())}}),x.command(`build`).description(`Build the current Ash application.`).action(async()=>{let e=await(a.buildHost??await c())(s);r.log(n(S,{message:`built output at ${e}`,tag:`build`,tone:`success`}))}),x.command(`dev`).description(`Start the Ash development server or connect the REPL to an existing URL.`).option(`--host <host>`,`Host interface to bind`).option(`--no-repl`,`Start the server without the interactive REPL`).option(`--port <port>`,`Port to listen on (defaults to $PORT, then 3000)`,_).option(`--schedules`,`Run scheduled tasks during development (off by default)`).option(`-u, --url <url>`,`Connect the REPL to an existing server URL`,o).addHelpText(`after`,`
2
2
  You can also pass a bare URL as the only argument, for example: ash dev https://example.com
3
3
  `).action(async e=>{let t=b(e),{loadDevelopmentEnvironmentFiles:i}=await import(`./dev/environment.js`);if(i(s),t){if(r.log(n(S,{message:`REPL connecting to ${t}`,tag:`dev`,tone:`info`})),!v()){r.log(n(S,{message:`Interactive REPL disabled because the current terminal is not a TTY.`,tag:`dev`,tone:`warning`}));return}r.log(``),await(a.runDevelopmentRepl??await u())({serverUrl:t});return}let o=await(a.startHost??await f())(s,{host:e.host,port:e.port,schedules:e.schedules===!0}),c=!1,l=async()=>{c||(c=!0,await o.close())};try{if(r.log(n(S,{message:`server listening at ${o.url}`,tag:`dev`,tone:`success`})),e.repl===!1)return await g({close:l});if(!v())return r.log(n(S,{message:`Interactive REPL disabled because the current terminal is not a TTY.`,tag:`dev`,tone:`warning`})),await g({close:l});r.log(``),await(a.runDevelopmentRepl??await u())({serverUrl:o.url})}finally{await l()}}),x.command(`info`).description(`Print resolved application information.`).action(async()=>{await(a.printApplicationInfo??await l())(r,s)}),x.command(`eval`).description(`Run eval suites against an Ash agent.`).option(`--suite <id...>`,`Suite IDs to run (repeatable)`).option(`--all`,`Run all discovered suites`).option(`--url <url>`,`Remote agent URL (skip local host startup)`).option(`--timeout <ms>`,`Per-case timeout in milliseconds`).option(`--max-concurrency <n>`,`Max concurrent case executions per suite`).option(`--json`,`Output results as JSON`).option(`--list-suites`,`List discovered suites and exit`).option(`--skip-report`,`Skip suite-defined reporters (e.g. Braintrust)`).action(async e=>{await(a.runEvalCommand??await d())(e,r)}),x}async function S(e=process.argv.slice(2),t=console,n={}){let r=x(t,n),i=e.length===0?[`info`]:y(e);try{await r.parseAsync(i,{from:`user`})}catch(e){if(e instanceof a){if(e.exitCode===0)return;throw Error(e.message)}throw e}}export{S as runCli};
@@ -9,10 +9,10 @@ import type { ChannelRouteIdentityMap } from "#compiler/normalize-schedule.js";
9
9
  * entry so the runtime can short-circuit channel registration without
10
10
  * losing the source path for diagnostics.
11
11
  *
12
- * For CompiledChannel values (produced by `defineChannel()`), each route
13
- * in the `routes` array becomes a separate compiled channel entry. The
14
- * channel name is derived from the filesystem path as before; the URL
15
- * path comes from the route's `path` field.
12
+ * Authored channels are always `CompiledChannel` values (from
13
+ * `defineChannel`). Each route in the channel's `routes` array becomes
14
+ * a separate compiled channel entry. The channel name is derived from
15
+ * the filesystem path; the URL path comes from the route's `path` field.
16
16
  */
17
17
  export declare function compileChannelDefinition(agentRoot: string, source: ChannelSourceRef): Promise<CompiledChannelEntry | readonly CompiledChannelEntry[]>;
18
18
  /**
@@ -1,9 +1,8 @@
1
1
  import { join, resolve } from "node:path";
2
2
  import { stripLogicalPathExtension } from "#discover/filesystem.js";
3
- import { isCompiledChannel, normalizeChannelDefinition, } from "#internal/authored-definition/channel.js";
3
+ import { normalizeChannelDefinition } from "#internal/authored-definition/channel.js";
4
4
  import { registerChannelModuleInCache } from "#internal/authored-module-loader.js";
5
5
  import { isDisabledRouteSentinel } from "#public/definitions/channel.js";
6
- import { deriveChannelUrlPath } from "#compiler/channel-url.js";
7
6
  import { loadModuleBackedDefinition } from "#compiler/normalize-helpers.js";
8
7
  /**
9
8
  * Compiles one authored channel module into the normalized channel
@@ -13,10 +12,10 @@ import { loadModuleBackedDefinition } from "#compiler/normalize-helpers.js";
13
12
  * entry so the runtime can short-circuit channel registration without
14
13
  * losing the source path for diagnostics.
15
14
  *
16
- * For CompiledChannel values (produced by `defineChannel()`), each route
17
- * in the `routes` array becomes a separate compiled channel entry. The
18
- * channel name is derived from the filesystem path as before; the URL
19
- * path comes from the route's `path` field.
15
+ * Authored channels are always `CompiledChannel` values (from
16
+ * `defineChannel`). Each route in the channel's `routes` array becomes
17
+ * a separate compiled channel entry. The channel name is derived from
18
+ * the filesystem path; the URL path comes from the route's `path` field.
20
19
  */
21
20
  export async function compileChannelDefinition(agentRoot, source) {
22
21
  const rawValue = await loadModuleBackedDefinition({
@@ -33,28 +32,16 @@ export async function compileChannelDefinition(agentRoot, source) {
33
32
  };
34
33
  }
35
34
  const definition = normalizeChannelDefinition(rawValue, `Expected the channel export "${source.exportName ?? "default"}" from "${source.logicalPath}" to match the public Ash shape.`);
36
- if (isCompiledChannel(definition)) {
37
- return definition.routes.map((route) => ({
38
- kind: "channel",
39
- name: channelName,
40
- logicalPath: source.logicalPath,
41
- method: route.method.toUpperCase(),
42
- urlPath: route.path,
43
- sourceId: source.sourceId,
44
- sourceKind: "module",
45
- exportName: source.exportName,
46
- }));
47
- }
48
- return {
35
+ return definition.routes.map((route) => ({
49
36
  kind: "channel",
50
37
  name: channelName,
51
38
  logicalPath: source.logicalPath,
52
- method: definition.method ?? "POST",
53
- urlPath: deriveChannelUrlPath(channelName),
39
+ method: route.method.toUpperCase(),
40
+ urlPath: route.path,
54
41
  sourceId: source.sourceId,
55
42
  sourceKind: "module",
56
43
  exportName: source.exportName,
57
- };
44
+ }));
58
45
  }
59
46
  /**
60
47
  * Loads all channel modules and builds a map from route object identity
@@ -1 +1 @@
1
- import{n as e}from"../../chunks/paths-DvimrhJF.js";import{loadDevelopmentEnvironmentFiles as t}from"../../cli/dev/environment.js";import{a as n,n as r,t as i}from"../../chunks/client-DBMG7iuf.js";import{n as a}from"../../chunks/host-CsF9KDv8.js";import{discoverAndImportSuites as o,discoverSuiteFiles as s,importSuiteFile as c}from"../runner/discover.js";import{executeSuite as l}from"../runner/execute-suite.js";import{ConsoleReporter as u}from"../runner/reporters/console.js";var d=n();function f(e,t){e.command(`eval`).description(`Run eval suites against an Ash agent.`).option(`--suite <id...>`,`Suite IDs to run (repeatable)`).option(`--all`,`Run all discovered suites`).option(`--url <url>`,`Remote agent URL (skip local host startup)`).option(`--timeout <ms>`,`Per-case timeout in milliseconds`).option(`--max-concurrency <n>`,`Max concurrent case executions per suite`).option(`--json`,`Output results as JSON`).option(`--list-suites`,`List discovered suites and exit`).option(`--skip-report`,`Skip suite-defined reporters (e.g. Braintrust)`).action(async e=>{await p(e,t)})}async function p(n,r){let i=e();if(t(i),n.listSuites){await y(i,r);return}let s=n.suite,c=await o(i,s);if(c.length===0){s&&s.length>0?r.error(`No suites found matching: ${s.join(`, `)}`):r.error(`No eval suites found. Create suite files under evals/ with the *.eval.ts extension.`),process.exitCode=1;return}let u,d;n.url?d={kind:`remote`,url:n.url}:(u=await a(i,{host:`127.0.0.1`,port:0}),d={kind:`local`,url:u.url});let f=m(d);try{let e=[];for(let t of c){let r=_(t,n),a=v(r,{json:n.json===!0,skipReport:n.skipReport===!0}),o=await l({suite:r,target:d,reporters:a,appRoot:i,client:f});e.push(o)}n.json&&r.log(JSON.stringify(e,null,2)),e.some(e=>e.errored>0)&&(process.exitCode=1)}finally{u&&await u.close()}process.exit(process.exitCode??0)}function m(e){if(e.kind===`local`)return new i({host:e.url});let t={},n=process.env.VERCEL_AUTOMATION_BYPASS_SECRET?.trim();return n&&(t[r]=n),new i({auth:h(),headers:Object.keys(t).length>0?t:void 0,host:e.url})}function h(){let e=process.env.ASH_EVAL_AUTH_TOKEN?.trim();return e?{bearer:e}:{bearer:g}}async function g(){try{let e=(await(0,d.getVercelOidcToken)()).trim();if(e.length>0)return e}catch{}return process.env.VERCEL_OIDC_TOKEN?.trim()??``}function _(e,t){let n=t.maxConcurrency?Number.parseInt(t.maxConcurrency,10):void 0,r=t.timeout?Number.parseInt(t.timeout,10):void 0;if(n===void 0&&r===void 0)return e;let i={...e};return n!==void 0&&(i.maxConcurrency=n),r!==void 0&&(i.timeoutMs=r),i}function v(e,t){let n=t.json?[]:[new u];return!t.skipReport&&e.reporters&&n.push(...e.reporters),n}async function y(e,t){let n=await s(e);if(n.length===0){t.log(`No eval suites found.`);return}t.log(`Found ${n.length} eval suite file(s):\n`);for(let r of n){let n=await c(e,r);t.log(` ${n.id}${n.description?` - ${n.description}`:``}`)}}export{f as registerEvalCommand,p as runEvalCommand};
1
+ import{n as e}from"../../chunks/paths-DQbfjCIS.js";import{loadDevelopmentEnvironmentFiles as t}from"../../cli/dev/environment.js";import{a as n,n as r,t as i}from"../../chunks/client-DBMG7iuf.js";import{n as a}from"../../chunks/host-CQ7AZID3.js";import{discoverAndImportSuites as o,discoverSuiteFiles as s,importSuiteFile as c}from"../runner/discover.js";import{executeSuite as l}from"../runner/execute-suite.js";import{ConsoleReporter as u}from"../runner/reporters/console.js";var d=n();function f(e,t){e.command(`eval`).description(`Run eval suites against an Ash agent.`).option(`--suite <id...>`,`Suite IDs to run (repeatable)`).option(`--all`,`Run all discovered suites`).option(`--url <url>`,`Remote agent URL (skip local host startup)`).option(`--timeout <ms>`,`Per-case timeout in milliseconds`).option(`--max-concurrency <n>`,`Max concurrent case executions per suite`).option(`--json`,`Output results as JSON`).option(`--list-suites`,`List discovered suites and exit`).option(`--skip-report`,`Skip suite-defined reporters (e.g. Braintrust)`).action(async e=>{await p(e,t)})}async function p(n,r){let i=e();if(t(i),n.listSuites){await y(i,r);return}let s=n.suite,c=await o(i,s);if(c.length===0){s&&s.length>0?r.error(`No suites found matching: ${s.join(`, `)}`):r.error(`No eval suites found. Create suite files under evals/ with the *.eval.ts extension.`),process.exitCode=1;return}let u,d;n.url?d={kind:`remote`,url:n.url}:(u=await a(i,{host:`127.0.0.1`,port:0}),d={kind:`local`,url:u.url});let f=m(d);try{let e=[];for(let t of c){let r=_(t,n),a=v(r,{json:n.json===!0,skipReport:n.skipReport===!0}),o=await l({suite:r,target:d,reporters:a,appRoot:i,client:f});e.push(o)}n.json&&r.log(JSON.stringify(e,null,2)),e.some(e=>e.errored>0)&&(process.exitCode=1)}finally{u&&await u.close()}process.exit(process.exitCode??0)}function m(e){if(e.kind===`local`)return new i({host:e.url});let t={},n=process.env.VERCEL_AUTOMATION_BYPASS_SECRET?.trim();return n&&(t[r]=n),new i({auth:h(),headers:Object.keys(t).length>0?t:void 0,host:e.url})}function h(){let e=process.env.ASH_EVAL_AUTH_TOKEN?.trim();return e?{bearer:e}:{bearer:g}}async function g(){try{let e=(await(0,d.getVercelOidcToken)()).trim();if(e.length>0)return e}catch{}return process.env.VERCEL_OIDC_TOKEN?.trim()??``}function _(e,t){let n=t.maxConcurrency?Number.parseInt(t.maxConcurrency,10):void 0,r=t.timeout?Number.parseInt(t.timeout,10):void 0;if(n===void 0&&r===void 0)return e;let i={...e};return n!==void 0&&(i.maxConcurrency=n),r!==void 0&&(i.timeoutMs=r),i}function v(e,t){let n=t.json?[]:[new u];return!t.skipReport&&e.reporters&&n.push(...e.reporters),n}async function y(e,t){let n=await s(e);if(n.length===0){t.log(`No eval suites found.`);return}t.log(`Found ${n.length} eval suite file(s):\n`);for(let r of n){let n=await c(e,r);t.log(` ${n.id}${n.description?` - ${n.description}`:``}`)}}export{f as registerEvalCommand,p as runEvalCommand};
@@ -6,7 +6,7 @@ import { ASH_PACKAGE_NAME } from "#package-name.js";
6
6
  let cachedPackageInfo;
7
7
  // The package build stamps the published version into `dist` so bundled
8
8
  // deployments can still report package metadata without resolving package.json.
9
- const BUNDLED_FALLBACK_PACKAGE_VERSION = "0.7.1";
9
+ const BUNDLED_FALLBACK_PACKAGE_VERSION = "0.7.3";
10
10
  const BUNDLED_FALLBACK_PACKAGE_VERSION_PLACEHOLDER = "__ASH_PACKAGE_VERSION_PLACEHOLDER__";
11
11
  const WORKFLOW_MODULE_ALIASES = {
12
12
  "workflow/api": "src/compiled/@workflow/core/runtime.js",
@@ -1,4 +1,3 @@
1
- import type { Route } from "#public/definitions/channel.js";
2
1
  import { type CompiledChannel } from "#channel/compiled-channel.js";
3
2
  /**
4
3
  * Type guard: returns whether `value` is a {@link CompiledChannel} produced
@@ -9,15 +8,14 @@ export declare function isCompiledChannel(value: unknown): value is CompiledChan
9
8
  * Normalizes one authored channel definition into the canonical internal
10
9
  * shape consumed by the compiler.
11
10
  *
12
- * Accepts both:
13
- * - New CompiledChannel shape: `{ __kind: "ash:channel", routes, adapter, receive? }`
14
- * - Old Route shape: `{ fetch, method?, adapter?, receive? }`
15
- *
16
- * For CompiledChannel values, the normalizer returns the object as-is
17
- * since it is already validated by `defineChannel()`. Old Route shapes
18
- * are validated and returned as Route.
11
+ * Authored channels must go through {@link defineChannel} (or a wrapper
12
+ * like `slackChannel` / `ashChannel`) and therefore must be
13
+ * {@link CompiledChannel} values. The legacy plain-`{ fetch, receive? }`
14
+ * Route shape is no longer supported — drop a clear error for it so
15
+ * users on old patterns get a useful migration hint instead of a silent
16
+ * runtime crash deeper in dispatch.
19
17
  *
20
18
  * Disable sentinels are handled by the compiler before this function is
21
19
  * called.
22
20
  */
23
- export declare function normalizeChannelDefinition(value: unknown, message: string): Route | CompiledChannel;
21
+ export declare function normalizeChannelDefinition(value: unknown, message: string): CompiledChannel;
@@ -1,12 +1,4 @@
1
1
  import { CHANNEL_SENTINEL } from "#channel/compiled-channel.js";
2
- import { expectFunction, expectObjectRecord, expectOnlyKnownKeys, } from "#internal/authored-module.js";
3
- const CHANNEL_METHODS = new Set([
4
- "GET",
5
- "POST",
6
- "PUT",
7
- "DELETE",
8
- "PATCH",
9
- ]);
10
2
  /**
11
3
  * Type guard: returns whether `value` is a {@link CompiledChannel} produced
12
4
  * by {@link defineChannel}.
@@ -20,35 +12,19 @@ export function isCompiledChannel(value) {
20
12
  * Normalizes one authored channel definition into the canonical internal
21
13
  * shape consumed by the compiler.
22
14
  *
23
- * Accepts both:
24
- * - New CompiledChannel shape: `{ __kind: "ash:channel", routes, adapter, receive? }`
25
- * - Old Route shape: `{ fetch, method?, adapter?, receive? }`
26
- *
27
- * For CompiledChannel values, the normalizer returns the object as-is
28
- * since it is already validated by `defineChannel()`. Old Route shapes
29
- * are validated and returned as Route.
15
+ * Authored channels must go through {@link defineChannel} (or a wrapper
16
+ * like `slackChannel` / `ashChannel`) and therefore must be
17
+ * {@link CompiledChannel} values. The legacy plain-`{ fetch, receive? }`
18
+ * Route shape is no longer supported — drop a clear error for it so
19
+ * users on old patterns get a useful migration hint instead of a silent
20
+ * runtime crash deeper in dispatch.
30
21
  *
31
22
  * Disable sentinels are handled by the compiler before this function is
32
23
  * called.
33
24
  */
34
25
  export function normalizeChannelDefinition(value, message) {
35
- if (isCompiledChannel(value)) {
36
- return value;
37
- }
38
- const record = expectObjectRecord(value, message);
39
- expectOnlyKnownKeys(record, ["adapter", "fetch", "method", "receive"], message);
40
- const fetchHandler = expectFunction(record.fetch, message);
41
- let method;
42
- if (record.method !== undefined) {
43
- if (typeof record.method !== "string" || !CHANNEL_METHODS.has(record.method)) {
44
- throw new Error(message);
45
- }
46
- method = record.method;
47
- }
48
- const adapter = record.adapter;
49
- const receive = typeof record.receive === "function" ? record.receive : undefined;
50
- if (method === undefined) {
51
- return { adapter, fetch: fetchHandler, receive };
26
+ if (!isCompiledChannel(value)) {
27
+ throw new Error(`${message} Use \`defineChannel({ routes, ... })\` (or a wrapper like \`slackChannel\` / \`ashChannel\`) — bare \`{ fetch, receive? }\` channel objects are no longer supported.`);
52
28
  }
53
- return { adapter, fetch: fetchHandler, method, receive };
29
+ return value;
54
30
  }
@@ -11,8 +11,10 @@ import type { NitroArtifactsConfig } from "#internal/nitro/routes/runtime-artifa
11
11
  * Nitro forwards that work to `event.waitUntil()` so webhook
12
12
  * acknowledgements can return immediately.
13
13
  *
14
- * For CompiledChannel routes (those with a `handler` field), the dispatch
15
- * builds RouteHandlerArgs with `send`, `getSession`, `params`, etc.
16
- * For old-style routes, it builds the legacy RouteContext with `agent`.
14
+ * Two dispatch shapes: authored channels (`defineChannel` and its
15
+ * wrappers) carry a `handler` field and receive `RouteHandlerArgs` with
16
+ * `send`, `getSession`, etc. Framework-internal channels (the
17
+ * connection callback route) build `ResolvedChannelDefinition` directly
18
+ * with just `fetch` and receive a `RouteContext` carrying `agent`.
17
19
  */
18
20
  export declare function dispatchChannelRequest(event: H3Event, routeKey: string, config: NitroArtifactsConfig): Promise<Response>;
@@ -12,9 +12,11 @@ import { resolveNitroChannelRuntimeBundle } from "#internal/nitro/routes/runtime
12
12
  * Nitro forwards that work to `event.waitUntil()` so webhook
13
13
  * acknowledgements can return immediately.
14
14
  *
15
- * For CompiledChannel routes (those with a `handler` field), the dispatch
16
- * builds RouteHandlerArgs with `send`, `getSession`, `params`, etc.
17
- * For old-style routes, it builds the legacy RouteContext with `agent`.
15
+ * Two dispatch shapes: authored channels (`defineChannel` and its
16
+ * wrappers) carry a `handler` field and receive `RouteHandlerArgs` with
17
+ * `send`, `getSession`, etc. Framework-internal channels (the
18
+ * connection callback route) build `ResolvedChannelDefinition` directly
19
+ * with just `fetch` and receive a `RouteContext` carrying `agent`.
18
20
  */
19
21
  export async function dispatchChannelRequest(event, routeKey, config) {
20
22
  const bundle = await resolveNitroChannelRuntimeBundle(config);
@@ -34,7 +36,7 @@ export async function dispatchChannelRequest(event, routeKey, config) {
34
36
  };
35
37
  let response;
36
38
  if (matchedChannel.handler) {
37
- // New CompiledChannel route — build RouteHandlerArgs
39
+ // Authored CompiledChannel route — build RouteHandlerArgs.
38
40
  const adapter = matchedChannel.adapter ?? { kind: "channel" };
39
41
  const send = createSendFn(bundle.runtime, adapter, matchedChannel.name);
40
42
  const getSession = createGetSessionFn(bundle.runtime);
@@ -48,7 +50,8 @@ export async function dispatchChannelRequest(event, routeKey, config) {
48
50
  response = await matchedChannel.handler(event.req, args);
49
51
  }
50
52
  else {
51
- // Old-style Route build legacy RouteContext
53
+ // Framework-internal fetch-only channel (e.g. the connection
54
+ // callback route). Build a RouteContext with the agent handle.
52
55
  const ctx = {
53
56
  agent: bundle.runtime,
54
57
  waitUntil,
@@ -42,7 +42,7 @@ export function resolveNitroScheduleDispatcher(config) {
42
42
  if (!channel) {
43
43
  throw new Error(`Channel "${channelName}" not found in agent/channels/.`);
44
44
  }
45
- return { receive: channel.receive };
45
+ return { receive: channel.receive, adapter: channel.adapter };
46
46
  },
47
47
  });
48
48
  }
@@ -2,12 +2,11 @@ import type { NitroArtifactsConfig } from "#internal/nitro/routes/runtime-artifa
2
2
  /**
3
3
  * Dispatches one Ash authored schedule via the execution engine.
4
4
  *
5
- * Invoked by a Nitro virtual task handler that bakes in the artifacts
6
- * config so the dispatcher can resolve compiled artifacts without a global
7
- * runtime configuration store.
5
+ * Fire-and-forget: the workflow runtime owns terminal completion and
6
+ * its own failure observability. The task return value reports only
7
+ * that the session was dispatched.
8
8
  */
9
9
  export declare function dispatchScheduleTask(taskName: string, config: NitroArtifactsConfig): Promise<{
10
10
  scheduleId: string;
11
11
  sessionId: string;
12
- status: string;
13
12
  }>;
@@ -4,9 +4,9 @@ import { resolveNitroScheduleDispatcher } from "#internal/nitro/routes/runtime-s
4
4
  /**
5
5
  * Dispatches one Ash authored schedule via the execution engine.
6
6
  *
7
- * Invoked by a Nitro virtual task handler that bakes in the artifacts
8
- * config so the dispatcher can resolve compiled artifacts without a global
9
- * runtime configuration store.
7
+ * Fire-and-forget: the workflow runtime owns terminal completion and
8
+ * its own failure observability. The task return value reports only
9
+ * that the session was dispatched.
10
10
  */
11
11
  export async function dispatchScheduleTask(taskName, config) {
12
12
  const compiledArtifactsSource = resolveNitroCompiledArtifactsSource(config);
@@ -19,11 +19,9 @@ export async function dispatchScheduleTask(taskName, config) {
19
19
  markdown: schedule.markdown,
20
20
  scheduleId: schedule.name,
21
21
  };
22
- const handle = await dispatcher.trigger(triggerInput);
23
- const result = await handle.result;
22
+ const session = await dispatcher.trigger(triggerInput);
24
23
  return {
25
24
  scheduleId: schedule.name,
26
- sessionId: handle.sessionId,
27
- status: result.status,
25
+ sessionId: session.id,
28
26
  };
29
27
  }
@@ -1,8 +1,8 @@
1
1
  import { type AuthFn } from "#public/channels/auth.js";
2
2
  import { type UploadPolicy } from "#public/channels/upload-policy.js";
3
3
  import { type Channel } from "#public/definitions/defineChannel.js";
4
- export interface HttpRouteInput {
4
+ export interface AshChannelInput {
5
5
  readonly auth: AuthFn<Request>;
6
6
  readonly uploadPolicy?: Partial<UploadPolicy>;
7
7
  }
8
- export declare function httpChannel(input: HttpRouteInput): Channel;
8
+ export declare function ashChannel(input: AshChannelInput): Channel;
@@ -3,7 +3,7 @@ import { isInputResponse } from "#runtime/input/types.js";
3
3
  import { createUnauthorizedResponse } from "#public/channels/auth.js";
4
4
  import { collectUploadPolicyViolations, formatUploadPolicyViolation, mergeUploadPolicy, } from "#public/channels/upload-policy.js";
5
5
  import { defineChannel, POST, GET } from "#public/definitions/defineChannel.js";
6
- export function httpChannel(input) {
6
+ export function ashChannel(input) {
7
7
  const uploadPolicy = mergeUploadPolicy(input.uploadPolicy);
8
8
  return defineChannel({
9
9
  routes: [
@@ -28,7 +28,7 @@ export function httpChannel(input) {
28
28
  const policyRejection = checkUploadPolicy(body, uploadPolicy);
29
29
  if (policyRejection !== null)
30
30
  return policyRejection;
31
- const token = `http:${crypto.randomUUID()}`;
31
+ const token = `ash:${crypto.randomUUID()}`;
32
32
  const messageText = typeof body.message === "string"
33
33
  ? body.message
34
34
  : body.message
@@ -275,7 +275,7 @@ export function slackChannel(config = {}) {
275
275
  isDM: false,
276
276
  });
277
277
  return send(input.message, {
278
- auth: null,
278
+ auth: input.auth,
279
279
  continuationToken: `slack:${channelId}:`,
280
280
  state: {
281
281
  serializedThread: thread.toJSON(),
@@ -1,5 +1,4 @@
1
- import type { ChannelAdapter } from "#channel/adapter.js";
2
- import type { DeliverInput, GetEventStreamOptions, RunHandle, RunInput, SessionAuthContext } from "#channel/types.js";
1
+ import type { DeliverInput, GetEventStreamOptions, RunHandle, RunInput } from "#channel/types.js";
3
2
  import type { HandleMessageStreamEvent } from "#protocol/message.js";
4
3
  export type { GetEventStreamOptions } from "#channel/types.js";
5
4
  /**
@@ -94,59 +93,6 @@ export interface Agent {
94
93
  */
95
94
  getEventStream(sessionId: string, options?: GetEventStreamOptions): Promise<ReadableStream<HandleMessageStreamEvent>>;
96
95
  }
97
- /**
98
- * Input for {@link Route.receive} — the universal entry point for
99
- * inbound messages to a channel.
100
- */
101
- export interface ReceiveInput {
102
- readonly message: string;
103
- readonly args: Readonly<Record<string, unknown>>;
104
- readonly auth: SessionAuthContext | null;
105
- }
106
- /**
107
- * Context passed to {@link Route.receive}.
108
- */
109
- export interface ReceiveContext {
110
- readonly agent: Agent;
111
- readonly waitUntil: (task: Promise<unknown>) => void;
112
- }
113
- /**
114
- * Public authored route definition. A route is a Web-standard
115
- * `(Request, ctx) => Response` handler whose URL path is derived from the
116
- * file's location under `agent/channels/`.
117
- *
118
- * Filename is identity. `agent/channels/slack.ts` mounts at `POST /slack`.
119
- * Nested directories and Nitro `[name]` path parameters are preserved.
120
- */
121
- export interface Route {
122
- /**
123
- * HTTP method this route handles. Optional — defaults to `"POST"`.
124
- */
125
- readonly method?: ChannelMethod;
126
- /**
127
- * Optional adapter that this route attaches to sessions it starts.
128
- *
129
- * Declared here so the runtime can register the adapter's `kind` in
130
- * the adapter registry and rehydrate it after workflow step boundaries.
131
- * Routes that don't start sessions (e.g. stream readers) omit this.
132
- */
133
- readonly adapter?: ChannelAdapter<any>;
134
- /**
135
- * The request handler. Receives the inbound `Request` and a
136
- * {@link RouteContext} carrying the agent handle and host helpers.
137
- */
138
- fetch(request: Request, ctx: RouteContext): Promise<Response>;
139
- /**
140
- * Universal entry point for inbound messages. Handles both new
141
- * sessions (start) and continuations (deliver to existing session).
142
- * Called by `fetch` after HTTP parsing and by the schedule dispatcher
143
- * directly.
144
- *
145
- * Returns `RunHandle` for new sessions, `void` for deliveries to
146
- * existing sessions.
147
- */
148
- receive?(input: ReceiveInput, ctx: ReceiveContext): Promise<RunHandle | void>;
149
- }
150
96
  /**
151
97
  * Marker discriminator written into every {@link DisabledRouteSentinel}.
152
98
  */
@@ -171,11 +117,3 @@ export declare function disableRoute(): DisabledRouteSentinel;
171
117
  * produced by {@link disableRoute}.
172
118
  */
173
119
  export declare function isDisabledRouteSentinel(value: unknown): value is DisabledRouteSentinel;
174
- /**
175
- * Type guard: returns whether `value` looks like a {@link Route} or a
176
- * `CompiledChannel` produced by `defineChannel()`.
177
- *
178
- * Used by the compiler to distinguish authored channel exports from
179
- * `DisabledRouteSentinel` exports.
180
- */
181
- export declare function isRouteDefinition(value: unknown): boolean;
@@ -22,19 +22,3 @@ export function isDisabledRouteSentinel(value) {
22
22
  value !== null &&
23
23
  value.kind === DISABLED_ROUTE_SENTINEL_KIND);
24
24
  }
25
- /**
26
- * Type guard: returns whether `value` looks like a {@link Route} or a
27
- * `CompiledChannel` produced by `defineChannel()`.
28
- *
29
- * Used by the compiler to distinguish authored channel exports from
30
- * `DisabledRouteSentinel` exports.
31
- */
32
- export function isRouteDefinition(value) {
33
- if (typeof value !== "object" || value === null)
34
- return false;
35
- // New CompiledChannel shape from defineChannel()
36
- if (value.__kind === "ash:channel")
37
- return true;
38
- // Old Route shape with fetch handler
39
- return typeof value.fetch === "function";
40
- }
@@ -1,5 +1,6 @@
1
1
  import type { AttachmentResolver } from "#channel/adapter.js";
2
2
  import { CHANNEL_SENTINEL } from "#channel/compiled-channel.js";
3
+ import type { SessionAuthContext } from "#channel/types.js";
3
4
  import type { HandleMessageStreamEvent } from "#protocol/message.js";
4
5
  import type { RouteDefinition, SendFn } from "#channel/routes.js";
5
6
  import type { Session, SessionHandle } from "#channel/session.js";
@@ -34,8 +35,9 @@ export interface ChannelConfig<TState = undefined, TCtx = void> {
34
35
  context?(state: NonNullable<TState>): TCtx;
35
36
  readonly routes: readonly RouteDefinition<TState>[];
36
37
  receive?(input: {
37
- message: string;
38
- args: Readonly<Record<string, unknown>>;
38
+ readonly message: string;
39
+ readonly args: Readonly<Record<string, unknown>>;
40
+ readonly auth: SessionAuthContext | null;
39
41
  }, args: {
40
42
  send: SendFn<TState>;
41
43
  }): Promise<Session>;
@@ -55,8 +57,9 @@ export interface Channel<TState = undefined> {
55
57
  path: string;
56
58
  }[];
57
59
  readonly receive?: (input: {
58
- message: string;
59
- args: Readonly<Record<string, unknown>>;
60
+ readonly message: string;
61
+ readonly args: Readonly<Record<string, unknown>>;
62
+ readonly auth: SessionAuthContext | null;
60
63
  }, args: {
61
64
  send: SendFn<TState>;
62
65
  }) => Promise<Session>;
@@ -1,4 +1,4 @@
1
1
  import type { ResolvedChannelDefinition } from "#runtime/types.js";
2
- export declare const HTTP_CHANNEL_NAME = "default";
2
+ export declare const ASH_CHANNEL_NAME = "ash";
3
3
  export declare function getFrameworkChannelDefinitions(): readonly ResolvedChannelDefinition[];
4
4
  export declare function getAllFrameworkChannelNames(): ReadonlySet<string>;
@@ -1,14 +1,14 @@
1
1
  import { none, vercelOidc } from "#public/channels/auth.js";
2
- import { httpChannel } from "#public/channels/http.js";
2
+ import { ashChannel } from "#public/channels/ash.js";
3
3
  import { getConnectionCallbackChannelDefinitions, getConnectionCallbackChannelNames, } from "#runtime/connections/callback-route.js";
4
- export const HTTP_CHANNEL_NAME = "default";
4
+ export const ASH_CHANNEL_NAME = "ash";
5
5
  export function getFrameworkChannelDefinitions() {
6
- const auth = resolveFrameworkHttpAuth();
7
- const compiled = httpChannel({ auth });
6
+ const auth = resolveFrameworkAshAuth();
7
+ const compiled = ashChannel({ auth });
8
8
  const result = [];
9
9
  for (const route of compiled.routes) {
10
10
  result.push({
11
- name: HTTP_CHANNEL_NAME,
11
+ name: ASH_CHANNEL_NAME,
12
12
  method: route.method.toUpperCase(),
13
13
  urlPath: route.path,
14
14
  fetch: async (req, ctx) => route.handler(req, ctx),
@@ -23,9 +23,9 @@ export function getFrameworkChannelDefinitions() {
23
23
  return result;
24
24
  }
25
25
  export function getAllFrameworkChannelNames() {
26
- return new Set([HTTP_CHANNEL_NAME, ...getConnectionCallbackChannelNames()]);
26
+ return new Set([ASH_CHANNEL_NAME, ...getConnectionCallbackChannelNames()]);
27
27
  }
28
- function resolveFrameworkHttpAuth() {
28
+ function resolveFrameworkAshAuth() {
29
29
  if (process.env.VERCEL) {
30
30
  return vercelOidc();
31
31
  }
@@ -3,10 +3,15 @@ import type { CompiledModuleMap } from "#compiler/module-map.js";
3
3
  import type { ResolvedChannelDefinition } from "#runtime/types.js";
4
4
  /**
5
5
  * Resolves one compiled channel entry into a runtime-owned definition
6
- * with a live `fetch` or `handler` attached from the authored module.
6
+ * with a live `handler` (the per-route handler authored via `POST` /
7
+ * `GET` / etc. inside `defineChannel`) and the channel's `receive` hook
8
+ * if the author declared one.
7
9
  *
8
- * For CompiledChannel values (produced by `defineChannel()`), the
9
- * resolver finds the matching route by method + path and attaches its
10
- * `handler`. For old-style Route values, it attaches `fetch` directly.
10
+ * Every authored channel is a `CompiledChannel` from `defineChannel`
11
+ * the bare `{ fetch, receive? }` Route shape is rejected by
12
+ * {@link normalizeChannelDefinition}. Framework-internal channels
13
+ * (the connection callback route, the `ash` session channel) build
14
+ * `ResolvedChannelDefinition` values directly and do not flow through
15
+ * this resolver.
11
16
  */
12
17
  export declare function resolveChannelDefinition(definition: CompiledChannelDefinition, moduleMap: CompiledModuleMap, nodeId: string | undefined): Promise<ResolvedChannelDefinition>;