experimental-ash 0.7.6 → 0.8.1

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 (80) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +79 -56
  3. package/dist/docs/public/channels/README.md +33 -23
  4. package/dist/docs/public/channels/attachments.md +42 -29
  5. package/dist/src/channel/adapter.d.ts +21 -28
  6. package/dist/src/channel/compiled-channel.d.ts +1 -1
  7. package/dist/src/channel/http.d.ts +29 -0
  8. package/dist/src/channel/http.js +30 -0
  9. package/dist/src/channel/schedule.d.ts +20 -0
  10. package/dist/src/channel/schedule.js +22 -1
  11. package/dist/src/channel/send.d.ts +1 -1
  12. package/dist/src/channel/send.js +22 -1
  13. package/dist/src/channel/types.d.ts +1 -1
  14. package/dist/src/chunks/{client-DBMG7iuf.js → client-BeZ_W7vl.js} +2 -2
  15. package/dist/src/chunks/{dev-authored-source-watcher-BcN7BUDE.js → dev-authored-source-watcher-BFC_yNcP.js} +1 -1
  16. package/dist/src/chunks/host-DMccRKcz.js +22 -0
  17. package/dist/src/chunks/{paths-BYIdCNw9.js → paths-B-aiDznc.js} +26 -26
  18. package/dist/src/chunks/{prewarm-DXhyk7i9.js → prewarm-CCbReSNm.js} +1 -1
  19. package/dist/src/chunks/types-MZUhN0Zy.js +1 -0
  20. package/dist/src/cli/commands/info.js +1 -1
  21. package/dist/src/cli/dev/repl.js +1 -1
  22. package/dist/src/cli/run.js +1 -1
  23. package/dist/src/compiled/.vendor-stamp.json +1 -1
  24. package/dist/src/compiled/@vercel/sandbox/index.d.ts +37 -3
  25. package/dist/src/evals/cli/eval.js +1 -1
  26. package/dist/src/execution/sandbox/bindings/local.d.ts +0 -2
  27. package/dist/src/execution/sandbox/bindings/local.js +1 -20
  28. package/dist/src/execution/sandbox/bindings/vercel.d.ts +2 -2
  29. package/dist/src/execution/sandbox/bindings/vercel.js +1 -12
  30. package/dist/src/harness/attachment-staging.js +54 -50
  31. package/dist/src/harness/emission.d.ts +14 -1
  32. package/dist/src/harness/emission.js +15 -2
  33. package/dist/src/harness/tool-loop.js +28 -2
  34. package/dist/src/internal/application/package.js +1 -1
  35. package/dist/src/internal/attachments/url-refs.d.ts +14 -0
  36. package/dist/src/internal/attachments/url-refs.js +20 -0
  37. package/dist/src/internal/nitro/host/configure-nitro-routes.d.ts +0 -1
  38. package/dist/src/internal/nitro/host/configure-nitro-routes.js +24 -17
  39. package/dist/src/internal/nitro/host/create-application-nitro.js +1 -16
  40. package/dist/src/internal/nitro/routes/agent-info/build-agent-info-response.d.ts +87 -0
  41. package/dist/src/internal/nitro/routes/{home-page/build-home-page-response.js → agent-info/build-agent-info-response.js} +6 -6
  42. package/dist/src/internal/nitro/routes/{home-page/load-home-page-data.d.ts → agent-info/load-agent-info-data.d.ts} +8 -8
  43. package/dist/src/internal/nitro/routes/{home-page/load-home-page-data.js → agent-info/load-agent-info-data.js} +7 -8
  44. package/dist/src/internal/nitro/routes/index.d.ts +10 -5
  45. package/dist/src/internal/nitro/routes/index.js +225 -18
  46. package/dist/src/internal/nitro/routes/info.d.ts +14 -0
  47. package/dist/src/internal/nitro/routes/info.js +50 -0
  48. package/dist/src/protocol/routes.d.ts +8 -6
  49. package/dist/src/protocol/routes.js +8 -6
  50. package/dist/src/public/channels/ash.d.ts +8 -1
  51. package/dist/src/public/channels/ash.js +1 -6
  52. package/dist/src/public/channels/index.d.ts +1 -1
  53. package/dist/src/public/channels/slack/attachments.d.ts +14 -18
  54. package/dist/src/public/channels/slack/attachments.js +30 -36
  55. package/dist/src/public/channels/slack/index.d.ts +1 -2
  56. package/dist/src/public/channels/slack/slackChannel.d.ts +12 -1
  57. package/dist/src/public/channels/slack/slackChannel.js +34 -48
  58. package/dist/src/public/definitions/defineChannel.d.ts +9 -7
  59. package/dist/src/public/definitions/defineChannel.js +5 -11
  60. package/dist/src/public/definitions/sandbox.d.ts +3 -3
  61. package/dist/src/public/sandbox/backends/vercel.d.ts +2 -2
  62. package/dist/src/public/sandbox/index.d.ts +2 -2
  63. package/dist/src/public/sandbox/vercel-sandbox.d.ts +11 -10
  64. package/dist/src/runtime/channels/registry.js +9 -3
  65. package/dist/src/runtime/resolve-channel.js +2 -1
  66. package/dist/src/shared/sandbox-backend.d.ts +4 -4
  67. package/dist/src/shared/sandbox-definition.d.ts +6 -36
  68. package/package.json +1 -1
  69. package/dist/src/chunks/host-33-Sb6vq.js +0 -22
  70. package/dist/src/chunks/types-D9Uv7nU4.js +0 -1
  71. package/dist/src/internal/nitro/host/load-home-page-web-assets.d.ts +0 -12
  72. package/dist/src/internal/nitro/host/load-home-page-web-assets.js +0 -34
  73. package/dist/src/internal/nitro/routes/home-page/build-home-page-response.d.ts +0 -87
  74. package/dist/src/internal/nitro/routes/home.d.ts +0 -6
  75. package/dist/src/internal/nitro/routes/home.js +0 -21
  76. package/dist/src/internal/nitro/routes/web-ui/assets/index-BQa8fbHJ.js +0 -11
  77. package/dist/src/internal/nitro/routes/web-ui/assets/style-Kqb6YxTP.css +0 -2
  78. package/dist/src/internal/nitro/routes/web-ui/index.html +0 -17
  79. package/dist/src/public/sandboxes/vercel-sandbox.d.ts +0 -41
  80. package/dist/src/public/sandboxes/vercel-sandbox.js +0 -1
