experimental-ash 0.7.5 → 0.8.0

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 (79) hide show
  1. package/CHANGELOG.md +16 -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/environment.d.ts +3 -2
  22. package/dist/src/cli/dev/repl.js +1 -1
  23. package/dist/src/cli/run.js +1 -1
  24. package/dist/src/compiled/.vendor-stamp.json +1 -1
  25. package/dist/src/compiled/@vercel/sandbox/index.d.ts +37 -3
  26. package/dist/src/evals/cli/eval.js +1 -1
  27. package/dist/src/execution/sandbox/bindings/local.d.ts +0 -2
  28. package/dist/src/execution/sandbox/bindings/local.js +1 -20
  29. package/dist/src/execution/sandbox/bindings/vercel.d.ts +2 -2
  30. package/dist/src/execution/sandbox/bindings/vercel.js +1 -12
  31. package/dist/src/harness/attachment-staging.js +54 -50
  32. package/dist/src/harness/emission.d.ts +14 -1
  33. package/dist/src/harness/emission.js +15 -2
  34. package/dist/src/harness/tool-loop.js +58 -15
  35. package/dist/src/internal/application/package.js +1 -1
  36. package/dist/src/internal/attachments/url-refs.d.ts +14 -0
  37. package/dist/src/internal/attachments/url-refs.js +20 -0
  38. package/dist/src/internal/nitro/host/configure-nitro-routes.d.ts +0 -1
  39. package/dist/src/internal/nitro/host/configure-nitro-routes.js +24 -17
  40. package/dist/src/internal/nitro/host/create-application-nitro.js +1 -16
  41. package/dist/src/internal/nitro/routes/agent-info/build-agent-info-response.d.ts +87 -0
  42. package/dist/src/internal/nitro/routes/{home-page/build-home-page-response.js → agent-info/build-agent-info-response.js} +6 -6
  43. package/dist/src/internal/nitro/routes/{home-page/load-home-page-data.d.ts → agent-info/load-agent-info-data.d.ts} +8 -8
  44. package/dist/src/internal/nitro/routes/{home-page/load-home-page-data.js → agent-info/load-agent-info-data.js} +7 -8
  45. package/dist/src/internal/nitro/routes/index.d.ts +10 -5
  46. package/dist/src/internal/nitro/routes/index.js +225 -18
  47. package/dist/src/internal/nitro/routes/info.d.ts +14 -0
  48. package/dist/src/internal/nitro/routes/info.js +50 -0
  49. package/dist/src/protocol/routes.d.ts +8 -6
  50. package/dist/src/protocol/routes.js +8 -6
  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 +0 -1
  56. package/dist/src/public/channels/slack/slackChannel.js +3 -3
  57. package/dist/src/public/definitions/defineChannel.d.ts +9 -7
  58. package/dist/src/public/definitions/defineChannel.js +5 -11
  59. package/dist/src/public/definitions/sandbox.d.ts +3 -3
  60. package/dist/src/public/sandbox/backends/vercel.d.ts +2 -2
  61. package/dist/src/public/sandbox/index.d.ts +2 -2
  62. package/dist/src/public/sandbox/vercel-sandbox.d.ts +11 -10
  63. package/dist/src/runtime/channels/registry.js +9 -3
  64. package/dist/src/runtime/resolve-channel.js +2 -1
  65. package/dist/src/shared/sandbox-backend.d.ts +4 -4
  66. package/dist/src/shared/sandbox-definition.d.ts +6 -36
  67. package/package.json +1 -1
  68. package/dist/src/chunks/host-33-Sb6vq.js +0 -22
  69. package/dist/src/chunks/types-D9Uv7nU4.js +0 -1
  70. package/dist/src/internal/nitro/host/load-home-page-web-assets.d.ts +0 -12
  71. package/dist/src/internal/nitro/host/load-home-page-web-assets.js +0 -34
  72. package/dist/src/internal/nitro/routes/home-page/build-home-page-response.d.ts +0 -87
  73. package/dist/src/internal/nitro/routes/home.d.ts +0 -6
  74. package/dist/src/internal/nitro/routes/home.js +0 -21
  75. package/dist/src/internal/nitro/routes/web-ui/assets/index-BQa8fbHJ.js +0 -11
  76. package/dist/src/internal/nitro/routes/web-ui/assets/style-Kqb6YxTP.css +0 -2
  77. package/dist/src/internal/nitro/routes/web-ui/index.html +0 -17
  78. package/dist/src/public/sandboxes/vercel-sandbox.d.ts +0 -41
  79. 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,6 +1,7 @@
