experimental-ash 0.43.0 → 0.45.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 +13 -0
- package/bin/ash.js +1 -0
- package/dist/docs/internals/mechanical-invariants.md +16 -0
- package/dist/docs/public/advanced/instrumentation.md +71 -21
- package/dist/docs/public/advanced/typescript-api.md +1 -1
- package/dist/docs/public/sandbox.md +38 -0
- package/dist/src/channel/compiled-channel.d.ts +4 -1
- package/dist/src/channel/compiled-channel.js +1 -1
- package/dist/src/channel/routes.d.ts +8 -10
- package/dist/src/compiled/.vendor-stamp.json +2 -2
- package/dist/src/compiled/@vercel/sandbox/index.d.ts +24 -19
- package/dist/src/compiled/@vercel/sandbox/index.js +5 -5
- package/dist/src/compiled/@vercel/sandbox/network-policy.d.ts +161 -0
- package/dist/src/compiled/@vercel/sandbox/package.json +1 -1
- package/dist/src/compiled/@workflow/core/runtime.js +13 -13
- package/dist/src/compiled/_chunks/node/{auth-CVVvWjaK.js → auth-BsyzphzW.js} +1 -1
- package/dist/src/compiled/_chunks/node/{version-nR4RSpFw.js → version-BGue04qw.js} +1 -1
- package/dist/src/compiled/just-bash/index.d.ts +23 -2
- package/dist/src/compiled/just-bash/network/types.d.ts +155 -0
- package/dist/src/compiler/artifacts.d.ts +1 -0
- package/dist/src/compiler/artifacts.js +1 -1
- package/dist/src/compiler/channel-instrumentation-types.d.ts +8 -0
- package/dist/src/compiler/channel-instrumentation-types.js +2 -0
- package/dist/src/context/dynamic-skill-lifecycle.d.ts +4 -3
- package/dist/src/context/dynamic-skill-lifecycle.js +1 -1
- package/dist/src/context/keys.d.ts +11 -4
- package/dist/src/execution/sandbox/bindings/local.js +1 -1
- package/dist/src/execution/sandbox/bindings/vercel.js +1 -1
- package/dist/src/execution/sandbox/session.d.ts +6 -1
- package/dist/src/execution/sandbox/session.js +1 -1
- package/dist/src/harness/tool-loop.js +1 -1
- package/dist/src/internal/application/package.js +1 -1
- package/dist/src/packages/ash-scaffold/src/channels.js +1 -1
- package/dist/src/packages/ash-scaffold/src/web-template.js +1 -0
- package/dist/src/public/channels/discord/discordChannel.d.ts +5 -2
- package/dist/src/public/channels/index.d.ts +1 -1
- package/dist/src/public/channels/slack/slackChannel.d.ts +6 -8
- package/dist/src/public/channels/teams/teamsChannel.d.ts +5 -2
- package/dist/src/public/channels/telegram/telegramChannel.d.ts +5 -2
- package/dist/src/public/channels/twilio/twilioChannel.d.ts +6 -3
- package/dist/src/public/definitions/defineChannel.d.ts +6 -3
- package/dist/src/public/definitions/instrumentation.d.ts +1 -152
- package/dist/src/public/definitions/instrumentation.js +1 -1
- package/dist/src/public/definitions/sandbox.d.ts +1 -1
- package/dist/src/public/instrumentation/index.d.ts +178 -1
- package/dist/src/public/instrumentation/index.js +1 -1
- package/dist/src/public/sandbox/index.d.ts +1 -0
- package/dist/src/runtime/resolve-channel.js +1 -1
- package/dist/src/shared/sandbox-network-policy.d.ts +23 -0
- package/dist/src/shared/sandbox-network-policy.js +1 -0
- package/dist/src/shared/sandbox-session.d.ts +36 -0
- package/dist/src/shared/skill-package.d.ts +7 -0
- package/dist/src/shared/skill-package.js +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createLogger}from"#internal/logging.js";import{getAdapterKind}from"#channel/adapter.js";import{AuthKey,ContinuationTokenKey,DynamicSkillManifestKey,InitiatorAuthKey,SandboxKey,SessionIdKey}from"#context/keys.js";import{toErrorMessage}from"#shared/errors.js";import{ALLOWED_DYNAMIC_TOOL_EVENTS}from"#shared/dynamic-tool-definition.js";import{normalizeSkillPackage,writeSkillPackageToSandbox}from"#shared/skill-package.js";import{ContextKey}from"#context/key.js";import{formatAvailableSkillsSection}from"#execution/skills/instructions.js";import{BundleKey,ChannelKey}from"#runtime/sessions/runtime-context-keys.js";const log=createLogger(`dynamic-skills`)
|
|
1
|
+
import{createLogger}from"#internal/logging.js";import{getAdapterKind}from"#channel/adapter.js";import{AuthKey,ContinuationTokenKey,DynamicSkillManifestKey,InitiatorAuthKey,SandboxKey,SessionIdKey}from"#context/keys.js";import{toErrorMessage}from"#shared/errors.js";import{ALLOWED_DYNAMIC_TOOL_EVENTS}from"#shared/dynamic-tool-definition.js";import{normalizeSkillPackage,removeSkillPackageFromSandbox,writeSkillPackageToSandbox}from"#shared/skill-package.js";import{ContextKey}from"#context/key.js";import{formatAvailableSkillsSection}from"#execution/skills/instructions.js";import{BundleKey,ChannelKey}from"#runtime/sessions/runtime-context-keys.js";const log=createLogger(`dynamic-skills`);function buildResolveContext(e,i){let o=e.get(SessionIdKey)??``,c=e.get(AuthKey)??null,l=e.get(InitiatorAuthKey)??null,u=e.get(ChannelKey),d=e.get(ContinuationTokenKey);return{session:{id:o,auth:{current:c,initiator:l}},channel:{kind:u===void 0?void 0:getAdapterKind(u),continuationToken:d},messages:i}}function qualifyDynamicSkillNames(e,t,n){let r=Object.keys(n),i=[];if(r.length===0)return i;if(t||r.length===1)return i.push({name:e,entryKey:r[0],entry:n[r[0]]}),i;for(let t of r)i.push({name:`${e}__${t}`,entryKey:t,entry:n[t]});return i}function formatDynamicSkillAnnouncement(e){return formatAvailableSkillsSection(Object.values(e).flat())??``}function isSkillEntry(e){return typeof e==`object`&&!!e&&typeof e.markdown==`string`&&typeof e.description==`string`}const PendingSkillAnnouncementKey=new ContextKey(`ash.pendingSkillAnnouncement`);async function dispatchDynamicSkillEvent(e){let{ctx:t,resolvers:n,event:r,messages:a}=e;if(!ALLOWED_DYNAMIC_TOOL_EVENTS.has(r.type))return;let s=n.filter(e=>e.eventNames.includes(r.type));if(s.length===0)return;let f=buildResolveContext(t,a),p=new Set(t.require(BundleKey).resolvedAgent.skills.map(e=>e.name)),m=t.get(DynamicSkillManifestKey)??{},h=[],g=await Promise.allSettled(s.map(async e=>{let t=e.events[r.type];if(t===void 0)return null;let n=await t(r,f);if(n==null)return{resolver:e,named:[]};let i,a;return isSkillEntry(n)?(i={_single:n},a=!0):(i=n,a=!1),{resolver:e,named:qualifyDynamicSkillNames(e.slug,a,i)}}));for(let e of g){if(e.status===`rejected`){log.error(`Dynamic skill resolver (${r.type}) threw — skipping.`,{error:toErrorMessage(e.reason)});continue}e.value!==null&&h.push({resolver:e.value.resolver,skills:e.value.named.map(({name:e,entry:t})=>normalizeSkillPackage({...t,name:e}))})}if(h.length===0)return;let _={...m};for(let{resolver:e,skills:t}of h)t.length===0?delete _[e.slug]:_[e.slug]=t.map(e=>({description:e.description,name:e.name}));let v=new Map;for(let[e,t]of Object.entries(_))for(let{name:n}of t){if(p.has(n))throw Error(`Dynamic skill "${n}" from resolver "${e}" conflicts with an authored skill.`);let t=v.get(n);if(t!==void 0)throw Error(`Dynamic skill "${n}" from resolver "${e}" conflicts with dynamic resolver "${t}".`);v.set(n,e)}let y=await t.require(SandboxKey).get();if(y!==null)for(let{resolver:e,skills:t}of h){let n=new Set((m[e.slug]??[]).map(e=>e.name)),r=new Set(t.map(e=>e.name));for(let e of n)r.has(e)||await removeSkillPackageFromSandbox({sandbox:y,name:e});for(let e of t)await writeSkillPackageToSandbox({sandbox:y,skill:e})}t.set(DynamicSkillManifestKey,_),t.set(PendingSkillAnnouncementKey,formatDynamicSkillAnnouncement(_))}export{PendingSkillAnnouncementKey,dispatchDynamicSkillEvent};
|
|
@@ -88,8 +88,15 @@ export interface DurableDynamicToolMetadata {
|
|
|
88
88
|
}
|
|
89
89
|
export declare const DynamicSessionToolMetadataKey: ContextKey<readonly DurableDynamicToolMetadata[]>;
|
|
90
90
|
/**
|
|
91
|
-
* Durable
|
|
92
|
-
* produced. Used to diff on re-resolution and clean up removed skills
|
|
93
|
-
* from the sandbox.
|
|
91
|
+
* Durable metadata for one session-scoped dynamic skill.
|
|
94
92
|
*/
|
|
95
|
-
export
|
|
93
|
+
export interface DurableDynamicSkillMetadata {
|
|
94
|
+
readonly name: string;
|
|
95
|
+
readonly description: string;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Durable map from resolver slug to the qualified skills it last produced.
|
|
99
|
+
* Used to diff on re-resolution, clean up removed skills from the sandbox,
|
|
100
|
+
* and rebuild the model-visible announcement across turns.
|
|
101
|
+
*/
|
|
102
|
+
export declare const DynamicSkillManifestKey: ContextKey<Record<string, readonly DurableDynamicSkillMetadata[]>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{dirname,join}from"node:path";import{access,mkdir,readFile,writeFile}from"node:fs/promises";import{resolveSandboxCacheDirectory}from"#internal/application/paths.js";import{shellQuote}from"#execution/sandbox/shell-quote.js";import{SandboxTemplateNotProvisionedError}from"#public/definitions/sandbox-backend.js";import{WORKSPACE_ROOT}from"#runtime/workspace/types.js";import{buildSandboxSession}from"#execution/sandbox/session.js";import{bufferToStream,streamToBuffer}from"#execution/sandbox/stream-utils.js";function createLocalSandboxBackend(e={}){return{name:`local`,async prewarm(e){let t=resolveTemplateSnapshotPath(resolveSandboxCacheDirectory(e.runtimeContext.appRoot),e.templateKey);if(await doesPathExist(t))return;let n=await createBashSandbox({sessionKey:e.templateKey,snapshotPath:t}),r=buildSandboxSession(createLocalInternalSandboxSession(n));try{e.bootstrap!==void 0&&await e.bootstrap({use:async()=>r});for(let t of e.seedFiles)typeof t.content==`string`?await r.writeTextFile({content:t.content,path:t.path}):await r.writeBinaryFile({content:t.content,path:t.path});if(await n.captureSnapshot()===null)throw Error(`Failed to capture local sandbox template state for "${e.templateKey}".`)}finally{await n.dispose()}},async create(e){let t=resolveSandboxCacheDirectory(e.runtimeContext.appRoot),n=await readLocalSnapshot(resolveTemplateSnapshotPath(t,e.templateKey));if(n===null)throw new SandboxTemplateNotProvisionedError({backendName:`local`,templateKey:e.templateKey});let r=getLocalSnapshotPath(e.existingMetadata)??resolveSessionSnapshotPath(t,e.sessionKey);return await doesPathExist(r)||await writeLocalSnapshot(r,n),createHandle(await createBashSandbox({sessionKey:e.sessionKey,snapshotPath:r}))}}}async function createBashSandbox(t){let{InMemoryFs:n,Sandbox:r}=await import(`#compiled/just-bash/index.js`),i=await readLocalSnapshot(t.snapshotPath),a=new n(createInitialFiles(i));await ensureLocalSandboxDirectories(a),await restoreLocalSandboxDirectories(a,i?.entries??[]);let o=await r.create({cwd:WORKSPACE_ROOT,env:i?.env,fs:a,network:{dangerouslyAllowFullInternetAccess:!0}});return{async captureSnapshot(){let e=await captureLocalSnapshot({filesystem:a,sandbox:o});return await writeLocalSnapshot(t.snapshotPath,e),{snapshotPath:t.snapshotPath}},async dispose(){await o.stop()},async readFileBytes(e){let t;try{t=await a.readFileBuffer(e)}catch{return null}return Buffer.from(t)},sessionKey:t.sessionKey,snapshotPath:t.snapshotPath,async spawn(e){if(e.abortSignal?.aborted)throw new DOMException(`The operation was aborted.`,`AbortError`);let t=e.workingDirectory===void 0?e.command:`( cd ${shellQuote(e.workingDirectory)} && ${e.command} )`;return adaptJustBashCommandToSandboxProcess(await o.runCommand({args:[t],cmd:`eval`,detached:!0,signal:e.abortSignal}))},async writeFiles(t){for(let n of t){let t=dirname(n.path);await a.mkdir(t,{recursive:!0}),await a.writeFile(n.path,n.content)}}}}function adaptJustBashCommandToSandboxProcess(e){let t=new TextEncoder,n,r,i=!1,a,o=new ReadableStream({start(e){n=e}}),s=new ReadableStream({start(e){r=e}});return(async()=>{try{for await(let i of e.logs()){let e=t.encode(i.data);i.type===`stdout`?n?.enqueue(e):r?.enqueue(e)}}catch(e){a=e,n?.error(e),r?.error(e)}finally{i=!0,a===void 0&&(n?.close(),r?.close())}})(),{stdout:o,stderr:s,async wait(){let t=await e.wait();for(;!i;)await new Promise(e=>setTimeout(e,0));if(a!==void 0)throw a;return{exitCode:t.exitCode}},async kill(){await e.kill()}}}function createHandle(e){let t=buildSandboxSession(createLocalInternalSandboxSession(e));return{session:t,useSessionFn:async()=>t,async captureState(){return{backendName:`local`,metadata:await e.captureSnapshot()??{},sessionKey:e.sessionKey}},async dispose(){await e.dispose()}}}function createLocalInternalSandboxSession(e){return{id:e.sessionKey,resolvePath:resolveLocalPath,async spawn(t){return await e.spawn(t)},async readFile(t){let n=await e.readFileBytes(t.path);return n===null?null:bufferToStream(n)},async writeFile(t){let n=await streamToBuffer(t.content);await e.writeFiles([{content:n,path:t.path}])}}}function resolveLocalPath(e){return e.startsWith(`/`)?e:`${WORKSPACE_ROOT}/${e}`}function resolveTemplateSnapshotPath(e,n){return join(e,`local`,`templates`,`${n}.json`)}function resolveSessionSnapshotPath(e,n){return join(e,`local`,`sessions`,`${n}.json`)}function createInitialFiles(e){let t={};for(let n of e?.entries??[])n.kind===`file`&&(t[n.path]=Buffer.from(n.contentBase64,`base64`));return t}async function ensureLocalSandboxDirectories(e){await e.mkdir(WORKSPACE_ROOT,{recursive:!0})}async function restoreLocalSandboxDirectories(e,t){let n=t.filter(e=>e.kind===`directory`).map(e=>e.path).sort((e,t)=>e.localeCompare(t));for(let t of n)t!==WORKSPACE_ROOT&&await e.mkdir(t,{recursive:!0})}async function captureLocalSnapshot(e){let t=[],n=e.filesystem.getAllPaths().sort((e,t)=>e.localeCompare(t));for(let r of n){let n=await e.filesystem.stat(r);if(n.isSymbolicLink)continue;if(n.isDirectory){t.push({kind:`directory`,path:r});continue}if(!n.isFile)continue;let i=await e.filesystem.readFileBuffer(r);t.push({contentBase64:Buffer.from(i).toString(`base64`),kind:`file`,path:r})}return{entries:t,env:{...e.sandbox.bashEnvInstance.getEnv()},version:1}}async function readLocalSnapshot(e){if(!await doesPathExist(e))return null;let t=JSON.parse(await readFile(e,`utf8`));return t.version===1?t:null}async function writeLocalSnapshot(t,n){await mkdir(dirname(t),{recursive:!0}),await writeFile(t,`${JSON.stringify(n,null,2)}\n`)}async function doesPathExist(e){try{return await access(e),!0}catch{return!1}}function getLocalSnapshotPath(e){let t=e?.snapshotPath;return typeof t==`string`?t:void 0}export{createLocalSandboxBackend};
|
|
1
|
+
import{dirname,join}from"node:path";import{access,mkdir,readFile,writeFile}from"node:fs/promises";import{resolveSandboxCacheDirectory}from"#internal/application/paths.js";import{shellQuote}from"#execution/sandbox/shell-quote.js";import{SandboxTemplateNotProvisionedError}from"#public/definitions/sandbox-backend.js";import{WORKSPACE_ROOT}from"#runtime/workspace/types.js";import{buildSandboxSession}from"#execution/sandbox/session.js";import{bufferToStream,streamToBuffer}from"#execution/sandbox/stream-utils.js";function createLocalSandboxBackend(e={}){return{name:`local`,async prewarm(e){let t=resolveTemplateSnapshotPath(resolveSandboxCacheDirectory(e.runtimeContext.appRoot),e.templateKey);if(await doesPathExist(t))return;let n=await createBashSandbox({sessionKey:e.templateKey,snapshotPath:t}),r=buildSandboxSession(createLocalInternalSandboxSession(n),localSetNetworkPolicyUnsupported);try{e.bootstrap!==void 0&&await e.bootstrap({use:async()=>r});for(let t of e.seedFiles)typeof t.content==`string`?await r.writeTextFile({content:t.content,path:t.path}):await r.writeBinaryFile({content:t.content,path:t.path});if(await n.captureSnapshot()===null)throw Error(`Failed to capture local sandbox template state for "${e.templateKey}".`)}finally{await n.dispose()}},async create(e){let t=resolveSandboxCacheDirectory(e.runtimeContext.appRoot),n=await readLocalSnapshot(resolveTemplateSnapshotPath(t,e.templateKey));if(n===null)throw new SandboxTemplateNotProvisionedError({backendName:`local`,templateKey:e.templateKey});let r=getLocalSnapshotPath(e.existingMetadata)??resolveSessionSnapshotPath(t,e.sessionKey);return await doesPathExist(r)||await writeLocalSnapshot(r,n),createHandle(await createBashSandbox({sessionKey:e.sessionKey,snapshotPath:r}))}}}async function createBashSandbox(t){let{InMemoryFs:n,Sandbox:r}=await import(`#compiled/just-bash/index.js`),i=await readLocalSnapshot(t.snapshotPath),a=new n(createInitialFiles(i));await ensureLocalSandboxDirectories(a),await restoreLocalSandboxDirectories(a,i?.entries??[]);let o=await r.create({cwd:WORKSPACE_ROOT,env:i?.env,fs:a,network:{dangerouslyAllowFullInternetAccess:!0}});return{async captureSnapshot(){let e=await captureLocalSnapshot({filesystem:a,sandbox:o});return await writeLocalSnapshot(t.snapshotPath,e),{snapshotPath:t.snapshotPath}},async dispose(){await o.stop()},async readFileBytes(e){let t;try{t=await a.readFileBuffer(e)}catch{return null}return Buffer.from(t)},async removePath(e){await a.rm(e.path,{force:e.force,recursive:e.recursive})},sessionKey:t.sessionKey,snapshotPath:t.snapshotPath,async spawn(e){if(e.abortSignal?.aborted)throw new DOMException(`The operation was aborted.`,`AbortError`);let t=e.workingDirectory===void 0?e.command:`( cd ${shellQuote(e.workingDirectory)} && ${e.command} )`;return adaptJustBashCommandToSandboxProcess(await o.runCommand({args:[t],cmd:`eval`,detached:!0,signal:e.abortSignal}))},async writeFiles(t){for(let n of t){let t=dirname(n.path);await a.mkdir(t,{recursive:!0}),await a.writeFile(n.path,n.content)}}}}function adaptJustBashCommandToSandboxProcess(e){let t=new TextEncoder,n,r,i=!1,a,o=new ReadableStream({start(e){n=e}}),s=new ReadableStream({start(e){r=e}});return(async()=>{try{for await(let i of e.logs()){let e=t.encode(i.data);i.type===`stdout`?n?.enqueue(e):r?.enqueue(e)}}catch(e){a=e,n?.error(e),r?.error(e)}finally{i=!0,a===void 0&&(n?.close(),r?.close())}})(),{stdout:o,stderr:s,async wait(){let t=await e.wait();for(;!i;)await new Promise(e=>setTimeout(e,0));if(a!==void 0)throw a;return{exitCode:t.exitCode}},async kill(){await e.kill()}}}async function localSetNetworkPolicyUnsupported(){throw Error(`setNetworkPolicy() is not supported on the local sandbox backend. just-bash applies its network policy only at sandbox creation (no run-time update) and does not run git or other binaries. Use the Vercel backend for credential brokering and egress control.`)}function createHandle(e){let t=buildSandboxSession(createLocalInternalSandboxSession(e),localSetNetworkPolicyUnsupported);return{session:t,useSessionFn:async()=>t,async captureState(){return{backendName:`local`,metadata:await e.captureSnapshot()??{},sessionKey:e.sessionKey}},async dispose(){await e.dispose()}}}function createLocalInternalSandboxSession(e){return{id:e.sessionKey,resolvePath:resolveLocalPath,async spawn(t){return await e.spawn(t)},async readFile(t){let n=await e.readFileBytes(t.path);return n===null?null:bufferToStream(n)},async removePath(t){await e.removePath(t)},async writeFile(t){let n=await streamToBuffer(t.content);await e.writeFiles([{content:n,path:t.path}])}}}function resolveLocalPath(e){return e.startsWith(`/`)?e:`${WORKSPACE_ROOT}/${e}`}function resolveTemplateSnapshotPath(e,n){return join(e,`local`,`templates`,`${n}.json`)}function resolveSessionSnapshotPath(e,n){return join(e,`local`,`sessions`,`${n}.json`)}function createInitialFiles(e){let t={};for(let n of e?.entries??[])n.kind===`file`&&(t[n.path]=Buffer.from(n.contentBase64,`base64`));return t}async function ensureLocalSandboxDirectories(e){await e.mkdir(WORKSPACE_ROOT,{recursive:!0})}async function restoreLocalSandboxDirectories(e,t){let n=t.filter(e=>e.kind===`directory`).map(e=>e.path).sort((e,t)=>e.localeCompare(t));for(let t of n)t!==WORKSPACE_ROOT&&await e.mkdir(t,{recursive:!0})}async function captureLocalSnapshot(e){let t=[],n=e.filesystem.getAllPaths().sort((e,t)=>e.localeCompare(t));for(let r of n){let n=await e.filesystem.stat(r);if(n.isSymbolicLink)continue;if(n.isDirectory){t.push({kind:`directory`,path:r});continue}if(!n.isFile)continue;let i=await e.filesystem.readFileBuffer(r);t.push({contentBase64:Buffer.from(i).toString(`base64`),kind:`file`,path:r})}return{entries:t,env:{...e.sandbox.bashEnvInstance.getEnv()},version:1}}async function readLocalSnapshot(e){if(!await doesPathExist(e))return null;let t=JSON.parse(await readFile(e,`utf8`));return t.version===1?t:null}async function writeLocalSnapshot(t,n){await mkdir(dirname(t),{recursive:!0}),await writeFile(t,`${JSON.stringify(n,null,2)}\n`)}async function doesPathExist(e){try{return await access(e),!0}catch{return!1}}function getLocalSnapshotPath(e){let t=e?.snapshotPath;return typeof t==`string`?t:void 0}export{createLocalSandboxBackend};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{SandboxTemplateNotProvisionedError}from"#public/definitions/sandbox-backend.js";import{WORKSPACE_ROOT}from"#runtime/workspace/types.js";import{buildSandboxSession}from"#execution/sandbox/session.js";import{streamToBuffer}from"#execution/sandbox/stream-utils.js";function createVercelSandboxBackend(t={}){let n=t.loadSandboxModule??(async()=>await import(`#compiled/@vercel/sandbox/index.js`)),r={timeout:DEFAULT_SANDBOX_TIMEOUT_MS,...t.createOptions},i=new Map;return{name:`vercel`,async create(t){let a=resolveVercelSandboxTags(r.tags,t.tags),o;try{o=await readTemplate({loadSandboxModule:n,prewarmedTemplates:i,templateKey:t.templateKey})}catch(n){throw SandboxTemplateNotProvisionedError.is(n)?n:Error(`Failed to read sandbox template "${t.templateKey}": ${errorMessage(n)}`,{cause:n})}let s;try{s=await ensureSession({createOptions:r,existingMetadata:t.existingMetadata,sandboxModule:await n(),sessionKey:t.sessionKey,snapshotId:o.snapshotId,tags:a})}catch(e){throw Error(`Failed to create sandbox session "${t.sessionKey}": ${errorMessage(e)}`,{cause:e})}return createHandle(s,t.sessionKey)},async prewarm(e){let t;try{t=await ensureTemplate({bootstrap:e.bootstrap,createOptions:r,loadSandboxModule:n,seedFiles:e.seedFiles,templateKey:e.templateKey})}catch(t){throw Error(`Failed to prewarm Vercel sandbox template "${e.templateKey}": ${errorMessage(t)}. Run \`vercel login\` and \`vercel link\` so the SDK can authenticate, or set VERCEL_TOKEN.`,{cause:t})}i.set(e.templateKey,t)}}}async function readTemplate(t){let n=t.prewarmedTemplates.get(t.templateKey);if(n!==void 0)return n;let r=await getNamedSandbox(await t.loadSandboxModule(),t.templateKey);if(r===null||typeof r.currentSnapshotId!=`string`)throw new SandboxTemplateNotProvisionedError({backendName:`vercel`,templateKey:t.templateKey});return{sandboxName:r.name,snapshotId:r.currentSnapshotId,templateKey:t.templateKey}}async function ensureTemplate(e){let t=await e.loadSandboxModule(),r=await getNamedSandbox(t,e.templateKey),i=resolveVercelSandboxTags(e.createOptions.tags,e.tags);if(r===null){let n={...e.createOptions,name:e.templateKey,persistent:!1};i!==void 0&&(n.tags=i),r=await t.Sandbox.create(n)}else await ensureVercelSandboxTags(r,i);let a=extractAuthorSnapshotId(e.createOptions);if(typeof r.currentSnapshotId==`string`&&r.currentSnapshotId.length>0&&r.currentSnapshotId!==a)return{sandboxName:r.name,snapshotId:r.currentSnapshotId,templateKey:e.templateKey};await ensureSandboxWorkingDirectory(r,e.createOptions);let o=buildSandboxSession(createVercelInternalSandboxSession(r,e.templateKey));e.bootstrap!==void 0&&await e.bootstrap({use:async e=>(e!==void 0&&await r.update(e),o)});for(let t of e.seedFiles)typeof t.content==`string`?await o.writeTextFile({content:t.content,path:t.path}):await o.writeBinaryFile({content:t.content,path:t.path});let s=await r.snapshot();return{sandboxName:r.name,snapshotId:s.snapshotId,templateKey:e.templateKey}}async function ensureSession(e){let t=getVercelSandboxName(e.existingMetadata)??e.sessionKey,n=await getNamedSandbox(e.sandboxModule,t);if(n!==null)return await ensureVercelSandboxTags(n,e.tags),n;let{runtime:r,source:i,...a}=e.createOptions,o={...a,name:t,persistent:!0,source:{snapshotId:e.snapshotId,type:`snapshot`}};return e.tags!==void 0&&(o.tags=e.tags),await e.sandboxModule.Sandbox.create(o)}function createHandle(e,t){return{session:buildSandboxSession(createVercelInternalSandboxSession(e,t)),useSessionFn:async r=>(r!==void 0&&await e.update(r),buildSandboxSession(createVercelInternalSandboxSession(e,t))),async captureState(){return{backendName:`vercel`,metadata:{sandboxName:e.name},sessionKey:t}},async dispose(){}}}function createVercelInternalSandboxSession(e,n){return{id:n,resolvePath:resolveVercelSandboxPath,async spawn(n){return adaptVercelCommandToSandboxProcess(await e.runCommand({args:[`-lc`,n.command],cmd:`bash`,cwd:n.workingDirectory??WORKSPACE_ROOT,detached:!0,signal:n.abortSignal}))},async readFile(t){return await e.readFile({path:t.path})??null},async writeFile(t){let n=await streamToBuffer(t.content);await e.writeFiles([{content:n,path:t.path}])}}}function adaptVercelCommandToSandboxProcess(e){let t=new TextEncoder,n,r,i=!1,a,o=new ReadableStream({start(e){n=e}}),s=new ReadableStream({start(e){r=e}});return(async()=>{try{for await(let i of e.logs()){let e=t.encode(i.data);i.stream===`stdout`?n?.enqueue(e):r?.enqueue(e)}}catch(e){a=e,n?.error(e),r?.error(e)}finally{i=!0,a===void 0&&(n?.close(),r?.close())}})(),{stdout:o,stderr:s,async wait(){let t=await e.wait();for(;!i;)await new Promise(e=>setTimeout(e,0));if(a!==void 0)throw a;return{exitCode:t.exitCode}},async kill(){await e.kill()}}}function resolveVercelSandboxPath(e){return e.startsWith(`/`)?e:`${WORKSPACE_ROOT}/${e}`}async function ensureSandboxWorkingDirectory(e,n){await runSandboxBootstrapStep(e,{failureMessage:`Failed to initialize Vercel sandbox workspace.`,script:`mkdir -p ${WORKSPACE_ROOT} && chown ${SANDBOX_USER}:${SANDBOX_USER} ${WORKSPACE_ROOT}`}),n.networkPolicy!==`deny-all`&&await runSandboxBootstrapStep(e,{failureMessage:`Failed to install ripgrep in Vercel sandbox.`,script:`command -v rg >/dev/null 2>&1 || { dnf install -y spal-release && dnf install -y ripgrep; }`})}async function runSandboxBootstrapStep(e,t){let n=await e.runCommand({args:[`-lc`,t.script],cmd:`bash`,sudo:!0});if(n.exitCode!==0){let e=await n.stderr();throw Error(`${t.failureMessage} ${e}`.trim())}}const SANDBOX_USER=`vercel-sandbox`;async function getNamedSandbox(e,t){try{return await e.Sandbox.get({name:t})}catch(e){if(isSandboxMissingError(e))return null;throw Error(`Failed to look up Vercel sandbox "${t}": ${errorMessage(e)}`,{cause:e})}}function isSandboxMissingError(e){return e instanceof Error?(e.response?.status??e.cause?.response?.status)===404:!1}function extractAuthorSnapshotId(e){let t=e.source;if(t?.type===`snapshot`&&typeof t.snapshotId==`string`)return t.snapshotId}function getVercelSandboxName(e){let t=e?.sandboxName;return typeof t==`string`?t:void 0}function resolveVercelSandboxTags(e,t){let n={};if(e!==void 0)for(let[t,r]of Object.entries(e))n[t]=r;if(t!==void 0)for(let[e,r]of Object.entries(t))n[e]=r;let r=Object.keys(n).length;if(r!==0){if(r>VERCEL_SANDBOX_TAG_LIMIT)throw Error(`Vercel Sandbox supports at most ${VERCEL_SANDBOX_TAG_LIMIT} tags. Ash reserves "agent", "channel", and "sessionId"; remove or consolidate custom tags passed to vercelBackend().`);return n}}async function ensureVercelSandboxTags(e,t){t===void 0||areVercelSandboxTagsEqual(e.tags,t)||await e.update({tags:t})}function areVercelSandboxTagsEqual(e,t){let n=e??{},r=Object.entries(n),i=Object.entries(t);return r.length===i.length?i.every(([e,t])=>n[e]===t):!1}function errorMessage(e){return e instanceof Error?e.message:String(e)}const DEFAULT_SANDBOX_TIMEOUT_MS=1800*1e3,VERCEL_SANDBOX_TAG_LIMIT=5;export{createVercelSandboxBackend};
|
|
1
|
+
import{SandboxTemplateNotProvisionedError}from"#public/definitions/sandbox-backend.js";import{WORKSPACE_ROOT}from"#runtime/workspace/types.js";import{buildSandboxSession}from"#execution/sandbox/session.js";import{streamToBuffer}from"#execution/sandbox/stream-utils.js";function createVercelSandboxBackend(t={}){let n=t.loadSandboxModule??(async()=>await import(`#compiled/@vercel/sandbox/index.js`)),r={timeout:DEFAULT_SANDBOX_TIMEOUT_MS,...t.createOptions},i=new Map;return{name:`vercel`,async create(t){let a=resolveVercelSandboxTags(r.tags,t.tags),o;try{o=await readTemplate({loadSandboxModule:n,prewarmedTemplates:i,templateKey:t.templateKey})}catch(n){throw SandboxTemplateNotProvisionedError.is(n)?n:Error(`Failed to read sandbox template "${t.templateKey}": ${errorMessage(n)}`,{cause:n})}let s;try{s=await ensureSession({createOptions:r,existingMetadata:t.existingMetadata,sandboxModule:await n(),sessionKey:t.sessionKey,snapshotId:o.snapshotId,tags:a})}catch(e){throw Error(`Failed to create sandbox session "${t.sessionKey}": ${errorMessage(e)}`,{cause:e})}return createHandle(s,t.sessionKey)},async prewarm(e){let t;try{t=await ensureTemplate({bootstrap:e.bootstrap,createOptions:r,loadSandboxModule:n,seedFiles:e.seedFiles,templateKey:e.templateKey})}catch(t){throw Error(`Failed to prewarm Vercel sandbox template "${e.templateKey}": ${errorMessage(t)}. Run \`vercel login\` and \`vercel link\` so the SDK can authenticate, or set VERCEL_TOKEN.`,{cause:t})}i.set(e.templateKey,t)}}}async function readTemplate(t){let n=t.prewarmedTemplates.get(t.templateKey);if(n!==void 0)return n;let r=await getNamedSandbox(await t.loadSandboxModule(),t.templateKey);if(r===null||typeof r.currentSnapshotId!=`string`)throw new SandboxTemplateNotProvisionedError({backendName:`vercel`,templateKey:t.templateKey});return{sandboxName:r.name,snapshotId:r.currentSnapshotId,templateKey:t.templateKey}}async function ensureTemplate(e){let t=await e.loadSandboxModule(),r=await getNamedSandbox(t,e.templateKey),i=resolveVercelSandboxTags(e.createOptions.tags,e.tags);if(r===null){let n={...e.createOptions,name:e.templateKey,persistent:!1};i!==void 0&&(n.tags=i),r=await t.Sandbox.create(n)}else await ensureVercelSandboxTags(r,i);let a=extractAuthorSnapshotId(e.createOptions);if(typeof r.currentSnapshotId==`string`&&r.currentSnapshotId.length>0&&r.currentSnapshotId!==a)return{sandboxName:r.name,snapshotId:r.currentSnapshotId,templateKey:e.templateKey};await ensureSandboxWorkingDirectory(r,e.createOptions);let o=buildSandboxSession(createVercelInternalSandboxSession(r,e.templateKey),createVercelNetworkPolicySetter(r));e.bootstrap!==void 0&&await e.bootstrap({use:async e=>(e!==void 0&&await r.update(e),o)});for(let t of e.seedFiles)typeof t.content==`string`?await o.writeTextFile({content:t.content,path:t.path}):await o.writeBinaryFile({content:t.content,path:t.path});let s=await r.snapshot();return{sandboxName:r.name,snapshotId:s.snapshotId,templateKey:e.templateKey}}async function ensureSession(e){let t=getVercelSandboxName(e.existingMetadata)??e.sessionKey,n=await getNamedSandbox(e.sandboxModule,t);if(n!==null)return await ensureVercelSandboxTags(n,e.tags),n;let{runtime:r,source:i,...a}=e.createOptions,o={...a,name:t,persistent:!0,source:{snapshotId:e.snapshotId,type:`snapshot`}};return e.tags!==void 0&&(o.tags=e.tags),await e.sandboxModule.Sandbox.create(o)}function createHandle(e,t){return{session:buildSandboxSession(createVercelInternalSandboxSession(e,t),createVercelNetworkPolicySetter(e)),useSessionFn:async r=>(r!==void 0&&await e.update(r),buildSandboxSession(createVercelInternalSandboxSession(e,t),createVercelNetworkPolicySetter(e))),async captureState(){return{backendName:`vercel`,metadata:{sandboxName:e.name},sessionKey:t}},async dispose(){}}}function createVercelNetworkPolicySetter(e){return async t=>{await e.update({networkPolicy:t})}}function createVercelInternalSandboxSession(e,n){return{id:n,resolvePath:resolveVercelSandboxPath,async spawn(n){return adaptVercelCommandToSandboxProcess(await e.runCommand({args:[`-lc`,n.command],cmd:`bash`,cwd:n.workingDirectory??WORKSPACE_ROOT,detached:!0,signal:n.abortSignal}))},async readFile(t){return await e.readFile({path:t.path})??null},async writeFile(t){let n=await streamToBuffer(t.content);await e.writeFiles([{content:n,path:t.path}])},async removePath(t){await e.fs.rm(t.path,{force:t.force,recursive:t.recursive,signal:t.abortSignal})}}}function adaptVercelCommandToSandboxProcess(e){let t=new TextEncoder,n,r,i=!1,a,o=new ReadableStream({start(e){n=e}}),s=new ReadableStream({start(e){r=e}});return(async()=>{try{for await(let i of e.logs()){let e=t.encode(i.data);i.stream===`stdout`?n?.enqueue(e):r?.enqueue(e)}}catch(e){a=e,n?.error(e),r?.error(e)}finally{i=!0,a===void 0&&(n?.close(),r?.close())}})(),{stdout:o,stderr:s,async wait(){let t=await e.wait();for(;!i;)await new Promise(e=>setTimeout(e,0));if(a!==void 0)throw a;return{exitCode:t.exitCode}},async kill(){await e.kill()}}}function resolveVercelSandboxPath(e){return e.startsWith(`/`)?e:`${WORKSPACE_ROOT}/${e}`}async function ensureSandboxWorkingDirectory(e,n){await runSandboxBootstrapStep(e,{failureMessage:`Failed to initialize Vercel sandbox workspace.`,script:`mkdir -p ${WORKSPACE_ROOT} && chown ${SANDBOX_USER}:${SANDBOX_USER} ${WORKSPACE_ROOT}`}),n.networkPolicy!==`deny-all`&&await runSandboxBootstrapStep(e,{failureMessage:`Failed to install ripgrep in Vercel sandbox.`,script:`command -v rg >/dev/null 2>&1 || { dnf install -y spal-release && dnf install -y ripgrep; }`})}async function runSandboxBootstrapStep(e,t){let n=await e.runCommand({args:[`-lc`,t.script],cmd:`bash`,sudo:!0});if(n.exitCode!==0){let e=await n.stderr();throw Error(`${t.failureMessage} ${e}`.trim())}}const SANDBOX_USER=`vercel-sandbox`;async function getNamedSandbox(e,t){try{return await e.Sandbox.get({name:t})}catch(e){if(isSandboxMissingError(e))return null;throw Error(`Failed to look up Vercel sandbox "${t}": ${errorMessage(e)}`,{cause:e})}}function isSandboxMissingError(e){return e instanceof Error?(e.response?.status??e.cause?.response?.status)===404:!1}function extractAuthorSnapshotId(e){let t=e.source;if(t?.type===`snapshot`&&typeof t.snapshotId==`string`)return t.snapshotId}function getVercelSandboxName(e){let t=e?.sandboxName;return typeof t==`string`?t:void 0}function resolveVercelSandboxTags(e,t){let n={};if(e!==void 0)for(let[t,r]of Object.entries(e))n[t]=r;if(t!==void 0)for(let[e,r]of Object.entries(t))n[e]=r;let r=Object.keys(n).length;if(r!==0){if(r>VERCEL_SANDBOX_TAG_LIMIT)throw Error(`Vercel Sandbox supports at most ${VERCEL_SANDBOX_TAG_LIMIT} tags. Ash reserves "agent", "channel", and "sessionId"; remove or consolidate custom tags passed to vercelBackend().`);return n}}async function ensureVercelSandboxTags(e,t){t===void 0||areVercelSandboxTagsEqual(e.tags,t)||await e.update({tags:t})}function areVercelSandboxTagsEqual(e,t){let n=e??{},r=Object.entries(n),i=Object.entries(t);return r.length===i.length?i.every(([e,t])=>n[e]===t):!1}function errorMessage(e){return e instanceof Error?e.message:String(e)}const DEFAULT_SANDBOX_TIMEOUT_MS=1800*1e3,VERCEL_SANDBOX_TAG_LIMIT=5;export{createVercelSandboxBackend};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { InternalSandboxSession, SandboxSession } from "#shared/sandbox-session.js";
|
|
2
|
+
import type { SandboxNetworkPolicy } from "#shared/sandbox-network-policy.js";
|
|
2
3
|
export type { InternalSandboxSession };
|
|
3
4
|
/**
|
|
4
5
|
* Builds a public {@link SandboxSession} from backend-specific primitives.
|
|
@@ -8,5 +9,9 @@ export type { InternalSandboxSession };
|
|
|
8
9
|
* read/write primitives. `run` is implemented as a thin wrapper over the
|
|
9
10
|
* backend's `spawn`: collect stdout/stderr to strings, await `wait()`,
|
|
10
11
|
* then return the combined result.
|
|
12
|
+
*
|
|
13
|
+
* `setNetworkPolicy` applies a firewall policy to the live sandbox. It
|
|
14
|
+
* defaults to a no-op so backends without a firewall (and test doubles)
|
|
15
|
+
* need not supply one; the Vercel backend wires it to `sandbox.update`.
|
|
11
16
|
*/
|
|
12
|
-
export declare function buildSandboxSession(primitives: InternalSandboxSession): SandboxSession;
|
|
17
|
+
export declare function buildSandboxSession(primitives: InternalSandboxSession, setNetworkPolicy?: (policy: SandboxNetworkPolicy) => Promise<void>): SandboxSession;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{bufferToStream,streamToBuffer}from"./stream-utils.js";function buildSandboxSession(n){async function run(e){let t=await n.spawn(e),[r,i,{exitCode:a}]=await Promise.all([collectStreamToString(t.stdout),collectStreamToString(t.stderr),t.wait()]);return{exitCode:a,stderr:i,stdout:r}}return{id:n.id,resolvePath(e){return n.resolvePath(e)},run,async spawn(e){return await n.spawn(e)},async readFile(e){return await n.readFile({abortSignal:e.abortSignal,path:n.resolvePath(e.path)})},async readBinaryFile(e){let r=await n.readFile({abortSignal:e.abortSignal,path:n.resolvePath(e.path)});return r===null?null:await streamToBuffer(r)},async readTextFile(e){validateReadTextFileOptions(e);let r=await n.readFile({abortSignal:e.abortSignal,path:n.resolvePath(e.path)});return r===null?null:applyLineRange(decodeBytes(await streamToBuffer(r),e.encoding??`utf-8`),e)},async writeFile(e){await n.writeFile({abortSignal:e.abortSignal,content:e.content,path:n.resolvePath(e.path)})},async writeBinaryFile(t){await n.writeFile({abortSignal:t.abortSignal,content:bufferToStream(t.content),path:n.resolvePath(t.path)})},async writeTextFile(t){let r=encodeString(t.content,t.encoding??`utf-8`);await n.writeFile({abortSignal:t.abortSignal,content:bufferToStream(r),path:n.resolvePath(t.path)})}}}async function collectStreamToString(e){let n=await streamToBuffer(e);return new TextDecoder().decode(n)}function validateReadTextFileOptions(e){let{startLine:t,endLine:n}=e;if(t!==void 0&&(!Number.isInteger(t)||t<1))throw Error(`startLine must be a positive integer (1-based).`);if(n!==void 0&&(!Number.isInteger(n)||n<1))throw Error(`endLine must be a positive integer (1-based).`);if(t!==void 0&&n!==void 0&&t>n)throw Error(`startLine must not be greater than endLine.`)}function splitLinesPreservingEndings(e){let t=[],n=0;for(let r=0;r<e.length;r++)e[r]===`\r`?r+1<e.length&&e[r+1]===`
|
|
1
|
+
import{bufferToStream,streamToBuffer}from"./stream-utils.js";function buildSandboxSession(n,r=async()=>{}){async function run(e){let t=await n.spawn(e),[r,i,{exitCode:a}]=await Promise.all([collectStreamToString(t.stdout),collectStreamToString(t.stderr),t.wait()]);return{exitCode:a,stderr:i,stdout:r}}return{id:n.id,resolvePath(e){return n.resolvePath(e)},run,async spawn(e){return await n.spawn(e)},async readFile(e){return await n.readFile({abortSignal:e.abortSignal,path:n.resolvePath(e.path)})},async readBinaryFile(e){let r=await n.readFile({abortSignal:e.abortSignal,path:n.resolvePath(e.path)});return r===null?null:await streamToBuffer(r)},async readTextFile(e){validateReadTextFileOptions(e);let r=await n.readFile({abortSignal:e.abortSignal,path:n.resolvePath(e.path)});return r===null?null:applyLineRange(decodeBytes(await streamToBuffer(r),e.encoding??`utf-8`),e)},async writeFile(e){await n.writeFile({abortSignal:e.abortSignal,content:e.content,path:n.resolvePath(e.path)})},async writeBinaryFile(t){await n.writeFile({abortSignal:t.abortSignal,content:bufferToStream(t.content),path:n.resolvePath(t.path)})},async writeTextFile(t){let r=encodeString(t.content,t.encoding??`utf-8`);await n.writeFile({abortSignal:t.abortSignal,content:bufferToStream(r),path:n.resolvePath(t.path)})},async removePath(e){await n.removePath({abortSignal:e.abortSignal,force:e.force,path:n.resolvePath(e.path),recursive:e.recursive})},setNetworkPolicy:r}}async function collectStreamToString(e){let n=await streamToBuffer(e);return new TextDecoder().decode(n)}function validateReadTextFileOptions(e){let{startLine:t,endLine:n}=e;if(t!==void 0&&(!Number.isInteger(t)||t<1))throw Error(`startLine must be a positive integer (1-based).`);if(n!==void 0&&(!Number.isInteger(n)||n<1))throw Error(`endLine must be a positive integer (1-based).`);if(t!==void 0&&n!==void 0&&t>n)throw Error(`startLine must not be greater than endLine.`)}function splitLinesPreservingEndings(e){let t=[],n=0;for(let r=0;r<e.length;r++)e[r]===`\r`?r+1<e.length&&e[r+1]===`
|
|
2
2
|
`?(t.push(e.slice(n,r+2)),n=r+2,r++):(t.push(e.slice(n,r+1)),n=r+1):e[r]===`
|
|
3
3
|
`&&(t.push(e.slice(n,r+1)),n=r+1);return n<e.length&&t.push(e.slice(n)),t}function applyLineRange(e,t){if(t.startLine===void 0&&t.endLine===void 0)return e;let n=splitLinesPreservingEndings(e),r=n.length,i=t.startLine??1,a=Math.min(t.endLine??r,r);return i>r?``:n.slice(i-1,a).join(``)}function decodeBytes(e,t){return t===`utf-8`||t===`utf8`?new TextDecoder(`utf-8`,{fatal:!0}).decode(e):Buffer.from(e.buffer,e.byteOffset,e.byteLength).toString(t)}function encodeString(e,t){return t===`utf-8`||t===`utf8`?new TextEncoder().encode(e):Buffer.from(e,t)}export{buildSandboxSession};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createErrorId,createLogger,formatError,logError,recordErrorOnSpan}from"#internal/logging.js";import{DynamicToolsKey}from"#context/keys.js";import{createAuthorizationRequiredEvent,createCompactionCompletedEvent,createCompactionRequestedEvent,createInputRequestedEvent}from"#protocol/message.js";import{toErrorMessage}from"#shared/errors.js";import{resolveInstalledPackageInfo}from"#internal/application/package.js";import{formatLanguageModelGatewayId}from"#internal/runtime-model.js";import{contextStorage}from"#context/container.js";import{ToolLoopAgent,isStepCount}from"ai";import{advanceStep,emitFailedStep,emitRecoverableFailedTurn,emitStepStarted,emitStreamContent,emitTurnEpilogue,emitTurnPreamble,getHarnessEmissionState,setHarnessEmissionState}from"#harness/emission.js";import{setAshAttributes}from"#runtime/attributes/emit.js";import{createRuntimeActionRequestFromToolCall,resolvePendingRuntimeActions,setPendingRuntimeActionBatch}from"#harness/runtime-actions.js";import{CODE_MODE_TOOL_NAME,loadCodeModeModule}from"#shared/code-mode.js";import{resolveAssistantStepText}from"#harness/messages.js";import{PendingSkillAnnouncementKey}from"#context/dynamic-skill-lifecycle.js";import{consumeDeferredStepInput,getApprovedTools,hasDeferredStepInput,hasStepInput,resolvePendingInput,setPendingInputBatch}from"#harness/input-requests.js";import{isAuthorizationSignal,setPendingAuthorization}from"#harness/authorization.js";import{isCodeModeConnectionAuthInterrupt}from"#runtime/framework-tools/code-mode-connection-auth.js";import{buildToolSetWithProviderTools}from"#harness/tools.js";import{ASK_QUESTION_TOOL_NAME}from"#runtime/framework-tools/ask-question.js";import{WEB_SEARCH_TOOL_DEFINITION}from"#runtime/framework-tools/web-search.js";import{extractQuestionInputRequests,extractToolApprovalInputRequests}from"#harness/input-extraction.js";import{applyLastToolCacheBreakpoint,detectPromptCachePath,getAnthropicCacheMarker}from"#harness/prompt-cache.js";import{resolveFrameworkToolFromUpstreamType,resolveGatewayPinForWebSearchBackend,resolveWebSearchBackend}from"#harness/provider-tools.js";import{context,trace}from"#compiled/@opentelemetry/api/index.js";import{hydrateSandboxAttachments,stageAttachmentsToSandbox}from"#harness/attachment-staging.js";import{applyCodeModeToToolSet,buildCodeModeHostTools,createAshCodeModeOptions}from"#harness/code-mode.js";import{createCodeModeLifecycle}from"#harness/code-mode-lifecycle.js";import{clearPendingCodeModeApproval,getPendingCodeModeApproval,replaceCodeModeApprovalInterruptResult,setPendingCodeModeApproval}from"#harness/code-mode-approval.js";import{compactMessages,getInputTokenCount,resolveCompactionModel,shouldCompact}from"#harness/compaction.js";import{accumulateTurnUsage,getTurnUsageState,setTurnUsageState}from"#harness/turn-tag-state.js";import{buildTelemetryRuntimeContext}from"#harness/instrumentation-metadata.js";import{getInstrumentationConfig}from"#harness/instrumentation-config.js";import{clearPendingCodeModeConnectionAuth,getPendingCodeModeConnectionAuth,setPendingCodeModeConnectionAuth}from"#harness/code-mode-connection-auth-state.js";import{classifyModelCallError,extractModelCallErrorDetails,extractUnsupportedProviderToolTypes,summarizeKnownModelCallConfigError,summarizeKnownModelCallRequestError}from"#harness/model-call-error.js";import{ensureOtelIntegration}from"#harness/otel-integration.js";import{buildStepHooks,emitStepActions,isInvalidToolCall}from"#harness/step-hooks.js";import{pruneToolResults}from"#harness/tool-result-pruning.js";const environment=process.env.NODE_ENV??`unknown`,ashVersion=resolveInstalledPackageInfo().version,log=createLogger(`harness.tool-loop`);function logToolExecutionError(e){e.toolOutput.type===`tool-error`&&logError(log,`tool execution failed`,e.toolOutput.error,{toolName:e.toolCall.toolName,toolCallId:e.toolCall.toolCallId})}function enrichTelemetry(e,t,n){if(e===void 0)return;let r={};for(let e of Object.keys(n??{}))r[e]=!0;return{functionId:e.functionId??t,includeRuntimeContext:r,isEnabled:!0,recordInputs:e.recordInputs??!0,recordOutputs:e.recordOutputs??!0}}function resolveGatewayPinForStep(e){if(e.cachePath.kind!==`gateway-auto`||e.tools[WEB_SEARCH_TOOL_DEFINITION.name]===void 0)return;let t=resolveWebSearchBackend(e.modelReference);return t===null?void 0:resolveGatewayPinForWebSearchBackend(t)??void 0}function buildGatewayAttributionHeaders(e,t){if(typeof e!=`string`)return;let n=t?.agentName??t?.agentId,r=process.env.VERCEL_PROJECT_PRODUCTION_URL||process.env.VERCEL_URL,i=r?`https://${r}`:void 0;if(!n&&!i)return;let a={};return n&&(a[`x-title`]=n),i&&(a[`http-referer`]=i),a}const TURN_TRACE_STATE_KEY=`ash.harness.turnTrace`;function getTurnTraceState(e){return e.state?.[TURN_TRACE_STATE_KEY]}function setTurnTraceState(e,t){let n={traceId:t.traceId,spanId:t.spanId,traceFlags:t.traceFlags};return{...e,state:{...e.state,[TURN_TRACE_STATE_KEY]:n}}}function resolveStepOtelContext(e,t,n){if(t)return trace.setSpan(context.active(),t);if(e){let e=getTurnTraceState(n);if(e){let t=trace.wrapSpanContext({traceId:e.traceId,spanId:e.spanId,traceFlags:e.traceFlags});return trace.setSpan(context.active(),t)}}}function createToolLoopHarness(t){let n=t.handleEvent,o=getInstrumentationConfig();o!==void 0&&ensureOtelIntegration();let s=o===void 0?void 0:trace.getTracer(`ash`),c=t.runtimeIdentity?.agentName;async function runStep(e,t){let n;if(s&&hasStepInput(t)){let t=o?.functionId??c,r={"ash.version":ashVersion,"ash.environment":environment,"ash.session.id":e.sessionId};t&&(r[`ai.telemetry.functionId`]=t),n=s.startSpan(`ai.ash.turn`,{attributes:r})}let r=resolveStepOtelContext(s,n,e),executeStep=()=>executeStepBody(e,t,n);try{return r?await context.with(r,executeStep):await executeStep()}finally{n?.end()}}async function executeStepBody(s,l,d){let g=s;d&&(g=setTurnTraceState(g,d.spanContext()));let x=getHarnessEmissionState(g.state),T=consumeDeferredStepInput({input:l,session:g});g=T.session;let D=await resolvePendingRuntimeActions({emit:n,session:g,stepInput:T.input});if(D.outcome===`unresolved`)return{next:null,session:D.session};g=D.session;let k=resolvePendingInput({history:D.messages,resolveApprovalKey:resolveApprovalKeyFromTools(t.tools),session:g,stepInput:T.input});if(k.outcome===`unresolved`)return{next:null,session:k.session};n&&hasStepInput(l)&&(x=await emitTurnPreamble(n,l??{},x,t.runtimeIdentity),g=setHarnessEmissionState(g,x),d&&d.setAttribute(`ash.turn.id`,x.turnId)),g=k.session;let A=k.messages;if(T.input?.message!==void 0&&!k.deferredMessage){let e=await stageAttachmentsToSandbox(T.input.message);A.push({content:e,role:`user`})}let j=await t.resolveModel(g.agent.modelReference),M=detectPromptCachePath(j),N=M.kind===`anthropic-direct`?getAnthropicCacheMarker():void 0,P=buildGatewayAttributionHeaders(j,t.runtimeIdentity);({messages:A,session:g}=await maybeCompact({emit:n,emissionState:x,headers:P,messages:A,model:j,onCompaction:t.onCompaction,resolveModel:t.resolveModel,session:g,telemetry:enrichTelemetry(o,c)??void 0}));let F=getApprovedTools(g),I=contextStorage.getStore(),L=await hydrateSandboxAttachments(A),R=T.input?.modelContext,z=[],B=[];for(let e of L)e.role===`system`?z.push(e):B.push(e);if(R!==void 0)for(let e of R)e.role===`system`?z.push(e):B.push(e);if(I!==void 0){let e=I.get(PendingSkillAnnouncementKey);e!==void 0&&z.push({role:`system`,content:e})}let V=B,prepareModelCallInput=e=>{let t=e?[{role:`system`,content:e}]:[],n=g.agent.system?[{role:`system`,content:g.agent.system}]:[],r=z.length>0||t.length>0?[...t,...n,...z]:g.agent.system||void 0;return{instructions:r,telemetryRuntimeContext:buildTelemetryRuntimeContext({ashVersion,authored:o,emissionState:x,environment,modelInput:{instructions:r,messages:V},session:g})}},runOneModelCall=async e=>{let{instructions:i,telemetryRuntimeContext:s}=e.preparedInput??prepareModelCallInput(e.extraSystemNote),l=t.codeMode===!0,u=await buildToolSetWithProviderTools({approvedTools:F,capabilities:t.capabilities,disabledProviderTools:e.disabledProviderTools,modelReference:g.agent.modelReference,tools:t.tools});if(I!==void 0){let e=I.get(DynamicToolsKey);if(e!==void 0)for(let t of e)u[t.name]??={description:t.description,inputSchema:t.inputSchema,execute:t.execute}}let d=l?(await applyCodeModeToToolSet({harnessTools:t.tools,lifecycle:n===void 0?void 0:createCodeModeLifecycle({emit:n,emissionState:x,tools:t.tools}),tools:u})).modelTools:u,f=N?applyLastToolCacheBreakpoint(d,N):d,p=resolveGatewayPinForStep({cachePath:M,modelReference:g.agent.modelReference,tools:f}),_=buildStepHooks({cachePath:M,emit:n,emissionState:x,emitStepStarted:e.suppressStepStartedEmission!==!0,gatewayPinProvider:p,marker:N,session:g}),v=new ToolLoopAgent({headers:P,instructions:i,model:j,onToolExecutionEnd:logToolExecutionError,onError(e){logError(log,`tool-loop stream error`,e.error)},onStepFinish:_.onStepFinish,prepareStep:_.prepareStep,runtimeContext:s,stopWhen:isStepCount(1),telemetry:enrichTelemetry(o,c,s),tools:f});return runModelCallWithRetries(async()=>{if(n){let e=await v.stream({messages:V}),{inlineActionResultCallIds:r,inlineToolResultParts:i}=await emitStreamContent(n,x,e.fullStream),a=await _.stepResult;return await emitStepActions(n,x,a,{excludedActionToolNames:new Set([ASK_QUESTION_TOOL_NAME,CODE_MODE_TOOL_NAME]),inlineActionResultCallIds:r,tools:t.tools}),i.length>0?{content:a.content,finishReason:a.finishReason,response:{...a.response,messages:[{role:`tool`,content:[...i]},...a.response.messages]},text:a.text,toolCalls:a.toolCalls,toolResults:a.toolResults,usage:a.usage}:a}return await v.generate({messages:V}),await _.stepResult},{sessionId:g.sessionId,turnId:x.turnId})},H=prepareModelCallInput();n&&await emitStepStarted(n,x,A);let U=await continuePendingCodeModeConnectionAuth({capabilities:t.capabilities,config:t,emit:n,emissionState:x,messages:A,runStep,session:g});if(U!==null)return U;let W=await continuePendingCodeModeApproval({capabilities:t.capabilities,config:t,emit:n,emissionState:x,messages:A,runStep,session:g});if(W!==null)return W;let G;try{G=await runOneModelCall({preparedInput:H,suppressStepStartedEmission:!0})}catch(t){let r=await attemptUnsupportedProviderToolRecovery({error:t,runOneModelCall,sessionId:g.sessionId,turnId:x.turnId});if(r.outcome===`recovered`)G=r.result;else{let t=r.error;if(d&&recordErrorOnSpan(d,t),!n)throw t;let a=classifyModelCallError(t),o=createErrorId(),s=a===`terminal`?summarizeKnownModelCallConfigError(t):null,c=s===null?summarizeKnownModelCallRequestError(t):null,l=s?.message??c?.message??toErrorMessage(t),f=extractModelCallErrorDetails(t),p=buildModelCallFailureDetails({configSummary:s,error:t,errorId:o,modelCallDetails:f,requestSummary:c}),m=buildModelCallFailureLogFields({error:t,errorId:o,modelCallDetails:f,requestSummary:c,sessionId:g.sessionId,turnId:x.turnId});return a===`terminal`?(s===null?log.error(c?.message??`model call failed terminally`,m):log.error(`${s.name}: ${s.message}`,{errorId:o,sessionId:g.sessionId,turnId:x.turnId}),await emitFailedStep(n,x,{code:`MODEL_CALL_FAILED`,details:p,message:l,sessionId:g.sessionId}),{next:{done:!0,output:``},session:g}):(log.error(c?.message??`model call failed — parking session for retry by the user`,m),x=await emitRecoverableFailedTurn(n,x,{code:`MODEL_CALL_FAILED`,details:p,message:l}),{next:null,session:setHarnessEmissionState(g,x)})}}let K=accumulateTurnUsage({previous:getTurnUsageState(g.state),turnId:x.turnId,usage:G.usage??{}});g=setTurnUsageState(g,K);let q;try{q=formatLanguageModelGatewayId(j)}catch{q=void 0}return await setAshAttributes({"$ash.model":q,"$ash.input_tokens":K.inputTokens,"$ash.output_tokens":K.outputTokens,"$ash.cache_read_tokens":K.cacheReadTokens,"$ash.tool_count":t.tools.size}),handleStepResult({config:t,emit:n,emissionState:x,promptMessages:A,result:G,runStep,session:g})}return runStep}function buildModelCallFailureDetails(e){let{configSummary:t,error:r,errorId:i,modelCallDetails:a,requestSummary:o}=e;return t===null?o===null?{...formatError(r,i),...a}:{errorId:i,message:toErrorMessage(r),name:o.name,...a}:{errorId:i,message:t.message,name:t.name,...a}}function buildModelCallFailureLogFields(e){let t={errorId:e.errorId,sessionId:e.sessionId,turnId:e.turnId};return e.requestSummary===null?{...t,error:e.error}:{...t,details:e.modelCallDetails}}async function attemptUnsupportedProviderToolRecovery(e){let t=extractUnsupportedProviderToolTypes(e.error);if(t.length===0)return{outcome:`failed`,error:e.error};let n=[];for(let e of t){let t=resolveFrameworkToolFromUpstreamType(e);t!==null&&!n.includes(t)&&n.push(t)}if(n.length===0)return{outcome:`failed`,error:e.error};log.warn(`disabling unsupported provider tool(s); retrying step once`,{disabled:n,sessionId:e.sessionId,turnId:e.turnId,upstreamTypes:t});try{return{outcome:`recovered`,result:await e.runOneModelCall({disabledProviderTools:new Set(n),extraSystemNote:buildDisabledToolNote(n),suppressStepStartedEmission:!0})}}catch(e){return{outcome:`failed`,error:e}}}function buildDisabledToolNote(e){let t=e.join(`, `);return`The following ${e.length===1?`tool is`:`tools are`} not available with the current model and has been removed: ${t}. Proceed using the remaining tools or your training knowledge.`}async function handleStepResult(e){let{config:t,emit:n,promptMessages:r,result:i,runStep:a}=e,{emissionState:s,session:c}=e,u=i.response.messages,d=resolveAssistantStepText(u,i.text),f={...c,compaction:createNextCompactionConfig(c.compaction,r,i)};if(t.codeMode===!0){let{getCodeModeInterrupt:e,isCodeModeApprovalInterrupt:a}=await loadCodeModeModule(),o=e(i);if(o!==void 0){if(isCodeModeConnectionAuthInterrupt(o))return parkOnCodeModeConnectionAuth({baseSession:f,config:t,emit:n,emissionState:s,interrupt:o,promptMessages:r,responseMessages:u});if(a(o))return parkOnCodeModeApproval({baseSession:f,config:t,emit:n,emissionState:s,interrupt:o,promptMessages:r,responseMessages:u})}}let p=extractToolApprovalInputRequests({content:i.content??[]}),m=new Set(p.map(e=>e.action.callId)),h=extractQuestionInputRequests({toolCalls:i.toolCalls,excludedCallIds:m}),_=[...p,...h],v=(i.toolCalls??[]).filter(e=>!isInvalidToolCall(e)).filter(e=>t.tools.get(e.toolName)?.runtimeAction!==void 0).map(e=>createRuntimeActionRequestFromToolCall({toolCall:e,tools:t.tools}));if(v.length>0)return{next:null,session:setHarnessEmissionState(setPendingRuntimeActionBatch({actions:v,event:{sequence:s.sequence,stepIndex:s.stepIndex,turnId:s.turnId},responseMessages:u,session:{...f,history:[...r]}}),s)};if(_.length>0){let e=setPendingInputBatch({requests:_,responseMessages:u,session:{...f,history:[...r]}});return n&&(await n(createInputRequestedEvent({requests:_,sequence:s.sequence,stepIndex:s.stepIndex,turnId:s.turnId})),t.mode===`conversation`&&(s=await emitTurnEpilogue(n,s,t.mode),e=setHarnessEmissionState(e,s))),{next:null,session:e}}let y=(i.toolResults??[]).find(e=>isAuthorizationSignal(e.output));if(y&&isAuthorizationSignal(y.output)){let{challenges:e}=y.output;if(n)for(let t of e)await n(createAuthorizationRequiredEvent({authorization:t.challenge,name:t.name,description:t.challenge.instructions??`Authorization required for ${t.name}`,webhookUrl:t.hookUrl,sequence:s.sequence,stepIndex:s.stepIndex,turnId:s.turnId}));return{next:null,session:setHarnessEmissionState({...f,history:[...r],state:setPendingAuthorization(f.state,{challenges:e})},s)}}let b=pruneToolResults(r,t.retentionPolicies),S=b!==r,C=f.compaction;S&&C.lastKnownInputTokens!==void 0&&(C={recentWindowSize:C.recentWindowSize,threshold:C.threshold});let w=[...b,...u],E={...f,compaction:C,history:w},O=u.at(-1)?.role===`tool`||hasDeferredStepInput(E);return n&&(s=O?advanceStep(s):await emitTurnEpilogue(n,s,t.mode),E=setHarnessEmissionState(E,s)),O?{next:a,session:E}:{next:t.mode===`task`?{done:!0,output:d??``}:null,session:E}}async function continuePendingCodeModeApproval(e){let t=getPendingCodeModeApproval(e.session.state);if(t===void 0)return null;let{continueCodeModeApproval:n,getCodeModeApprovalResponse:i,isCodeModeApprovalInterrupt:a,unwrapCodeModeResult:o}=await loadCodeModeModule(),s=i([...e.messages],t.interrupt);if(s===void 0)return{next:null,session:e.session};let c=await buildCodeModeHostTools({approvedTools:getApprovedTools(e.session),capabilities:e.capabilities,tools:e.config.tools}),l;try{l=await n({approvalResponse:s,interrupt:t.interrupt,options:createAshCodeModeOptions({lifecycle:e.emit===void 0?void 0:createCodeModeLifecycle({emit:e.emit,emissionState:e.emissionState,skipReplayed:!0,tools:e.config.tools})}),tools:c})}catch(e){logError(log,`code-mode approval continuation failed`,e),l={error:`code_mode_continuation_failed`,message:toErrorMessage(e),retryable:!1}}let d=o(l),f=d.status===`interrupted`?d.interrupt:d.output,p=replaceCodeModeApprovalInterruptResult([...e.session.history,...t.responseMessages],t.interrupt,f),m=clearPendingCodeModeApproval({...e.session,history:p});if(d.status===`interrupted`){if(isCodeModeConnectionAuthInterrupt(d.interrupt)){let t=e.session.history.length,n=p.slice(0,t),r=p.slice(t);return m={...m,history:n},parkOnCodeModeConnectionAuth({baseSession:m,config:e.config,emit:e.emit,emissionState:e.emissionState,interrupt:d.interrupt,promptMessages:n,responseMessages:r})}if(a(d.interrupt)){let t=e.session.history.length,n=p.slice(0,t),r=p.slice(t);return m={...m,history:n},parkOnCodeModeApproval({baseSession:m,config:e.config,emit:e.emit,emissionState:e.emissionState,interrupt:d.interrupt,promptMessages:n,responseMessages:r})}}return{next:e.runStep,session:m}}async function parkOnCodeModeConnectionAuth(e){let t=[...e.interrupt.payload.challenges??[]];if(e.emit)for(let n of t)await e.emit(createAuthorizationRequiredEvent({authorization:n.challenge,name:n.name,description:n.challenge.instructions??`Authorization required for ${n.name}`,webhookUrl:n.hookUrl,sequence:e.emissionState.sequence,stepIndex:e.emissionState.stepIndex,turnId:e.emissionState.turnId}));return{next:null,session:setPendingCodeModeConnectionAuth({interrupt:e.interrupt,responseMessages:e.responseMessages,session:{...e.baseSession,history:[...e.promptMessages],state:setPendingAuthorization(e.baseSession.state,{challenges:t})}})}}async function continuePendingCodeModeConnectionAuth(e){let t=getPendingCodeModeConnectionAuth(e.session.state);if(t===void 0)return null;let{continueCodeModeInterrupt:n,isCodeModeApprovalInterrupt:i,replaceCodeModeInterruptResult:a,unwrapCodeModeResult:o}=await loadCodeModeModule(),s=await buildCodeModeHostTools({approvedTools:getApprovedTools(e.session),capabilities:e.capabilities,tools:e.config.tools}),c=e.emit===void 0?void 0:createCodeModeLifecycle({emit:e.emit,emissionState:e.emissionState,skipReplayed:!0,tools:e.config.tools}),l;try{l=await n({interrupt:t.interrupt,resolution:{status:`authorized`},tools:s,options:createAshCodeModeOptions({lifecycle:c})})}catch(e){logError(log,`code-mode interrupt continuation failed`,e),l={error:`code_mode_continuation_failed`,message:toErrorMessage(e),retryable:!1}}let d=o(l),f=d.status===`interrupted`?d.interrupt:d.output,p=a([...e.session.history,...t.responseMessages],t.interrupt.pendingContinuation,f),m=clearPendingCodeModeConnectionAuth({...e.session,history:p});if(d.status===`interrupted`){if(isCodeModeConnectionAuthInterrupt(d.interrupt)){let t=e.session.history.length,n=p.slice(0,t),r=p.slice(t);return m={...m,history:n},parkOnCodeModeConnectionAuth({baseSession:m,config:e.config,emit:e.emit,emissionState:e.emissionState,interrupt:d.interrupt,promptMessages:n,responseMessages:r})}if(i(d.interrupt)){let t=e.session.history.length,n=p.slice(0,t),r=p.slice(t);return m={...m,history:n},parkOnCodeModeApproval({baseSession:m,config:e.config,emit:e.emit,emissionState:e.emissionState,interrupt:d.interrupt,promptMessages:n,responseMessages:r})}}return{next:e.runStep,session:m}}async function parkOnCodeModeApproval(e){let{toCodeModeApprovalMessages:t}=await loadCodeModeModule(),n=t(e.interrupt),r=extractToolApprovalInputRequests({content:extractAssistantContent(n)}),i=setPendingInputBatch({requests:r,responseMessages:n,session:setPendingCodeModeApproval({interrupt:e.interrupt,responseMessages:e.responseMessages,session:{...e.baseSession,history:[...e.promptMessages]}})});if(e.emit&&(await e.emit(createInputRequestedEvent({requests:r,sequence:e.emissionState.sequence,stepIndex:e.emissionState.stepIndex,turnId:e.emissionState.turnId})),e.config.mode===`conversation`)){let t=await emitTurnEpilogue(e.emit,e.emissionState,e.config.mode);i=setHarnessEmissionState(i,t)}return{next:null,session:i}}function extractAssistantContent(e){let t=[];for(let n of e)n.role===`assistant`&&Array.isArray(n.content)&&t.push(...n.content);return t}function createNextCompactionConfig(e,t,n){let r={recentWindowSize:e.recentWindowSize,threshold:e.threshold};return n.usage?.inputTokens!==void 0&&(r.lastKnownInputTokens=n.usage.inputTokens,r.lastKnownPromptMessageCount=t.length),r}async function maybeCompact(e){let{emit:t,emissionState:n}=e,r=e.messages,i=e.session;if(!shouldCompact(r,i.compaction))return{messages:r,session:i};let a=await resolveCompactionModel({compactionModelReference:i.agent.compactionModelReference,model:e.model,modelReference:i.agent.modelReference,resolveModel:e.resolveModel});if(t&&await t(createCompactionRequestedEvent({modelId:formatLanguageModelGatewayId(a.model),sequence:n.sequence,sessionId:i.sessionId,turnId:n.turnId,usageInputTokens:getInputTokenCount(r,i.compaction)})),r=await compactMessages(r,a.model,i.compaction,a.providerOptions,e.telemetry,e.headers),e.onCompaction){let t=await e.onCompaction(i);i=t.session;for(let e of t.messages)r.push(e)}return t&&await t(createCompactionCompletedEvent({modelId:formatLanguageModelGatewayId(a.model),sequence:n.sequence,sessionId:i.sessionId,turnId:n.turnId})),{messages:r,session:i}}function resolveApprovalKeyFromTools(e){return t=>{let n=e.get(t.action.toolName);if(n?.approvalKey!==void 0)return n.approvalKey(t.action.input)}}async function runModelCallWithRetries(e,t){for(let n=1;;n++)try{return await e()}catch(e){if(n===3||classifyModelCallError(e)!==`retry`)throw e;let r=500*2**(n-1)+Math.floor(Math.random()*250);log.warn(`model call failed transiently — retrying`,{attempt:n,delayMs:r,sessionId:t.sessionId,turnId:t.turnId,error:e}),await new Promise(e=>setTimeout(e,r))}}export{createToolLoopHarness};
|
|
1
|
+
import{createErrorId,createLogger,formatError,logError,recordErrorOnSpan}from"#internal/logging.js";import{DynamicToolsKey}from"#context/keys.js";import{createAuthorizationRequiredEvent,createCompactionCompletedEvent,createCompactionRequestedEvent,createInputRequestedEvent}from"#protocol/message.js";import{toErrorMessage}from"#shared/errors.js";import{resolveInstalledPackageInfo}from"#internal/application/package.js";import{formatLanguageModelGatewayId}from"#internal/runtime-model.js";import{contextStorage}from"#context/container.js";import{ToolLoopAgent,isStepCount}from"ai";import{advanceStep,emitFailedStep,emitRecoverableFailedTurn,emitStepStarted,emitStreamContent,emitTurnEpilogue,emitTurnPreamble,getHarnessEmissionState,setHarnessEmissionState}from"#harness/emission.js";import{setAshAttributes}from"#runtime/attributes/emit.js";import{createRuntimeActionRequestFromToolCall,resolvePendingRuntimeActions,setPendingRuntimeActionBatch}from"#harness/runtime-actions.js";import{CODE_MODE_TOOL_NAME,loadCodeModeModule}from"#shared/code-mode.js";import{resolveAssistantStepText}from"#harness/messages.js";import{PendingSkillAnnouncementKey}from"#context/dynamic-skill-lifecycle.js";import{consumeDeferredStepInput,getApprovedTools,hasDeferredStepInput,hasStepInput,resolvePendingInput,setPendingInputBatch}from"#harness/input-requests.js";import{isAuthorizationSignal,setPendingAuthorization}from"#harness/authorization.js";import{isCodeModeConnectionAuthInterrupt}from"#runtime/framework-tools/code-mode-connection-auth.js";import{buildToolSetWithProviderTools}from"#harness/tools.js";import{ASK_QUESTION_TOOL_NAME}from"#runtime/framework-tools/ask-question.js";import{WEB_SEARCH_TOOL_DEFINITION}from"#runtime/framework-tools/web-search.js";import{extractQuestionInputRequests,extractToolApprovalInputRequests}from"#harness/input-extraction.js";import{applyLastToolCacheBreakpoint,detectPromptCachePath,getAnthropicCacheMarker}from"#harness/prompt-cache.js";import{resolveFrameworkToolFromUpstreamType,resolveGatewayPinForWebSearchBackend,resolveWebSearchBackend}from"#harness/provider-tools.js";import{context,trace}from"#compiled/@opentelemetry/api/index.js";import{hydrateSandboxAttachments,stageAttachmentsToSandbox}from"#harness/attachment-staging.js";import{applyCodeModeToToolSet,buildCodeModeHostTools,createAshCodeModeOptions}from"#harness/code-mode.js";import{createCodeModeLifecycle}from"#harness/code-mode-lifecycle.js";import{clearPendingCodeModeApproval,getPendingCodeModeApproval,replaceCodeModeApprovalInterruptResult,setPendingCodeModeApproval}from"#harness/code-mode-approval.js";import{compactMessages,getInputTokenCount,resolveCompactionModel,shouldCompact}from"#harness/compaction.js";import{accumulateTurnUsage,getTurnUsageState,setTurnUsageState}from"#harness/turn-tag-state.js";import{buildTelemetryRuntimeContext}from"#harness/instrumentation-metadata.js";import{getInstrumentationConfig}from"#harness/instrumentation-config.js";import{clearPendingCodeModeConnectionAuth,getPendingCodeModeConnectionAuth,setPendingCodeModeConnectionAuth}from"#harness/code-mode-connection-auth-state.js";import{classifyModelCallError,extractModelCallErrorDetails,extractUnsupportedProviderToolTypes,summarizeKnownModelCallConfigError,summarizeKnownModelCallRequestError}from"#harness/model-call-error.js";import{ensureOtelIntegration}from"#harness/otel-integration.js";import{buildStepHooks,emitStepActions,isInvalidToolCall}from"#harness/step-hooks.js";import{pruneToolResults}from"#harness/tool-result-pruning.js";const environment=process.env.NODE_ENV??`unknown`,ashVersion=resolveInstalledPackageInfo().version,log=createLogger(`harness.tool-loop`);function logToolExecutionError(e){e.toolOutput.type===`tool-error`&&logError(log,`tool execution failed`,e.toolOutput.error,{toolName:e.toolCall.toolName,toolCallId:e.toolCall.toolCallId})}function enrichTelemetry(e,t,n){if(e===void 0)return;let r={};for(let e of Object.keys(n??{}))r[e]=!0;return{functionId:e.functionId??t,includeRuntimeContext:r,isEnabled:!0,recordInputs:e.recordInputs??!0,recordOutputs:e.recordOutputs??!0}}function resolveGatewayPinForStep(e){if(e.cachePath.kind!==`gateway-auto`||e.tools[WEB_SEARCH_TOOL_DEFINITION.name]===void 0)return;let t=resolveWebSearchBackend(e.modelReference);return t===null?void 0:resolveGatewayPinForWebSearchBackend(t)??void 0}function buildGatewayAttributionHeaders(e,t){if(typeof e!=`string`)return;let n=t?.agentName??t?.agentId,r=process.env.VERCEL_PROJECT_PRODUCTION_URL||process.env.VERCEL_URL,i=r?`https://${r}`:void 0;if(!n&&!i)return;let a={};return n&&(a[`x-title`]=n),i&&(a[`http-referer`]=i),a}const TURN_TRACE_STATE_KEY=`ash.harness.turnTrace`;function getTurnTraceState(e){return e.state?.[TURN_TRACE_STATE_KEY]}function setTurnTraceState(e,t){let n={traceId:t.traceId,spanId:t.spanId,traceFlags:t.traceFlags};return{...e,state:{...e.state,[TURN_TRACE_STATE_KEY]:n}}}function resolveStepOtelContext(e,t,n){if(t)return trace.setSpan(context.active(),t);if(e){let e=getTurnTraceState(n);if(e){let t=trace.wrapSpanContext({traceId:e.traceId,spanId:e.spanId,traceFlags:e.traceFlags});return trace.setSpan(context.active(),t)}}}function createToolLoopHarness(t){let n=t.handleEvent,o=getInstrumentationConfig();o!==void 0&&ensureOtelIntegration();let s=o===void 0?void 0:trace.getTracer(`ash`),c=t.runtimeIdentity?.agentName;async function runStep(e,t){let n;if(s&&hasStepInput(t)){let t=o?.functionId??c,r={"ash.version":ashVersion,"ash.environment":environment,"ash.session.id":e.sessionId};t&&(r[`ai.telemetry.functionId`]=t),n=s.startSpan(`ai.ash.turn`,{attributes:r})}let r=resolveStepOtelContext(s,n,e),executeStep=()=>executeStepBody(e,t,n);try{return r?await context.with(r,executeStep):await executeStep()}finally{n?.end()}}async function executeStepBody(s,l,d){let g=s;d&&(g=setTurnTraceState(g,d.spanContext()));let x=getHarnessEmissionState(g.state),T=consumeDeferredStepInput({input:l,session:g});g=T.session;let D=await resolvePendingRuntimeActions({emit:n,session:g,stepInput:T.input});if(D.outcome===`unresolved`)return{next:null,session:D.session};g=D.session;let k=resolvePendingInput({history:D.messages,resolveApprovalKey:resolveApprovalKeyFromTools(t.tools),session:g,stepInput:T.input});if(k.outcome===`unresolved`)return{next:null,session:k.session};n&&hasStepInput(l)&&(x=await emitTurnPreamble(n,l??{},x,t.runtimeIdentity),g=setHarnessEmissionState(g,x),d&&d.setAttribute(`ash.turn.id`,x.turnId)),g=k.session;let A=k.messages;if(T.input?.message!==void 0&&!k.deferredMessage){let e=await stageAttachmentsToSandbox(T.input.message);A.push({content:e,role:`user`})}let j=await t.resolveModel(g.agent.modelReference),M=detectPromptCachePath(j),N=M.kind===`anthropic-direct`?getAnthropicCacheMarker():void 0,P=buildGatewayAttributionHeaders(j,t.runtimeIdentity);({messages:A,session:g}=await maybeCompact({emit:n,emissionState:x,headers:P,messages:A,model:j,onCompaction:t.onCompaction,resolveModel:t.resolveModel,session:g,telemetry:enrichTelemetry(o,c)??void 0}));let F=getApprovedTools(g),I=contextStorage.getStore(),L=await hydrateSandboxAttachments(A),R=T.input?.modelContext,z=[],B=[];for(let e of L)e.role===`system`?z.push(e):B.push(e);if(R!==void 0)for(let e of R)e.role===`system`?z.push(e):B.push(e);if(I!==void 0){let e=I.get(PendingSkillAnnouncementKey);e!==void 0&&e.length>0&&z.push({role:`system`,content:e})}let V=B,prepareModelCallInput=e=>{let t=e?[{role:`system`,content:e}]:[],n=g.agent.system?[{role:`system`,content:g.agent.system}]:[],r=z.length>0||t.length>0?[...t,...n,...z]:g.agent.system||void 0;return{instructions:r,telemetryRuntimeContext:buildTelemetryRuntimeContext({ashVersion,authored:o,emissionState:x,environment,modelInput:{instructions:r,messages:V},session:g})}},runOneModelCall=async e=>{let{instructions:i,telemetryRuntimeContext:s}=e.preparedInput??prepareModelCallInput(e.extraSystemNote),l=t.codeMode===!0,u=await buildToolSetWithProviderTools({approvedTools:F,capabilities:t.capabilities,disabledProviderTools:e.disabledProviderTools,modelReference:g.agent.modelReference,tools:t.tools});if(I!==void 0){let e=I.get(DynamicToolsKey);if(e!==void 0)for(let t of e)u[t.name]??={description:t.description,inputSchema:t.inputSchema,execute:t.execute}}let d=l?(await applyCodeModeToToolSet({harnessTools:t.tools,lifecycle:n===void 0?void 0:createCodeModeLifecycle({emit:n,emissionState:x,tools:t.tools}),tools:u})).modelTools:u,f=N?applyLastToolCacheBreakpoint(d,N):d,p=resolveGatewayPinForStep({cachePath:M,modelReference:g.agent.modelReference,tools:f}),_=buildStepHooks({cachePath:M,emit:n,emissionState:x,emitStepStarted:e.suppressStepStartedEmission!==!0,gatewayPinProvider:p,marker:N,session:g}),v=new ToolLoopAgent({headers:P,instructions:i,model:j,onToolExecutionEnd:logToolExecutionError,onError(e){logError(log,`tool-loop stream error`,e.error)},onStepFinish:_.onStepFinish,prepareStep:_.prepareStep,runtimeContext:s,stopWhen:isStepCount(1),telemetry:enrichTelemetry(o,c,s),tools:f});return runModelCallWithRetries(async()=>{if(n){let e=await v.stream({messages:V}),{inlineActionResultCallIds:r,inlineToolResultParts:i}=await emitStreamContent(n,x,e.fullStream),a=await _.stepResult;return await emitStepActions(n,x,a,{excludedActionToolNames:new Set([ASK_QUESTION_TOOL_NAME,CODE_MODE_TOOL_NAME]),inlineActionResultCallIds:r,tools:t.tools}),i.length>0?{content:a.content,finishReason:a.finishReason,response:{...a.response,messages:[{role:`tool`,content:[...i]},...a.response.messages]},text:a.text,toolCalls:a.toolCalls,toolResults:a.toolResults,usage:a.usage}:a}return await v.generate({messages:V}),await _.stepResult},{sessionId:g.sessionId,turnId:x.turnId})},H=prepareModelCallInput();n&&await emitStepStarted(n,x,A);let U=await continuePendingCodeModeConnectionAuth({capabilities:t.capabilities,config:t,emit:n,emissionState:x,messages:A,runStep,session:g});if(U!==null)return U;let W=await continuePendingCodeModeApproval({capabilities:t.capabilities,config:t,emit:n,emissionState:x,messages:A,runStep,session:g});if(W!==null)return W;let G;try{G=await runOneModelCall({preparedInput:H,suppressStepStartedEmission:!0})}catch(t){let r=await attemptUnsupportedProviderToolRecovery({error:t,runOneModelCall,sessionId:g.sessionId,turnId:x.turnId});if(r.outcome===`recovered`)G=r.result;else{let t=r.error;if(d&&recordErrorOnSpan(d,t),!n)throw t;let a=classifyModelCallError(t),o=createErrorId(),s=a===`terminal`?summarizeKnownModelCallConfigError(t):null,c=s===null?summarizeKnownModelCallRequestError(t):null,l=s?.message??c?.message??toErrorMessage(t),f=extractModelCallErrorDetails(t),p=buildModelCallFailureDetails({configSummary:s,error:t,errorId:o,modelCallDetails:f,requestSummary:c}),m=buildModelCallFailureLogFields({error:t,errorId:o,modelCallDetails:f,requestSummary:c,sessionId:g.sessionId,turnId:x.turnId});return a===`terminal`?(s===null?log.error(c?.message??`model call failed terminally`,m):log.error(`${s.name}: ${s.message}`,{errorId:o,sessionId:g.sessionId,turnId:x.turnId}),await emitFailedStep(n,x,{code:`MODEL_CALL_FAILED`,details:p,message:l,sessionId:g.sessionId}),{next:{done:!0,output:``},session:g}):(log.error(c?.message??`model call failed — parking session for retry by the user`,m),x=await emitRecoverableFailedTurn(n,x,{code:`MODEL_CALL_FAILED`,details:p,message:l}),{next:null,session:setHarnessEmissionState(g,x)})}}let K=accumulateTurnUsage({previous:getTurnUsageState(g.state),turnId:x.turnId,usage:G.usage??{}});g=setTurnUsageState(g,K);let q;try{q=formatLanguageModelGatewayId(j)}catch{q=void 0}return await setAshAttributes({"$ash.model":q,"$ash.input_tokens":K.inputTokens,"$ash.output_tokens":K.outputTokens,"$ash.cache_read_tokens":K.cacheReadTokens,"$ash.tool_count":t.tools.size}),handleStepResult({config:t,emit:n,emissionState:x,promptMessages:A,result:G,runStep,session:g})}return runStep}function buildModelCallFailureDetails(e){let{configSummary:t,error:r,errorId:i,modelCallDetails:a,requestSummary:o}=e;return t===null?o===null?{...formatError(r,i),...a}:{errorId:i,message:toErrorMessage(r),name:o.name,...a}:{errorId:i,message:t.message,name:t.name,...a}}function buildModelCallFailureLogFields(e){let t={errorId:e.errorId,sessionId:e.sessionId,turnId:e.turnId};return e.requestSummary===null?{...t,error:e.error}:{...t,details:e.modelCallDetails}}async function attemptUnsupportedProviderToolRecovery(e){let t=extractUnsupportedProviderToolTypes(e.error);if(t.length===0)return{outcome:`failed`,error:e.error};let n=[];for(let e of t){let t=resolveFrameworkToolFromUpstreamType(e);t!==null&&!n.includes(t)&&n.push(t)}if(n.length===0)return{outcome:`failed`,error:e.error};log.warn(`disabling unsupported provider tool(s); retrying step once`,{disabled:n,sessionId:e.sessionId,turnId:e.turnId,upstreamTypes:t});try{return{outcome:`recovered`,result:await e.runOneModelCall({disabledProviderTools:new Set(n),extraSystemNote:buildDisabledToolNote(n),suppressStepStartedEmission:!0})}}catch(e){return{outcome:`failed`,error:e}}}function buildDisabledToolNote(e){let t=e.join(`, `);return`The following ${e.length===1?`tool is`:`tools are`} not available with the current model and has been removed: ${t}. Proceed using the remaining tools or your training knowledge.`}async function handleStepResult(e){let{config:t,emit:n,promptMessages:r,result:i,runStep:a}=e,{emissionState:s,session:c}=e,u=i.response.messages,d=resolveAssistantStepText(u,i.text),f={...c,compaction:createNextCompactionConfig(c.compaction,r,i)};if(t.codeMode===!0){let{getCodeModeInterrupt:e,isCodeModeApprovalInterrupt:a}=await loadCodeModeModule(),o=e(i);if(o!==void 0){if(isCodeModeConnectionAuthInterrupt(o))return parkOnCodeModeConnectionAuth({baseSession:f,config:t,emit:n,emissionState:s,interrupt:o,promptMessages:r,responseMessages:u});if(a(o))return parkOnCodeModeApproval({baseSession:f,config:t,emit:n,emissionState:s,interrupt:o,promptMessages:r,responseMessages:u})}}let p=extractToolApprovalInputRequests({content:i.content??[]}),m=new Set(p.map(e=>e.action.callId)),h=extractQuestionInputRequests({toolCalls:i.toolCalls,excludedCallIds:m}),_=[...p,...h],v=(i.toolCalls??[]).filter(e=>!isInvalidToolCall(e)).filter(e=>t.tools.get(e.toolName)?.runtimeAction!==void 0).map(e=>createRuntimeActionRequestFromToolCall({toolCall:e,tools:t.tools}));if(v.length>0)return{next:null,session:setHarnessEmissionState(setPendingRuntimeActionBatch({actions:v,event:{sequence:s.sequence,stepIndex:s.stepIndex,turnId:s.turnId},responseMessages:u,session:{...f,history:[...r]}}),s)};if(_.length>0){let e=setPendingInputBatch({requests:_,responseMessages:u,session:{...f,history:[...r]}});return n&&(await n(createInputRequestedEvent({requests:_,sequence:s.sequence,stepIndex:s.stepIndex,turnId:s.turnId})),t.mode===`conversation`&&(s=await emitTurnEpilogue(n,s,t.mode),e=setHarnessEmissionState(e,s))),{next:null,session:e}}let y=(i.toolResults??[]).find(e=>isAuthorizationSignal(e.output));if(y&&isAuthorizationSignal(y.output)){let{challenges:e}=y.output;if(n)for(let t of e)await n(createAuthorizationRequiredEvent({authorization:t.challenge,name:t.name,description:t.challenge.instructions??`Authorization required for ${t.name}`,webhookUrl:t.hookUrl,sequence:s.sequence,stepIndex:s.stepIndex,turnId:s.turnId}));return{next:null,session:setHarnessEmissionState({...f,history:[...r],state:setPendingAuthorization(f.state,{challenges:e})},s)}}let b=pruneToolResults(r,t.retentionPolicies),S=b!==r,C=f.compaction;S&&C.lastKnownInputTokens!==void 0&&(C={recentWindowSize:C.recentWindowSize,threshold:C.threshold});let w=[...b,...u],E={...f,compaction:C,history:w},O=u.at(-1)?.role===`tool`||hasDeferredStepInput(E);return n&&(s=O?advanceStep(s):await emitTurnEpilogue(n,s,t.mode),E=setHarnessEmissionState(E,s)),O?{next:a,session:E}:{next:t.mode===`task`?{done:!0,output:d??``}:null,session:E}}async function continuePendingCodeModeApproval(e){let t=getPendingCodeModeApproval(e.session.state);if(t===void 0)return null;let{continueCodeModeApproval:n,getCodeModeApprovalResponse:i,isCodeModeApprovalInterrupt:a,unwrapCodeModeResult:o}=await loadCodeModeModule(),s=i([...e.messages],t.interrupt);if(s===void 0)return{next:null,session:e.session};let c=await buildCodeModeHostTools({approvedTools:getApprovedTools(e.session),capabilities:e.capabilities,tools:e.config.tools}),l;try{l=await n({approvalResponse:s,interrupt:t.interrupt,options:createAshCodeModeOptions({lifecycle:e.emit===void 0?void 0:createCodeModeLifecycle({emit:e.emit,emissionState:e.emissionState,skipReplayed:!0,tools:e.config.tools})}),tools:c})}catch(e){logError(log,`code-mode approval continuation failed`,e),l={error:`code_mode_continuation_failed`,message:toErrorMessage(e),retryable:!1}}let d=o(l),f=d.status===`interrupted`?d.interrupt:d.output,p=replaceCodeModeApprovalInterruptResult([...e.session.history,...t.responseMessages],t.interrupt,f),m=clearPendingCodeModeApproval({...e.session,history:p});if(d.status===`interrupted`){if(isCodeModeConnectionAuthInterrupt(d.interrupt)){let t=e.session.history.length,n=p.slice(0,t),r=p.slice(t);return m={...m,history:n},parkOnCodeModeConnectionAuth({baseSession:m,config:e.config,emit:e.emit,emissionState:e.emissionState,interrupt:d.interrupt,promptMessages:n,responseMessages:r})}if(a(d.interrupt)){let t=e.session.history.length,n=p.slice(0,t),r=p.slice(t);return m={...m,history:n},parkOnCodeModeApproval({baseSession:m,config:e.config,emit:e.emit,emissionState:e.emissionState,interrupt:d.interrupt,promptMessages:n,responseMessages:r})}}return{next:e.runStep,session:m}}async function parkOnCodeModeConnectionAuth(e){let t=[...e.interrupt.payload.challenges??[]];if(e.emit)for(let n of t)await e.emit(createAuthorizationRequiredEvent({authorization:n.challenge,name:n.name,description:n.challenge.instructions??`Authorization required for ${n.name}`,webhookUrl:n.hookUrl,sequence:e.emissionState.sequence,stepIndex:e.emissionState.stepIndex,turnId:e.emissionState.turnId}));return{next:null,session:setPendingCodeModeConnectionAuth({interrupt:e.interrupt,responseMessages:e.responseMessages,session:{...e.baseSession,history:[...e.promptMessages],state:setPendingAuthorization(e.baseSession.state,{challenges:t})}})}}async function continuePendingCodeModeConnectionAuth(e){let t=getPendingCodeModeConnectionAuth(e.session.state);if(t===void 0)return null;let{continueCodeModeInterrupt:n,isCodeModeApprovalInterrupt:i,replaceCodeModeInterruptResult:a,unwrapCodeModeResult:o}=await loadCodeModeModule(),s=await buildCodeModeHostTools({approvedTools:getApprovedTools(e.session),capabilities:e.capabilities,tools:e.config.tools}),c=e.emit===void 0?void 0:createCodeModeLifecycle({emit:e.emit,emissionState:e.emissionState,skipReplayed:!0,tools:e.config.tools}),l;try{l=await n({interrupt:t.interrupt,resolution:{status:`authorized`},tools:s,options:createAshCodeModeOptions({lifecycle:c})})}catch(e){logError(log,`code-mode interrupt continuation failed`,e),l={error:`code_mode_continuation_failed`,message:toErrorMessage(e),retryable:!1}}let d=o(l),f=d.status===`interrupted`?d.interrupt:d.output,p=a([...e.session.history,...t.responseMessages],t.interrupt.pendingContinuation,f),m=clearPendingCodeModeConnectionAuth({...e.session,history:p});if(d.status===`interrupted`){if(isCodeModeConnectionAuthInterrupt(d.interrupt)){let t=e.session.history.length,n=p.slice(0,t),r=p.slice(t);return m={...m,history:n},parkOnCodeModeConnectionAuth({baseSession:m,config:e.config,emit:e.emit,emissionState:e.emissionState,interrupt:d.interrupt,promptMessages:n,responseMessages:r})}if(i(d.interrupt)){let t=e.session.history.length,n=p.slice(0,t),r=p.slice(t);return m={...m,history:n},parkOnCodeModeApproval({baseSession:m,config:e.config,emit:e.emit,emissionState:e.emissionState,interrupt:d.interrupt,promptMessages:n,responseMessages:r})}}return{next:e.runStep,session:m}}async function parkOnCodeModeApproval(e){let{toCodeModeApprovalMessages:t}=await loadCodeModeModule(),n=t(e.interrupt),r=extractToolApprovalInputRequests({content:extractAssistantContent(n)}),i=setPendingInputBatch({requests:r,responseMessages:n,session:setPendingCodeModeApproval({interrupt:e.interrupt,responseMessages:e.responseMessages,session:{...e.baseSession,history:[...e.promptMessages]}})});if(e.emit&&(await e.emit(createInputRequestedEvent({requests:r,sequence:e.emissionState.sequence,stepIndex:e.emissionState.stepIndex,turnId:e.emissionState.turnId})),e.config.mode===`conversation`)){let t=await emitTurnEpilogue(e.emit,e.emissionState,e.config.mode);i=setHarnessEmissionState(i,t)}return{next:null,session:i}}function extractAssistantContent(e){let t=[];for(let n of e)n.role===`assistant`&&Array.isArray(n.content)&&t.push(...n.content);return t}function createNextCompactionConfig(e,t,n){let r={recentWindowSize:e.recentWindowSize,threshold:e.threshold};return n.usage?.inputTokens!==void 0&&(r.lastKnownInputTokens=n.usage.inputTokens,r.lastKnownPromptMessageCount=t.length),r}async function maybeCompact(e){let{emit:t,emissionState:n}=e,r=e.messages,i=e.session;if(!shouldCompact(r,i.compaction))return{messages:r,session:i};let a=await resolveCompactionModel({compactionModelReference:i.agent.compactionModelReference,model:e.model,modelReference:i.agent.modelReference,resolveModel:e.resolveModel});if(t&&await t(createCompactionRequestedEvent({modelId:formatLanguageModelGatewayId(a.model),sequence:n.sequence,sessionId:i.sessionId,turnId:n.turnId,usageInputTokens:getInputTokenCount(r,i.compaction)})),r=await compactMessages(r,a.model,i.compaction,a.providerOptions,e.telemetry,e.headers),e.onCompaction){let t=await e.onCompaction(i);i=t.session;for(let e of t.messages)r.push(e)}return t&&await t(createCompactionCompletedEvent({modelId:formatLanguageModelGatewayId(a.model),sequence:n.sequence,sessionId:i.sessionId,turnId:n.turnId})),{messages:r,session:i}}function resolveApprovalKeyFromTools(e){return t=>{let n=e.get(t.action.toolName);if(n?.approvalKey!==void 0)return n.approvalKey(t.action.input)}}async function runModelCallWithRetries(e,t){for(let n=1;;n++)try{return await e()}catch(e){if(n===3||classifyModelCallError(e)!==`retry`)throw e;let r=500*2**(n-1)+Math.floor(Math.random()*250);log.warn(`model call failed transiently — retrying`,{attempt:n,delayMs:r,sessionId:t.sessionId,turnId:t.turnId,error:e}),await new Promise(e=>setTimeout(e,r))}}export{createToolLoopHarness};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createRequire}from"node:module";import{basename,dirname,join}from"node:path";import{existsSync,readFileSync,realpathSync}from"node:fs";import{ASH_PACKAGE_NAME}from"#internal/package-name.js";import{fileURLToPath}from"node:url";let cachedPackageInfo;const WORKFLOW_MODULE_ALIASES={"workflow/api":`src/compiled/@workflow/core/runtime.js`,"workflow/errors":`src/compiled/@workflow/errors/index.js`,"workflow/internal/private":`src/compiled/@workflow/core/private.js`,"workflow/runtime":`src/compiled/@workflow/core/runtime.js`};function resolveFallbackPackageVersion(){return`0.
|
|
1
|
+
import{createRequire}from"node:module";import{basename,dirname,join}from"node:path";import{existsSync,readFileSync,realpathSync}from"node:fs";import{ASH_PACKAGE_NAME}from"#internal/package-name.js";import{fileURLToPath}from"node:url";let cachedPackageInfo;const WORKFLOW_MODULE_ALIASES={"workflow/api":`src/compiled/@workflow/core/runtime.js`,"workflow/errors":`src/compiled/@workflow/errors/index.js`,"workflow/internal/private":`src/compiled/@workflow/core/private.js`,"workflow/runtime":`src/compiled/@workflow/core/runtime.js`};function resolveFallbackPackageVersion(){return`0.45.0`}const FALLBACK_PACKAGE_INFO={name:ASH_PACKAGE_NAME,version:resolveFallbackPackageVersion()};function resolveCurrentModulePath(){return typeof __filename==`string`?__filename:resolveCurrentModulePathFromStack()}function resolveCurrentModulePathFromStack(){let e=Error.prepareStackTrace;try{Error.prepareStackTrace=(e,t)=>t;let e=Error().stack?.[0]?.getFileName();if(typeof e!=`string`||e.length===0)throw Error(`Failed to resolve the current module path from the stack trace.`);return e.startsWith(`file:`)?fileURLToPath(e):e}finally{Error.prepareStackTrace=e}}const require=createRequire(resolveCurrentModulePath());function isBuildOutputPackageRoot(e){return basename(e)===`dist`&&existsSync(join(dirname(e),`package.json`))}function resolvePackageBuildRoot(){let e=dirname(realpathSync(resolveCurrentModulePath()));for(;;){if(isBuildOutputPackageRoot(e))return e;let t=dirname(e);if(t===e)return null;e=t}}function findNearestPackageRoot(e){let t=e;for(;;){if(existsSync(join(t,`package.json`))&&!isBuildOutputPackageRoot(t))return t;let r=dirname(t);if(r===t)throw Error(`Failed to resolve package root from "${e}".`);t=r}}function resolvePackageRoot(){return findNearestPackageRoot(dirname(realpathSync(resolveCurrentModulePath())))}function tryResolvePackageRoot(){try{return resolvePackageRoot()}catch{return}}function rewriteSourceFilePathForBuild(e){return e.replace(/\.[cm]?tsx?$/,`.js`)}function resolvePackageSourceFilePath(e){let t=resolvePackageBuildRoot();return t===null?join(resolvePackageRoot(),e):join(t,rewriteSourceFilePathForBuild(e))}function resolvePackageSourceDirectoryPath(e){let t=resolvePackageBuildRoot();return join(t===null?resolvePackageRoot():t,e)}function resolvePackageCompiledFilePath(e){let t=resolvePackageBuildRoot();return t===null?join(resolvePackageRoot(),`.generated`,`compiled`,e.replace(/^src\/compiled\//,``)):join(t,e)}function normalizeInstalledPackageInfo(e){let t=e;if(!(typeof t.name!=`string`||typeof t.version!=`string`))return{name:t.name,version:t.version}}function tryReadInstalledPackageInfo(e,t){let n=normalizeInstalledPackageInfo(JSON.parse(readFileSync(e,`utf8`)));if(n?.name===t)return n}function resolveInstalledPackageInfo(){if(cachedPackageInfo)return cachedPackageInfo;let e=tryResolvePackageRoot(),t=e===void 0?void 0:tryReadInstalledPackageInfo(join(e,`package.json`),ASH_PACKAGE_NAME);if(t)return cachedPackageInfo=t,cachedPackageInfo;try{let e=tryReadInstalledPackageInfo(require.resolve(`${ASH_PACKAGE_NAME}/package.json`),ASH_PACKAGE_NAME);if(e)return cachedPackageInfo=e,cachedPackageInfo}catch{}return cachedPackageInfo={...FALLBACK_PACKAGE_INFO},cachedPackageInfo}function resolveWorkflowModulePath(e){if(e===`workflow`)return resolvePackageSourceFilePath(`src/internal/workflow/index.ts`);if(e===`workflow/internal/builtins`)return resolvePackageSourceFilePath(`src/internal/workflow/builtins.ts`);let t=WORKFLOW_MODULE_ALIASES[e];return t===void 0?require.resolve(e):resolvePackageCompiledFilePath(t)}export{resolveInstalledPackageInfo,resolvePackageRoot,resolvePackageSourceDirectoryPath,resolvePackageSourceFilePath,resolveWorkflowModulePath};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{getSupportedModuleBaseName,matchesSupportedModuleBaseName}from"./module-files.js";import{pathExists,writeTextFile}from"./files.js";import{PNPM_WORKSPACE_PATH,ensurePnpmWorkspacePolicy}from"./pnpm-workspace.js";import{WEB_APP_TEMPLATE_FILES,WEB_APP_TEMPLATE_PACKAGE_JSON}from"./web-template.js";import"./project.js";import{patchPackageJson}from"./package-json.js";import{basename,join,resolve}from"node:path";import{readFile,readdir,writeFile}from"node:fs/promises";const SLACK_CHANNEL_DEFAULT_ROUTE=`/ash/v1/slack`,DEFAULT_SLACK_CONNECTOR_SLUG=`my-agent`,PACKAGE_DEPENDENCY_FIELDS=[`dependencies`,`devDependencies`,`peerDependencies`,`optionalDependencies`],WEB_VERCEL_JSON_PATH=`vercel.json`,WEB_VERCEL_JSON_SCHEMA=`https://openapi.vercel.sh/vercel.json`,WEB_DEFAULT_VERCEL_SERVICES={web:{entrypoint:`.`,framework:`nextjs`,routePrefix:`/`},ash:{buildCommand:`ash build`,entrypoint:`.`,framework:`ash`,routePrefix:`/_ash_internal/ash`}};function toSlackConnectorSlug(e){return e}function isJsonObject(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}async function readDependencyVersion(e,t){let n=JSON.parse(await readFile(e,`utf8`));if(!isJsonObject(n)||!isJsonObject(n.dependencies))return;let r=n.dependencies[t];return typeof r==`string`?r:void 0}function packageJsonHasDependency(e,t){for(let n of PACKAGE_DEPENDENCY_FIELDS){let r=e[n];if(isJsonObject(r)&&typeof r[t]==`string`)return!0}return!1}async function hasPackageDependency(e,t){if(!await pathExists(e))return!1;let r=JSON.parse(await readFile(e,`utf8`));return isJsonObject(r)&&packageJsonHasDependency(r,t)}async function ensurePackageDependency(e,t,r){return!await pathExists(e)||await readDependencyVersion(e,t)===r?[]:(await patchPackageJson(e,{dependencies:{[t]:r}}),[{path:e,dependencies:[t],devDependencies:[],scripts:[]}])}function resolveWebPackageVersions(e){return{ashPackageVersion:e?.ashPackageVersion??`0.
|
|
1
|
+
import{getSupportedModuleBaseName,matchesSupportedModuleBaseName}from"./module-files.js";import{pathExists,writeTextFile}from"./files.js";import{PNPM_WORKSPACE_PATH,ensurePnpmWorkspacePolicy}from"./pnpm-workspace.js";import{WEB_APP_TEMPLATE_FILES,WEB_APP_TEMPLATE_PACKAGE_JSON}from"./web-template.js";import"./project.js";import{patchPackageJson}from"./package-json.js";import{basename,join,resolve}from"node:path";import{readFile,readdir,writeFile}from"node:fs/promises";const SLACK_CHANNEL_DEFAULT_ROUTE=`/ash/v1/slack`,DEFAULT_SLACK_CONNECTOR_SLUG=`my-agent`,PACKAGE_DEPENDENCY_FIELDS=[`dependencies`,`devDependencies`,`peerDependencies`,`optionalDependencies`],WEB_VERCEL_JSON_PATH=`vercel.json`,WEB_VERCEL_JSON_SCHEMA=`https://openapi.vercel.sh/vercel.json`,WEB_DEFAULT_VERCEL_SERVICES={web:{entrypoint:`.`,framework:`nextjs`,routePrefix:`/`},ash:{buildCommand:`ash build`,entrypoint:`.`,framework:`ash`,routePrefix:`/_ash_internal/ash`}};function toSlackConnectorSlug(e){return e}function isJsonObject(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}async function readDependencyVersion(e,t){let n=JSON.parse(await readFile(e,`utf8`));if(!isJsonObject(n)||!isJsonObject(n.dependencies))return;let r=n.dependencies[t];return typeof r==`string`?r:void 0}function packageJsonHasDependency(e,t){for(let n of PACKAGE_DEPENDENCY_FIELDS){let r=e[n];if(isJsonObject(r)&&typeof r[t]==`string`)return!0}return!1}async function hasPackageDependency(e,t){if(!await pathExists(e))return!1;let r=JSON.parse(await readFile(e,`utf8`));return isJsonObject(r)&&packageJsonHasDependency(r,t)}async function ensurePackageDependency(e,t,r){return!await pathExists(e)||await readDependencyVersion(e,t)===r?[]:(await patchPackageJson(e,{dependencies:{[t]:r}}),[{path:e,dependencies:[t],devDependencies:[],scripts:[]}])}function resolveWebPackageVersions(e){return{ashPackageVersion:e?.ashPackageVersion??`0.45.0`,aiPackageVersion:e?.aiPackageVersion??`7.0.0-canary.159`,nextPackageVersion:e?.nextPackageVersion??`16.2.6`,reactPackageVersion:e?.reactPackageVersion??`19.2.6`,reactDomPackageVersion:e?.reactDomPackageVersion??`19.2.6`,streamdownPackageVersion:e?.streamdownPackageVersion??`2.5.0`,zodPackageVersion:e?.zodPackageVersion??`4.4.3`,tsgoPackageVersion:e?.tsgoPackageVersion??`7.0.0-dev.20260523.1`,typesNodePackageVersion:e?.typesNodePackageVersion??`25.9.1`,typesReactPackageVersion:e?.typesReactPackageVersion??`19.2.15`,typesReactDomPackageVersion:e?.typesReactDomPackageVersion??`19.2.3`}}function formatAshDependencySpecifier(e){return/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z-.]+)?$/.test(e)?`^${e}`:e}async function patchWebPackageJson(e,t){if(!await pathExists(e))return[];assertStampedVersion(`ashPackageVersion`,t.ashPackageVersion),assertStampedVersion(`aiPackageVersion`,t.aiPackageVersion),assertStampedVersion(`nextPackageVersion`,t.nextPackageVersion),assertStampedVersion(`reactPackageVersion`,t.reactPackageVersion),assertStampedVersion(`reactDomPackageVersion`,t.reactDomPackageVersion),assertStampedVersion(`streamdownPackageVersion`,t.streamdownPackageVersion),assertStampedVersion(`zodPackageVersion`,t.zodPackageVersion),assertStampedVersion(`tsgoPackageVersion`,t.tsgoPackageVersion),assertStampedVersion(`typesNodePackageVersion`,t.typesNodePackageVersion),assertStampedVersion(`typesReactPackageVersion`,t.typesReactPackageVersion),assertStampedVersion(`typesReactDomPackageVersion`,t.typesReactDomPackageVersion);let r={...WEB_APP_TEMPLATE_PACKAGE_JSON.dependencies,ai:t.aiPackageVersion,"experimental-ash":formatAshDependencySpecifier(t.ashPackageVersion),next:t.nextPackageVersion,react:t.reactPackageVersion,"react-dom":t.reactDomPackageVersion,streamdown:t.streamdownPackageVersion,zod:t.zodPackageVersion},i={...WEB_APP_TEMPLATE_PACKAGE_JSON.devDependencies,"@types/node":t.typesNodePackageVersion,"@types/react":t.typesReactPackageVersion,"@types/react-dom":t.typesReactDomPackageVersion,"@typescript/native-preview":t.tsgoPackageVersion},a=WEB_APP_TEMPLATE_PACKAGE_JSON.scripts;return await patchPackageJson(e,{dependencies:r,devDependencies:i,scripts:a}),[{path:e,dependencies:Object.keys(r),devDependencies:Object.keys(i),scripts:Object.keys(a)}]}function normalizeSlackConnectorSlug(e){return toSlackConnectorSlug((e.trim().replace(/^@/,``).split(`/`).at(-1)??``).toLowerCase().replace(/[^a-z0-9_-]+/g,`-`).replace(/^[^a-z0-9]+/,``).replace(/[^a-z0-9]+$/,``).slice(0,100).replace(/[^a-z0-9]+$/,``)||`my-agent`)}async function deriveSlackConnectorSlug(e,t){if(t!==void 0&&t.length>0&&t!==`.`)return normalizeSlackConnectorSlug(t);try{let t=await readFile(join(e,`package.json`),`utf8`),n=JSON.parse(t);if(typeof n.name==`string`&&n.name.length>0)return normalizeSlackConnectorSlug(n.name)}catch{}return normalizeSlackConnectorSlug(basename(resolve(e))||`my-agent`)}function buildSlackTemplate(e){return`import { connectSlackCredentials } from "@vercel/connect/ash";
|
|
2
2
|
import { slackChannel } from "experimental-ash/channels/slack";
|
|
3
3
|
|
|
4
4
|
export default slackChannel({
|
|
@@ -17,9 +17,12 @@ type EventData<T extends HandleMessageStreamEvent["type"]> = Extract<HandleMessa
|
|
|
17
17
|
export interface DiscordContext {
|
|
18
18
|
readonly discord: DiscordHandle;
|
|
19
19
|
}
|
|
20
|
-
/**
|
|
21
|
-
export
|
|
20
|
+
/** Channel-owned Discord context returned by `context()`. */
|
|
21
|
+
export type DiscordChannelContext = DiscordContext & {
|
|
22
22
|
state: DiscordChannelState;
|
|
23
|
+
};
|
|
24
|
+
/** Event-handler Discord context, including session operations. */
|
|
25
|
+
export interface DiscordEventContext extends DiscordChannelContext, ChannelSessionOps {
|
|
23
26
|
}
|
|
24
27
|
/** JSON-serializable Discord channel state. */
|
|
25
28
|
export interface DiscordChannelState {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { defineChannel, GET, POST, PUT, PATCH, DELETE, type Channel, type ChannelDefinition, type ChannelSessionOps, type ChannelEvents, type Session, type SessionHandle, type RouteDefinition, type RouteHandlerArgs, type SendFn, type SendOptions, type SendPayload, type GetSessionFn, } from "#public/definitions/defineChannel.js";
|
|
1
|
+
export { defineChannel, GET, POST, PUT, PATCH, DELETE, type Channel, type ChannelDefinition, type ChannelSessionOps, type ChannelEvents, type InferChannelMetadata, type Session, type SessionHandle, type RouteDefinition, type RouteHandlerArgs, type SendFn, type SendOptions, type SendPayload, type GetSessionFn, } from "#public/definitions/defineChannel.js";
|
|
@@ -30,15 +30,13 @@ export interface SlackContext {
|
|
|
30
30
|
readonly thread: SlackThread;
|
|
31
31
|
readonly slack: SlackHandle;
|
|
32
32
|
}
|
|
33
|
-
/**
|
|
34
|
-
|
|
35
|
-
* runtime's mutable channel state. Handlers can read or mutate
|
|
36
|
-
* `state` directly; the runtime auto-snapshots the result at step
|
|
37
|
-
* boundaries.
|
|
38
|
-
*/
|
|
39
|
-
export interface SlackEventContext extends SlackContext, ChannelSessionOps {
|
|
33
|
+
/** Channel-owned Slack context returned by `context()`. */
|
|
34
|
+
export interface SlackChannelContext extends SlackContext {
|
|
40
35
|
state: SlackChannelState;
|
|
41
36
|
}
|
|
37
|
+
/** Event-handler Slack context, including session operations. */
|
|
38
|
+
export interface SlackEventContext extends SlackChannelContext, ChannelSessionOps {
|
|
39
|
+
}
|
|
42
40
|
export type { SlackApiResponse, SlackBotToken, SlackHandle, SlackThread, } from "#public/channels/slack/api.js";
|
|
43
41
|
export type { SlackWebhookVerifier } from "#public/channels/slack/verify.js";
|
|
44
42
|
type SlackEventHandler<T extends HandleMessageStreamEvent["type"]> = (data: EventData<T>, channel: SlackEventContext, ctx: SessionContext) => void | Promise<void>;
|
|
@@ -294,7 +292,7 @@ export interface SlackChannelConfig {
|
|
|
294
292
|
* default-export a `slackChannel(...)` call under `declaration: true`
|
|
295
293
|
* without TypeScript falling back to an internal path for `Channel`.
|
|
296
294
|
*/
|
|
297
|
-
export interface SlackChannel extends Channel<SlackChannelState, SlackReceiveArgs> {
|
|
295
|
+
export interface SlackChannel extends Channel<SlackChannelState, SlackReceiveArgs, SlackInstrumentationMetadata> {
|
|
298
296
|
}
|
|
299
297
|
/**
|
|
300
298
|
* Slack channel factory. Wires up the Slack webhook route, mention
|
|
@@ -19,11 +19,14 @@ export interface TeamsContext {
|
|
|
19
19
|
readonly teams: TeamsHandle;
|
|
20
20
|
readonly thread: TeamsThread;
|
|
21
21
|
}
|
|
22
|
-
/**
|
|
23
|
-
export interface
|
|
22
|
+
/** Channel-owned Teams context returned by `context()`. */
|
|
23
|
+
export interface TeamsChannelContext extends TeamsContext {
|
|
24
24
|
readonly adaptiveCardVersion: string;
|
|
25
25
|
state: TeamsChannelState;
|
|
26
26
|
}
|
|
27
|
+
/** Event-handler Teams context, including session operations. */
|
|
28
|
+
export interface TeamsEventContext extends TeamsChannelContext, ChannelSessionOps {
|
|
29
|
+
}
|
|
27
30
|
/** JSON-serializable Teams channel state. */
|
|
28
31
|
export interface TeamsChannelState {
|
|
29
32
|
/** Bot account captured from the inbound activity recipient. */
|
|
@@ -19,10 +19,13 @@ type EventData<T extends HandleMessageStreamEvent["type"]> = Extract<HandleMessa
|
|
|
19
19
|
export interface TelegramContext {
|
|
20
20
|
readonly telegram: TelegramHandle;
|
|
21
21
|
}
|
|
22
|
-
/**
|
|
23
|
-
export interface
|
|
22
|
+
/** Channel-owned Telegram context returned by `context()`. */
|
|
23
|
+
export interface TelegramChannelContext extends TelegramContext {
|
|
24
24
|
state: TelegramChannelState;
|
|
25
25
|
}
|
|
26
|
+
/** Event-handler Telegram context, including session operations. */
|
|
27
|
+
export interface TelegramEventContext extends TelegramChannelContext, ChannelSessionOps {
|
|
28
|
+
}
|
|
26
29
|
/** JSON-serializable Telegram channel state. */
|
|
27
30
|
export interface TelegramChannelState extends TelegramHitlState {
|
|
28
31
|
/** Telegram bot username used for group mention detection, when configured. */
|
|
@@ -15,10 +15,13 @@ type EventData<T extends HandleMessageStreamEvent["type"]> = Extract<HandleMessa
|
|
|
15
15
|
export interface TwilioContext {
|
|
16
16
|
readonly twilio: TwilioHandle;
|
|
17
17
|
}
|
|
18
|
-
/**
|
|
19
|
-
export interface
|
|
18
|
+
/** Channel-owned Twilio context returned by `context()`. */
|
|
19
|
+
export interface TwilioChannelContext extends TwilioContext {
|
|
20
20
|
state: TwilioChannelState;
|
|
21
21
|
}
|
|
22
|
+
/** Event-handler Twilio context, including session operations. */
|
|
23
|
+
export interface TwilioEventContext extends TwilioChannelContext, ChannelSessionOps {
|
|
24
|
+
}
|
|
22
25
|
/** JSON-serializable state for the phone-number conversation. */
|
|
23
26
|
export interface TwilioChannelState {
|
|
24
27
|
/** Caller / sender phone number. */
|
|
@@ -180,7 +183,7 @@ export interface TwilioSendMessageOptions {
|
|
|
180
183
|
readonly statusCallbackUrl?: string;
|
|
181
184
|
}
|
|
182
185
|
/** Concrete return type of {@link twilioChannel}. */
|
|
183
|
-
export interface TwilioChannel extends Channel<TwilioChannelState, TwilioReceiveArgs> {
|
|
186
|
+
export interface TwilioChannel extends Channel<TwilioChannelState, TwilioReceiveArgs, TwilioInstrumentationMetadata> {
|
|
184
187
|
}
|
|
185
188
|
/** Twilio channel factory for SMS and speech-transcribed inbound calls. */
|
|
186
189
|
export declare function twilioChannel(config: TwilioChannelConfig): TwilioChannel;
|
|
@@ -6,6 +6,7 @@ import type { HandleMessageStreamEvent } from "#protocol/message.js";
|
|
|
6
6
|
import type { SessionContext } from "#public/definitions/callback-context.js";
|
|
7
7
|
import type { RouteDefinition, SendFn } from "#channel/routes.js";
|
|
8
8
|
import type { Session, SessionHandle } from "#channel/session.js";
|
|
9
|
+
declare const CHANNEL_METADATA_TYPE: unique symbol;
|
|
9
10
|
export type { Session, SessionHandle } from "#channel/session.js";
|
|
10
11
|
export { GET, POST, PUT, PATCH, DELETE } from "#channel/routes.js";
|
|
11
12
|
export type { RouteDefinition, RouteHandlerArgs, SendFn, SendOptions, SendPayload, GetSessionFn, } from "#channel/routes.js";
|
|
@@ -68,7 +69,7 @@ export interface ChannelDefinition<TState = undefined, TCtx = void, TReceiveArgs
|
|
|
68
69
|
* (with {@link ChannelSessionOps} injected) and passes
|
|
69
70
|
* {@link SessionContext} as a separate `ctx` argument.
|
|
70
71
|
*/
|
|
71
|
-
context?(state: NonNullable<TState>, session: SessionHandle):
|
|
72
|
+
context?(state: NonNullable<TState>, session: SessionHandle): TCtx;
|
|
72
73
|
readonly routes: readonly RouteDefinition<TState>[];
|
|
73
74
|
receive?(input: ReceiveInput<TReceiveArgs>, args: {
|
|
74
75
|
send: SendFn<TState>;
|
|
@@ -107,8 +108,9 @@ export interface ChannelDefinition<TState = undefined, TCtx = void, TReceiveArgs
|
|
|
107
108
|
*/
|
|
108
109
|
readonly kindHint?: string;
|
|
109
110
|
}
|
|
110
|
-
export interface Channel<TState = undefined, TReceiveArgs = Record<string, unknown>> extends TypedReceiveRoute<TReceiveArgs> {
|
|
111
|
+
export interface Channel<TState = undefined, TReceiveArgs = Record<string, unknown>, TMetadata extends Record<string, unknown> = Record<string, unknown>> extends TypedReceiveRoute<TReceiveArgs> {
|
|
111
112
|
readonly __kind: typeof CHANNEL_SENTINEL;
|
|
113
|
+
readonly [CHANNEL_METADATA_TYPE]?: TMetadata;
|
|
112
114
|
readonly routes: readonly {
|
|
113
115
|
method: string;
|
|
114
116
|
path: string;
|
|
@@ -117,4 +119,5 @@ export interface Channel<TState = undefined, TReceiveArgs = Record<string, unkno
|
|
|
117
119
|
send: SendFn<TState>;
|
|
118
120
|
}) => Promise<Session>;
|
|
119
121
|
}
|
|
120
|
-
export
|
|
122
|
+
export type InferChannelMetadata<TChannel> = TChannel extends Channel<any, any, infer TMetadata> ? TMetadata : Record<string, unknown>;
|
|
123
|
+
export declare function defineChannel<TState = undefined, TCtx = void, TReceiveArgs = Record<string, unknown>, TMetadata extends Record<string, unknown> = Record<string, unknown>>(definition: ChannelDefinition<TState, TCtx, TReceiveArgs, TMetadata>): Channel<TState, TReceiveArgs, TMetadata>;
|
|
@@ -1,152 +1 @@
|
|
|
1
|
-
|
|
2
|
-
/**
|
|
3
|
-
* Instrumentation authoring helpers for `agent/instrumentation.ts`.
|
|
4
|
-
*/
|
|
5
|
-
import type { ModelMessage, SystemModelMessage } from "ai";
|
|
6
|
-
import type { SessionAuthContext, SessionParent } from "#channel/types.js";
|
|
7
|
-
/**
|
|
8
|
-
* Context passed to the {@link InstrumentationDefinition.setup} callback.
|
|
9
|
-
*/
|
|
10
|
-
export interface InstrumentationSetupContext {
|
|
11
|
-
/**
|
|
12
|
-
* The agent name declared by `defineAgent`.
|
|
13
|
-
*
|
|
14
|
-
* Use this as the `serviceName` for `registerOTel` instead of
|
|
15
|
-
* hard-coding a string — it prevents copy-paste drift across agents.
|
|
16
|
-
*/
|
|
17
|
-
readonly agentName: string;
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* User-authored metadata attached to AI SDK telemetry spans.
|
|
21
|
-
*
|
|
22
|
-
* Keys beginning with `ash.` are reserved for framework-owned metadata
|
|
23
|
-
* and are ignored when returned from authored instrumentation.
|
|
24
|
-
*/
|
|
25
|
-
export type InstrumentationMetadata = Readonly<Record<string, string>>;
|
|
26
|
-
/**
|
|
27
|
-
* Base channel metadata shape used by framework channel kinds.
|
|
28
|
-
*/
|
|
29
|
-
export type InstrumentationChannelMetadata = Readonly<Record<string, unknown>>;
|
|
30
|
-
/**
|
|
31
|
-
* Channel metadata projections keyed by instrumentation channel kind.
|
|
32
|
-
*
|
|
33
|
-
* Channel packages and user-authored channels declaration-merge additional
|
|
34
|
-
* `channel:<registered-name>` entries into this map so `input.channel.metadata`
|
|
35
|
-
* narrows after checking `input.channel.kind`.
|
|
36
|
-
*/
|
|
37
|
-
export interface ChannelMetadataMap {
|
|
38
|
-
readonly http: InstrumentationChannelMetadata;
|
|
39
|
-
readonly schedule: InstrumentationChannelMetadata;
|
|
40
|
-
readonly subagent: InstrumentationChannelMetadata;
|
|
41
|
-
readonly unknown: InstrumentationChannelMetadata;
|
|
42
|
-
}
|
|
43
|
-
export type InstrumentationChannelKind = keyof ChannelMetadataMap;
|
|
44
|
-
/**
|
|
45
|
-
* Channel projection exposed to instrumentation callbacks.
|
|
46
|
-
*
|
|
47
|
-
* `kind` is the channel identity Ash has for the current turn (`"http"`,
|
|
48
|
-
* `"schedule"`, `"subagent"`, or `channel:<name>` for authored channels,
|
|
49
|
-
* where `<name>` is the channel's filename under `agent/channels/`).
|
|
50
|
-
*/
|
|
51
|
-
export interface InstrumentationChannelForKind<K extends InstrumentationChannelKind> {
|
|
52
|
-
readonly kind: K;
|
|
53
|
-
readonly metadata: ChannelMetadataMap[K];
|
|
54
|
-
}
|
|
55
|
-
export type InstrumentationChannel = {
|
|
56
|
-
readonly [K in InstrumentationChannelKind]: InstrumentationChannelForKind<K>;
|
|
57
|
-
}[InstrumentationChannelKind];
|
|
58
|
-
export interface InstrumentationSession {
|
|
59
|
-
readonly auth: {
|
|
60
|
-
readonly current: SessionAuthContext | null;
|
|
61
|
-
readonly initiator: SessionAuthContext | null;
|
|
62
|
-
};
|
|
63
|
-
readonly id: string;
|
|
64
|
-
readonly parent?: SessionParent;
|
|
65
|
-
}
|
|
66
|
-
export interface InstrumentationTurn {
|
|
67
|
-
readonly id: string;
|
|
68
|
-
readonly sequence: number;
|
|
69
|
-
}
|
|
70
|
-
export interface InstrumentationStep {
|
|
71
|
-
readonly index: number;
|
|
72
|
-
}
|
|
73
|
-
export interface InstrumentationModelInput {
|
|
74
|
-
readonly instructions: string | readonly SystemModelMessage[] | undefined;
|
|
75
|
-
readonly messages: readonly ModelMessage[];
|
|
76
|
-
}
|
|
77
|
-
/**
|
|
78
|
-
* Input passed to `metadata["step.started"]`.
|
|
79
|
-
*
|
|
80
|
-
* The callback runs after Ash has built the final model input for this
|
|
81
|
-
* model-call attempt and before the AI SDK model call is constructed.
|
|
82
|
-
*/
|
|
83
|
-
export interface InstrumentationStepStartedMetadataInput {
|
|
84
|
-
readonly channel: InstrumentationChannel;
|
|
85
|
-
readonly modelInput: InstrumentationModelInput;
|
|
86
|
-
readonly session: InstrumentationSession;
|
|
87
|
-
readonly step: InstrumentationStep;
|
|
88
|
-
readonly turn: InstrumentationTurn;
|
|
89
|
-
}
|
|
90
|
-
/**
|
|
91
|
-
* Metadata hooks accepted by {@link defineInstrumentation}.
|
|
92
|
-
*/
|
|
93
|
-
export interface InstrumentationMetadataConfig {
|
|
94
|
-
/**
|
|
95
|
-
* Per-attempt metadata resolved before the model call so child spans
|
|
96
|
-
* created by the AI SDK inherit the returned values.
|
|
97
|
-
*/
|
|
98
|
-
readonly "step.started"?: (input: InstrumentationStepStartedMetadataInput) => InstrumentationMetadata;
|
|
99
|
-
}
|
|
100
|
-
/**
|
|
101
|
-
* Authored instrumentation settings accepted by `defineInstrumentation`.
|
|
102
|
-
*
|
|
103
|
-
* The presence of a `defineInstrumentation` export implicitly enables
|
|
104
|
-
* telemetry — there is no separate `isEnabled` toggle.
|
|
105
|
-
*/
|
|
106
|
-
export interface InstrumentationDefinition {
|
|
107
|
-
/**
|
|
108
|
-
* Override the default function identifier attached to telemetry spans.
|
|
109
|
-
*
|
|
110
|
-
* When omitted, Ash derives the identifier from the agent name.
|
|
111
|
-
*/
|
|
112
|
-
readonly functionId?: string;
|
|
113
|
-
/**
|
|
114
|
-
* Additional metadata merged into AI SDK telemetry spans.
|
|
115
|
-
*/
|
|
116
|
-
readonly metadata?: InstrumentationMetadataConfig;
|
|
117
|
-
/**
|
|
118
|
-
* Whether to record full model inputs in telemetry spans.
|
|
119
|
-
*
|
|
120
|
-
* Defaults to `true` when `instrumentation.ts` is present. Set to `false`
|
|
121
|
-
* to disable recording inputs, which may be useful if inputs contain
|
|
122
|
-
* sensitive content or you want to reduce span payload size.
|
|
123
|
-
*/
|
|
124
|
-
readonly recordInputs?: boolean;
|
|
125
|
-
/**
|
|
126
|
-
* Whether to record full model outputs in telemetry spans.
|
|
127
|
-
*
|
|
128
|
-
* Defaults to `true` when `instrumentation.ts` is present. Set to `false`
|
|
129
|
-
* to disable recording outputs.
|
|
130
|
-
*/
|
|
131
|
-
readonly recordOutputs?: boolean;
|
|
132
|
-
/**
|
|
133
|
-
* Optional setup callback invoked at server startup with the resolved
|
|
134
|
-
* agent name.
|
|
135
|
-
*
|
|
136
|
-
* Use this to call `registerOTel` or any other OTel provider setup.
|
|
137
|
-
* The `context.agentName` value comes from `defineAgent`, so you never
|
|
138
|
-
* need to hard-code a service name.
|
|
139
|
-
*/
|
|
140
|
-
readonly setup?: (context: InstrumentationSetupContext) => void;
|
|
141
|
-
}
|
|
142
|
-
/**
|
|
143
|
-
* Defines instrumentation settings for the agent application.
|
|
144
|
-
*
|
|
145
|
-
* Export the result as the default export of `agent/instrumentation.ts`.
|
|
146
|
-
* Ash picks up these settings at server startup and applies them to every
|
|
147
|
-
* AI SDK model call.
|
|
148
|
-
*
|
|
149
|
-
* The `setup` callback is invoked later by the framework with the resolved
|
|
150
|
-
* agent name — it is not called during `defineInstrumentation` itself.
|
|
151
|
-
*/
|
|
152
|
-
export declare function defineInstrumentation<T extends InstrumentationDefinition>(definition: ExactDefinition<T, InstrumentationDefinition>): T;
|
|
1
|
+
export * from "#public/instrumentation/index.js";
|