@@ -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-BYIdCNw9.js";import{t as m}from"./authored-module-loader-Pt_g8xX2.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-B-aiDznc.js";import{t as m}from"./authored-module-loader-Pt_g8xX2.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(`
@@ -0,0 +1 @@
1
+ import{z as e}from"#compiled/zod/index.js";const t=Symbol(`invalid-json-value-candidate`);function n(e){let n=i(e);if(n===t)throw TypeError(`Expected a JSON-serializable value.`);return n}function r(e){let t=n(e);if(!a(t))throw TypeError(`Expected a JSON-serializable object.`);return t}function i(e,n=new WeakSet){if(e===null||typeof e==`boolean`||typeof e==`string`)return e;if(typeof e==`number`)return Number.isFinite(e)?e:t;if(Array.isArray(e)){let r=[];for(let a of e){let e=i(a,n);if(e===t)return t;r.push(e)}return r}if(typeof e!=`object`||e===void 0||!o(e)||n.has(e))return t;n.add(e);let r={};for(let[a,o]of Object.entries(e)){if(o===void 0)continue;let e=i(o,n);if(e===t)return t;r[a]=e}return n.delete(e),r}function a(e){return e!==null&&!Array.isArray(e)&&typeof e==`object`}function o(e){let t=Object.getPrototypeOf(e);return t===null||t===Object.prototype}const s=e.unknown().transform((t,r)=>{try{return n(t)}catch(t){return r.addIssue({code:`custom`,message:l(t,`Expected a JSON-serializable value.`)}),e.NEVER}}),c=e.unknown().transform((t,n)=>{try{return r(t)}catch(t){return n.addIssue({code:`custom`,message:l(t,`Expected a JSON-serializable object.`)}),e.NEVER}});function l(e,t){return e instanceof Error?e.message:t}const u=`/ash/v1`,d=`${u}/health`,f=`${u}/info`,p=`${u}/session`,m=`${u}/session/:sessionId`,h=`${u}/session/:sessionId/stream`,g=`${u}/connections/:name/callback/:token`;function _(e){return`${u}/session/${encodeURIComponent(e)}/stream`}function v(e){return`${u}/session/${encodeURIComponent(e)}`}const y=`x-ash-session-id`,b=`x-ash-stream-format`,x=`x-ash-stream-version`,S=`application/x-ndjson; charset=utf-8`,C=`ndjson`;new TextEncoder;function w(e){return _(e)}function T(e){return e.type===`session.completed`||e.type===`session.failed`||e.type===`session.waiting`}const E=e.object({callId:e.string(),input:c,kind:e.literal(`tool-call`),toolName:e.string()}).strict(),D=e.object({callId:e.string(),description:e.string(),input:c,kind:e.literal(`subagent-call`),name:e.string(),nodeId:e.string(),subagentName:e.string()}).strict(),O=e.object({callId:e.string(),input:c,kind:e.literal(`load-skill`)}).strict();e.discriminatedUnion(`kind`,[O,D,E]);const k=e.object({callId:e.string(),isError:e.boolean().optional(),kind:e.literal(`tool-result`),output:s,toolName:e.string()}).strict(),A=e.object({callId:e.string(),isError:e.boolean().optional(),kind:e.literal(`subagent-result`),output:s,subagentName:e.string()}).strict(),j=e.object({callId:e.string(),isError:e.boolean().optional(),kind:e.literal(`load-skill-result`),output:s,name:e.string().optional()}).strict();e.discriminatedUnion(`kind`,[j,A,k]);const M=e.object({description:e.string().describe(`Optional additional context for this option.`).optional(),id:e.string().describe(`Stable identifier for the option.`),label:e.string().describe(`User-facing label for the option.`),style:e.enum([`primary`,`danger`,`default`]).describe(`Visual treatment hint for the option.`).optional()}).strict(),N=e.object({action:E,allowFreeform:e.boolean().describe(`Whether the user may answer with freeform text instead of selecting one of the provided options.`).optional(),display:e.enum([`confirmation`,`select`,`text`]).describe(`Rendering hint — the channel uses this to pick a UX treatment.`).optional(),options:e.array(M).describe(`Selectable answer options to present to the user.`).optional(),prompt:e.string().describe(`The prompt to present to the user.`),requestId:e.string().describe(`Stable identifier for this request.`)}).strict(),P=e.object({optionId:e.string().optional(),requestId:e.string(),text:e.string().optional()}).strict();function F(e){return P.safeParse(e).success}export{v as _,y as a,r as b,w as c,m as d,p as f,u as g,h,C as i,T as l,f as m,F as n,b as o,d as p,S as r,x as s,N as t,g as u,_ as v,c as y};
@@ -1,2 +1,2 @@
1
- import{D as e,b as t,t as n,v as r,y as i}from"../../chunks/paths-BYIdCNw9.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-B-aiDznc.js";import{d as a,f as o,h as s}from"../../chunks/types-MZUhN0Zy.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/errors-DsO9xmQL.js";import{c as t,d as n,f as r,g as i,l as a}from"../../chunks/types-D9Uv7nU4.js";import{createCliTheme as o,renderCliBanner as s,renderCliSection as c,renderCliSpeakerLine as l,renderCliTaggedLine as u}from"../ui/output.js";import{n as d,r as f,t as p}from"../../chunks/input-requests-BsBi7_5K.js";import{parseDevReplInput as m}from"./input.js";import{a as h,i as g,n as _,o as v,r as y,s as b,t as x}from"../../chunks/client-DBMG7iuf.js";import{createDevelopmentTerminal as S}from"./terminal.js";import{createInterface as C,emitKeypressEvents as ee}from"node:readline";function te(e){return[...e].reverse().find(a)}function ne(e){let t=new URL(e);return t.hash=``,t.search=``,t.pathname.endsWith(`/`)||(t.pathname=`${t.pathname}/`),t}function w(e){return new URL(e.routePath.replace(/^\/+/,``),ne(e.serverUrl))}function T(e){let t=e.resource.trim();try{return new URL(t)}catch{return w({routePath:t,serverUrl:e.serverUrl})}}const E=[`vercel.com/sso-api`,`<title>Authentication Required</title>`,`Vercel Authentication`];function D(e){if(e.length===0)return!1;for(let t of E)if(e.includes(t))return!0;return!1}function O(e){return e instanceof b||typeof e==`object`&&e&&`body`in e&&typeof e.body==`string`?D(e.body):!1}function k(e){return[`Vercel Deployment Protection blocked the request to ${e.serverUrl}.`,``,"To access the deployment from `ash dev`, do one of:"," • Run `vercel link` in this project so the CLI can mint an OIDC",` token for the deployment automatically.`,` • Set VERCEL_AUTOMATION_BYPASS_SECRET to a Protection Bypass for`,` Automation token (Project Settings → Deployment Protection).`,` • Disable Deployment Protection on the target deployment.`,``,`Docs: https://vercel.com/docs/deployment-protection`].join(`
1
+ import{t as e}from"../../chunks/errors-DsO9xmQL.js";import{c as t,d as n,f as r,h as i,l as a}from"../../chunks/types-MZUhN0Zy.js";import{createCliTheme as o,renderCliBanner as s,renderCliSection as c,renderCliSpeakerLine as l,renderCliTaggedLine as u}from"../ui/output.js";import{n as d,r as f,t as p}from"../../chunks/input-requests-BsBi7_5K.js";import{parseDevReplInput as m}from"./input.js";import{a as h,i as g,n as _,o as v,r as y,s as b,t as x}from"../../chunks/client-BeZ_W7vl.js";import{createDevelopmentTerminal as S}from"./terminal.js";import{createInterface as C,emitKeypressEvents as ee}from"node:readline";function te(e){return[...e].reverse().find(a)}function ne(e){let t=new URL(e);return t.hash=``,t.search=``,t.pathname.endsWith(`/`)||(t.pathname=`${t.pathname}/`),t}function w(e){return new URL(e.routePath.replace(/^\/+/,``),ne(e.serverUrl))}function T(e){let t=e.resource.trim();try{return new URL(t)}catch{return w({routePath:t,serverUrl:e.serverUrl})}}const E=[`vercel.com/sso-api`,`<title>Authentication Required</title>`,`Vercel Authentication`];function D(e){if(e.length===0)return!1;for(let t of E)if(e.includes(t))return!0;return!1}function O(e){return e instanceof b||typeof e==`object`&&e&&`body`in e&&typeof e.body==`string`?D(e.body):!1}function k(e){return[`Vercel Deployment Protection blocked the request to ${e.serverUrl}.`,``,"To access the deployment from `ash dev`, do one of:"," • Run `vercel link` in this project so the CLI can mint an OIDC",` token for the deployment automatically.`,` • Set VERCEL_AUTOMATION_BYPASS_SECRET to a Protection Bypass for`,` Automation token (Project Settings → Deployment Protection).`,` • Disable Deployment Protection on the target deployment.`,``,`Docs: https://vercel.com/docs/deployment-protection`].join(`
2
2
  `)}var A=h();async function j(){try{let e=(await(0,A.getVercelOidcToken)()).trim();if(e.length>0)return e}catch{}return process.env.VERCEL_OIDC_TOKEN?.trim()||``}const M=new Set([`localhost`,`127.0.0.1`,`0.0.0.0`,`::1`,`[::1]`]);function N(e){try{return M.has(new URL(e).hostname)}catch{return!1}}async function P(e){let t={},n=process.env.VERCEL_AUTOMATION_BYPASS_SECRET?.trim();if(n&&(t[_]=n),!e.isLocal){let e=await j();e.length>0&&(t[y]=e)}return t}var F=class{#e;#t;constructor(e){let t=N(e.serverUrl);this.#e=new x({headers:()=>P({isLocal:t}),host:e.serverUrl,...t?{}:{auth:{bearer:j}}}),this.#t=this.#e.session()}async clear(){this.#t=this.#e.session()}async close(){}getSession(){return this.#t.state}async sendMessage(e){return await this.send({message:e.message,onEvent:e.onEvent,onResponseStart:e.onResponseStart})}async send(e){let t=await this.#t.send({inputResponses:e.inputResponses,message:e.message});e.onResponseStart?.({sessionId:t.sessionId});let n=[];for await(let r of t)n.push(r),e.onEvent?.(r);return{events:n,sessionId:t.sessionId,session:this.#t.state}}};function I(e){return new F(e)}function L(e){let t=[{label:`Server`,tone:`info`,value:e.serverUrl},{label:`Create`,tone:`info`,value:`POST ${r}`},{label:`Continue`,tone:`info`,value:`POST ${n}`},{label:`Stream`,tone:`info`,value:`GET ${i}`}];return t.push({label:`Session`,value:`Follow-up messages reuse the active continuation token.`}),t}function R(){return[{label:`/help`,value:`Print the connection contract and available commands.`},{label:`/new`,value:`Clear the current durable session cursor.`},{label:`/exit`,value:`Exit the REPL.`}]}function z(e,t){return[s(e,{subtitle:`Interactive development REPL for the active Ash server.`,title:`Ash Dev`}),``,c(e,{rows:L(t),title:`Connection`}),``,c(e,{rows:R(),title:`Commands`})].join(`
3
3
  `)}function re(e){let t=e.trim();return t.length>0?t:null}function B(e){return e.options?.sourceKind===`subagent`?`subagent`:e.fallback}function V(e){return e.options?.sourceKind===`subagent`?`subagent`:e.fallback}function H(e){return e.options?.sourceLabel===void 0?e.message:`${e.options.sourceLabel}${e.separator??` `}${e.message}`}function U(e,t,n){switch(t.type){case`message.appended`:return{finalized:!1,kind:`message`,line:l(e,{message:t.data.messageSoFar,speaker:n?.sourceLabel??`agent`,tone:V({fallback:`accent`,options:n})})};case`message.completed`:if(t.data.message===null)return;if(t.data.finishReason===`tool-calls`){let r=re(t.data.message);return r===null?void 0:{finalized:!0,kind:`message`,line:u(e,{message:H({message:r,options:n,separator:`: `}),tag:B({fallback:`step`,options:n}),tone:V({fallback:`accent`,options:n})})}}return{finalized:!0,kind:`message`,line:l(e,{message:t.data.message,speaker:n?.sourceLabel??`agent`,tone:V({fallback:`accent`,options:n})})};case`reasoning.appended`:return{finalized:!1,kind:`reasoning`,line:u(e,{message:H({message:t.data.reasoningSoFar,options:n,separator:`: `}),tag:B({fallback:`reasoning`,options:n}),tone:V({fallback:`info`,options:n})})};case`reasoning.completed`:return{finalized:!0,kind:`reasoning`,line:u(e,{message:H({message:t.data.reasoning,options:n,separator:`: `}),tag:B({fallback:`reasoning`,options:n}),tone:V({fallback:`info`,options:n})})};default:return}}function W(e,t,n){let r=U(e,t,n);if(r!==void 0)return r.line;switch(t.type){case`message.received`:return;case`actions.requested`:return u(e,{message:H({message:`${t.type} (${t.data.actions.length} action${t.data.actions.length===1?``:`s`})`,options:n}),tag:B({fallback:`event`,options:n}),tone:V({fallback:`muted`,options:n})});case`input.requested`:return u(e,{message:H({message:`${t.type} (${t.data.requests.length} request${t.data.requests.length===1?``:`s`})`,options:n}),tag:B({fallback:`event`,options:n}),tone:V({fallback:`info`,options:n})});case`action.result`:return u(e,{message:H({message:`${t.type} (${Z(t.data.result)})`,options:n}),tag:B({fallback:`event`,options:n}),tone:V({fallback:`muted`,options:n})});case`session.waiting`:case`session.completed`:return;case`connection.authorization_required`:return u(e,{message:H({message:`${t.type} (${t.data.connectionName}: ${t.data.description}${ae(t.data.authorization)})`,options:n}),tag:B({fallback:`event`,options:n}),tone:V({fallback:`warning`,options:n})});case`compaction.requested`:return u(e,{message:H({message:`compacting conversation history`,options:n}),tag:B({fallback:`event`,options:n}),tone:V({fallback:`muted`,options:n})});case`compaction.completed`:return u(e,{message:H({message:`conversation history compacted`,options:n}),tag:B({fallback:`event`,options:n}),tone:V({fallback:`muted`,options:n})});case`step.failed`:case`turn.failed`:case`session.failed`:return;case`subagent.called`:return u(e,{message:H({message:`${t.type} (${t.data.name} -> ${t.data.childSessionId})`,options:n}),tag:B({fallback:`event`,options:n}),tone:V({fallback:`info`,options:n})});case`subagent.started`:return u(e,{message:H({message:`${t.type} (${t.data.subagentName})`,options:n}),tag:B({fallback:`event`,options:n}),tone:V({fallback:`info`,options:n})});case`subagent.event`:return u(e,{message:H({message:`${t.type} (${t.data.subagentName}: ${oe(t.data.event)})`,options:n}),tag:B({fallback:`event`,options:n}),tone:V({fallback:`muted`,options:n})});case`subagent.completed`:return u(e,{message:H({message:`${t.type} (${t.data.subagentName})`,options:n}),tag:B({fallback:`event`,options:n}),tone:V({fallback:`info`,options:n})});default:return}}function G(e){switch(e.type){case`message.appended`:case`message.completed`:case`reasoning.appended`:case`reasoning.completed`:return`content`;default:return`meta`}}function K(){return{activeLiveContentKind:null,lastPrintedBlockKind:null}}function q(e){let t=e.state;return t.activeLiveContentKind!==null&&(e.terminal.commitLive(),t={activeLiveContentKind:null,lastPrintedBlockKind:`content`}),t.lastPrintedBlockKind!==null&&t.lastPrintedBlockKind!==e.kind&&e.terminal.print(``),e.terminal.print(e.line),{activeLiveContentKind:null,lastPrintedBlockKind:e.kind}}function J(e){let t=U(e.theme,e.event,e.options);if(t!==void 0){let n=e.state;return n.activeLiveContentKind!==null&&n.activeLiveContentKind!==t.kind&&(e.terminal.commitLive(),n={activeLiveContentKind:null,lastPrintedBlockKind:`content`}),n.lastPrintedBlockKind!==null&&n.lastPrintedBlockKind!==`content`&&e.terminal.print(``),e.terminal.updateLive(t.line),t.finalized?(e.terminal.commitLive(),{activeLiveContentKind:null,lastPrintedBlockKind:`content`}):{activeLiveContentKind:t.kind,lastPrintedBlockKind:`content`}}let n=W(e.theme,e.event,e.options);return n===void 0?e.state:q({kind:G(e.event),line:n,state:e.state,terminal:e.terminal})}function Y(e){return(e instanceof DOMException||e instanceof Error)&&e.name===`AbortError`}function X(e){return e.length===0}var ie=class{#e=new Map;#t;#n;#r;#i;constructor(e){this.#t=e.displayStateRef,this.#n=e.serverUrl,this.#r=e.terminal,this.#i=e.theme}subscribe(e){if(this.#e.has(e.sessionId))return;let t=new AbortController,n=this.#a({controller:t,sessionId:e.sessionId,subagentName:e.subagentName}).finally(()=>{this.#e.delete(e.sessionId)});this.#e.set(e.sessionId,{controller:t,done:n,label:e.subagentName})}async waitForIdle(){for(;this.#e.size>0;)await Promise.all([...this.#e.values()].map(e=>e.done))}async close(){let e=[...this.#e.values()];for(let t of e)t.controller.abort();await Promise.allSettled(e.map(e=>e.done))}async#a(n){let r=T({resource:t(n.sessionId),serverUrl:this.#n});try{for await(let e of v({host:this.#n,maxReconnectAttempts:3,resolveHeaders:async()=>await g({resourceUrl:r}),sessionId:n.sessionId,signal:n.controller.signal,startIndex:0}))if(this.#t.current=J({event:e,options:{sourceKind:`subagent`,sourceLabel:n.subagentName},state:this.#t.current,terminal:this.#r,theme:this.#i}),e.type===`subagent.called`&&this.subscribe({sessionId:e.data.childSessionId,subagentName:e.data.name}),a(e))return}catch(t){if(Y(t))return;let r=e(t);this.#t.current=q({kind:`meta`,line:u(this.#i,{message:`${n.subagentName} stream failed: ${r}`,tag:`subagent`,tone:`danger`}),state:this.#t.current,terminal:this.#r})}finally{n.controller.abort()}}};function Z(e){switch(e.kind){case`load-skill-result`:return e.kind;case`subagent-result`:return`${e.kind}:${e.subagentName}`;case`tool-result`:return`${e.kind}:${e.toolName}`}}function ae(e){if(e===void 0)return``;let t=[];return e.url!==void 0&&t.push(e.url),e.userCode!==void 0&&t.push(`code ${e.userCode}`),e.instructions!==void 0&&t.push(e.instructions),t.length===0?``:` — ${t.join(`, `)}`}function oe(e){switch(e.type){case`actions.requested`:{let t=e.data.actions,n=t.map(e=>e.kind===`tool-call`?e.toolName:e.kind).join(`,`);return`${e.type} (${t.length} action${t.length===1?``:`s`}${n.length>0?`: ${n}`:``})`}case`action.result`:return`${e.type} (${Z(e.data.result)})`;case`input.requested`:return`${e.type} (${e.data.requests.length} request${e.data.requests.length===1?``:`s`})`;default:return e.type}}function se(e,t){return t.continuationToken?u(e,{message:`resuming session ${t.continuationToken}`,tag:`session`,tone:`info`}):u(e,{message:`starting a new session`,tag:`session`,tone:`info`})}function ce(e,t){return t.turn.inputResponses!==void 0&&t.turn.message===void 0?u(e,{message:`responding to pending input request${t.turn.inputResponses.length===1?``:`s`}`,tag:`session`,tone:`info`}):se(e,t.session)}function le(e,t){let n=te(t),r=d(t);switch(n?.type){case`session.waiting`:return[u(e,{message:r.length>0?`waiting for input approval/answer or the next message`:`waiting for the next message`,tag:`session`,tone:`success`})];case`session.completed`:return[u(e,{message:`session completed; the next input starts a new session`,tag:`session`,tone:`success`})];case`session.failed`:{let t=n.data.details&&typeof n.data.details.name==`string`?n.data.details.name:void 0;return[u(e,{message:t?`session failed (${t}): ${n.data.message}`:`session failed: ${n.data.message}`,tag:`session`,tone:`danger`}),u(e,{message:`cleared; the next input starts a new session`,tag:`session`,tone:`warning`})]}default:return[]}}async function Q(e,t,n={}){return await new Promise(r=>{let i=e.input,a=()=>{e.off(`close`,o),e.off(`line`,s),i.off(`keypress`,c)},o=()=>{a(),r(void 0)},s=e=>{a(),r(e)},c=(t,i)=>{n.allowEscape!==!0||i.name!==`escape`||(a(),e.write(null,{ctrl:!0,name:`u`}),r(p))};e.setPrompt(t),e.once(`close`,o),e.once(`line`,s),n.allowEscape&&(ee(i,e),i.on(`keypress`,c)),e.prompt()})}async function $(e){let n=e.turn,r={current:K()},i=new ie({displayStateRef:r,serverUrl:e.serverUrl,terminal:e.terminal,theme:e.theme}),a=async t=>{e.terminal.startPrompt(e.rl,t);let n=await Q(e.rl,t,{allowEscape:!0});return e.terminal.stopPrompt(),n};try{for(;;){let o=e.client.getSession();r.current=q({kind:`meta`,line:ce(e.theme,{session:o,turn:n}),state:r.current,terminal:e.terminal});let s=await e.client.send({inputResponses:n.inputResponses,message:n.message,onEvent(t){r.current=J({event:t,state:r.current,terminal:e.terminal,theme:e.theme}),t.type===`subagent.called`&&i.subscribe({sessionId:t.data.childSessionId,subagentName:t.data.name})},onResponseStart(n){n.sessionId&&o.sessionId!==n.sessionId&&(r.current=q({kind:`meta`,line:u(e.theme,{message:n.sessionId,tag:`session`,tone:`accent`}),state:r.current,terminal:e.terminal}),r.current=q({kind:`meta`,line:u(e.theme,{message:T({resource:t(n.sessionId),serverUrl:e.serverUrl}).toString(),tag:`stream`,tone:`info`}),state:r.current,terminal:e.terminal}))}}),c=d(s.events);if(X(c)){await i.waitForIdle();for(let t of le(e.theme,s.events))r.current=q({kind:`meta`,line:t,state:r.current,terminal:e.terminal});return`continue`}let l=await f({ask:a,print(t){r.current=q({kind:`meta`,line:t,state:r.current,terminal:e.terminal})},requests:c,theme:e.theme});if(l.kind===`aborted`)return`exit`;if(l.kind===`deferred`)return r.current=q({kind:`meta`,line:u(e.theme,{message:`left pending input requests unresolved; send a new message to ignore them`,tag:`session`,tone:`warning`}),state:r.current,terminal:e.terminal}),`continue`;n={inputResponses:l.inputResponses}}}finally{await i.close()}}async function ue(t){let n=S(),r=o({color:!0}),i=C({input:process.stdin,output:n.output,terminal:!0});i.on(`SIGINT`,()=>{i.close()});let a=I({serverUrl:t.serverUrl});try{for(n.print(z(r,t)),n.print(``);;){n.print(``),n.startPrompt(i,`you> `);let o=await Q(i,`you> `);if(n.stopPrompt(),o===void 0)return;let s=m(o);switch(s.kind!==`empty`&&s.kind!==`exit`&&n.print(``),s.kind){case`empty`:continue;case`help`:n.print(z(r,t)),n.print(``);continue;case`exit`:return;case`new`:await a.clear(),n.print(u(r,{message:`cleared; the next input starts a new session`,tag:`session`,tone:`warning`})),n.print(``);continue;case`message`:try{if(await $({client:a,rl:i,serverUrl:t.serverUrl,terminal:n,theme:r,turn:{message:s.message}})===`exit`)return}catch(i){O(i)?n.printError(u(r,{message:k({serverUrl:t.serverUrl}),tag:`auth`,tone:`warning`})):n.printError(u(r,{message:e(i),tag:`error`,tone:`danger`}))}n.print(``)}}}finally{await a.close(),i.close(),n.dispose()}}export{K as createTurnDisplayState,U as formatContentEvent,W as formatEvent,J as renderTurnEvent,ue as runDevelopmentRepl,X as shouldDrainSubagentStreamsOnBoundary};
@@ -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-33-Sb6vq.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-33-Sb6vq.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{loadDevelopmentEnvironmentFiles:e}=await import(`./dev/environment.js`);e(s);let t=await(a.buildHost??await c())(s);r.log(n(S,{message:`built output at ${t}`,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-DMccRKcz.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-DMccRKcz.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{loadDevelopmentEnvironmentFiles:e}=await import(`./dev/environment.js`);e(s);let t=await(a.buildHost??await c())(s);r.log(n(S,{message:`built output at ${t}`,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};
@@ -20,5 +20,5 @@
20
20
  "just-bash": "2.14.5",
21
21
  "@standard-schema/spec": "1.1.0"
22
22
  },
23
- "scriptHash": "400e58e190d6f5b6d099b3ab9c99a528b763d59c990b872b71d24b34cb033bda"
23
+ "scriptHash": "520ce458abd555329457db163ebe68cdefda54855571c6d96b4f2a0fb76aa0c9"
24
24
  }
@@ -1,6 +1,37 @@
1
+ export interface NetworkTransformer {
2
+ headers?: Record<string, string> | undefined;
3
+ }
4
+
5
+ export interface NetworkPolicyRule {
6
+ transform?: NetworkTransformer[] | undefined;
7
+ }
8
+
9
+ export type NetworkPolicy =
10
+ | "allow-all"
11
+ | "deny-all"
12
+ | {
13
+ allow?: string[] | Record<string, NetworkPolicyRule[]> | undefined;
14
+ subnets?:
15
+ | {
16
+ allow?: string[] | undefined;
17
+ deny?: string[] | undefined;
18
+ }
19
+ | undefined;
20
+ };
21
+
22
+ export interface SandboxUpdateParams {
23
+ currentSnapshotId?: string | undefined;
24
+ networkPolicy?: NetworkPolicy | undefined;
25
+ persistent?: boolean | undefined;
26
+ resources?: { vcpus?: number | undefined } | undefined;
27
+ snapshotExpiration?: number | undefined;
28
+ tags?: Record<string, string> | undefined;
29
+ timeout?: number | undefined;
30
+ }
31
+
1
32
  export interface SandboxCreateOptions {
2
33
  name?: string | undefined;
3
- networkPolicy?: unknown;
34
+ networkPolicy?: NetworkPolicy | undefined;
4
35
  persistent?: boolean | undefined;
5
36
  source?: unknown;
6
37
  signal?: AbortSignal | undefined;
@@ -19,7 +50,7 @@ export declare class Sandbox {
19
50
  currentSnapshotId?: string | undefined;
20
51
  id: string;
21
52
  name: string;
22
- networkPolicy?: unknown;
53
+ networkPolicy?: NetworkPolicy | undefined;
23
54
  persistent: boolean;
24
55
  status: string;
25
56
  tags?: Record<string, string> | undefined;
@@ -36,7 +67,10 @@ export declare class Sandbox {
36
67
  }): Promise<SandboxCommandResult>;
37
68
  snapshot(options?: unknown): Promise<{ snapshotId: string }>;
38
69
  stop(options?: unknown): Promise<void>;
39
- update(options: Record<string, unknown>): Promise<void>;
70
+ update(
71
+ params: SandboxUpdateParams,
72
+ opts?: { signal?: AbortSignal | undefined } | undefined,
73
+ ): Promise<void>;
40
74
  writeFiles(files: readonly { content: string | Uint8Array; path: string }[], options?: unknown): Promise<void>;
41
75
  [key: string]: any;
42
76
  }
@@ -1 +1 @@
1
- import{n as e}from"../../chunks/paths-BYIdCNw9.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-33-Sb6vq.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-B-aiDznc.js";import{loadDevelopmentEnvironmentFiles as t}from"../../cli/dev/environment.js";import{a as n,n as r,t as i}from"../../chunks/client-BeZ_W7vl.js";import{n as a}from"../../chunks/host-DMccRKcz.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,4 +1,3 @@
1
- import type { SandboxSessionUseOptions } from "#public/definitions/sandbox.js";
2
1
  import type { SandboxBackend } from "#public/definitions/sandbox-backend.js";
3
2
  /**
4
3
  * Creates the local just-bash-backed sandbox backend.
@@ -8,4 +7,3 @@ import type { SandboxBackend } from "#public/definitions/sandbox-backend.js";
8
7
  * the framework's per-call dispatch contract.
9
8
  */
10
9
  export declare function createLocalSandboxBackend(): SandboxBackend;
11
- export declare function assertLocalSessionUseOptionsSupported(options: SandboxSessionUseOptions): void;
@@ -138,30 +138,11 @@ async function createBashSandbox(input) {
138
138
  },
139
139
  };
140
140
  }
141
- export function assertLocalSessionUseOptionsSupported(options) {
142
- if (options.networkPolicy !== undefined && options.networkPolicy !== "allow-all") {
143
- throw new Error("Unsupported networkPolicy. Local sandbox supports only 'allow-all'.");
144
- }
145
- if (options.resources !== undefined) {
146
- throw new Error("Local sandbox does not support resources.");
147
- }
148
- if (options.timeout !== undefined) {
149
- throw new Error("Local sandbox does not support timeout.");
150
- }
151
- if (options.tags !== undefined) {
152
- throw new Error("Local sandbox does not support tags.");
153
- }
154
- }
155
141
  function createHandle(sandbox) {
156
142
  const session = buildSandboxSession(createLocalSessionPrimitives(sandbox));
157
143
  return {
158
144
  session,
159
- useSessionFn: async (options) => {
160
- if (options !== undefined) {
161
- assertLocalSessionUseOptionsSupported(options);
162
- }
163
- return session;
164
- },
145
+ useSessionFn: async () => session,
165
146
  async captureState() {
166
147
  const metadata = (await sandbox.captureSnapshot()) ?? {};
167
148
  return {
@@ -1,7 +1,7 @@
1
1
  import type * as VercelSandboxSdk from "#compiled/@vercel/sandbox/index.js";
2
2
  import type { Sandbox as SdkSandbox } from "#compiled/@vercel/sandbox/index.js";
3
3
  import type { SandboxBackend } from "#public/definitions/sandbox-backend.js";
4
- import type { VercelSandbox } from "#public/sandbox/vercel-sandbox.js";
4
+ import type { VercelSandbox, VercelSandboxSessionUseOptions } from "#public/sandbox/vercel-sandbox.js";
5
5
  type VercelSandboxModule = typeof VercelSandboxSdk;
6
6
  /**
7
7
  * User-controllable subset of `Sandbox.create` parameters.
@@ -18,5 +18,5 @@ export interface CreateVercelSandboxBackendInput {
18
18
  /**
19
19
  * Creates the Vercel-backed sandbox backend.
20
20
  */
21
- export declare function createVercelSandboxBackend(input?: CreateVercelSandboxBackendInput): SandboxBackend<VercelSandbox>;
21
+ export declare function createVercelSandboxBackend(input?: CreateVercelSandboxBackendInput): SandboxBackend<VercelSandbox, VercelSandboxSessionUseOptions>;
22
22
  export {};
@@ -153,18 +153,7 @@ function createHandle(sandbox, sessionKey) {
153
153
  session: buildSandboxSession(createVercelSessionPrimitives(sandbox, sessionKey)),
154
154
  useSessionFn: async (options) => {
155
155
  if (options !== undefined) {
156
- const updateParams = {};
157
- if (options.networkPolicy !== undefined)
158
- updateParams.networkPolicy = options.networkPolicy;
159
- if (options.resources !== undefined)
160
- updateParams.resources = options.resources;
161
- if (options.timeout !== undefined)
162
- updateParams.timeout = options.timeout;
163
- if (options.tags !== undefined)
164
- updateParams.tags = options.tags;
165
- if (Object.keys(updateParams).length > 0) {
166
- await sandbox.update(updateParams);
167
- }
156
+ await sandbox.update(options);
168
157
  }
169
158
  return buildVercelSandbox(sandbox, sessionKey);
170
159
  },
@@ -6,7 +6,7 @@ import { loadContext } from "#context/container.js";
6
6
  import { ChannelKey, SandboxKey } from "#context/keys.js";
7
7
  import { fileDataToBytes } from "#internal/attachments/data.js";
8
8
  import { AshAttachmentError } from "#internal/attachments/errors.js";
9
- import { isAttachmentRefUrl, parseAttachmentRef } from "#internal/attachments/refs.js";
9
+ import { deserializeUrlFilePart, isSerializedUrlFilePart } from "#internal/attachments/url-refs.js";
10
10
  import { decodeSandboxRef, encodeSandboxRef, isSandboxRefUrl, } from "#internal/attachments/sandbox-refs.js";
11
11
  import { toErrorMessage } from "#shared/errors.js";
12
12
  /**
@@ -43,7 +43,8 @@ export async function stageAttachmentsForAdapter(content, sandbox, adapterCtx) {
43
43
  if (typeof content === "string") {
44
44
  return content;
45
45
  }
46
- return Promise.all(content.map(async (part) => {
46
+ const reconstituted = reconstitueFilePartUrls(content);
47
+ return Promise.all(reconstituted.map(async (part) => {
47
48
  if (part.type === "file") {
48
49
  return stageFilePart(part, sandbox, adapterCtx);
49
50
  }
@@ -210,47 +211,51 @@ function renderSandboxRefAsTextPart(ref) {
210
211
  return { text: `Attached file ${ref.path} (${ref.mediaType})`, type: "text" };
211
212
  }
212
213
  async function stageFilePart(part, sandbox, adapterCtx) {
213
- // Already-staged parts carry an `ash-sandbox:` URL. Pass them
214
- // through unchanged so repeated staging is a no-op — important
215
- // when a message re-enters the pipeline (for example, a message
216
- // coalesced across multiple deliver calls).
217
214
  if (isSandboxRefUrl(part.data)) {
218
215
  return part;
219
216
  }
220
- const resolved = isAttachmentRefUrl(part.data)
221
- ? await resolveAttachmentRefViaActiveChannel(part.data, adapterCtx)
222
- : null;
223
- const bytes = resolved?.bytes ?? (await fileDataToBytes(part.data));
217
+ // URL objects (including reconstituted ones) → try fetchFile
218
+ if (part.data instanceof URL && part.data.protocol !== "data:") {
219
+ const resolved = await tryFetchFile(part.data.href, adapterCtx);
220
+ if (resolved === null) {
221
+ return part;
222
+ }
223
+ return stageResolvedBytes(part, resolved, sandbox);
224
+ }
225
+ // Inline data (Buffer, base64, data URL) → stage directly
226
+ const bytes = await fileDataToBytes(part.data);
224
227
  if (bytes === null) {
225
228
  return part;
226
229
  }
230
+ return stageResolvedBytes(part, { bytes }, sandbox);
231
+ }
232
+ function stageResolvedBytes(part, resolved, sandbox) {
233
+ const bytes = resolved.bytes;
227
234
  const sha = sha256Prefix(bytes);
228
- const name = safeFilename(resolved?.filename ?? part.filename, sha);
235
+ const mediaType = resolved.mediaType ?? part.mediaType ?? DEFAULT_MEDIA_TYPE;
236
+ const name = safeFilename(resolved.filename ?? part.filename, sha);
229
237
  const authored = `${ATTACHMENTS_ROOT}/${sha}/${name}`;
230
- await sandbox.writeFile(authored, bytes);
231
- const resolvedPath = sandbox.resolvePath(authored);
232
- const mediaType = resolved?.mediaType ?? part.mediaType ?? DEFAULT_MEDIA_TYPE;
233
- // `data` becomes a compact `ash-sandbox:` URL so no bytes flow into
234
- // `session.history`. Bytes are rehydrated at the model call site via
235
- // `hydrateSandboxAttachments`.
236
- return {
237
- ...part,
238
- data: encodeSandboxRef({ mediaType, path: resolvedPath, size: bytes.byteLength }),
239
- filename: resolvedPath,
240
- mediaType,
241
- };
238
+ return sandbox.writeFile(authored, bytes).then(() => {
239
+ const resolvedPath = sandbox.resolvePath(authored);
240
+ return {
241
+ ...part,
242
+ data: encodeSandboxRef({ mediaType, path: resolvedPath, size: bytes.byteLength }),
243
+ filename: resolvedPath,
244
+ mediaType,
245
+ };
246
+ });
242
247
  }
243
- /**
244
- * Resolves an `ash-attachment:` URL via the resolver declared on the
245
- * channel adapter bound at {@link ChannelKey} on the supplied
246
- * {@link ChannelAdapterContext}.
247
- */
248
- async function resolveAttachmentRefViaActiveChannel(url, adapterCtx) {
249
- const ref = parseAttachmentRef(url);
250
- const adapter = getAttachmentAdapter(adapterCtx);
248
+ async function tryFetchFile(url, adapterCtx) {
249
+ const adapter = adapterCtx.ctx.get(ChannelKey);
250
+ if (adapter?.fetchFile === undefined) {
251
+ return null;
252
+ }
251
253
  const adapterKind = getAdapterKind(adapter);
252
254
  try {
253
- const result = await adapter.attachments.resolve(ref, adapterCtx);
255
+ const result = await adapter.fetchFile(url);
256
+ if (result === null) {
257
+ return null;
258
+ }
254
259
  return Buffer.isBuffer(result) ? { bytes: result } : result;
255
260
  }
256
261
  catch (cause) {
@@ -261,27 +266,26 @@ async function resolveAttachmentRefViaActiveChannel(url, adapterCtx) {
261
266
  adapterKind,
262
267
  cause,
263
268
  kind: "resolver-threw",
264
- message: `Attachment resolver for adapter kind="${adapterKind}" threw: ${toErrorMessage(cause)}`,
269
+ message: `fetchFile for adapter kind="${adapterKind}" threw: ${toErrorMessage(cause)}`,
265
270
  });
266
271
  }
267
272
  }
268
- function getAttachmentAdapter(adapterCtx) {
269
- const adapter = adapterCtx.ctx.get(ChannelKey);
270
- if (adapter === undefined) {
271
- throw new AshAttachmentError({
272
- kind: "missing-adapter",
273
- message: "No channel adapter is bound on the active context — cannot resolve ash-attachment ref.",
274
- });
275
- }
276
- if (adapter.attachments === undefined) {
277
- const adapterKind = getAdapterKind(adapter);
278
- throw new AshAttachmentError({
279
- adapterKind,
280
- kind: "resolver-missing",
281
- message: `Channel adapter kind="${adapterKind}" did not declare an \`attachments\` resolver but received an attachment ref.`,
282
- });
283
- }
284
- return adapter;
273
+ /**
274
+ * Reconstitutes `URL` objects from `ash-url:` serialized strings in
275
+ * `FilePart.data`. Before the queue boundary, `send()` serializes
276
+ * `URL` objects as `ash-url:{href}` strings. This pass restores them
277
+ * so the staging pipeline can use `instanceof URL`.
278
+ */
279
+ function reconstitueFilePartUrls(content) {
280
+ let changed = false;
281
+ const result = content.map((part) => {
282
+ if (part.type === "file" && isSerializedUrlFilePart(part.data)) {
283
+ changed = true;
284
+ return { ...part, data: deserializeUrlFilePart(part.data) };
285
+ }
286
+ return part;
287
+ });
288
+ return changed ? result : content;
285
289
  }