1
1
  /**
2
- * Development environment files loaded by `ash dev`, ordered from highest to
3
- * lowest precedence.
2
+ * Development environment files loaded by local CLI commands such as
3
+ * `ash dev`, `ash build`, and `ash eval`, ordered from highest to lowest
4
+ * precedence.
4
5
  */
5
6
  export declare const DEVELOPMENT_ENV_FILE_NAMES: readonly [".env.development.local", ".env.local", ".env.development", ".env"];
6
7
  /**
@@ -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 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-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
  }
@@ -314,9 +314,37 @@ export function createToolLoopHarness(config) {
314
314
  * `console.error(error)` handler inside `streamText`. Errors are
315
315
  * handled by the harness catch block and emitted as stream events.
316
316
  */
317
+ // Hydrate `ash-sandbox:` ref FileParts into inline bytes for the
318
+ // model call only. The result is transient — `messages` itself
319
+ // remains ref-only so it can flow into `session.history` without
320
+ // bloating every future step boundary.
321
+ const hydratedMessages = await hydrateSandboxAttachments(messages);
322
+ const ephemeralContext = stepInput.input?.modelContext;
323
+ // AI SDK rejects role:"system" in `messages` — route those entries
324
+ // to `instructions` instead.
325
+ const systemMessages = [];
326
+ const nonSystemMessages = [];
327
+ if (ephemeralContext !== undefined) {
328
+ for (const entry of ephemeralContext) {
329
+ if (entry.role === "system") {
330
+ systemMessages.push(entry);
331
+ }
332
+ else {
333
+ nonSystemMessages.push(entry);
334
+ }
335
+ }
336
+ }
337
+ const instructions = systemMessages.length > 0
338
+ ? [
339
+ ...(session.agent.system
340
+ ? [{ role: "system", content: session.agent.system }]
341
+ : []),
342
+ ...systemMessages,
343
+ ]
344
+ : session.agent.system || undefined;
317
345
  const agentSettings = {
318
346
  headers: attributionHeaders,
319
- instructions: session.agent.system || undefined,
347
+ instructions,
320
348
  model,
321
349
  onError() { },
322
350
  onStepFinish: hooks.onStepFinish,
@@ -327,29 +355,44 @@ export function createToolLoopHarness(config) {
327
355
  tools: effectiveTools,
328
356
  };
329
357
  const agent = new ToolLoopAgent(agentSettings);
330
- // Hydrate `ash-sandbox:` ref FileParts into inline bytes for the
331
- // model call only. The result is transient — `messages` itself
332
- // remains ref-only so it can flow into `session.history` without
333
- // bloating every future step boundary.
334
- const hydratedMessages = await hydrateSandboxAttachments(messages);
335
- // Append ephemeral `lifecycle.{session,turn}` modelContext to the
336
- // model call's message list. modelContext is one-shot per turn and
337
- // never written back to `session.history`.
338
- const ephemeralContext = stepInput.input?.modelContext;
339
- const modelMessages = ephemeralContext !== undefined && ephemeralContext.length > 0
340
- ? [...hydratedMessages, ...ephemeralContext]
341
- : hydratedMessages;
358
+ const modelMessages = nonSystemMessages.length > 0 ? [...hydratedMessages, ...nonSystemMessages] : hydratedMessages;
342
359
  let result;
343
360
  const executeModelCall = async () => {
344
361
  if (emit) {
345
362
  const streamResult = await agent.stream({ messages: modelMessages });
346
- const { inlineActionResultCallIds } = await emitStreamContent(emit, emissionState, streamResult.fullStream);
363
+ const { inlineActionResultCallIds, inlineToolResultParts } = await emitStreamContent(emit, emissionState, streamResult.fullStream);
347
364
  const stepResult = await hooks.stepResult;
348
365
  await emitStepActions(emit, emissionState, stepResult, {
349
366
  excludedActionToolNames: new Set([ASK_QUESTION_TOOL_NAME]),
350
367
  inlineActionResultCallIds,
351
368
  tools: config.tools,
352
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
+ }
353
396
  return stepResult;
354
397
  }
355
398
  await agent.generate({ messages: modelMessages });
@@ -373,7 +416,7 @@ export function createToolLoopHarness(config) {
373
416
  }
374
417
  if (!emit) {
375
418
  // Internal harness callers without an emit fn (tests, task-only
376
- // codepaths) get the raw throw. Only runtime-connected harness
419
+ // code paths) get the raw throw. Only runtime-connected harness
377
420
  // calls go through the structured failure path below.
378
421
  throw error;
379
422
  }