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.
- package/CHANGELOG.md +16 -0
- package/README.md +79 -56
- package/dist/docs/public/channels/README.md +33 -23
- package/dist/docs/public/channels/attachments.md +42 -29
- package/dist/src/channel/adapter.d.ts +21 -28
- package/dist/src/channel/compiled-channel.d.ts +1 -1
- package/dist/src/channel/http.d.ts +29 -0
- package/dist/src/channel/http.js +30 -0
- package/dist/src/channel/schedule.d.ts +20 -0
- package/dist/src/channel/schedule.js +22 -1
- package/dist/src/channel/send.d.ts +1 -1
- package/dist/src/channel/send.js +22 -1
- package/dist/src/channel/types.d.ts +1 -1
- package/dist/src/chunks/{client-DBMG7iuf.js → client-BeZ_W7vl.js} +2 -2
- package/dist/src/chunks/{dev-authored-source-watcher-BcN7BUDE.js → dev-authored-source-watcher-BFC_yNcP.js} +1 -1
- package/dist/src/chunks/host-DMccRKcz.js +22 -0
- package/dist/src/chunks/{paths-BYIdCNw9.js → paths-B-aiDznc.js} +26 -26
- package/dist/src/chunks/{prewarm-DXhyk7i9.js → prewarm-CCbReSNm.js} +1 -1
- package/dist/src/chunks/types-MZUhN0Zy.js +1 -0
- package/dist/src/cli/commands/info.js +1 -1
- package/dist/src/cli/dev/environment.d.ts +3 -2
- package/dist/src/cli/dev/repl.js +1 -1
- package/dist/src/cli/run.js +1 -1
- package/dist/src/compiled/.vendor-stamp.json +1 -1
- package/dist/src/compiled/@vercel/sandbox/index.d.ts +37 -3
- package/dist/src/evals/cli/eval.js +1 -1
- package/dist/src/execution/sandbox/bindings/local.d.ts +0 -2
- package/dist/src/execution/sandbox/bindings/local.js +1 -20
- package/dist/src/execution/sandbox/bindings/vercel.d.ts +2 -2
- package/dist/src/execution/sandbox/bindings/vercel.js +1 -12
- package/dist/src/harness/attachment-staging.js +54 -50
- package/dist/src/harness/emission.d.ts +14 -1
- package/dist/src/harness/emission.js +15 -2
- package/dist/src/harness/tool-loop.js +58 -15
- package/dist/src/internal/application/package.js +1 -1
- package/dist/src/internal/attachments/url-refs.d.ts +14 -0
- package/dist/src/internal/attachments/url-refs.js +20 -0
- package/dist/src/internal/nitro/host/configure-nitro-routes.d.ts +0 -1
- package/dist/src/internal/nitro/host/configure-nitro-routes.js +24 -17
- package/dist/src/internal/nitro/host/create-application-nitro.js +1 -16
- package/dist/src/internal/nitro/routes/agent-info/build-agent-info-response.d.ts +87 -0
- package/dist/src/internal/nitro/routes/{home-page/build-home-page-response.js → agent-info/build-agent-info-response.js} +6 -6
- package/dist/src/internal/nitro/routes/{home-page/load-home-page-data.d.ts → agent-info/load-agent-info-data.d.ts} +8 -8
- package/dist/src/internal/nitro/routes/{home-page/load-home-page-data.js → agent-info/load-agent-info-data.js} +7 -8
- package/dist/src/internal/nitro/routes/index.d.ts +10 -5
- package/dist/src/internal/nitro/routes/index.js +225 -18
- package/dist/src/internal/nitro/routes/info.d.ts +14 -0
- package/dist/src/internal/nitro/routes/info.js +50 -0
- package/dist/src/protocol/routes.d.ts +8 -6
- package/dist/src/protocol/routes.js +8 -6
- package/dist/src/public/channels/ash.js +1 -6
- package/dist/src/public/channels/index.d.ts +1 -1
- package/dist/src/public/channels/slack/attachments.d.ts +14 -18
- package/dist/src/public/channels/slack/attachments.js +30 -36
- package/dist/src/public/channels/slack/index.d.ts +0 -1
- package/dist/src/public/channels/slack/slackChannel.js +3 -3
- package/dist/src/public/definitions/defineChannel.d.ts +9 -7
- package/dist/src/public/definitions/defineChannel.js +5 -11
- package/dist/src/public/definitions/sandbox.d.ts +3 -3
- package/dist/src/public/sandbox/backends/vercel.d.ts +2 -2
- package/dist/src/public/sandbox/index.d.ts +2 -2
- package/dist/src/public/sandbox/vercel-sandbox.d.ts +11 -10
- package/dist/src/runtime/channels/registry.js +9 -3
- package/dist/src/runtime/resolve-channel.js +2 -1
- package/dist/src/shared/sandbox-backend.d.ts +4 -4
- package/dist/src/shared/sandbox-definition.d.ts +6 -36
- package/package.json +1 -1
- package/dist/src/chunks/host-33-Sb6vq.js +0 -22
- package/dist/src/chunks/types-D9Uv7nU4.js +0 -1
- package/dist/src/internal/nitro/host/load-home-page-web-assets.d.ts +0 -12
- package/dist/src/internal/nitro/host/load-home-page-web-assets.js +0 -34
- package/dist/src/internal/nitro/routes/home-page/build-home-page-response.d.ts +0 -87
- package/dist/src/internal/nitro/routes/home.d.ts +0 -6
- package/dist/src/internal/nitro/routes/home.js +0 -21
- package/dist/src/internal/nitro/routes/web-ui/assets/index-BQa8fbHJ.js +0 -11
- package/dist/src/internal/nitro/routes/web-ui/assets/style-Kqb6YxTP.css +0 -2
- package/dist/src/internal/nitro/routes/web-ui/index.html +0 -17
- package/dist/src/public/sandboxes/vercel-sandbox.d.ts +0 -41
- 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-
|
|
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-
|
|
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
|
|
3
|
-
* lowest
|
|
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
|
/**
|
package/dist/src/cli/dev/repl.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{t as e}from"../../chunks/errors-DsO9xmQL.js";import{c as t,d as n,f as r,
|
|
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};
|
package/dist/src/cli/run.js
CHANGED
|
@@ -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-
|
|
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};
|
|
@@ -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?:
|
|
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?:
|
|
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(
|
|
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-
|
|
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 (
|
|
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
|
-
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
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
|
|
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
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
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
|
-
|
|
245
|
-
|
|
246
|
-
|
|
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.
|
|
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: `
|
|
269
|
+
message: `fetchFile for adapter kind="${adapterKind}" threw: ${toErrorMessage(cause)}`,
|
|
265
270
|
});
|
|
266
271
|
}
|
|
267
272
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
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(
|
|
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
|
|
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
|
-
|
|
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
|
-
//
|
|
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
|
}
|