286
290
  function sha256Prefix(bytes) {
287
291
  return createHash("sha256").update(bytes).digest("hex").slice(0, SHA_PREFIX_LENGTH);
@@ -1,4 +1,10 @@
1
- import type { TextStreamPart, ToolSet } from "ai";
1
+ import type { ModelMessage, TextStreamPart, ToolSet } from "ai";
2
+ type ToolResponsePart = Extract<ModelMessage, {
3
+ role: "tool";
4
+ }>["content"][number];
5
+ type InlineToolResultPart = Extract<ToolResponsePart, {
6
+ type: "tool-result";
7
+ }>;
2
8
  import type { AssistantStepFinishReason, RuntimeIdentity } from "#protocol/message.js";
3
9
  import type { RunMode } from "#run-mode.js";
4
10
  import type { JsonObject } from "#shared/json.js";
@@ -90,9 +96,16 @@ export declare function normalizeAssistantStepFinishReason(value: string | undef
90
96
  * `inlineActionResultCallIds` lists tool-result call ids the stream
91
97
  * already emitted as `action.result` events. `emitStepActions` skips
92
98
  * these to avoid double-emission.
99
+ *
100
+ * `inlineToolResultParts` holds the same tool-results in
101
+ * `ToolResultPart` shape. The AI SDK omits them from
102
+ * `stepResult.response.messages` on the approval-resume path, so the
103
+ * harness splices them into persisted history to keep the prior turn's
104
+ * `tool_use` block balanced with a matching `tool_result` on replay.
93
105
  */
94
106
  export interface EmittedStreamContent {
95
107
  readonly inlineActionResultCallIds: ReadonlySet<string>;
108
+ readonly inlineToolResultParts: readonly InlineToolResultPart[];
96
109
  }
97
110
  /**
98
111
  * Consumes the AI SDK `fullStream` and emits real-time text and reasoning
@@ -199,6 +199,7 @@ export async function emitStreamContent(emitFn, state, fullStream) {
199
199
  let streamError;
200
200
  const toolCallIdsSeenInStream = new Set();
201
201
  const inlineActionResultCallIds = new Set();
202
+ const inlineToolResultParts = [];
202
203
  const flushCurrentMessage = async () => {
203
204
  if (currentMessage.length === 0) {
204
205
  return;
@@ -260,13 +261,25 @@ export async function emitStreamContent(emitFn, state, fullStream) {
260
261
  break;
261
262
  }
262
263
  await flushCurrentMessage();
264
+ const inlineToolResult = part;
263
265
  await emitFn(createActionResultEvent({
264
- result: createRuntimeToolResultFromStepResult(part),
266
+ result: createRuntimeToolResultFromStepResult(inlineToolResult),
265
267
  sequence: state.sequence,
266
268
  stepIndex: state.stepIndex,
267
269
  turnId: state.turnId,
268
270
  }));
269
271
  inlineActionResultCallIds.add(part.toolCallId);
272
+ // Match AI SDK's `createToolModelOutput` shape (json for non-strings,
273
+ // text for strings) so persisted history is shape-compatible.
274
+ const rawOutput = inlineToolResult.output;
275
+ inlineToolResultParts.push({
276
+ type: "tool-result",
277
+ toolCallId: inlineToolResult.toolCallId,
278
+ toolName: inlineToolResult.toolName,
279
+ output: typeof rawOutput === "string"
280
+ ? { type: "text", value: rawOutput }
281
+ : { type: "json", value: (rawOutput ?? null) },
282
+ });
270
283
  break;
271
284
  }
272
285
  case "finish-step":
@@ -306,5 +319,5 @@ export async function emitStreamContent(emitFn, state, fullStream) {
306
319
  turnId: state.turnId,
307
320
  }));
308
321
  }
309
- return { inlineActionResultCallIds };
322
+ return { inlineActionResultCallIds, inlineToolResultParts };
310
323
  }
@@ -360,13 +360,39 @@ export function createToolLoopHarness(config) {
360
360
  const executeModelCall = async () => {
361
361
  if (emit) {
362
362
  const streamResult = await agent.stream({ messages: modelMessages });
363
- const { inlineActionResultCallIds } = await emitStreamContent(emit, emissionState, streamResult.fullStream);
363
+ const { inlineActionResultCallIds, inlineToolResultParts } = await emitStreamContent(emit, emissionState, streamResult.fullStream);
364
364
  const stepResult = await hooks.stepResult;
365
365
  await emitStepActions(emit, emissionState, stepResult, {
366
366
  excludedActionToolNames: new Set([ASK_QUESTION_TOOL_NAME]),
367
367
  inlineActionResultCallIds,
368
368
  tools: config.tools,
369
369
  });
370
+ if (inlineToolResultParts.length > 0) {
371
+ /*
372
+ * AI SDK `StepResult` is a class whose `content`,
373
+ * `toolCalls`, `toolResults`, `text` are prototype getters.
374
+ * Each field is read explicitly here rather than via spread
375
+ * so the returned plain object carries the values — spread
376
+ * would copy only own enumerable properties and the
377
+ * downstream `extractQuestionInputRequests` would crash on
378
+ * `toolCalls === undefined`.
379
+ */
380
+ return {
381
+ content: stepResult.content,
382
+ finishReason: stepResult.finishReason,
383
+ response: {
384
+ ...stepResult.response,
385
+ messages: [
386
+ { role: "tool", content: [...inlineToolResultParts] },
387
+ ...stepResult.response.messages,
388
+ ],
389
+ },
390
+ text: stepResult.text,
391
+ toolCalls: stepResult.toolCalls,
392
+ toolResults: stepResult.toolResults,
393
+ usage: stepResult.usage,
394
+ };
395
+ }
370
396
  return stepResult;
371
397
  }
