experimental-ash 0.51.0 → 0.52.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 +8 -0
- package/dist/docs/public/advanced/instrumentation.md +87 -119
- package/dist/docs/public/advanced/typescript-api.md +4 -2
- package/dist/docs/public/channels/discord.mdx +2 -2
- package/dist/docs/public/channels/index.md +4 -4
- package/dist/docs/public/channels/teams.mdx +1 -1
- package/dist/docs/public/channels/telegram.mdx +2 -2
- package/dist/docs/public/meta.json +1 -0
- package/dist/docs/public/onboarding.md +119 -0
- package/dist/docs/public/schedules.mdx +4 -4
- package/dist/src/channel/compiled-channel.d.ts +2 -2
- package/dist/src/channel/cross-channel-receive.d.ts +6 -6
- package/dist/src/channel/cross-channel-receive.js +1 -1
- package/dist/src/channel/receive-target.d.ts +17 -0
- package/dist/src/cli/commands/channels.d.ts +2 -0
- package/dist/src/cli/commands/channels.js +1 -1
- package/dist/src/cli/commands/info.d.ts +46 -1
- package/dist/src/cli/commands/info.js +2 -2
- package/dist/src/cli/run.d.ts +3 -1
- package/dist/src/cli/run.js +2 -2
- package/dist/src/harness/{instrumentation-metadata.d.ts → instrumentation-runtime-context.d.ts} +2 -2
- package/dist/src/harness/instrumentation-runtime-context.js +1 -0
- package/dist/src/harness/tool-loop.js +1 -1
- package/dist/src/internal/application/package.js +1 -1
- package/dist/src/internal/instrumentation.d.ts +8 -7
- package/dist/src/internal/instrumentation.js +1 -1
- package/dist/src/packages/ash-scaffold/src/channels.js +2 -2
- package/dist/src/packages/ash-scaffold/src/human-action.js +1 -0
- package/dist/src/packages/ash-scaffold/src/index.js +1 -1
- package/dist/src/packages/ash-scaffold/src/steps/run-add-to-agent.js +1 -1
- package/dist/src/packages/ash-scaffold/src/steps/setup-slackbot.js +1 -1
- package/dist/src/public/channels/discord/discordChannel.d.ts +3 -3
- package/dist/src/public/channels/discord/discordChannel.js +1 -1
- package/dist/src/public/channels/discord/index.d.ts +1 -1
- package/dist/src/public/channels/slack/index.d.ts +1 -1
- package/dist/src/public/channels/slack/slackChannel.d.ts +4 -3
- package/dist/src/public/channels/slack/slackChannel.js +1 -1
- package/dist/src/public/channels/teams/index.d.ts +1 -1
- package/dist/src/public/channels/teams/teamsChannel.d.ts +3 -3
- package/dist/src/public/channels/teams/teamsChannel.js +1 -1
- package/dist/src/public/channels/telegram/index.d.ts +1 -1
- package/dist/src/public/channels/telegram/telegramChannel.d.ts +3 -3
- package/dist/src/public/channels/telegram/telegramChannel.js +1 -1
- package/dist/src/public/channels/twilio/index.d.ts +1 -1
- package/dist/src/public/channels/twilio/twilioChannel.d.ts +3 -3
- package/dist/src/public/channels/twilio/twilioChannel.js +1 -1
- package/dist/src/public/definitions/defineChannel.d.ts +8 -8
- package/dist/src/public/definitions/schedule.d.ts +2 -2
- package/dist/src/public/instrumentation/index.d.ts +21 -11
- package/dist/src/public/schedules/index.d.ts +1 -1
- package/package.json +1 -1
- package/dist/src/channel/receive-args.d.ts +0 -17
- package/dist/src/harness/instrumentation-metadata.js +0 -1
- /package/dist/src/channel/{receive-args.js → receive-target.js} +0 -0
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{isCompiledChannel}from"#channel/compiled-channel.js";import{createSendFn}from"#channel/send.js";function toCrossChannelTargets(e){return e.flatMap(e=>e.definition===void 0?[]:[{name:e.name,definition:e.definition,receive:e.receive,adapter:e.adapter}])}function createCrossChannelReceiveFn(e,t){return async(n,r)=>{let i=resolveTargetByReference(n,t);return await invokeChannelReceive({runtime:e,target:i,input:{message:r.message,
|
|
1
|
+
import{isCompiledChannel}from"#channel/compiled-channel.js";import{createSendFn}from"#channel/send.js";function toCrossChannelTargets(e){return e.flatMap(e=>e.definition===void 0?[]:[{name:e.name,definition:e.definition,receive:e.receive,adapter:e.adapter}])}function createCrossChannelReceiveFn(e,t){return async(n,r)=>{let i=resolveTargetByReference(n,t);return await invokeChannelReceive({runtime:e,target:i,input:{message:r.message,target:r.target,auth:r.auth},describeMissingReceive:()=>`args.receive(): channel "${i.name}" does not implement receive(). Declare a receive hook on the channel to accept cross-channel sessions.`,describeMissingAdapter:()=>`args.receive(): channel "${i.name}" has no adapter — cannot build send().`})}}async function invokeChannelReceive(e){if(!e.target.receive)throw Error(e.describeMissingReceive());if(!e.target.adapter)throw Error(e.describeMissingAdapter());let n=createSendFn(e.runtime,e.target.adapter,e.target.name);return await e.target.receive(e.input,{send:n})}function resolveTargetByReference(e,t){for(let n of t)if(n.definition===e)return n;let n=resolveTargetByRouteFingerprint(e,t);if(n!==null)return n;throw Error(`args.receive(): the channel passed as the first argument is not registered in this agent's channels/. Import the channel module's default export from agent/channels/<name>.ts and pass that value.`)}function resolveTargetByRouteFingerprint(t,n){if(!isCompiledChannel(t))return null;let r=createRouteFingerprint(t);if(r===null)return null;let i=new Map;for(let e of n)createRouteFingerprint(e.definition)===r&&i.set(e.name,e);if(i.size===1)return[...i.values()][0];if(i.size>1)throw Error(`args.receive(): the channel passed as the first argument matches multiple registered channels by route shape. Import a channel with a unique route set from agent/channels/<name>.ts before passing it to args.receive().`);return null}function createRouteFingerprint(e){return e.routes.length===0?null:e.routes.map(e=>`${e.method.toUpperCase()} ${e.path}`).sort().join(`
|
|
2
2
|
`)}export{createCrossChannelReceiveFn,invokeChannelReceive,toCrossChannelTargets};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
declare const receiveTargetMarker: unique symbol;
|
|
2
|
+
/**
|
|
3
|
+
* Structural marker attached by channel factories (e.g. `slackChannel`,
|
|
4
|
+
* `twilioChannel`) to advertise the target type their `receive()` accepts.
|
|
5
|
+
* `receive(channel, { target })` helpers and the route-handler
|
|
6
|
+
* `args.receive(channel, ...)` use this marker to infer typed targets
|
|
7
|
+
* from a plain channel import.
|
|
8
|
+
*/
|
|
9
|
+
export interface TypedReceiveTarget<TTarget = Record<string, unknown>> {
|
|
10
|
+
readonly [receiveTargetMarker]?: TTarget;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Extracts the receive-target type from a channel value, falling back to
|
|
14
|
+
* `Record<string, unknown>` when the channel does not advertise one.
|
|
15
|
+
*/
|
|
16
|
+
export type InferReceiveTarget<TChannel> = TChannel extends TypedReceiveTarget<infer TTarget> ? TTarget : Record<string, unknown>;
|
|
17
|
+
export {};
|
|
@@ -7,6 +7,8 @@ export interface CliLogger {
|
|
|
7
7
|
}
|
|
8
8
|
export interface AddChannelCommandOptions {
|
|
9
9
|
force?: boolean;
|
|
10
|
+
/** Assume yes for confirmations (slackbot creation). Requires an explicit kind. */
|
|
11
|
+
yes?: boolean;
|
|
10
12
|
}
|
|
11
13
|
export interface ChannelsAddDependencies {
|
|
12
14
|
createPrompter?: () => ChannelAddPrompter;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{assertCanAddSelectedChannels,inspectExistingChannelRegistrations}from"./channel-add-conflicts.js";import{isAshProject}from"../../packages/ash-scaffold/src/project.js";import{listAuthoredChannels}from"../../packages/ash-scaffold/src/channels.js";import"../../packages/ash-scaffold/src/index.js";import{detectDeployment}from"../../packages/ash-scaffold/src/primitives/detect-deployment.js";import{createAddToAgentState,deployProject,runAddToAgent}from"../../packages/ash-scaffold/src/steps/run-add-to-agent.js";import"../../packages/ash-scaffold/src/steps/index.js";import{ChannelAddCancelledError,createChannelAddPrompter}from"../../packages/ash-scaffold/src/cli/channel-add-prompter.js";import"../../packages/ash-scaffold/src/cli/index.js";import"../../packages/ash-scaffold/src/primitives/index.js";const NOT_AN_AGENT_MESSAGE="No Ash agent in this directory. Run `pnpm create experimental-ash-agent`, then run this command from inside the new project.",KNOWN_CHANNEL_KINDS=[`slack`,`web`];function isChannelKind(e){return KNOWN_CHANNEL_KINDS.includes(e)}function parseChannelKind(e){if(!isChannelKind(e))throw Error(`Unknown channel kind "${e}". Known: ${KNOWN_CHANNEL_KINDS.join(`, `)}.`);return e}const defaultChannelsAddDependencies={detectDeployment,deployProject,runAddToAgent};async function runAddChannelsFlow(n,r,i,o){if(r===void 0&&(
|
|
1
|
+
import{assertCanAddSelectedChannels,inspectExistingChannelRegistrations}from"./channel-add-conflicts.js";import{isAshProject}from"../../packages/ash-scaffold/src/project.js";import{listAuthoredChannels}from"../../packages/ash-scaffold/src/channels.js";import"../../packages/ash-scaffold/src/index.js";import{detectDeployment}from"../../packages/ash-scaffold/src/primitives/detect-deployment.js";import{createAddToAgentState,deployProject,runAddToAgent}from"../../packages/ash-scaffold/src/steps/run-add-to-agent.js";import"../../packages/ash-scaffold/src/steps/index.js";import{ChannelAddCancelledError,createChannelAddPrompter}from"../../packages/ash-scaffold/src/cli/channel-add-prompter.js";import"../../packages/ash-scaffold/src/cli/index.js";import"../../packages/ash-scaffold/src/primitives/index.js";const NOT_AN_AGENT_MESSAGE="No Ash agent in this directory. Run `pnpm create experimental-ash-agent`, then run this command from inside the new project.",KNOWN_CHANNEL_KINDS=[`slack`,`web`];function isChannelKind(e){return KNOWN_CHANNEL_KINDS.includes(e)}function parseChannelKind(e){if(!isChannelKind(e))throw Error(`Unknown channel kind "${e}". Known: ${KNOWN_CHANNEL_KINDS.join(`, `)}.`);return e}const defaultChannelsAddDependencies={detectDeployment,deployProject,runAddToAgent};async function runAddChannelsFlow(n,r,i,o){if(r===void 0&&(i.yes||!process.stdin.isTTY||!process.stdout.isTTY))throw Error(`Pass a channel kind: \`ash channels add <${KNOWN_CHANNEL_KINDS.join(`|`)}>\`.`);let s=o.createPrompter?.()??createChannelAddPrompter();s.intro(`Add channels to your Ash agent`),s.log.message(`Checking the current Vercel project...`);let c=createAddToAgentState(await o.detectDeployment(n)),l;function inspectRegistrations(){return l===void 0&&(s.log.message(`Inspecting existing channel registrations...`),l=inspectExistingChannelRegistrations(n)),l}let u=r===void 0?(await inspectRegistrations()).disabledChannelReasons:void 0;await o.runAddToAgent({prompter:s,projectPath:n,state:c,presetChannels:r===void 0?void 0:[r],presetCreateSlackbot:i.yes?!0:void 0,disabledChannelReasons:u,force:i.force,validateSelectedChannels:async t=>{!t.includes(`web`)&&!t.includes(`slack`)||assertCanAddSelectedChannels(t,await inspectRegistrations())}}),await o.deployProject({prompter:s,projectPath:n,vercelProjectId:c.vercelProjectId,state:c}),s.outro(c.channels.length===0?`No channels added.`:`Channels added.`)}async function runChannelsAddCommand(e,t,r,i=defaultChannelsAddDependencies){if(!await isAshProject(t)){e.error(NOT_AN_AGENT_MESSAGE),process.exitCode=1;return}try{await runAddChannelsFlow(t,r.kind===void 0?void 0:parseChannelKind(r.kind),r.options,i)}catch(t){if(t instanceof ChannelAddCancelledError)return;e.error(t instanceof Error?t.message:String(t)),process.exitCode=1}}async function runChannelsListCommand(e,t,i){if(!await isAshProject(t)){e.error(NOT_AN_AGENT_MESSAGE),process.exitCode=1;return}let a=await listAuthoredChannels(t);if(i.json){e.log(JSON.stringify({channels:a},null,2));return}if(a.length===0){e.log("No channels defined. Run `ash channels add` to add one.");return}for(let t of a)e.log(t)}export{runChannelsAddCommand,runChannelsListCommand};
|
|
@@ -1,8 +1,53 @@
|
|
|
1
|
+
import { type ApplicationInspection } from "#services/inspect-application.js";
|
|
1
2
|
interface CliInfoLogger {
|
|
2
3
|
log(message: string): void;
|
|
3
4
|
}
|
|
5
|
+
/** Options accepted by {@link printApplicationInfo}. */
|
|
6
|
+
export interface PrintApplicationInfoOptions {
|
|
7
|
+
/** Emit a machine-readable JSON document instead of the human table. */
|
|
8
|
+
json?: boolean;
|
|
9
|
+
}
|
|
10
|
+
/** Machine-readable view of an application inspection, emitted by `ash info --json`. */
|
|
11
|
+
export interface ApplicationInfoJson {
|
|
12
|
+
appRoot: string;
|
|
13
|
+
agentRoot: string | null;
|
|
14
|
+
layout: string | null;
|
|
15
|
+
status: string;
|
|
16
|
+
diagnostics: {
|
|
17
|
+
errors: number;
|
|
18
|
+
warnings: number;
|
|
19
|
+
} | null;
|
|
20
|
+
model: string | null;
|
|
21
|
+
instructions: string | null;
|
|
22
|
+
skills: string[];
|
|
23
|
+
tools: string[];
|
|
24
|
+
channels: {
|
|
25
|
+
name: string;
|
|
26
|
+
kind: string | null;
|
|
27
|
+
method: string | null;
|
|
28
|
+
urlPath: string | null;
|
|
29
|
+
}[];
|
|
30
|
+
messaging: {
|
|
31
|
+
create: string;
|
|
32
|
+
continue: string;
|
|
33
|
+
stream: string;
|
|
34
|
+
};
|
|
35
|
+
artifacts: {
|
|
36
|
+
compiledManifest: string;
|
|
37
|
+
discoveryManifest: string;
|
|
38
|
+
diagnostics: string;
|
|
39
|
+
moduleMap: string;
|
|
40
|
+
metadata: string;
|
|
41
|
+
} | null;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Projects a structured inspection into the stable `ash info --json` contract.
|
|
45
|
+
* Tools and channels an agent relies on to verify setup come straight from the
|
|
46
|
+
* compiled manifest, so this stays in sync with what the runtime actually serves.
|
|
47
|
+
*/
|
|
48
|
+
export declare function buildApplicationInfoJson(inspection: ApplicationInspection): ApplicationInfoJson;
|
|
4
49
|
/**
|
|
5
50
|
* Writes resolved application details and the active message-route contract.
|
|
6
51
|
*/
|
|
7
|
-
export declare function printApplicationInfo(logger: CliInfoLogger, appRoot: string): Promise<void>;
|
|
52
|
+
export declare function printApplicationInfo(logger: CliInfoLogger, appRoot: string, options?: PrintApplicationInfoOptions): Promise<void>;
|
|
8
53
|
export {};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{inspectApplication}from"#services/inspect-application.js";import{createCliTheme,renderCliBanner,renderCliSection}from"#cli/ui/output.js";function pluralize(e,t){return`${e} ${t}${e===1?``:`s`}`}function formatDiscoverySummary(e,t){return`${pluralize(e,`error`)}, ${pluralize(t,`warning`)}`}function resolveCompileTone(e){switch(e){case`ready`:return`success`;case`failed`:return`danger`;default:return`warning`}}async function printApplicationInfo(
|
|
2
|
-
`))}export{printApplicationInfo};
|
|
1
|
+
import{inspectApplication}from"#services/inspect-application.js";import{createCliTheme,renderCliBanner,renderCliSection}from"#cli/ui/output.js";function buildApplicationInfoJson(e){let{application:t,compiledState:n,messaging:r}=e;return{appRoot:t.appRoot,agentRoot:n?.project.agentRoot??null,layout:n?.project.layout??null,status:n?.metadata.status??`unavailable`,diagnostics:n?{errors:n.metadata.discovery.summary.errors,warnings:n.metadata.discovery.summary.warnings}:null,model:n?.manifest.config.model.id??null,instructions:n?.manifest.instructions?.logicalPath??null,skills:(n?.manifest.skills??[]).map(e=>e.name),tools:(n?.manifest.tools??[]).map(e=>e.name),channels:(n?.manifest.channels??[]).map(e=>e.kind===`channel`?{name:e.name,kind:e.adapterKind??null,method:e.method,urlPath:e.urlPath}:{name:e.name,kind:`disabled`,method:null,urlPath:null}),messaging:{create:r.createSessionRoutePath,continue:r.continueSessionRoutePattern,stream:r.streamRoutePattern},artifacts:n?{compiledManifest:n.paths.compiledManifestPath,discoveryManifest:n.paths.discoveryManifestPath,diagnostics:n.paths.diagnosticsPath,moduleMap:n.paths.moduleMapPath,metadata:n.paths.compileMetadataPath}:null}}function pluralize(e,t){return`${e} ${t}${e===1?``:`s`}`}function formatDiscoverySummary(e,t){return`${pluralize(e,`error`)}, ${pluralize(t,`warning`)}`}function resolveCompileTone(e){switch(e){case`ready`:return`success`;case`failed`:return`danger`;default:return`warning`}}async function printApplicationInfo(i,a,o={}){let s=await inspectApplication(a);if(o.json){i.log(JSON.stringify(buildApplicationInfoJson(s),null,2));return}let c=s.compiledState,l=s.application,u=createCliTheme(),d=[{label:`App Root`,value:l.appRoot}],f=[{label:`Workflow Build`,value:l.workflowBuildDir},{label:`Output`,value:l.outputDir}],p=[];c===null?d.push({label:`Compile`,tone:`warning`,value:`unavailable`}):(d.push({label:`Agent Root`,value:c.project.agentRoot},{label:`Layout`,value:c.project.layout},{label:`Compile`,tone:resolveCompileTone(c.metadata.status),value:c.metadata.status},{label:`Diagnostics`,tone:c.metadata.discovery.summary.errors>0?`danger`:c.metadata.discovery.summary.warnings>0?`warning`:`success`,value:formatDiscoverySummary(c.metadata.discovery.summary.errors,c.metadata.discovery.summary.warnings)},{label:`Instructions`,value:c.manifest.instructions?.logicalPath??`none`},{label:`Skills`,value:pluralize(c.manifest.skills.length,`skill`)}),f.unshift({label:`Compiled Manifest`,value:c.paths.compiledManifestPath},{label:`Discovery Manifest`,value:c.paths.discoveryManifestPath},{label:`Diagnostics`,value:c.paths.diagnosticsPath},{label:`Module Map`,value:c.paths.moduleMapPath},{label:`Metadata`,value:c.paths.compileMetadataPath}),p.push(c.manifest.instructions===void 0?{label:`Instructions`,value:`No instructions prompt discovered.`}:{label:`Instructions`,value:c.manifest.instructions.logicalPath})),i.log([renderCliBanner(u,{subtitle:`Resolved application paths and the active message contract.`,title:`Ash Info`}),``,renderCliSection(u,{rows:d,title:`Application`}),``,renderCliSection(u,{rows:f,title:`Artifacts`}),...c===null?[]:[``,renderCliSection(u,{rows:p,title:`Instructions`})],``,renderCliSection(u,{rows:[{label:`Workflow ID`,value:l.workflowId},{label:`Source Dir`,value:l.workflowSourceDir},{label:`Create`,tone:`info`,value:`POST ${s.messaging.createSessionRoutePath}`},{label:`Continue`,tone:`info`,value:`POST ${s.messaging.continueSessionRoutePattern}`},{label:`Stream`,tone:`info`,value:`GET ${s.messaging.streamRoutePattern}`}],title:`Messaging`})].join(`
|
|
2
|
+
`))}export{buildApplicationInfoJson,printApplicationInfo};
|
package/dist/src/cli/run.d.ts
CHANGED
|
@@ -13,7 +13,9 @@ interface ProductionServerHandle {
|
|
|
13
13
|
}
|
|
14
14
|
interface CliRuntimeDependencies {
|
|
15
15
|
buildHost(appRoot: string): Promise<string>;
|
|
16
|
-
printApplicationInfo(logger: CliLogger, appRoot: string
|
|
16
|
+
printApplicationInfo(logger: CliLogger, appRoot: string, options?: {
|
|
17
|
+
json?: boolean;
|
|
18
|
+
}): Promise<void>;
|
|
17
19
|
runDevelopmentRepl(input: {
|
|
18
20
|
serverUrl: string;
|
|
19
21
|
}): Promise<void>;
|
package/dist/src/cli/run.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{createCliTheme,renderCliTaggedLine}from"#cli/ui/output.js";import{Command,CommanderError,InvalidArgumentError}from"#compiled/commander/index.js";import{resolveApplicationRoot}from"#internal/application/paths.js";import{resolveInstalledPackageInfo}from"#internal/application/package.js";import{parseDevelopmentServerUrl}from"#cli/dev/url.js";async function loadBuildHost(){return(await import(`#internal/nitro/host.js`)).buildApplication}async function loadPrintApplicationInfo(){return(await import(`#cli/commands/info.js`)).printApplicationInfo}async function loadRunDevelopmentRepl(){return(await import(`#cli/dev/repl.js`)).runDevelopmentRepl}async function loadRunEvalCommand(){return(await import(`#evals/cli/eval.js`)).runEvalCommand}async function loadStartHost(){return(await import(`#internal/nitro/host.js`)).startDevelopmentServer}async function loadStartProductionHost(){return(await import(`#internal/nitro/host.js`)).startProductionServer}function shouldPrintCliBootBanner(e){return e.name()===`info`||e.name()===`dev`}async function waitForShutdownSignal(e){await new Promise((t,n)=>{let r=!1,cleanup=()=>{process.off(`SIGINT`,handleSignal),process.off(`SIGTERM`,handleSignal)},handleSignal=()=>{r||(r=!0,cleanup(),e.close().then(t,n))};process.once(`SIGINT`,handleSignal),process.once(`SIGTERM`,handleSignal)})}async function waitForProductionServer(e){await Promise.race([e.wait(),waitForShutdownSignal({close:()=>e.close()})])}function parsePortOption(e){if(!/^-?\d+$/.test(e))throw new InvalidArgumentError(`Expected a numeric port, received "${e}".`);let t=Number(e);if(t<0||t>65535)throw new InvalidArgumentError(`Expected a port between 0 and 65535, received "${e}".`);return t}function hasInteractiveTerminal(){return!!(process.stdin.isTTY&&process.stdout.isTTY)}function rewriteDevelopmentUrlShorthand(e){let t=e[1];return e[0]!==`dev`||e.length!==2||t===void 0||t.startsWith(`-`)?[...e]:[`dev`,`--url`,t]}function resolveRemoteDevelopmentServerUrl(e){if(e.url){if(e.host!==void 0)throw new InvalidArgumentError(`The --host option cannot be used with --url.`);if(e.port!==void 0)throw new InvalidArgumentError(`The --port option cannot be used with --url.`);if(e.repl===!1)throw new InvalidArgumentError(`The --no-repl option cannot be used with --url.`);return e.url}}function createCliProgram(r,i){let c=resolveApplicationRoot(),l=resolveInstalledPackageInfo().version,u=new Command,d=createCliTheme();u.name(`ash`).description(`Build and run an Ash application.`).version(l).showHelpAfterError().exitOverride().hook(`preAction`,(e,t)=>{shouldPrintCliBootBanner(t)&&r.log(`Ash (v${l})`)}).configureOutput({writeErr:e=>{r.error(e.trimEnd())},writeOut:e=>{r.log(e.trimEnd())}});let f=u.command(`channels`).description(`Manage user-authored channels in the current project.`);return f.command(`add [kind]`).description(`Add channels interactively, or scaffold a channel kind (slack | web).`).option(`-f, --force`,`Overwrite existing channel files`).action(async(e,t)=>{let{runChannelsAddCommand:n}=await import(`#cli/commands/channels.js`);await n(r,c,{kind:e,options:t})}),f.command(`list`).description(`List user-authored channels in the current project.`).option(`--json`,`Output as JSON`).action(async e=>{let{runChannelsListCommand:t}=await import(`#cli/commands/channels.js`);await t(r,c,e)}),u.command(`build`).description(`Build the current Ash application.`).action(async()=>{let{loadDevelopmentEnvironmentFiles:e}=await import(`#cli/dev/environment.js`);e(c);let n=await(i.buildHost??await loadBuildHost())(c);r.log(renderCliTaggedLine(d,{message:`built output at ${n}`,tag:`build`,tone:`success`}))}),u.command(`start`).description(`Start a built Ash application.`).option(`--host <host>`,`Host interface to bind`).option(`--port <port>`,`Port to listen on (defaults to $PORT, then 3000)`,parsePortOption).action(async e=>{let{loadDevelopmentEnvironmentFiles:n}=await import(`#cli/dev/environment.js`);n(c);let a=await(i.startProductionHost??await loadStartProductionHost())(c,{host:e.host,port:e.port});r.log(renderCliTaggedLine(d,{message:`server listening at ${a.url}`,tag:`start`,tone:`success`})),await waitForProductionServer(a)}),u.command(`dev`).description(`Start the Ash development server or connect the REPL to an existing URL.`).option(`--host <host>`,`Host interface to bind`).option(`--no-repl`,`Start the server without the interactive REPL`).option(`--port <port>`,`Port to listen on (defaults to $PORT, then 3000)`,parsePortOption).option(`-u, --url <url>`,`Connect the REPL to an existing server URL`,parseDevelopmentServerUrl).addHelpText(`after`,`
|
|
1
|
+
import{createCliTheme,renderCliTaggedLine}from"#cli/ui/output.js";import{Command,CommanderError,InvalidArgumentError}from"#compiled/commander/index.js";import{resolveApplicationRoot}from"#internal/application/paths.js";import{resolveInstalledPackageInfo}from"#internal/application/package.js";import{parseDevelopmentServerUrl}from"#cli/dev/url.js";async function loadBuildHost(){return(await import(`#internal/nitro/host.js`)).buildApplication}async function loadPrintApplicationInfo(){return(await import(`#cli/commands/info.js`)).printApplicationInfo}async function loadRunDevelopmentRepl(){return(await import(`#cli/dev/repl.js`)).runDevelopmentRepl}async function loadRunEvalCommand(){return(await import(`#evals/cli/eval.js`)).runEvalCommand}async function loadStartHost(){return(await import(`#internal/nitro/host.js`)).startDevelopmentServer}async function loadStartProductionHost(){return(await import(`#internal/nitro/host.js`)).startProductionServer}function shouldPrintCliBootBanner(e){return e.name()===`info`||e.name()===`dev`}async function waitForShutdownSignal(e){await new Promise((t,n)=>{let r=!1,cleanup=()=>{process.off(`SIGINT`,handleSignal),process.off(`SIGTERM`,handleSignal)},handleSignal=()=>{r||(r=!0,cleanup(),e.close().then(t,n))};process.once(`SIGINT`,handleSignal),process.once(`SIGTERM`,handleSignal)})}async function waitForProductionServer(e){await Promise.race([e.wait(),waitForShutdownSignal({close:()=>e.close()})])}function parsePortOption(e){if(!/^-?\d+$/.test(e))throw new InvalidArgumentError(`Expected a numeric port, received "${e}".`);let t=Number(e);if(t<0||t>65535)throw new InvalidArgumentError(`Expected a port between 0 and 65535, received "${e}".`);return t}function hasInteractiveTerminal(){return!!(process.stdin.isTTY&&process.stdout.isTTY)}function rewriteDevelopmentUrlShorthand(e){let t=e[1];return e[0]!==`dev`||e.length!==2||t===void 0||t.startsWith(`-`)?[...e]:[`dev`,`--url`,t]}function resolveRemoteDevelopmentServerUrl(e){if(e.url){if(e.host!==void 0)throw new InvalidArgumentError(`The --host option cannot be used with --url.`);if(e.port!==void 0)throw new InvalidArgumentError(`The --port option cannot be used with --url.`);if(e.repl===!1)throw new InvalidArgumentError(`The --no-repl option cannot be used with --url.`);return e.url}}function createCliProgram(r,i){let c=resolveApplicationRoot(),l=resolveInstalledPackageInfo().version,u=new Command,d=createCliTheme();u.name(`ash`).description(`Build and run an Ash application.`).version(l).showHelpAfterError().exitOverride().hook(`preAction`,(e,t)=>{shouldPrintCliBootBanner(t)&&r.log(`Ash (v${l})`)}).configureOutput({writeErr:e=>{r.error(e.trimEnd())},writeOut:e=>{r.log(e.trimEnd())}});let f=u.command(`channels`).description(`Manage user-authored channels in the current project.`);return f.command(`add [kind]`).description(`Add channels interactively, or scaffold a channel kind (slack | web).`).option(`-f, --force`,`Overwrite existing channel files`).option(`-y, --yes`,`Assume yes for confirmations; requires an explicit channel kind`).action(async(e,t)=>{let{runChannelsAddCommand:n}=await import(`#cli/commands/channels.js`);await n(r,c,{kind:e,options:t})}),f.command(`list`).description(`List user-authored channels in the current project.`).option(`--json`,`Output as JSON`).action(async e=>{let{runChannelsListCommand:t}=await import(`#cli/commands/channels.js`);await t(r,c,e)}),u.command(`build`).description(`Build the current Ash application.`).action(async()=>{let{loadDevelopmentEnvironmentFiles:e}=await import(`#cli/dev/environment.js`);e(c);let n=await(i.buildHost??await loadBuildHost())(c);r.log(renderCliTaggedLine(d,{message:`built output at ${n}`,tag:`build`,tone:`success`}))}),u.command(`start`).description(`Start a built Ash application.`).option(`--host <host>`,`Host interface to bind`).option(`--port <port>`,`Port to listen on (defaults to $PORT, then 3000)`,parsePortOption).action(async e=>{let{loadDevelopmentEnvironmentFiles:n}=await import(`#cli/dev/environment.js`);n(c);let a=await(i.startProductionHost??await loadStartProductionHost())(c,{host:e.host,port:e.port});r.log(renderCliTaggedLine(d,{message:`server listening at ${a.url}`,tag:`start`,tone:`success`})),await waitForProductionServer(a)}),u.command(`dev`).description(`Start the Ash development server or connect the REPL to an existing URL.`).option(`--host <host>`,`Host interface to bind`).option(`--no-repl`,`Start the server without the interactive REPL`).option(`--port <port>`,`Port to listen on (defaults to $PORT, then 3000)`,parsePortOption).option(`-u, --url <url>`,`Connect the REPL to an existing server URL`,parseDevelopmentServerUrl).addHelpText(`after`,`
|
|
2
2
|
You can also pass a bare URL as the only argument, for example: ash dev https://example.com
|
|
3
|
-
`).action(async e=>{let n=resolveRemoteDevelopmentServerUrl(e),{loadDevelopmentEnvironmentFiles:a}=await import(`#cli/dev/environment.js`);if(a(c),n){if(r.log(renderCliTaggedLine(d,{message:`REPL connecting to ${n}`,tag:`dev`,tone:`info`})),!hasInteractiveTerminal()){r.log(renderCliTaggedLine(d,{message:`Interactive REPL disabled because the current terminal is not a TTY.`,tag:`dev`,tone:`warning`}));return}r.log(``),await(i.runDevelopmentRepl??await loadRunDevelopmentRepl())({serverUrl:n});return}let o=await(i.startHost??await loadStartHost())(c,{host:e.host,port:e.port}),s=!1,closeServer=async()=>{s||(s=!0,await o.close())};try{if(r.log(renderCliTaggedLine(d,{message:`server listening at ${o.url}`,tag:`dev`,tone:`success`})),e.repl===!1)return await waitForShutdownSignal({close:closeServer});if(!hasInteractiveTerminal())return r.log(renderCliTaggedLine(d,{message:`Interactive REPL disabled because the current terminal is not a TTY.`,tag:`dev`,tone:`warning`})),await waitForShutdownSignal({close:closeServer});r.log(``),await(i.runDevelopmentRepl??await loadRunDevelopmentRepl())({serverUrl:o.url})}finally{await closeServer()}}),u.command(`info`).description(`Print resolved application information.`).action(async
|
|
3
|
+
`).action(async e=>{let n=resolveRemoteDevelopmentServerUrl(e),{loadDevelopmentEnvironmentFiles:a}=await import(`#cli/dev/environment.js`);if(a(c),n){if(r.log(renderCliTaggedLine(d,{message:`REPL connecting to ${n}`,tag:`dev`,tone:`info`})),!hasInteractiveTerminal()){r.log(renderCliTaggedLine(d,{message:`Interactive REPL disabled because the current terminal is not a TTY.`,tag:`dev`,tone:`warning`}));return}r.log(``),await(i.runDevelopmentRepl??await loadRunDevelopmentRepl())({serverUrl:n});return}let o=await(i.startHost??await loadStartHost())(c,{host:e.host,port:e.port}),s=!1,closeServer=async()=>{s||(s=!0,await o.close())};try{if(r.log(renderCliTaggedLine(d,{message:`server listening at ${o.url}`,tag:`dev`,tone:`success`})),e.repl===!1)return await waitForShutdownSignal({close:closeServer});if(!hasInteractiveTerminal())return r.log(renderCliTaggedLine(d,{message:`Interactive REPL disabled because the current terminal is not a TTY.`,tag:`dev`,tone:`warning`})),await waitForShutdownSignal({close:closeServer});r.log(``),await(i.runDevelopmentRepl??await loadRunDevelopmentRepl())({serverUrl:o.url})}finally{await closeServer()}}),u.command(`info`).description(`Print resolved application information.`).option(`--json`,`Output as JSON`).action(async e=>{await(i.printApplicationInfo??await loadPrintApplicationInfo())(r,c,e)}),u.command(`eval`).description(`Run eval suites against an Ash agent.`).option(`--suite <id...>`,`Suite IDs to run (repeatable)`).option(`--all`,`Run all discovered suites`).option(`--url <url>`,`Remote agent URL (skip local host startup)`).option(`--timeout <ms>`,`Per-case timeout in milliseconds`).option(`--max-concurrency <n>`,`Max concurrent case executions per suite`).option(`--json`,`Output results as JSON`).option(`--skip-report`,`Skip suite-defined reporters (e.g. Braintrust)`).action(async e=>{await(i.runEvalCommand??await loadRunEvalCommand())(e,r)}),u}async function runCli(e=process.argv.slice(2),t=console,n={}){let i=createCliProgram(t,n),a=e.length===0?[`info`]:rewriteDevelopmentUrlShorthand(e);try{await i.parseAsync(a,{from:`user`})}catch(e){if(e instanceof CommanderError){if(e.exitCode===0)return;throw Error(e.message)}throw e}}export{runCli};
|
package/dist/src/harness/{instrumentation-metadata.d.ts → instrumentation-runtime-context.d.ts}
RENAMED
|
@@ -16,8 +16,8 @@ export interface BuildTelemetryRuntimeContextInput {
|
|
|
16
16
|
/**
|
|
17
17
|
* Builds per-model-call runtime context for AI SDK telemetry spans.
|
|
18
18
|
*
|
|
19
|
-
* Authored
|
|
20
|
-
* `ash.*` keys, and callback failures are warning-only so
|
|
19
|
+
* Authored runtime context is parsed defensively. Invalid event results,
|
|
20
|
+
* reserved `ash.*` keys, and callback failures are warning-only so
|
|
21
21
|
* instrumentation cannot compromise the normal turn flow.
|
|
22
22
|
*/
|
|
23
23
|
export declare function buildTelemetryRuntimeContext(input: BuildTelemetryRuntimeContextInput): Record<string, unknown> | undefined;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createLogger,formatError}from"#internal/logging.js";import{normalizeInstrumentationChannelKind,resolveInstrumentationProjection}from"#internal/instrumentation.js";import{AuthKey,ChannelInstrumentationKey,InitiatorAuthKey,ParentSessionKey}from"#context/keys.js";import{parseJsonObject,parseJsonValue}from"#shared/json.js";import{contextStorage}from"#context/container.js";const log=createLogger(`harness.instrumentation-runtime-context`);function buildTelemetryRuntimeContext(e){if(e.authored===void 0)return;let t=resolveStepStartedRuntimeContext(e),r=contextStorage.getStore()?.get(ChannelInstrumentationKey);return{...t,"ash.channel.kind":normalizeInstrumentationChannelKind(r?.kind),"ash.environment":e.environment,"ash.session.id":e.session.sessionId,"ash.step.index":String(e.emissionState.stepIndex),"ash.turn.id":e.emissionState.turnId,"ash.turn.sequence":String(e.emissionState.sequence),"ash.version":e.ashVersion}}function buildInstrumentationStepStartedInput(e){let t=contextStorage.getStore(),r=t?.get(ChannelInstrumentationKey);return{channel:{kind:normalizeInstrumentationChannelKind(r?.kind),metadata:snapshotForInstrumentation(r?.metadata,`channel.metadata`)??{}},modelInput:snapshotForInstrumentation(e.modelInput,`modelInput`)??{instructions:void 0,messages:[]},session:{auth:projectSessionAuth(t),id:e.session.sessionId,parent:snapshotForInstrumentation(t?.get(ParentSessionKey),`session.parent`)},step:{index:e.emissionState.stepIndex},turn:{id:e.emissionState.turnId,sequence:e.emissionState.sequence}}}function filterAuthoredRuntimeContext(e,t){let n={};for(let[r,i]of Object.entries(e)){if(r.startsWith(`ash.`)){log.warn(`ignoring reserved instrumentation runtime context key`,{key:r,source:t});continue}n[r]=i}return Object.keys(n).length>0?n:void 0}function resolveStepStartedRuntimeContext(e){let n=e.authored?.events?.[`step.started`];if(n===void 0)return;let i=`events["step.started"]`,a=resolveInstrumentationProjection({invoke:()=>n(buildInstrumentationStepStartedInput(e)),log,source:i});if(a===void 0)return;if(!(`runtimeContext`in a)){log.warn(`ignoring instrumentation event result because runtimeContext is missing`,{source:i});return}let o;try{o=parseJsonObject(a.runtimeContext)}catch(e){log.warn(`ignoring instrumentation event result because runtimeContext is invalid`,{error:formatError(e),source:i});return}return filterAuthoredRuntimeContext(o,i)}function projectSessionAuth(e){let t=e?.get(AuthKey)??null,n=e?.get(InitiatorAuthKey)??t;return{current:snapshotForInstrumentation(t,`session.auth.current`)??null,initiator:snapshotForInstrumentation(n,`session.auth.initiator`)??null}}function snapshotForInstrumentation(e,n){if(e!==void 0)try{return parseJsonValue(e)}catch(e){log.warn(`dropping non-serializable instrumentation snapshot`,{error:formatError(e),source:n});return}}export{buildTelemetryRuntimeContext};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createErrorId,createLogger,formatError,logError,recordErrorOnSpan}from"#internal/logging.js";import{createAuthorizationRequiredEvent,createCompactionCompletedEvent,createCompactionRequestedEvent,createInputRequestedEvent,createResultCompletedEvent}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{setAshAttributes}from"#runtime/attributes/emit.js";import{createRuntimeActionRequestFromToolCall,resolvePendingRuntimeActions,setPendingRuntimeActionBatch}from"#harness/runtime-actions.js";import{advanceStep,emitFailedStep,emitRecoverableFailedTurn,emitStepStarted,emitStreamContent,emitTurnEpilogue,emitTurnPreamble,getHarnessEmissionState,setHarnessEmissionState}from"#harness/emission.js";import{CODE_MODE_TOOL_NAME,loadCodeModeModule}from"#shared/code-mode.js";import{resolveAssistantStepText}from"#harness/messages.js";import{buildDynamicInstructionMessages}from"#context/dynamic-instruction-lifecycle.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{buildDynamicTools}from"#context/build-dynamic-tools.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,applySystemCacheBreakpoint,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";import{FINAL_OUTPUT_TOOL_NAME,buildFinalOutputTool}from"#runtime/framework-tools/final-output.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,a=getInstrumentationConfig();a!==void 0&&ensureOtelIntegration();let o=a===void 0?void 0:trace.getTracer(`ash`),s=t.runtimeIdentity?.agentName;async function runStep(e,t){let n;if(o&&hasStepInput(t)){let t=a?.functionId??s,r={"ash.version":ashVersion,"ash.environment":environment,"ash.session.id":e.sessionId};t&&(r[`ai.telemetry.functionId`]=t),n=o.startSpan(`ai.ash.turn`,{attributes:r})}let r=resolveStepOtelContext(o,n,e),executeStep=()=>executeStepBody(e,t,n);try{return r?await context.with(r,executeStep):await executeStep()}finally{n?.end()}}async function executeStepBody(o,c,l){let d=o;l&&(d=setTurnTraceState(d,l.spanContext()));let _=getHarnessEmissionState(d.state),y=consumeDeferredStepInput({input:c,session:d});d=y.session;let b=await resolvePendingRuntimeActions({emit:n,session:d,stepInput:y.input});if(b.outcome===`unresolved`)return{next:null,session:b.session};d=b.session;let T=resolvePendingInput({history:b.messages,resolveApprovalKey:resolveApprovalKeyFromTools(t.tools),session:d,stepInput:y.input});if(T.outcome===`unresolved`)return{next:null,session:T.session};n&&hasStepInput(c)&&(_=await emitTurnPreamble(n,c??{},_,t.runtimeIdentity),d=setHarnessEmissionState(d,_),l&&l.setAttribute(`ash.turn.id`,_.turnId)),d=T.session;let O=T.messages;if(y.input?.context!==void 0)for(let e of y.input.context)O.push({content:e,role:`user`});if(y.input?.message!==void 0&&!T.deferredMessage){let e=await stageAttachmentsToSandbox(y.input.message);O.push({content:e,role:`user`})}let k=await t.resolveModel(d.agent.modelReference),A=detectPromptCachePath(k),j=A.kind===`anthropic-direct`?getAnthropicCacheMarker():void 0,M=buildGatewayAttributionHeaders(k,t.runtimeIdentity);({messages:O,session:d}=await maybeCompact({emit:n,emissionState:_,headers:M,messages:O,model:k,onCompaction:t.onCompaction,resolveModel:t.resolveModel,session:d,telemetry:enrichTelemetry(a,s)??void 0}));let N=getApprovedTools(d),P=contextStorage.getStore(),F=await hydrateSandboxAttachments(O),I=[],L=[];for(let e of F)e.role===`system`?I.push(e):L.push(e);if(P!==void 0){I.push(...buildDynamicInstructionMessages(P));let e=P.get(PendingSkillAnnouncementKey);e!==void 0&&e.length>0&&I.push({role:`system`,content:e})}let R=L,prepareModelCallInput=e=>{let t=e?[{role:`system`,content:e}]:[],n=d.agent.system?[{role:`system`,content:d.agent.system}]:[],r=I.length>0||t.length>0?[...t,...n,...I]:void 0,i=r!==void 0&&j?applySystemCacheBreakpoint(r,j):r??d.agent.system??void 0;return{instructions:i,telemetryRuntimeContext:buildTelemetryRuntimeContext({ashVersion,authored:a,emissionState:_,environment,modelInput:{instructions:i,messages:R},session:d})}},runOneModelCall=async e=>{let{instructions:i,telemetryRuntimeContext:o}=e.preparedInput??prepareModelCallInput(e.extraSystemNote),c=t.codeMode===!0,l=await buildToolSetWithProviderTools({approvedTools:N,capabilities:t.capabilities,disabledProviderTools:e.disabledProviderTools,modelReference:d.agent.modelReference,tools:t.tools});if(P!==void 0){let e=buildDynamicTools(P);for(let t of e)l[t.name]??={description:t.description,inputSchema:t.inputSchema,execute:t.execute}}d.outputSchema!==void 0&&(l[FINAL_OUTPUT_TOOL_NAME]=buildFinalOutputTool(d.outputSchema));let u=c?(await applyCodeModeToToolSet({harnessTools:t.tools,lifecycle:n===void 0?void 0:createCodeModeLifecycle({emit:n,emissionState:_,tools:t.tools}),tools:l})).modelTools:l,f=j?applyLastToolCacheBreakpoint(u,j):u,p=resolveGatewayPinForStep({cachePath:A,modelReference:d.agent.modelReference,tools:f}),g=buildStepHooks({cachePath:A,emit:n,emissionState:_,emitStepStarted:e.suppressStepStartedEmission!==!0,gatewayPinProvider:p,marker:j,session:d}),v=new ToolLoopAgent({headers:M,instructions:i,model:k,onToolExecutionEnd:logToolExecutionError,onError(e){logError(log,`tool-loop stream error`,e.error)},onStepFinish:g.onStepFinish,prepareStep:g.prepareStep,runtimeContext:o,stopWhen:isStepCount(1),telemetry:enrichTelemetry(a,s,o),tools:f});return runModelCallWithRetries(async()=>{if(n){let e=await v.stream({messages:R}),{inlineActionResultCallIds:r,inlineToolResultParts:i}=await emitStreamContent(n,_,e.fullStream),a=await g.stepResult;return await emitStepActions(n,_,a,{excludedActionToolNames:new Set([ASK_QUESTION_TOOL_NAME,CODE_MODE_TOOL_NAME,FINAL_OUTPUT_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:R}),await g.stepResult},{sessionId:d.sessionId,turnId:_.turnId})},z=prepareModelCallInput();n&&await emitStepStarted(n,_,O);let B=await continuePendingCodeModeConnectionAuth({capabilities:t.capabilities,config:t,emit:n,emissionState:_,messages:O,runStep,session:d});if(B!==null)return B;let V=await continuePendingCodeModeApproval({capabilities:t.capabilities,config:t,emit:n,emissionState:_,messages:O,runStep,session:d});if(V!==null)return V;let H;try{H=await runOneModelCall({preparedInput:z,suppressStepStartedEmission:!0})}catch(t){let r=await attemptUnsupportedProviderToolRecovery({error:t,runOneModelCall,sessionId:d.sessionId,turnId:_.turnId});if(r.outcome===`recovered`)H=r.result;else{let t=r.error;if(l&&recordErrorOnSpan(l,t),!n)throw t;let a=classifyModelCallError(t),o=createErrorId(),s=a===`terminal`?summarizeKnownModelCallConfigError(t):null,c=s===null?summarizeKnownModelCallRequestError(t):null,f=s?.message??c?.message??toErrorMessage(t),p=extractModelCallErrorDetails(t),m=buildModelCallFailureDetails({configSummary:s,error:t,errorId:o,modelCallDetails:p,requestSummary:c}),h=buildModelCallFailureLogFields({error:t,errorId:o,modelCallDetails:p,requestSummary:c,sessionId:d.sessionId,turnId:_.turnId});return a===`terminal`?(s===null?log.error(c?.message??`model call failed terminally`,h):log.error(`${s.name}: ${s.message}`,{errorId:o,sessionId:d.sessionId,turnId:_.turnId}),await emitFailedStep(n,_,{code:`MODEL_CALL_FAILED`,details:m,message:f,sessionId:d.sessionId}),{next:{done:!0,output:``},session:d}):(log.error(c?.message??`model call failed — parking session for retry by the user`,h),_=await emitRecoverableFailedTurn(n,_,{code:`MODEL_CALL_FAILED`,details:m,message:f}),{next:null,session:setHarnessEmissionState(d,_)})}}let U=accumulateTurnUsage({previous:getTurnUsageState(d.state),turnId:_.turnId,usage:H.usage??{}});d=setTurnUsageState(d,U);let W;try{W=formatLanguageModelGatewayId(k)}catch{W=void 0}return await setAshAttributes({"$ash.model":W,"$ash.input_tokens":U.inputTokens,"$ash.output_tokens":U.outputTokens,"$ash.cache_read_tokens":U.cacheReadTokens,"$ash.tool_count":t.tools.size}),handleStepResult({config:t,emit:n,emissionState:_,promptMessages:O,result:H,runStep,session:d})}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:o}=e,{emissionState:s,session:l}=e,u=i.response.messages,d=resolveAssistantStepText(u,i.text),f={...l,compaction:createNextCompactionConfig(l.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}),g=[...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(g.length>0){let e=setPendingInputBatch({requests:g,responseMessages:u,session:{...f,history:[...r]}});return n&&(await n(createInputRequestedEvent({requests:g,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 x=(i.toolResults??[]).find(e=>isAuthorizationSignal(e.output));if(x&&isAuthorizationSignal(x.output)){let{challenges:e}=x.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 S=pruneToolResults(r),C=S!==r,w=f.compaction;C&&w.lastKnownInputTokens!==void 0&&(w={recentWindowSize:w.recentWindowSize,threshold:w.threshold});let E=[...S,...u],D={...f,compaction:w,history:E};return!(D.outputSchema!==void 0&&extractFinalOutput(i)!==void 0)&&(u.at(-1)?.role===`tool`||hasDeferredStepInput(D))?(n&&(s=advanceStep(s),D=setHarnessEmissionState(D,s)),{next:o,session:D}):t.mode===`task`?finishTaskTurn({emissionState:s,emit:n,prunedHistory:S,result:i,schema:D.outputSchema,session:D,stepOutput:d}):finishConversationTurn({emissionState:s,emit:n,prunedHistory:S,result:i,schema:D.outputSchema,session:D})}const OUTPUT_SCHEMA_NOT_FULFILLED={code:`OUTPUT_SCHEMA_NOT_FULFILLED`,message:`The agent could not produce a result matching the requested schema.`};function extractFinalOutput(e){return(e.toolCalls??[]).find(e=>e.toolName===FINAL_OUTPUT_TOOL_NAME)?.input}function persistStructuredAssistantTurn(e,t,n){return{...e,history:[...t,{content:JSON.stringify(n),role:`assistant`}],outputSchema:void 0}}async function emitStructuredResult(e,t,n,r){return await e(createResultCompletedEvent({result:n,sequence:t.sequence,stepIndex:t.stepIndex,turnId:t.turnId})),emitTurnEpilogue(e,t,r)}async function finishTaskTurn(e){let{emit:t,prunedHistory:n,result:r,schema:i,stepOutput:a}=e,{emissionState:o,session:s}=e;if(i===void 0)return t&&(o=await emitTurnEpilogue(t,o,`task`),s=setHarnessEmissionState(s,o)),{next:{done:!0,output:a??``},session:s};let c=extractFinalOutput(r);return c===void 0?(t&&await emitFailedStep(t,o,{...OUTPUT_SCHEMA_NOT_FULFILLED,sessionId:s.sessionId}),{next:{done:!0,isError:!0,output:OUTPUT_SCHEMA_NOT_FULFILLED.message},session:s}):(s=persistStructuredAssistantTurn(s,n,c),t&&(o=await emitStructuredResult(t,o,c,`task`),s=setHarnessEmissionState(s,o)),{next:{done:!0,output:JSON.stringify(c)},session:s})}async function finishConversationTurn(e){let{emit:t,prunedHistory:n,result:r,schema:i}=e,{emissionState:a,session:o}=e;if(i===void 0)return t&&(a=await emitTurnEpilogue(t,a,`conversation`),o=setHarnessEmissionState(o,a)),{next:null,session:o};let s=extractFinalOutput(r);return s===void 0?(t&&(a=await emitRecoverableFailedTurn(t,a,OUTPUT_SCHEMA_NOT_FULFILLED),o=setHarnessEmissionState(o,a)),{next:null,session:o}):(o=persistStructuredAssistantTurn(o,n,s),t&&(a=await emitStructuredResult(t,a,s,`conversation`),o=setHarnessEmissionState(o,a)),{next:null,session:o})}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{createAuthorizationRequiredEvent,createCompactionCompletedEvent,createCompactionRequestedEvent,createInputRequestedEvent,createResultCompletedEvent}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{setAshAttributes}from"#runtime/attributes/emit.js";import{createRuntimeActionRequestFromToolCall,resolvePendingRuntimeActions,setPendingRuntimeActionBatch}from"#harness/runtime-actions.js";import{advanceStep,emitFailedStep,emitRecoverableFailedTurn,emitStepStarted,emitStreamContent,emitTurnEpilogue,emitTurnPreamble,getHarnessEmissionState,setHarnessEmissionState}from"#harness/emission.js";import{CODE_MODE_TOOL_NAME,loadCodeModeModule}from"#shared/code-mode.js";import{resolveAssistantStepText}from"#harness/messages.js";import{buildDynamicInstructionMessages}from"#context/dynamic-instruction-lifecycle.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{buildDynamicTools}from"#context/build-dynamic-tools.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,applySystemCacheBreakpoint,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-runtime-context.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";import{FINAL_OUTPUT_TOOL_NAME,buildFinalOutputTool}from"#runtime/framework-tools/final-output.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,a=getInstrumentationConfig();a!==void 0&&ensureOtelIntegration();let o=a===void 0?void 0:trace.getTracer(`ash`),s=t.runtimeIdentity?.agentName;async function runStep(e,t){let n;if(o&&hasStepInput(t)){let t=a?.functionId??s,r={"ash.version":ashVersion,"ash.environment":environment,"ash.session.id":e.sessionId};t&&(r[`ai.telemetry.functionId`]=t),n=o.startSpan(`ai.ash.turn`,{attributes:r})}let r=resolveStepOtelContext(o,n,e),executeStep=()=>executeStepBody(e,t,n);try{return r?await context.with(r,executeStep):await executeStep()}finally{n?.end()}}async function executeStepBody(o,c,l){let d=o;l&&(d=setTurnTraceState(d,l.spanContext()));let _=getHarnessEmissionState(d.state),y=consumeDeferredStepInput({input:c,session:d});d=y.session;let b=await resolvePendingRuntimeActions({emit:n,session:d,stepInput:y.input});if(b.outcome===`unresolved`)return{next:null,session:b.session};d=b.session;let T=resolvePendingInput({history:b.messages,resolveApprovalKey:resolveApprovalKeyFromTools(t.tools),session:d,stepInput:y.input});if(T.outcome===`unresolved`)return{next:null,session:T.session};n&&hasStepInput(c)&&(_=await emitTurnPreamble(n,c??{},_,t.runtimeIdentity),d=setHarnessEmissionState(d,_),l&&l.setAttribute(`ash.turn.id`,_.turnId)),d=T.session;let O=T.messages;if(y.input?.context!==void 0)for(let e of y.input.context)O.push({content:e,role:`user`});if(y.input?.message!==void 0&&!T.deferredMessage){let e=await stageAttachmentsToSandbox(y.input.message);O.push({content:e,role:`user`})}let k=await t.resolveModel(d.agent.modelReference),A=detectPromptCachePath(k),j=A.kind===`anthropic-direct`?getAnthropicCacheMarker():void 0,M=buildGatewayAttributionHeaders(k,t.runtimeIdentity);({messages:O,session:d}=await maybeCompact({emit:n,emissionState:_,headers:M,messages:O,model:k,onCompaction:t.onCompaction,resolveModel:t.resolveModel,session:d,telemetry:enrichTelemetry(a,s)??void 0}));let N=getApprovedTools(d),P=contextStorage.getStore(),F=await hydrateSandboxAttachments(O),I=[],L=[];for(let e of F)e.role===`system`?I.push(e):L.push(e);if(P!==void 0){I.push(...buildDynamicInstructionMessages(P));let e=P.get(PendingSkillAnnouncementKey);e!==void 0&&e.length>0&&I.push({role:`system`,content:e})}let R=L,prepareModelCallInput=e=>{let t=e?[{role:`system`,content:e}]:[],n=d.agent.system?[{role:`system`,content:d.agent.system}]:[],r=I.length>0||t.length>0?[...t,...n,...I]:void 0,i=r!==void 0&&j?applySystemCacheBreakpoint(r,j):r??d.agent.system??void 0;return{instructions:i,telemetryRuntimeContext:buildTelemetryRuntimeContext({ashVersion,authored:a,emissionState:_,environment,modelInput:{instructions:i,messages:R},session:d})}},runOneModelCall=async e=>{let{instructions:i,telemetryRuntimeContext:o}=e.preparedInput??prepareModelCallInput(e.extraSystemNote),c=t.codeMode===!0,l=await buildToolSetWithProviderTools({approvedTools:N,capabilities:t.capabilities,disabledProviderTools:e.disabledProviderTools,modelReference:d.agent.modelReference,tools:t.tools});if(P!==void 0){let e=buildDynamicTools(P);for(let t of e)l[t.name]??={description:t.description,inputSchema:t.inputSchema,execute:t.execute}}d.outputSchema!==void 0&&(l[FINAL_OUTPUT_TOOL_NAME]=buildFinalOutputTool(d.outputSchema));let u=c?(await applyCodeModeToToolSet({harnessTools:t.tools,lifecycle:n===void 0?void 0:createCodeModeLifecycle({emit:n,emissionState:_,tools:t.tools}),tools:l})).modelTools:l,f=j?applyLastToolCacheBreakpoint(u,j):u,p=resolveGatewayPinForStep({cachePath:A,modelReference:d.agent.modelReference,tools:f}),g=buildStepHooks({cachePath:A,emit:n,emissionState:_,emitStepStarted:e.suppressStepStartedEmission!==!0,gatewayPinProvider:p,marker:j,session:d}),v=new ToolLoopAgent({headers:M,instructions:i,model:k,onToolExecutionEnd:logToolExecutionError,onError(e){logError(log,`tool-loop stream error`,e.error)},onStepFinish:g.onStepFinish,prepareStep:g.prepareStep,runtimeContext:o,stopWhen:isStepCount(1),telemetry:enrichTelemetry(a,s,o),tools:f});return runModelCallWithRetries(async()=>{if(n){let e=await v.stream({messages:R}),{inlineActionResultCallIds:r,inlineToolResultParts:i}=await emitStreamContent(n,_,e.fullStream),a=await g.stepResult;return await emitStepActions(n,_,a,{excludedActionToolNames:new Set([ASK_QUESTION_TOOL_NAME,CODE_MODE_TOOL_NAME,FINAL_OUTPUT_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:R}),await g.stepResult},{sessionId:d.sessionId,turnId:_.turnId})},z=prepareModelCallInput();n&&await emitStepStarted(n,_,O);let B=await continuePendingCodeModeConnectionAuth({capabilities:t.capabilities,config:t,emit:n,emissionState:_,messages:O,runStep,session:d});if(B!==null)return B;let V=await continuePendingCodeModeApproval({capabilities:t.capabilities,config:t,emit:n,emissionState:_,messages:O,runStep,session:d});if(V!==null)return V;let H;try{H=await runOneModelCall({preparedInput:z,suppressStepStartedEmission:!0})}catch(t){let r=await attemptUnsupportedProviderToolRecovery({error:t,runOneModelCall,sessionId:d.sessionId,turnId:_.turnId});if(r.outcome===`recovered`)H=r.result;else{let t=r.error;if(l&&recordErrorOnSpan(l,t),!n)throw t;let a=classifyModelCallError(t),o=createErrorId(),s=a===`terminal`?summarizeKnownModelCallConfigError(t):null,c=s===null?summarizeKnownModelCallRequestError(t):null,f=s?.message??c?.message??toErrorMessage(t),p=extractModelCallErrorDetails(t),m=buildModelCallFailureDetails({configSummary:s,error:t,errorId:o,modelCallDetails:p,requestSummary:c}),h=buildModelCallFailureLogFields({error:t,errorId:o,modelCallDetails:p,requestSummary:c,sessionId:d.sessionId,turnId:_.turnId});return a===`terminal`?(s===null?log.error(c?.message??`model call failed terminally`,h):log.error(`${s.name}: ${s.message}`,{errorId:o,sessionId:d.sessionId,turnId:_.turnId}),await emitFailedStep(n,_,{code:`MODEL_CALL_FAILED`,details:m,message:f,sessionId:d.sessionId}),{next:{done:!0,output:``},session:d}):(log.error(c?.message??`model call failed — parking session for retry by the user`,h),_=await emitRecoverableFailedTurn(n,_,{code:`MODEL_CALL_FAILED`,details:m,message:f}),{next:null,session:setHarnessEmissionState(d,_)})}}let U=accumulateTurnUsage({previous:getTurnUsageState(d.state),turnId:_.turnId,usage:H.usage??{}});d=setTurnUsageState(d,U);let W;try{W=formatLanguageModelGatewayId(k)}catch{W=void 0}return await setAshAttributes({"$ash.model":W,"$ash.input_tokens":U.inputTokens,"$ash.output_tokens":U.outputTokens,"$ash.cache_read_tokens":U.cacheReadTokens,"$ash.tool_count":t.tools.size}),handleStepResult({config:t,emit:n,emissionState:_,promptMessages:O,result:H,runStep,session:d})}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:o}=e,{emissionState:s,session:l}=e,u=i.response.messages,d=resolveAssistantStepText(u,i.text),f={...l,compaction:createNextCompactionConfig(l.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}),g=[...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(g.length>0){let e=setPendingInputBatch({requests:g,responseMessages:u,session:{...f,history:[...r]}});return n&&(await n(createInputRequestedEvent({requests:g,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 x=(i.toolResults??[]).find(e=>isAuthorizationSignal(e.output));if(x&&isAuthorizationSignal(x.output)){let{challenges:e}=x.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 S=pruneToolResults(r),C=S!==r,w=f.compaction;C&&w.lastKnownInputTokens!==void 0&&(w={recentWindowSize:w.recentWindowSize,threshold:w.threshold});let E=[...S,...u],D={...f,compaction:w,history:E};return!(D.outputSchema!==void 0&&extractFinalOutput(i)!==void 0)&&(u.at(-1)?.role===`tool`||hasDeferredStepInput(D))?(n&&(s=advanceStep(s),D=setHarnessEmissionState(D,s)),{next:o,session:D}):t.mode===`task`?finishTaskTurn({emissionState:s,emit:n,prunedHistory:S,result:i,schema:D.outputSchema,session:D,stepOutput:d}):finishConversationTurn({emissionState:s,emit:n,prunedHistory:S,result:i,schema:D.outputSchema,session:D})}const OUTPUT_SCHEMA_NOT_FULFILLED={code:`OUTPUT_SCHEMA_NOT_FULFILLED`,message:`The agent could not produce a result matching the requested schema.`};function extractFinalOutput(e){return(e.toolCalls??[]).find(e=>e.toolName===FINAL_OUTPUT_TOOL_NAME)?.input}function persistStructuredAssistantTurn(e,t,n){return{...e,history:[...t,{content:JSON.stringify(n),role:`assistant`}],outputSchema:void 0}}async function emitStructuredResult(e,t,n,r){return await e(createResultCompletedEvent({result:n,sequence:t.sequence,stepIndex:t.stepIndex,turnId:t.turnId})),emitTurnEpilogue(e,t,r)}async function finishTaskTurn(e){let{emit:t,prunedHistory:n,result:r,schema:i,stepOutput:a}=e,{emissionState:o,session:s}=e;if(i===void 0)return t&&(o=await emitTurnEpilogue(t,o,`task`),s=setHarnessEmissionState(s,o)),{next:{done:!0,output:a??``},session:s};let c=extractFinalOutput(r);return c===void 0?(t&&await emitFailedStep(t,o,{...OUTPUT_SCHEMA_NOT_FULFILLED,sessionId:s.sessionId}),{next:{done:!0,isError:!0,output:OUTPUT_SCHEMA_NOT_FULFILLED.message},session:s}):(s=persistStructuredAssistantTurn(s,n,c),t&&(o=await emitStructuredResult(t,o,c,`task`),s=setHarnessEmissionState(s,o)),{next:{done:!0,output:JSON.stringify(c)},session:s})}async function finishConversationTurn(e){let{emit:t,prunedHistory:n,result:r,schema:i}=e,{emissionState:a,session:o}=e;if(i===void 0)return t&&(a=await emitTurnEpilogue(t,a,`conversation`),o=setHarnessEmissionState(o,a)),{next:null,session:o};let s=extractFinalOutput(r);return s===void 0?(t&&(a=await emitRecoverableFailedTurn(t,a,OUTPUT_SCHEMA_NOT_FULFILLED),o=setHarnessEmissionState(o,a)),{next:null,session:o}):(o=persistStructuredAssistantTurn(o,n,s),t&&(a=await emitStructuredResult(t,a,s,`conversation`),o=setHarnessEmissionState(o,a)),{next:null,session:o})}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.52.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,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shared instrumentation primitives used by both the channel projection
|
|
3
3
|
* builder (`#channel/instrumentation.ts`) and the harness telemetry
|
|
4
|
-
* builder (`#harness/instrumentation-
|
|
4
|
+
* builder (`#harness/instrumentation-runtime-context.ts`).
|
|
5
5
|
*
|
|
6
6
|
* Both layers resolve a user-authored projector callback into a plain
|
|
7
7
|
* record and reason about the same channel-kind vocabulary. Keeping that
|
|
@@ -25,12 +25,13 @@ export declare function normalizeInstrumentationChannelKind(rawKind: string | un
|
|
|
25
25
|
/**
|
|
26
26
|
* Invokes a user-authored instrumentation projector defensively.
|
|
27
27
|
*
|
|
28
|
-
* Returns the JSON object the projector produced
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
28
|
+
* Returns the JSON object the projector produced. Returns `undefined`
|
|
29
|
+
* without warning for a no-op `undefined`, or with a warning when it threw,
|
|
30
|
+
* returned a `Promise`, returned an incorrect shape, or included values
|
|
31
|
+
* outside Ash's JSON contract. Every rejection path is warning-only so
|
|
32
|
+
* instrumentation can never break the turn. Per-value shaping (for example
|
|
33
|
+
* reserved-key filtering) is left to the caller, since the channel and
|
|
34
|
+
* harness expose different value shapes.
|
|
34
35
|
*/
|
|
35
36
|
export declare function resolveInstrumentationProjection(input: {
|
|
36
37
|
readonly invoke: () => unknown;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{formatError}from"#internal/logging.js";import{isPlainRecord,isThenable}from"#shared/guards.js";import{parseJsonObject}from"#shared/json.js";const FRAMEWORK_CHANNEL_KINDS=new Set([`http`,`schedule`,`subagent`]);function isInstrumentationChannelKind(e){return e.startsWith(`channel:`)||FRAMEWORK_CHANNEL_KINDS.has(e)}function normalizeInstrumentationChannelKind(e){return e!==void 0&&isInstrumentationChannelKind(e)?e:`unknown`}function resolveInstrumentationProjection(n){let{invoke:r,log:i,source:a}=n,o;try{o=r()}catch(t){i.warn(`ignoring instrumentation
|
|
1
|
+
import{formatError}from"#internal/logging.js";import{isPlainRecord,isThenable}from"#shared/guards.js";import{parseJsonObject}from"#shared/json.js";const FRAMEWORK_CHANNEL_KINDS=new Set([`http`,`schedule`,`subagent`]);function isInstrumentationChannelKind(e){return e.startsWith(`channel:`)||FRAMEWORK_CHANNEL_KINDS.has(e)}function normalizeInstrumentationChannelKind(e){return e!==void 0&&isInstrumentationChannelKind(e)?e:`unknown`}function resolveInstrumentationProjection(n){let{invoke:r,log:i,source:a}=n,o;try{o=r()}catch(t){i.warn(`ignoring instrumentation projection after projector failure`,{error:formatError(t),source:a});return}if(isThenable(o)){i.warn(`ignoring instrumentation projection because it returned a Promise`,{source:a}),Promise.resolve(o).catch(t=>{i.warn(`ignored instrumentation projection Promise rejected`,{error:formatError(t),source:a})});return}if(o!==void 0){if(!isPlainRecord(o)){i.warn(`ignoring instrumentation projection because it is not a record`,{source:a});return}try{return parseJsonObject(o)}catch(t){i.warn(`ignoring instrumentation projection because it is outside the JSON contract`,{error:formatError(t),source:a});return}}}export{isInstrumentationChannelKind,normalizeInstrumentationChannelKind,resolveInstrumentationProjection};
|
|
@@ -1,7 +1,7 @@
|
|
|
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.52.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({
|
|
5
5
|
credentials: connectSlackCredentials("slack/${e}"),
|
|
6
6
|
});
|
|
7
|
-
`}function renderWebAppTemplate(e,t){return e.replaceAll(`__ASH_INIT_APP_NAME__`,t)}function withWebVercelServices(e){let t=JSON.parse(e);if(!isJsonObject(t))throw Error(`${WEB_VERCEL_JSON_PATH} must contain a JSON object.`);let n=t.experimentalServices;if(n!==void 0&&!isJsonObject(n))throw Error(`${WEB_VERCEL_JSON_PATH} experimentalServices must contain a JSON object.`);let r={...t,$schema:typeof t.$schema==`string`?t.$schema:WEB_VERCEL_JSON_SCHEMA,experimentalServices:{...n,web:n?.web??WEB_DEFAULT_VERCEL_SERVICES.web,ash:n?.ash??WEB_DEFAULT_VERCEL_SERVICES.ash}};return JSON.stringify(t)===JSON.stringify(r)?e:`${JSON.stringify(r,null,2)}\n`}async function ensureWebVercelServices(e){if(!await pathExists(e))return await writeTextFile(e,`${JSON.stringify({$schema:WEB_VERCEL_JSON_SCHEMA,experimentalServices:WEB_DEFAULT_VERCEL_SERVICES},null,2)}\n`,{force:!0}),`written`;let t=await readFile(e,`utf8`),i=withWebVercelServices(t);return i===t?`skipped`:(await writeFile(e,i,`utf8`),`written`)}function assertStampedVersion(e,t){if(t.startsWith(`__`))throw Error(`Channel scaffold received unstamped version token (${e}=${t}). Build @vercel/ash-scaffold before using its dist entrypoint.`)}async function ensureChannel(e){switch(e.kind){case`slack`:return ensureSlackChannel({...e,kind:`slack`});case`web`:return ensureWebChannel({...e,kind:`web`})}}async function ensureWebChannel(e){let t=join(e.projectRoot,`package.json`),s=await pathExists(join(e.projectRoot,`app/page.tsx`));if(!e.force&&await hasPackageDependency(t,`next`))return{kind:`web`,action:`skipped`,skipReason:`nextjs-project`,filesWritten:[],filesSkipped:[t],packageJsonUpdated:[]};let c=await patchWebPackageJson(t,resolveWebPackageVersions(e.webPackageVersions)),l=[],u=[],d=basename(resolve(e.projectRoot)),f=join(e.projectRoot,WEB_VERCEL_JSON_PATH);await ensureWebVercelServices(f)===`written`?l.push(f):u.push(f);let p=join(e.projectRoot,PNPM_WORKSPACE_PATH);await ensurePnpmWorkspacePolicy(p)===`written`?l.push(p):u.push(p);for(let[t,i]of Object.entries(WEB_APP_TEMPLATE_FILES)){let a=join(e.projectRoot,t);if(t===`agent/channels/ash.ts`&&!e.force&&await pathExists(a)){u.push(a);continue}await writeTextFile(a,renderWebAppTemplate(i,d),{force:!0}),l.push(a)}return{kind:`web`,action:s?`overwritten`:`created`,filesWritten:l,filesSkipped:u,packageJsonUpdated:c}}async function ensureSlackChannel(e){let t=join(e.projectRoot,`agent/channels/slack.ts`),i=await pathExists(t);if(!e.force&&i)return{kind:`slack`,action:`skipped`,filesWritten:[],filesSkipped:[t],packageJsonUpdated:[]};let a=e.connectPackageVersion??`0.
|
|
7
|
+
`}function renderWebAppTemplate(e,t){return e.replaceAll(`__ASH_INIT_APP_NAME__`,t)}function withWebVercelServices(e){let t=JSON.parse(e);if(!isJsonObject(t))throw Error(`${WEB_VERCEL_JSON_PATH} must contain a JSON object.`);let n=t.experimentalServices;if(n!==void 0&&!isJsonObject(n))throw Error(`${WEB_VERCEL_JSON_PATH} experimentalServices must contain a JSON object.`);let r={...t,$schema:typeof t.$schema==`string`?t.$schema:WEB_VERCEL_JSON_SCHEMA,experimentalServices:{...n,web:n?.web??WEB_DEFAULT_VERCEL_SERVICES.web,ash:n?.ash??WEB_DEFAULT_VERCEL_SERVICES.ash}};return JSON.stringify(t)===JSON.stringify(r)?e:`${JSON.stringify(r,null,2)}\n`}async function ensureWebVercelServices(e){if(!await pathExists(e))return await writeTextFile(e,`${JSON.stringify({$schema:WEB_VERCEL_JSON_SCHEMA,experimentalServices:WEB_DEFAULT_VERCEL_SERVICES},null,2)}\n`,{force:!0}),`written`;let t=await readFile(e,`utf8`),i=withWebVercelServices(t);return i===t?`skipped`:(await writeFile(e,i,`utf8`),`written`)}function assertStampedVersion(e,t){if(t.startsWith(`__`))throw Error(`Channel scaffold received unstamped version token (${e}=${t}). Build @vercel/ash-scaffold before using its dist entrypoint.`)}async function ensureChannel(e){switch(e.kind){case`slack`:return ensureSlackChannel({...e,kind:`slack`});case`web`:return ensureWebChannel({...e,kind:`web`})}}async function ensureWebChannel(e){let t=join(e.projectRoot,`package.json`),s=await pathExists(join(e.projectRoot,`app/page.tsx`));if(!e.force&&await hasPackageDependency(t,`next`))return{kind:`web`,action:`skipped`,skipReason:`nextjs-project`,filesWritten:[],filesSkipped:[t],packageJsonUpdated:[]};let c=await patchWebPackageJson(t,resolveWebPackageVersions(e.webPackageVersions)),l=[],u=[],d=basename(resolve(e.projectRoot)),f=join(e.projectRoot,WEB_VERCEL_JSON_PATH);await ensureWebVercelServices(f)===`written`?l.push(f):u.push(f);let p=join(e.projectRoot,PNPM_WORKSPACE_PATH);await ensurePnpmWorkspacePolicy(p)===`written`?l.push(p):u.push(p);for(let[t,i]of Object.entries(WEB_APP_TEMPLATE_FILES)){let a=join(e.projectRoot,t);if(t===`agent/channels/ash.ts`&&!e.force&&await pathExists(a)){u.push(a);continue}await writeTextFile(a,renderWebAppTemplate(i,d),{force:!0}),l.push(a)}return{kind:`web`,action:s?`overwritten`:`created`,filesWritten:l,filesSkipped:u,packageJsonUpdated:c}}async function ensureSlackChannel(e){let t=join(e.projectRoot,`agent/channels/slack.ts`),i=await pathExists(t);if(!e.force&&i)return{kind:`slack`,action:`skipped`,filesWritten:[],filesSkipped:[t],packageJsonUpdated:[]};let a=e.connectPackageVersion??`0.1.1`;assertStampedVersion(`connectPackageVersion`,a);let o=await ensurePackageDependency(join(e.projectRoot,`package.json`),`@vercel/connect`,a),s=e.slackConnectorSlug??await deriveSlackConnectorSlug(e.projectRoot);return await writeTextFile(t,buildSlackTemplate(s),{force:e.force}),{kind:`slack`,action:i?`overwritten`:`created`,filesWritten:[t],filesSkipped:[],packageJsonUpdated:o,slackConnectorSlug:s}}async function listAuthoredChannels(n){let r=join(n,`agent/channels`),i;try{i=await readdir(r,{withFileTypes:!0})}catch(e){if(e.code===`ENOENT`)return[];throw e}let a=[];for(let n of i){if(n.isFile()){let t=getSupportedModuleBaseName(n.name);t!==null&&a.push(t);continue}if(n.isDirectory())try{(await readdir(join(r,n.name))).some(e=>matchesSupportedModuleBaseName(e,`connection`))&&a.push(n.name)}catch{}}return a.sort()}export{DEFAULT_SLACK_CONNECTOR_SLUG,SLACK_CHANNEL_DEFAULT_ROUTE,deriveSlackConnectorSlug,ensureChannel,listAuthoredChannels,normalizeSlackConnectorSlug};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var HumanActionRequiredError=class extends Error{action;constructor(e){super(`Human action required: \`${e.command}\` — ${e.reason}`),this.name=`HumanActionRequiredError`,this.action=e}};export{HumanActionRequiredError};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import"./files.js";import"./project.js";import"./channels.js";export{};
|
|
1
|
+
import"./files.js";import"./project.js";import"./channels.js";import"./human-action.js";export{};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{deriveSlackConnectorSlug,ensureChannel}from"../channels.js";import{createPromptCommandOutput}from"../cli/command-output.js";import{runVercel}from"../primitives/run-vercel.js";import{detectDeployment}from"../primitives/detect-deployment.js";import{deployToVercel}from"./deploy-to-vercel.js";import"../cli/channel-setup-prompter.js";import{runPnpmInstall}from"../primitives/run-pnpm.js";import{reconcileSlackUid,setupSlackbot}from"./setup-slackbot.js";const CHANNEL_OPTIONS=[{value:`web`,label:`Web Chat`},{value:`slack`,label:`Slack`,hint:`creates slackbot and deploys to Vercel`}];function selectableChannels(e){return CHANNEL_OPTIONS.map(t=>{let n=e?.[t.value];return n===void 0?t:{...t,disabled:!0,disabledReason:n}})}function createAddToAgentState(e={state:`unlinked`}){return{channels:[],webScaffolded:!1,slackScaffolded:!1,deploymentDependenciesInstalled:!1,projectLinked:e.state!==`unlinked`,vercelProjectId:e.projectId,deployed:e.state===`deployed`,deploymentPending:!1,productionUrl:e.productionUrl,slackbotCreated:!1,slackbotAttached:!1,slackConnectorUid:void 0,slackWorkspaceUrl:void 0,slackWorkspaceName:void 0}}function recordScaffoldedChannel(e,t){e.channels.includes(t)||(e.channels.push(t),e.deploymentPending=!0)}async function promptCreateSlackbot(e){return await e.select({message:`Do you want to create your slackbot?`,options:[{value:`yes`,label:`Yes`,hint:`connects Slack, then deploys`},{value:`no`,label:`No`}]})===`yes`}async function installDeploymentDependencies(e,t,
|
|
1
|
+
import{deriveSlackConnectorSlug,ensureChannel}from"../channels.js";import{HumanActionRequiredError}from"../human-action.js";import{createPromptCommandOutput}from"../cli/command-output.js";import{runVercel}from"../primitives/run-vercel.js";import{detectDeployment}from"../primitives/detect-deployment.js";import{deployToVercel}from"./deploy-to-vercel.js";import"../cli/channel-setup-prompter.js";import{runPnpmInstall}from"../primitives/run-pnpm.js";import{reconcileSlackUid,setupSlackbot}from"./setup-slackbot.js";const CHANNEL_OPTIONS=[{value:`web`,label:`Web Chat`},{value:`slack`,label:`Slack`,hint:`creates slackbot and deploys to Vercel`}];function selectableChannels(e){return CHANNEL_OPTIONS.map(t=>{let n=e?.[t.value];return n===void 0?t:{...t,disabled:!0,disabledReason:n}})}function createAddToAgentState(e={state:`unlinked`}){return{channels:[],webScaffolded:!1,slackScaffolded:!1,deploymentDependenciesInstalled:!1,projectLinked:e.state!==`unlinked`,vercelProjectId:e.projectId,deployed:e.state===`deployed`,deploymentPending:!1,productionUrl:e.productionUrl,slackbotCreated:!1,slackbotAttached:!1,slackConnectorUid:void 0,slackWorkspaceUrl:void 0,slackWorkspaceName:void 0}}function recordScaffoldedChannel(e,t){e.channels.includes(t)||(e.channels.push(t),e.deploymentPending=!0)}async function promptCreateSlackbot(e){return await e.select({message:`Do you want to create your slackbot?`,options:[{value:`yes`,label:`Yes`,hint:`connects Slack, then deploys`},{value:`no`,label:`No`}]})===`yes`}async function installDeploymentDependencies(e,t,n){if(!n.deploymentDependenciesInstalled){if(e.log.message(`Installing project dependencies before deployment (pnpm install)...`),!await runPnpmInstall(t,{onOutput:createPromptCommandOutput(e.log)}))throw Error(`Dependency installation failed. Deployment did not start.`);n.deploymentDependenciesInstalled=!0}}async function linkProjectForSlackbot(e){if(e.state.projectLinked)return;let t;if(e.linkProjectForSlackbot!==void 0)t=await e.linkProjectForSlackbot();else if(e.headless)throw new HumanActionRequiredError({kind:`vercel-link`,command:`vercel link`,reason:`Slack needs this directory linked to a Vercel project.`});else e.prompter.log.message(`Linking this directory to a Vercel project...`),t=await runVercel([`link`],{cwd:e.projectPath});if(!t)throw Error(`Vercel project linking failed. Slackbot creation did not start.`);e.state.projectLinked=!0;let r=await detectDeployment(e.projectPath);e.state.vercelProjectId=r.projectId??e.state.vercelProjectId,e.state.productionUrl=r.productionUrl??e.state.productionUrl,e.state.deployed=e.state.deployed||r.state===`deployed`}async function scaffoldSlackChannel(e,n){let{prompter:r,projectPath:i,state:a}=e;if(!a.slackScaffolded){r.log.message(`Scaffolding Slack channel files...`);let o=await ensureChannel({projectRoot:i,kind:`slack`,slackConnectorSlug:n,force:e.force});o.action===`created`||o.action===`overwritten`?r.log.success(`Scaffolded channel: slack`):r.log.info(`Channel "slack" already exists. Skipping file creation.`),a.slackScaffolded=!0}recordScaffoldedChannel(a,`slack`)}async function linkProjectForDeployment(e){let{prompter:t,projectPath:a,state:o}=e;if(o.projectLinked)return;let s=e.vercelProjectId??o.vercelProjectId,c=createPromptCommandOutput(t.log);if(s===void 0){if(e.headless)throw new HumanActionRequiredError({kind:`vercel-link`,command:`vercel link`,reason:`Deployment needs this directory linked to a Vercel project.`});if(t.log.message(`Linking this directory to a Vercel project before deployment...`),!await runVercel([`link`],{cwd:a,onOutput:c}))throw Error(`Vercel project linking failed. Deployment did not start.`)}else{if(t.log.message(`Linking this directory to Vercel project "${s}" before deployment...`),!await runVercel([`link`,`--project`,s,`--yes`],{cwd:a,onOutput:c}))throw Error(`Vercel project linking failed. Deployment did not start.`);o.vercelProjectId=s}o.projectLinked=!0}async function deployProject(e){let{prompter:t,projectPath:n,state:r}=e;if(!r.deploymentPending)return;await linkProjectForDeployment(e),await installDeploymentDependencies(t,n,r);let i=await deployToVercel(t,n,{presetDeploy:!0});if(r.deployed=i.deployed,r.productionUrl=i.productionUrl??r.productionUrl,!r.deployed)throw Error(`Deployment failed after channel setup.`);r.deploymentPending=!1}async function reconcilePreparedSlackChannel(e,t,n,r){let i=n.slackConnectorUid;if(i===void 0)throw Error(`Slack connector UID was not resolved. Slack deployment did not start.`);if(!await reconcileSlackUid(e,t,{kind:`attached`,created:!0,attached:!0,connectorUid:i},r))throw Error(`Slack connector UID update is required before deployment.`)}function recordAttachedSlackbot(e,t){e.slackbotCreated=!0,e.slackbotAttached=!0,e.slackConnectorUid=t.connectorUid,e.slackWorkspaceUrl=t.workspaceUrl,e.slackWorkspaceName=t.workspaceName}async function runAddToAgentOnce(n,r){let{prompter:i,projectPath:a,projectName:o,state:s}=n;if(n.headless&&r.includes(`slack`))throw Error(`Slack setup is interactive. Run the guided create flow with -i, or add Slack from the onboarding steps after headless scaffolding.`);if(r.includes(`web`))if(s.webScaffolded)recordScaffoldedChannel(s,`web`);else{i.log.message(`Scaffolding Web Chat channel files...`);let e=await ensureChannel({projectRoot:a,kind:`web`,force:n.force,webPackageVersions:n.webPackageVersions});e.action===`created`||e.action===`overwritten`?(i.log.success(`Scaffolded channel: web`),s.webScaffolded=!0,recordScaffoldedChannel(s,`web`)):i.log.info(`Next.js project detected. Skipping Web Chat scaffolding.`)}if(r.includes(`slack`)){let t=await deriveSlackConnectorSlug(a,o);if(s.slackbotCreated){if(!s.slackbotAttached)throw Error(`Slackbot provisioning did not attach this project. Slack channel was not added.`);s.deploymentPending&&(await scaffoldSlackChannel(n,t),await reconcilePreparedSlackChannel(i,a,s,`slack/${t}`))}else if(n.presetCreateSlackbot??await promptCreateSlackbot(i)){await linkProjectForSlackbot(n);let e=await setupSlackbot(i,a,{presetCreate:!0,slackbotName:t});if(!e.created)throw Error(`Slackbot creation failed.`);if(e.kind!==`attached`)throw Error(`Slackbot provisioning did not attach this project. Slack channel was not added.`);recordAttachedSlackbot(s,e),await scaffoldSlackChannel(n,t),await reconcilePreparedSlackChannel(i,a,s,`slack/${t}`)}else i.log.info(`Slack channel was not added because Slackbot setup was skipped.`)}}async function runAddToAgent(e){for(;;){let t=e.presetChannels??await e.prompter.multiselect({message:`Add to agent`,options:selectableChannels(e.disabledChannelReasons),required:!1});await e.validateSelectedChannels?.(t);try{await runAddToAgentOnce(e,t);return}catch(t){if(t instanceof Error&&(t.name===`WizardCancelledError`||t.name===`ChannelAddCancelledError`))throw t;let n=t instanceof Error?t.message:String(t),r=n.split(`
|
|
2
2
|
`)[0]?.trim()??n;if(e.prompter.log.error(r),e.presetChannels!==void 0)throw t}}}export{createAddToAgentState,deployProject,runAddToAgent};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{SLACK_CHANNEL_DEFAULT_ROUTE}from"../channels.js";import{createPromptCommandOutput}from"../cli/command-output.js";import{captureVercel,runVercel}from"../primitives/run-vercel.js";import{updateSlackChannelConnectorUid}from"../primitives/update-slack-channel.js";import{join}from"node:path";import{readFile}from"node:fs/promises";async function runVercelConnectCreateSlack(e,t){let n=[`connect`,`create`,`slack`,`--triggers`];return t&&n.push(`--name`,t),runVercel(n,{cwd:e})}async function readProjectId(e){try{let t=await readFile(join(e,`.vercel`,`project.json`),`utf8`),n=JSON.parse(t);return typeof n.projectId==`string`?n.projectId:void 0}catch{return}}function pickSlackConnector(e,t,n){if(typeof e!=`object`||!e)return;let r=e.clients;if(!Array.isArray(r))return;let i;for(let e of r){if(e.type!==`slack`||typeof e.uid!=`string`||typeof e.id!=`string`)continue;let r={uid:e.uid,id:e.id};if(n!==void 0&&r.uid===n)return r;let a=typeof e.createdAt==`number`?e.createdAt:0;t!==void 0&&Array.isArray(e.projects)&&e.projects.some(e=>typeof e==`object`&&!!e&&e.id===t)&&(!i||a>i.createdAt)&&(i={ref:r,createdAt:a})}return i?.ref}function parseInstallation(e){if(typeof e!=`object`||!e)return;let t=e.installations;if(!Array.isArray(t)||t.length===0)return;let n=t[0];if(typeof n.tenantUrl==`string`)return{workspaceUrl:n.tenantUrl,workspaceName:typeof n.tenantName==`string`?n.tenantName:void 0}}async function findSlackConnector(e,t,r,i){let a=await captureVercel([`connect`,`list`,`-F`,`json`,`--all-projects`],{cwd:e,onOutput:i});if(a)try{return pickSlackConnector(JSON.parse(a),t,r)}catch{return}}async function fetchInstallationInfo(e,t,r){let i=await captureVercel([`api`,`/v1/connex/clients/${t}/installations`],{cwd:e,onOutput:r});if(i)try{return parseInstallation(JSON.parse(i))}catch{return}}async function setupSlackbot(n,i,a={}){let o=a.presetCreate;if(o===void 0&&(o=await n.select({message:`Do you want to create your slackbot?`,options:[{value:`yes`,label:`Yes`},{value:`no`,label:`No`}]})===`yes`),!o)return n.log.info("Skipping slackbot creation. Run `vercel connect create slack` later when ready."),{kind:`skipped`,created:!1,attached:!1};let s=createPromptCommandOutput(n.log);if(n.log.message(`Creating a Slackbot through Vercel Connect...`),!await runVercelConnectCreateSlack(i,a.slackbotName))return{kind:`create-failed`,created:!1,attached:!1};n.log.success(`Slackbot created via Vercel Connect.`),n.log.message(`Locating the Slack connector...`);let
|
|
1
|
+
import{SLACK_CHANNEL_DEFAULT_ROUTE}from"../channels.js";import{createPromptCommandOutput}from"../cli/command-output.js";import{captureVercel,runVercel}from"../primitives/run-vercel.js";import{updateSlackChannelConnectorUid}from"../primitives/update-slack-channel.js";import{join}from"node:path";import{readFile}from"node:fs/promises";async function runVercelConnectCreateSlack(e,t){let n=[`connect`,`create`,`slack`,`--triggers`];return t&&n.push(`--name`,t),runVercel(n,{cwd:e})}async function readProjectId(e){try{let t=await readFile(join(e,`.vercel`,`project.json`),`utf8`),n=JSON.parse(t);return typeof n.projectId==`string`?n.projectId:void 0}catch{return}}function pickSlackConnector(e,t,n){if(typeof e!=`object`||!e)return;let r=e.clients;if(!Array.isArray(r))return;let i;for(let e of r){if(e.type!==`slack`||typeof e.uid!=`string`||typeof e.id!=`string`)continue;let r={uid:e.uid,id:e.id};if(n!==void 0&&r.uid===n)return r;let a=typeof e.createdAt==`number`?e.createdAt:0;t!==void 0&&Array.isArray(e.projects)&&e.projects.some(e=>typeof e==`object`&&!!e&&e.id===t)&&(!i||a>i.createdAt)&&(i={ref:r,createdAt:a})}return i?.ref}function parseInstallation(e){if(typeof e!=`object`||!e)return;let t=e.installations;if(!Array.isArray(t)||t.length===0)return;let n=t[0];if(typeof n.tenantUrl==`string`)return{workspaceUrl:n.tenantUrl,workspaceName:typeof n.tenantName==`string`?n.tenantName:void 0}}async function findSlackConnector(e,t,r,i){let a=await captureVercel([`connect`,`list`,`-F`,`json`,`--all-projects`],{cwd:e,onOutput:i});if(a)try{return pickSlackConnector(JSON.parse(a),t,r)}catch{return}}async function fetchInstallationInfo(e,t,r){let i=await captureVercel([`api`,`/v1/connex/clients/${t}/installations`],{cwd:e,onOutput:r});if(i)try{return parseInstallation(JSON.parse(i))}catch{return}}async function setupSlackbot(n,i,a={}){let o=a.presetCreate;if(o===void 0&&(o=await n.select({message:`Do you want to create your slackbot?`,options:[{value:`yes`,label:`Yes`},{value:`no`,label:`No`}]})===`yes`),!o)return n.log.info("Skipping slackbot creation. Run `vercel connect create slack` later when ready."),{kind:`skipped`,created:!1,attached:!1};let s=createPromptCommandOutput(n.log),c=await readProjectId(i),l=a.slackbotName?`slack/${a.slackbotName}`:void 0;if(n.log.message(`Creating a Slackbot through Vercel Connect...`),!await runVercelConnectCreateSlack(i,a.slackbotName))return{kind:`create-failed`,created:!1,attached:!1};n.log.success(`Slackbot created via Vercel Connect.`),n.log.message(`Locating the Slack connector...`);let u=await findSlackConnector(i,c,l,s);if(!u)return n.log.warning("Could not locate the Slack connector. Run `vercel connect list --all-projects` to find its UID."),{kind:`connector-unresolved`,created:!0,attached:!1};if(n.log.info(`Slack connector: ${u.uid}`),n.log.message(`Configuring Slack event delivery for this agent...`),await runVercel([`connect`,`detach`,u.uid,`--yes`],{cwd:i,onOutput:s}),!await runVercel([`connect`,`attach`,u.uid,`--triggers`,`--trigger-path`,SLACK_CHANNEL_DEFAULT_ROUTE,`--yes`],{cwd:i,onOutput:s}))return n.log.warning(`Could not register this project as a trigger destination. Run \`vercel connect attach ${u.uid} --triggers --trigger-path ${SLACK_CHANNEL_DEFAULT_ROUTE} --yes\` to enable event delivery.`),{kind:`attach-failed`,created:!0,attached:!1,connectorUid:u.uid};n.log.message(`Reading Slack workspace details...`);let d=await fetchInstallationInfo(i,u.id,s);return d?{kind:`attached`,created:!0,attached:!0,connectorUid:u.uid,workspaceUrl:d.workspaceUrl,workspaceName:d.workspaceName}:{kind:`attached`,created:!0,attached:!0,connectorUid:u.uid}}async function reconcileSlackUid(e,t,n,r){if(n.kind!==`attached`||n.connectorUid===r)return!0;e.log.info(`Connect assigned UID ${n.connectorUid} (expected ${r}). Updating agent/channels/slack.ts before deployment...`);let{patched:o}=await updateSlackChannelConnectorUid(join(t,`agent/channels/slack.ts`),n.connectorUid);return o?!0:(e.log.warning(`Could not patch agent/channels/slack.ts automatically. Update \`connectSlackCredentials("...")\` to \`"${n.connectorUid}"\` and run \`vercel deploy --prod\`.`),!1)}export{reconcileSlackUid,setupSlackbot};
|
|
@@ -49,8 +49,8 @@ export interface DiscordChannelCredentials extends DiscordCredentials {
|
|
|
49
49
|
*/
|
|
50
50
|
readonly webhookVerifier?: DiscordWebhookVerifier;
|
|
51
51
|
}
|
|
52
|
-
/**
|
|
53
|
-
export interface
|
|
52
|
+
/** Target accepted by `receive(discord, { target })` for proactive sessions. */
|
|
53
|
+
export interface DiscordReceiveTarget {
|
|
54
54
|
readonly channelId: string;
|
|
55
55
|
readonly conversationId?: string;
|
|
56
56
|
readonly initialMessage?: string | DiscordMessageBody;
|
|
@@ -126,7 +126,7 @@ export interface DiscordRequestOptions {
|
|
|
126
126
|
readonly method?: "DELETE" | "GET" | "PATCH" | "POST" | "PUT";
|
|
127
127
|
}
|
|
128
128
|
/** Concrete return type of {@link discordChannel}. */
|
|
129
|
-
export interface DiscordChannel extends Channel<DiscordChannelState,
|
|
129
|
+
export interface DiscordChannel extends Channel<DiscordChannelState, DiscordReceiveTarget> {
|
|
130
130
|
}
|
|
131
131
|
/** Discord channel factory for HTTP Interactions and proactive channel messages. */
|
|
132
132
|
export declare function discordChannel(config?: DiscordChannelConfig): DiscordChannel;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createLogger,logError}from"#internal/logging.js";import{parseJsonObject}from"#shared/json.js";import{POST,defineChannel}from"#public/definitions/defineChannel.js";import"#public/channels/discord/verify.js";import{callDiscordApi,createDiscordFollowupMessage,discordContinuationToken,editDiscordOriginalResponse,sendDiscordChannelMessage,splitDiscordMessageContent,triggerDiscordTypingIndicator}from"#public/channels/discord/api.js";import{buildFreeformModalResponse,deriveComponentInputResponses,deriveModalInputResponses,isDiscordFreeformComponent}from"#public/channels/discord/hitl.js";import{defaultEvents,defaultOnCommand}from"#public/channels/discord/defaults.js";import{DISCORD_INTERACTION_RESPONSE_TYPE,DISCORD_INTERACTION_TYPE,commandInteractionMessage,formatDiscordContextBlock,parseDiscordInteraction}from"#public/channels/discord/inbound.js";import{discordDeferredJson,discordJson,discordJsonBody,readMessageContent}from"#public/channels/discord/responses.js";import{verifyDiscordInbound}from"#public/channels/discord/verifyInbound.js";const log=createLogger(`discord.channel`);function discordChannel(e={}){let t=e.onCommand??defaultOnCommand,a={...defaultEvents,...e.events};return defineChannel({kindHint:`discord`,state:initialDiscordState(),context(t,n){return rebuildDiscordContext(t,n,e)},routes:[POST(e.route??`/ash/v1/discord`,async(r,{send:i,waitUntil:a})=>{let o=await verifyDiscordInbound(r,e.credentials);if(o===null)return new Response(`unauthorized`,{status:401});let s;try{s=parseJsonObject(JSON.parse(o))}catch(e){return log.warn(`inbound Discord body is not valid JSON`,{error:e}),discordJson({content:`invalid request`,ephemeral:!0})}if(s.type===DISCORD_INTERACTION_TYPE.PING)return discordJson({type:DISCORD_INTERACTION_RESPONSE_TYPE.PONG});let c=parseDiscordInteraction(s);return c===null?discordJson({content:`Unsupported Discord interaction.`,ephemeral:!0}):handleInteraction({config:e,interaction:c,onCommand:t,send:i,waitUntil:a})})],async receive(t,{send:n}){let r=t.
|
|
1
|
+
import{createLogger,logError}from"#internal/logging.js";import{parseJsonObject}from"#shared/json.js";import{POST,defineChannel}from"#public/definitions/defineChannel.js";import"#public/channels/discord/verify.js";import{callDiscordApi,createDiscordFollowupMessage,discordContinuationToken,editDiscordOriginalResponse,sendDiscordChannelMessage,splitDiscordMessageContent,triggerDiscordTypingIndicator}from"#public/channels/discord/api.js";import{buildFreeformModalResponse,deriveComponentInputResponses,deriveModalInputResponses,isDiscordFreeformComponent}from"#public/channels/discord/hitl.js";import{defaultEvents,defaultOnCommand}from"#public/channels/discord/defaults.js";import{DISCORD_INTERACTION_RESPONSE_TYPE,DISCORD_INTERACTION_TYPE,commandInteractionMessage,formatDiscordContextBlock,parseDiscordInteraction}from"#public/channels/discord/inbound.js";import{discordDeferredJson,discordJson,discordJsonBody,readMessageContent}from"#public/channels/discord/responses.js";import{verifyDiscordInbound}from"#public/channels/discord/verifyInbound.js";const log=createLogger(`discord.channel`);function discordChannel(e={}){let t=e.onCommand??defaultOnCommand,a={...defaultEvents,...e.events};return defineChannel({kindHint:`discord`,state:initialDiscordState(),context(t,n){return rebuildDiscordContext(t,n,e)},routes:[POST(e.route??`/ash/v1/discord`,async(r,{send:i,waitUntil:a})=>{let o=await verifyDiscordInbound(r,e.credentials);if(o===null)return new Response(`unauthorized`,{status:401});let s;try{s=parseJsonObject(JSON.parse(o))}catch(e){return log.warn(`inbound Discord body is not valid JSON`,{error:e}),discordJson({content:`invalid request`,ephemeral:!0})}if(s.type===DISCORD_INTERACTION_TYPE.PING)return discordJson({type:DISCORD_INTERACTION_RESPONSE_TYPE.PONG});let c=parseDiscordInteraction(s);return c===null?discordJson({content:`Unsupported Discord interaction.`,ephemeral:!0}):handleInteraction({config:e,interaction:c,onCommand:t,send:i,waitUntil:a})})],async receive(t,{send:n}){let r=t.target,i=readString(r.channelId);if(!i)throw Error(`discordChannel().receive requires target.channelId.`);let a=readString(r.conversationId),o=r.initialMessage;if(o!==void 0&&a!==void 0)throw Error("discordChannel().receive: `conversationId` and `initialMessage` are mutually exclusive.");let c=a??``,l=a!==void 0;if(o!==void 0){let t=await buildDiscordHandle({config:e,state:{...initialDiscordState(),channelId:i}}).sendChannelMessage(o);c=t.id,l=t.id.length>0}return n(t.message,{auth:t.auth,continuationToken:discordContinuationToken(i,c),state:{applicationId:null,channelId:i,conversationId:c||null,guildId:null,hasMessageAnchor:l,initialResponseSent:!0,interactionToken:null}})},events:a})}function rebuildDiscordContext(e,t,n){return{discord:buildDiscordHandle({config:n,session:t,state:e}),state:e}}function buildDiscordHandle(e){let n=e.config.api,r=e.state,i=mergeCredentials(e.config.credentials,r);function anchor(t){!t.id||r.hasMessageAnchor||(r.conversationId=t.id,r.hasMessageAnchor=!0,r.channelId&&e.session?.setContinuationToken(discordContinuationToken(r.channelId,t.id)))}async function sendViaChannel(e){let t=r.channelId??``;if(!t)throw Error(`discordChannel: missing channel id for outbound message.`);let a=await sendDiscordChannelMessage({apiBaseUrl:n?.apiBaseUrl,body:normalizePostInput(e),credentials:i,fetch:n?.fetch,channelId:t});return anchor(a),a}async function editOriginal(e){let t=r.interactionToken??``;if(!t)throw Error(`discordChannel: missing interaction token for original response edit.`);let a=await editDiscordOriginalResponse({apiBaseUrl:n?.apiBaseUrl,body:normalizePostInput(e),credentials:i,fetch:n?.fetch,interactionToken:t});return r.initialResponseSent=!0,anchor(a),a}async function followup(e){let t=r.interactionToken??``;if(!t)throw Error(`discordChannel: missing interaction token for followup message.`);let a=await createDiscordFollowupMessage({apiBaseUrl:n?.apiBaseUrl,body:normalizePostInput(e),credentials:i,fetch:n?.fetch,interactionToken:t});return anchor(a),a}async function startTyping(){let e=r.channelId??``;if(e)try{await triggerDiscordTypingIndicator({apiBaseUrl:n?.apiBaseUrl,credentials:i,fetch:n?.fetch,channelId:e})}catch(n){logError(log,`Discord typing indicator failed — swallowed`,n,{channelId:e})}}return{applicationId:r.applicationId??void 0,channelId:r.channelId??``,conversationId:r.conversationId??``,guildId:r.guildId??void 0,interactionToken:r.interactionToken??void 0,request(e,t,r){return callDiscordApi({apiBaseUrl:n?.apiBaseUrl,body:t,botToken:r?.botAuth===!0?i.botToken:void 0,fetch:n?.fetch,method:r?.method,path:e})},async post(e){let t=expandPostBodies(normalizePostInput(e)),n;for(let e of t){let t=await postOne({body:e,editOriginal,followup,sendViaChannel,state:r});n===void 0&&(n=t)}return n??{id:``,raw:null}},editOriginalResponse:editOriginal,followup,sendChannelMessage:sendViaChannel,startTyping}}async function postOne(e){if(e.state.interactionToken&&e.state.applicationId)try{return e.state.initialResponseSent?await e.followup(e.body):await e.editOriginal(e.body)}catch(e){log.warn(`Discord interaction-token delivery failed, falling back to channel message`,{error:e})}return e.sendViaChannel(e.body)}async function handleInteraction(e){return e.interaction.type===DISCORD_INTERACTION_TYPE.APPLICATION_COMMAND?handleCommandInteraction({config:e.config,interaction:e.interaction,onCommand:e.onCommand,send:e.send,waitUntil:e.waitUntil}):e.interaction.type===DISCORD_INTERACTION_TYPE.MESSAGE_COMPONENT?handleComponentInteraction({interaction:e.interaction,send:e.send,waitUntil:e.waitUntil}):handleModalSubmitInteraction({interaction:e.interaction,send:e.send,waitUntil:e.waitUntil})}async function handleCommandInteraction(e){let t=stateFromInteraction(e.interaction,{conversationId:e.interaction.id,hasMessageAnchor:!1,initialResponseSent:!1}),n={discord:buildDiscordHandle({config:e.config,state:t})},r;try{r=await e.onCommand(n,e.interaction)}catch(e){return log.error(`command handler failed`,{error:e}),discordJson({content:`The Discord command handler failed.`,ephemeral:!0})}return r==null?discordJson({content:`Command ignored.`,ephemeral:!0}):(e.waitUntil(dispatchCommand({interaction:e.interaction,result:r,send:e.send,state:t})),discordDeferredJson(r.ephemeral===!0))}function handleComponentInteraction(e){if(isDiscordFreeformComponent(e.interaction.customId)){let t=readMessageContent(e.interaction.raw);return discordJsonBody(buildFreeformModalResponse({customId:e.interaction.customId,prompt:t}))}let t=deriveComponentInputResponses(e.interaction);return t.length>0&&e.waitUntil(dispatchInputResponses({conversationId:e.interaction.messageId,inputResponses:t,interaction:e.interaction,send:e.send})),discordJsonBody({type:DISCORD_INTERACTION_RESPONSE_TYPE.DEFERRED_UPDATE_MESSAGE})}function handleModalSubmitInteraction(e){let t=deriveModalInputResponses(e.interaction);return t.length>0&&e.waitUntil(dispatchInputResponses({conversationId:e.interaction.messageId??e.interaction.id,inputResponses:t,interaction:e.interaction,send:e.send})),discordJson({content:`Answer received.`,ephemeral:!0})}async function dispatchCommand(e){let t=commandInteractionMessage(e.interaction),n=formatDiscordContextBlock({channelId:e.interaction.channelId,commandName:e.interaction.commandName,guildId:e.interaction.guildId,interactionId:e.interaction.id,userId:e.interaction.user.id,username:e.interaction.user.username}),r=e.result.context??[];try{await e.send({message:t,context:[n,...r]},{auth:e.result.auth,continuationToken:discordContinuationToken(e.interaction.channelId,e.interaction.id),state:e.state})}catch(e){log.error(`command delivery failed`,{error:e})}}async function dispatchInputResponses(e){try{await e.send({inputResponses:e.inputResponses},{auth:null,continuationToken:discordContinuationToken(e.interaction.channelId,e.conversationId),state:stateFromInteraction(e.interaction,{conversationId:e.conversationId,hasMessageAnchor:!0,initialResponseSent:!0})})}catch(e){log.error(`interaction response delivery failed`,{error:e})}}function stateFromInteraction(e,t){return{applicationId:e.applicationId,channelId:e.channelId,conversationId:t.conversationId,guildId:e.guildId??null,hasMessageAnchor:t.hasMessageAnchor,initialResponseSent:t.initialResponseSent,interactionToken:e.token}}function initialDiscordState(){return{applicationId:null,channelId:null,conversationId:null,guildId:null,hasMessageAnchor:!1,initialResponseSent:!1,interactionToken:null}}function mergeCredentials(e,t){return{applicationId:t.applicationId??e?.applicationId,botToken:e?.botToken,publicKey:e?.publicKey,webhookVerifier:e?.webhookVerifier}}function normalizePostInput(e){return typeof e==`string`?{content:e}:e}function expandPostBodies(e){return typeof e.content==`string`?splitDiscordMessageContent(e.content).map((t,n)=>n===0?{...e,content:t}:{allowed_mentions:e.allowed_mentions,content:t}):[e]}function readString(e){return typeof e==`string`&&e.length>0?e:void 0}export{discordChannel};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export type { ModelMessage } from "ai";
|
|
2
|
-
export { discordChannel, type DiscordChannel, type DiscordChannelConfig, type DiscordChannelCredentials, type DiscordChannelEvents, type DiscordChannelState, type DiscordCommandResult, type DiscordCommandResultOrPromise, type DiscordContext, type DiscordEventContext, type DiscordHandle, type
|
|
2
|
+
export { discordChannel, type DiscordChannel, type DiscordChannelConfig, type DiscordChannelCredentials, type DiscordChannelEvents, type DiscordChannelState, type DiscordCommandResult, type DiscordCommandResultOrPromise, type DiscordContext, type DiscordEventContext, type DiscordHandle, type DiscordReceiveTarget, type DiscordRequestOptions, } from "#public/channels/discord/discordChannel.js";
|
|
3
3
|
export { callDiscordApi, createDiscordFollowupMessage, discordContinuationToken, DISCORD_MESSAGE_CONTENT_MAX_LENGTH, DISCORD_NO_MENTIONS, editDiscordOriginalResponse, resolveDiscordApplicationId, resolveDiscordBotToken, resolveDiscordPublicKey, sendDiscordChannelMessage, splitDiscordMessageContent, triggerDiscordTypingIndicator, type DiscordApiOptions, type DiscordApiResponse, type DiscordApplicationId, type DiscordBotToken, type DiscordCredentials, type DiscordFetch, type DiscordMessageBody, type DiscordPostedMessage, } from "#public/channels/discord/api.js";
|
|
4
4
|
export { DISCORD_EPHEMERAL_MESSAGE_FLAG, DISCORD_INTERACTION_RESPONSE_TYPE, DISCORD_INTERACTION_TYPE, commandInteractionMessage, formatDiscordContextBlock, parseDiscordInteraction, type DiscordCommandInteraction, type DiscordCommandOption, type DiscordComponentInteraction, type DiscordInboundContext, type DiscordInteraction, type DiscordInteractionBase, type DiscordMember, type DiscordModalSubmitInteraction, type DiscordUser, } from "#public/channels/discord/inbound.js";
|
|
5
5
|
export { DISCORD_COMPONENT_TYPE, DISCORD_HITL_CUSTOM_ID_PREFIX, DISCORD_HITL_FREEFORM_CUSTOM_ID_PREFIX, DISCORD_HITL_FREEFORM_TEXT_INPUT_ID, buildFreeformModalResponse, deriveComponentInputResponses, deriveModalInputResponses, isDiscordFreeformComponent, renderInputRequestComponents, } from "#public/channels/discord/hitl.js";
|