372
398
  await agent.generate({ messages: modelMessages });
@@ -390,7 +416,7 @@ export function createToolLoopHarness(config) {
390
416
  }
391
417
  if (!emit) {
392
418
  // Internal harness callers without an emit fn (tests, task-only
393
- // codepaths) get the raw throw. Only runtime-connected harness
419
+ // code paths) get the raw throw. Only runtime-connected harness
394
420
  // calls go through the structured failure path below.
395
421
  throw error;
396
422
  }
@@ -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.6";
9
+ const BUNDLED_FALLBACK_PACKAGE_VERSION = "0.8.1";
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",
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Custom URL scheme for serializing `URL` objects in `FilePart.data`
3
+ * across the queue boundary.
4
+ *
5
+ * Route handlers place `new URL("https://...")` on `FilePart.data`.
6
+ * Before the message crosses the queue, `send()` rewrites these as
7
+ * `ash-url:https://...` strings. After the queue, the staging pipeline
8
+ * reconstitutes them back to `URL` objects so `instanceof URL` checks
9
+ * work reliably.
10
+ */
11
+ export declare const ASH_URL_SCHEME = "ash-url:";
12
+ export declare function serializeUrlFilePart(url: URL): string;
13
+ export declare function isSerializedUrlFilePart(data: unknown): data is string;
14
+ export declare function deserializeUrlFilePart(data: string): URL;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Custom URL scheme for serializing `URL` objects in `FilePart.data`
3
+ * across the queue boundary.
4
+ *
5
+ * Route handlers place `new URL("https://...")` on `FilePart.data`.
6
+ * Before the message crosses the queue, `send()` rewrites these as
7
+ * `ash-url:https://...` strings. After the queue, the staging pipeline
8
+ * reconstitutes them back to `URL` objects so `instanceof URL` checks
9
+ * work reliably.
10
+ */
11
+ export const ASH_URL_SCHEME = "ash-url:";
12
+ export function serializeUrlFilePart(url) {
13
+ return `${ASH_URL_SCHEME}${url.href}`;
14
+ }
15
+ export function isSerializedUrlFilePart(data) {
16
+ return typeof data === "string" && data.startsWith(ASH_URL_SCHEME);
17
+ }
18
+ export function deserializeUrlFilePart(data) {
19
+ return new URL(data.slice(ASH_URL_SCHEME.length));
20
+ }
@@ -5,6 +5,5 @@ import type { NitroBuildSurface, PreparedApplicationHost } from "#internal/nitro
5
5
  * SDK endpoints into one Nitro host instance.
6
6
  */
7
7
  export declare function configureNitroRoutes(nitro: Nitro, preparedHost: PreparedApplicationHost, input: {
8
- homePageHtml: string;
9
8
  surface: NitroBuildSurface;
10
9
  }): Promise<void>;