experimental-ash 0.50.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 +15 -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 +47 -6
- 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/docs/public/tools.mdx +5 -40
- 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/execution/node-step.js +1 -1
- 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/harness/tool-result-pruning.d.ts +2 -16
- package/dist/src/harness/tool-result-pruning.js +1 -1
- package/dist/src/harness/types.d.ts +0 -9
- package/dist/src/internal/application/package.js +1 -1
- package/dist/src/internal/authored-definition/schema-backed.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/ash.d.ts +59 -1
- package/dist/src/public/channels/ash.js +2 -2
- 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/definitions/tool.d.ts +0 -40
- package/dist/src/public/instrumentation/index.d.ts +21 -11
- package/dist/src/public/schedules/index.d.ts +1 -1
- package/dist/src/public/tools/index.d.ts +1 -1
- package/dist/src/runtime/framework-tools/skill.js +1 -1
- package/dist/src/runtime/resolve-tool.d.ts +1 -1
- package/dist/src/runtime/resolve-tool.js +1 -1
- package/dist/src/runtime/types.d.ts +1 -9
- 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 +1 @@
|
|
|
1
|
-
import{createLogger}from"#internal/logging.js";import{POST,defineChannel}from"#public/definitions/defineChannel.js";import{verifyTwilioRequest}from"#public/channels/twilio/verify.js";import{callTwilioApi,sendTwilioMessage,twilioContinuationToken,updateTwilioCall}from"#public/channels/twilio/api.js";import{emptyTwilioResponse,gatherSpeechTwilioResponse,sayTwilioResponse}from"#public/channels/twilio/twiml.js";import{defaultEvents,defaultOnText,defaultOnVoice,defaultOnVoiceTranscription}from"#public/channels/twilio/defaults.js";import{formatTwilioContextBlock,parseTwilioTextMessage,parseTwilioVoiceCall,parseTwilioVoiceTranscription}from"#public/channels/twilio/inbound.js";const log=createLogger(`twilio.channel`);function twilioChannel(e){assertAllowFromConfigured(e);let r=buildRoutes(e.route??`/ash/v1/twilio`),i=e.onText??defaultOnText,a=e.onVoice??defaultOnVoice,s=e.onVoiceTranscription??defaultOnVoiceTranscription,l={...defaultEvents,...e.events};return defineChannel({kindHint:`twilio`,state:{from:null,to:null,lastCallSid:null,lastMessageSid:null},metadata(e){return{from:e.from,lastCallSid:e.lastCallSid??null,lastMessageSid:e.lastMessageSid??null,to:e.to}},context(t,n){return rebuildTwilioContext(t,n,e)},routes:[POST(r.messages,async(t,{send:n,waitUntil:r})=>{let a=await verifyInbound(t,e);if(a===null)return new Response(`unauthorized`,{status:401});let o=parseTwilioTextMessage(a.params);return o?await isAllowed(o.from,e.allowFrom)?(r(dispatchText({config:e,message:o,onText:i,send:n})),emptyTwilioResponse()):new Response(`forbidden`,{status:403}):emptyTwilioResponse()}),POST(r.voice,async t=>{let n=await verifyInbound(t,e);if(n===null)return new Response(`unauthorized`,{status:401});let i=parseTwilioVoiceCall(n.params);if(!i)return sayTwilioResponse(`Missing caller information.`);if(!await isAllowed(i.from,e.allowFrom))return new Response(`forbidden`,{status:403});let o=await acceptVoiceCall({call:i,config:e,onVoice:a});if(o===null)return new Response(`forbidden`,{status:403});let s=o??{};return gatherSpeechTwilioResponse({actionUrl:await buildActionUrl(t,e,r.transcription),hints:s.hints??e.voice?.hints,language:s.language??e.voice?.language,profanityFilter:s.profanityFilter??e.voice?.profanityFilter,prompt:s.prompt??e.voice?.prompt??`Please say your message after the tone.`,speechModel:s.speechModel??e.voice?.speechModel,speechTimeout:s.speechTimeout??e.voice?.speechTimeout??`auto`,timeoutSeconds:s.timeoutSeconds??e.voice?.timeoutSeconds,voice:s.voice??e.voice?.voice})}),POST(r.transcription,async(t,{send:n,waitUntil:i})=>{let a=await verifyInbound(t,e);if(a===null)return new Response(`unauthorized`,{status:401});let o=parseTwilioVoiceTranscription(a.params);return o?await isAllowed(o.from,e.allowFrom)?(i(dispatchVoiceTranscription({config:e,onVoiceTranscription:s,send:n,transcription:o})),sayTwilioResponse(e.voice?.acknowledgement??`Thanks. I'll follow up by text.`)):new Response(`forbidden`,{status:403}):gatherSpeechTwilioResponse({actionUrl:await buildActionUrl(t,e,r.transcription),language:e.voice?.language,prompt:e.voice?.prompt??`Please say your message after the tone.`,speechTimeout:e.voice?.speechTimeout??`auto`,timeoutSeconds:e.voice?.timeoutSeconds})})],async receive(t,{send:n}){let r=readString(t.
|
|
1
|
+
import{createLogger}from"#internal/logging.js";import{POST,defineChannel}from"#public/definitions/defineChannel.js";import{verifyTwilioRequest}from"#public/channels/twilio/verify.js";import{callTwilioApi,sendTwilioMessage,twilioContinuationToken,updateTwilioCall}from"#public/channels/twilio/api.js";import{emptyTwilioResponse,gatherSpeechTwilioResponse,sayTwilioResponse}from"#public/channels/twilio/twiml.js";import{defaultEvents,defaultOnText,defaultOnVoice,defaultOnVoiceTranscription}from"#public/channels/twilio/defaults.js";import{formatTwilioContextBlock,parseTwilioTextMessage,parseTwilioVoiceCall,parseTwilioVoiceTranscription}from"#public/channels/twilio/inbound.js";const log=createLogger(`twilio.channel`);function twilioChannel(e){assertAllowFromConfigured(e);let r=buildRoutes(e.route??`/ash/v1/twilio`),i=e.onText??defaultOnText,a=e.onVoice??defaultOnVoice,s=e.onVoiceTranscription??defaultOnVoiceTranscription,l={...defaultEvents,...e.events};return defineChannel({kindHint:`twilio`,state:{from:null,to:null,lastCallSid:null,lastMessageSid:null},metadata(e){return{from:e.from,lastCallSid:e.lastCallSid??null,lastMessageSid:e.lastMessageSid??null,to:e.to}},context(t,n){return rebuildTwilioContext(t,n,e)},routes:[POST(r.messages,async(t,{send:n,waitUntil:r})=>{let a=await verifyInbound(t,e);if(a===null)return new Response(`unauthorized`,{status:401});let o=parseTwilioTextMessage(a.params);return o?await isAllowed(o.from,e.allowFrom)?(r(dispatchText({config:e,message:o,onText:i,send:n})),emptyTwilioResponse()):new Response(`forbidden`,{status:403}):emptyTwilioResponse()}),POST(r.voice,async t=>{let n=await verifyInbound(t,e);if(n===null)return new Response(`unauthorized`,{status:401});let i=parseTwilioVoiceCall(n.params);if(!i)return sayTwilioResponse(`Missing caller information.`);if(!await isAllowed(i.from,e.allowFrom))return new Response(`forbidden`,{status:403});let o=await acceptVoiceCall({call:i,config:e,onVoice:a});if(o===null)return new Response(`forbidden`,{status:403});let s=o??{};return gatherSpeechTwilioResponse({actionUrl:await buildActionUrl(t,e,r.transcription),hints:s.hints??e.voice?.hints,language:s.language??e.voice?.language,profanityFilter:s.profanityFilter??e.voice?.profanityFilter,prompt:s.prompt??e.voice?.prompt??`Please say your message after the tone.`,speechModel:s.speechModel??e.voice?.speechModel,speechTimeout:s.speechTimeout??e.voice?.speechTimeout??`auto`,timeoutSeconds:s.timeoutSeconds??e.voice?.timeoutSeconds,voice:s.voice??e.voice?.voice})}),POST(r.transcription,async(t,{send:n,waitUntil:i})=>{let a=await verifyInbound(t,e);if(a===null)return new Response(`unauthorized`,{status:401});let o=parseTwilioVoiceTranscription(a.params);return o?await isAllowed(o.from,e.allowFrom)?(i(dispatchVoiceTranscription({config:e,onVoiceTranscription:s,send:n,transcription:o})),sayTwilioResponse(e.voice?.acknowledgement??`Thanks. I'll follow up by text.`)):new Response(`forbidden`,{status:403}):gatherSpeechTwilioResponse({actionUrl:await buildActionUrl(t,e,r.transcription),language:e.voice?.language,prompt:e.voice?.prompt??`Please say your message after the tone.`,speechTimeout:e.voice?.speechTimeout??`auto`,timeoutSeconds:e.voice?.timeoutSeconds})})],async receive(t,{send:n}){let r=readString(t.target.phoneNumber);if(!r)throw Error(`twilioChannel().receive requires target.phoneNumber.`);let i=readString(t.target.from)??e.messaging?.from??null;return n(t.message,{auth:t.auth,continuationToken:twilioContinuationToken(r,i??void 0),state:{from:r,lastCallSid:null,lastMessageSid:null,to:i}})},events:l})}function rebuildTwilioContext(e,t,n){return{state:e,twilio:buildTwilioHandle({callSid:e.lastCallSid??void 0,config:n,from:e.from??``,to:e.to??void 0})}}function buildTwilioHandle(e){let t=e.config.api,n=e.config.credentials,r=e.config.messaging?.from??e.to,o=e.config.messaging?.messagingServiceSid,c=e.config.messaging?.statusCallbackUrl;return{callSid:e.callSid,from:e.from,to:e.to,request(e,r){return callTwilioApi({apiBaseUrl:t?.apiBaseUrl,body:r,credentials:n,fetch:t?.fetch,path:e})},sendMessage(i,s){return sendTwilioMessage({apiBaseUrl:t?.apiBaseUrl,body:i,credentials:n,fetch:t?.fetch,from:s?.from??r,messagingServiceSid:s?.messagingServiceSid??o,statusCallbackUrl:s?.statusCallbackUrl??c,to:s?.to??e.from})},updateCall(e,r){return updateTwilioCall({apiBaseUrl:t?.apiBaseUrl,callSid:e,credentials:n,fetch:t?.fetch,twiml:r})}}}function buildRoutes(e){let t=e.endsWith(`/`)?e.slice(0,-1):e;return{messages:`${t}/messages`,transcription:`${t}/voice/transcription`,voice:`${t}/voice`}}function assertAllowFromConfigured(e){if(e?.allowFrom===void 0)throw Error(`twilioChannel requires allowFrom. Use allowFrom: "*" to allow all numbers.`)}async function verifyInbound(e,t){try{return await verifyTwilioRequest(e,{authToken:t.credentials?.authToken,webhookUrl:t.webhookUrl})}catch(e){return log.warn(`twilio inbound verification failed`,{error:e}),null}}async function dispatchText(e){let{message:t}=e,n={twilio:buildTwilioHandle({callSid:void 0,config:e.config,from:t.from,to:t.to})},r;try{r=await e.onText(n,t)}catch(e){log.error(`text handler failed`,{error:e});return}if(r==null)return;let i=formatTwilioContextBlock({channel:`text`,from:t.from,messageSid:t.messageSid,to:t.to});try{await e.send({message:t.body,context:[i]},{auth:r.auth,continuationToken:twilioContinuationToken(t.from,t.to),state:{from:t.from,lastCallSid:null,lastMessageSid:t.messageSid??null,to:t.to??null}})}catch(e){log.error(`text delivery failed`,{error:e})}}async function acceptVoiceCall(e){let{call:t}=e,n={twilio:buildTwilioHandle({callSid:t.callSid,config:e.config,from:t.from,to:t.to})};try{return await e.onVoice(n,t)}catch(e){return log.error(`voice handler failed`,{error:e}),null}}async function dispatchVoiceTranscription(e){let{transcription:t}=e,n={twilio:buildTwilioHandle({callSid:t.callSid,config:e.config,from:t.from,to:t.to})},r;try{r=await e.onVoiceTranscription(n,t)}catch(e){log.error(`voice transcription handler failed`,{error:e});return}if(r==null)return;let i=formatTwilioContextBlock({callSid:t.callSid,channel:`voice`,from:t.from,to:t.to});try{await e.send({message:t.text,context:[i]},{auth:r.auth,continuationToken:twilioContinuationToken(t.from,t.to),state:{from:t.from,lastCallSid:t.callSid??null,lastMessageSid:null,to:t.to??null}})}catch(e){log.error(`voice transcription delivery failed`,{error:e})}}async function isAllowed(e,t){let n=typeof t==`function`?await t():t;return n===`*`?!0:typeof n==`string`?n===e:n.includes(e)}async function buildActionUrl(e,t,n){let r=typeof t.publicBaseUrl==`function`?await t.publicBaseUrl(e):t.publicBaseUrl;if(r)return new URL(n,ensureTrailingSlash(r)).toString();let i=new URL(e.url);return i.pathname=n,i.search=``,i.toString()}function ensureTrailingSlash(e){return e.endsWith(`/`)?e:`${e}/`}function readString(e){return typeof e==`string`&&e.length>0?e:void 0}export{twilioChannel};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { FetchFileResult } from "#channel/adapter.js";
|
|
2
2
|
import { CHANNEL_SENTINEL } from "#channel/compiled-channel.js";
|
|
3
|
-
import type {
|
|
3
|
+
import type { TypedReceiveTarget } from "#channel/receive-target.js";
|
|
4
4
|
import type { SessionAuthContext } from "#channel/types.js";
|
|
5
5
|
import type { HandleMessageStreamEvent } from "#protocol/message.js";
|
|
6
6
|
import type { SessionContext } from "#public/definitions/callback-context.js";
|
|
@@ -49,12 +49,12 @@ export interface ChannelEvents<TCtx = void> {
|
|
|
49
49
|
* Input passed to a channel's `receive` callback when another channel or
|
|
50
50
|
* schedule proactively routes a message to it.
|
|
51
51
|
*/
|
|
52
|
-
export interface ReceiveInput<
|
|
52
|
+
export interface ReceiveInput<TReceiveTarget = Record<string, unknown>> {
|
|
53
53
|
readonly message: string;
|
|
54
|
-
readonly
|
|
54
|
+
readonly target: Readonly<TReceiveTarget>;
|
|
55
55
|
readonly auth: SessionAuthContext | null;
|
|
56
56
|
}
|
|
57
|
-
export interface ChannelDefinition<TState = undefined, TCtx = void,
|
|
57
|
+
export interface ChannelDefinition<TState = undefined, TCtx = void, TReceiveTarget = Record<string, unknown>, TMetadata extends Record<string, unknown> = Record<string, unknown>> {
|
|
58
58
|
readonly state?: TState;
|
|
59
59
|
/**
|
|
60
60
|
* Builds the per-step channel context handed to `events` and
|
|
@@ -71,7 +71,7 @@ export interface ChannelDefinition<TState = undefined, TCtx = void, TReceiveArgs
|
|
|
71
71
|
*/
|
|
72
72
|
context?(state: NonNullable<TState>, session: SessionHandle): TCtx;
|
|
73
73
|
readonly routes: readonly RouteDefinition<TState>[];
|
|
74
|
-
receive?(input: ReceiveInput<
|
|
74
|
+
receive?(input: ReceiveInput<TReceiveTarget>, args: {
|
|
75
75
|
send: SendFn<TState>;
|
|
76
76
|
}): Promise<Session>;
|
|
77
77
|
readonly events?: ChannelEvents<TCtx>;
|
|
@@ -108,16 +108,16 @@ export interface ChannelDefinition<TState = undefined, TCtx = void, TReceiveArgs
|
|
|
108
108
|
*/
|
|
109
109
|
readonly kindHint?: string;
|
|
110
110
|
}
|
|
111
|
-
export interface Channel<TState = undefined,
|
|
111
|
+
export interface Channel<TState = undefined, TReceiveTarget = Record<string, unknown>, TMetadata extends Record<string, unknown> = Record<string, unknown>> extends TypedReceiveTarget<TReceiveTarget> {
|
|
112
112
|
readonly __kind: typeof CHANNEL_SENTINEL;
|
|
113
113
|
readonly [CHANNEL_METADATA_TYPE]?: TMetadata;
|
|
114
114
|
readonly routes: readonly {
|
|
115
115
|
method: string;
|
|
116
116
|
path: string;
|
|
117
117
|
}[];
|
|
118
|
-
readonly receive?: (input: ReceiveInput<
|
|
118
|
+
readonly receive?: (input: ReceiveInput<TReceiveTarget>, args: {
|
|
119
119
|
send: SendFn<TState>;
|
|
120
120
|
}) => Promise<Session>;
|
|
121
121
|
}
|
|
122
122
|
export type InferChannelMetadata<TChannel> = TChannel extends Channel<any, any, infer TMetadata> ? TMetadata : Record<string, unknown>;
|
|
123
|
-
export declare function defineChannel<TState = undefined, TCtx = void,
|
|
123
|
+
export declare function defineChannel<TState = undefined, TCtx = void, TReceiveTarget = Record<string, unknown>, TMetadata extends Record<string, unknown> = Record<string, unknown>>(definition: ChannelDefinition<TState, TCtx, TReceiveTarget, TMetadata>): Channel<TState, TReceiveTarget, TMetadata>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { CrossChannelReceiveFn } from "#channel/cross-channel-receive.js";
|
|
2
2
|
import type { SessionAuthContext } from "#channel/types.js";
|
|
3
3
|
import type { ExactDefinition } from "#public/definitions/exact.js";
|
|
4
|
-
export type {
|
|
4
|
+
export type { InferReceiveTarget, TypedReceiveTarget } from "#channel/receive-target.js";
|
|
5
5
|
/**
|
|
6
6
|
* Arguments handed to {@link ScheduleDefinition.run}. Tight subset of
|
|
7
7
|
* `RouteHandlerArgs` — schedules can hand work off to a channel via
|
|
@@ -72,7 +72,7 @@ export type ScheduleDefinition = {
|
|
|
72
72
|
* async run({ receive, waitUntil, appAuth }) {
|
|
73
73
|
* waitUntil(receive(slack, {
|
|
74
74
|
* message: "Post the daily standup summary.",
|
|
75
|
-
*
|
|
75
|
+
* target: { channelId: "C0123ABC" },
|
|
76
76
|
* auth: appAuth,
|
|
77
77
|
* }));
|
|
78
78
|
* },
|
|
@@ -40,36 +40,6 @@ export interface NeedsApprovalContext<TInput = Record<string, unknown>> {
|
|
|
40
40
|
readonly toolName: string;
|
|
41
41
|
}
|
|
42
42
|
export type { ToolModelOutput } from "#shared/tool-definition.js";
|
|
43
|
-
/**
|
|
44
|
-
* Result returned from a {@link ToolRetentionPolicy} function.
|
|
45
|
-
*
|
|
46
|
-
* Always a named object — the field name carries the intent so the
|
|
47
|
-
* pruner and readers of the policy can't confuse it with a raw output
|
|
48
|
-
* replacement. Room exists to grow the object (token hints, placeholder
|
|
49
|
-
* overrides) without breaking existing policies.
|
|
50
|
-
*/
|
|
51
|
-
export interface RetentionSummary {
|
|
52
|
-
/**
|
|
53
|
-
* Compact text that replaces the original tool-result output in
|
|
54
|
-
* conversation history. Should be short and information-dense; the
|
|
55
|
-
* model reads it as prose, not as structured data.
|
|
56
|
-
*/
|
|
57
|
-
readonly summary: string;
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Between-step retention policy for a tool's results.
|
|
61
|
-
*
|
|
62
|
-
* - `"auto"` (default): the pruning system replaces old results with
|
|
63
|
-
* a fixed placeholder describing that the result was dropped.
|
|
64
|
-
* - `"keep"`: the pruning system never replaces results from this tool.
|
|
65
|
-
* - `(output) => RetentionSummary`: the pruning system calls this
|
|
66
|
-
* function with the original tool output and substitutes the
|
|
67
|
-
* returned `summary` text in place of the full result.
|
|
68
|
-
*
|
|
69
|
-
* The function runs synchronously during reactive pruning and must be
|
|
70
|
-
* pure — no external I/O.
|
|
71
|
-
*/
|
|
72
|
-
export type ToolRetentionPolicy<TOutput = unknown> = "auto" | "keep" | ((output: TOutput) => RetentionSummary);
|
|
73
43
|
/**
|
|
74
44
|
* Authored tool context. Passed as the last argument to
|
|
75
45
|
* {@link ToolDefinition.execute}.
|
|
@@ -97,14 +67,6 @@ export type ToolDefinition<TInput = unknown, TOutput = unknown> = PublicToolDefi
|
|
|
97
67
|
* - {@link once} — require approval only the first time per session
|
|
98
68
|
*/
|
|
99
69
|
needsApproval?: (ctx: NeedsApprovalContext<ApprovalContextInput<TInput>>) => boolean;
|
|
100
|
-
/**
|
|
101
|
-
* Optional between-step retention policy for this tool's results.
|
|
102
|
-
*
|
|
103
|
-
* The function form receives the awaited return value of
|
|
104
|
-
* {@link execute} — strongly typed as `TOutput`. See
|
|
105
|
-
* {@link ToolRetentionPolicy}.
|
|
106
|
-
*/
|
|
107
|
-
retentionPolicy?: ToolRetentionPolicy<TOutput>;
|
|
108
70
|
/**
|
|
109
71
|
* Optional projection that controls what the model sees as the tool
|
|
110
72
|
* result. Receives the full `TOutput` from {@link execute} and returns
|
|
@@ -141,7 +103,6 @@ export declare function defineTool<TSchema extends StandardJSONSchemaV1<unknown,
|
|
|
141
103
|
inputSchema: TSchema;
|
|
142
104
|
execute(input: StandardJSONSchemaV1.InferOutput<TSchema>, ctx: ToolContext): Promise<TOutput> | TOutput;
|
|
143
105
|
needsApproval?: ToolDefinition<StandardJSONSchemaV1.InferOutput<TSchema>, unknown>["needsApproval"];
|
|
144
|
-
retentionPolicy?: ToolDefinition<unknown, TOutput>["retentionPolicy"];
|
|
145
106
|
toModelOutput?: ToolDefinition<unknown, TOutput>["toModelOutput"];
|
|
146
107
|
onCompact?: ToolDefinition<unknown, unknown>["onCompact"];
|
|
147
108
|
}): ToolDefinition<StandardJSONSchemaV1.InferOutput<TSchema>, TOutput>;
|
|
@@ -150,7 +111,6 @@ export declare function defineTool<TOutput>(definition: {
|
|
|
150
111
|
inputSchema: JsonObject;
|
|
151
112
|
execute(input: Record<string, unknown>, ctx: ToolContext): Promise<TOutput> | TOutput;
|
|
152
113
|
needsApproval?: ToolDefinition<Record<string, unknown>, unknown>["needsApproval"];
|
|
153
|
-
retentionPolicy?: ToolDefinition<unknown, TOutput>["retentionPolicy"];
|
|
154
114
|
toModelOutput?: ToolDefinition<unknown, TOutput>["toModelOutput"];
|
|
155
115
|
onCompact?: ToolDefinition<unknown, unknown>["onCompact"];
|
|
156
116
|
}): ToolDefinition<Record<string, unknown>, TOutput>;
|
|
@@ -5,6 +5,7 @@ import type { ExactDefinition } from "#public/definitions/exact.js";
|
|
|
5
5
|
import type { ModelMessage, SystemModelMessage } from "ai";
|
|
6
6
|
import type { SessionAuthContext, SessionParent } from "#channel/types.js";
|
|
7
7
|
import type { Channel } from "#public/definitions/defineChannel.js";
|
|
8
|
+
import type { JsonObject } from "#shared/json.js";
|
|
8
9
|
/**
|
|
9
10
|
* Context passed to the {@link InstrumentationDefinition.setup} callback.
|
|
10
11
|
*/
|
|
@@ -18,12 +19,12 @@ export interface InstrumentationSetupContext {
|
|
|
18
19
|
readonly agentName: string;
|
|
19
20
|
}
|
|
20
21
|
/**
|
|
21
|
-
* User-authored
|
|
22
|
+
* User-authored runtime context values attached to AI SDK telemetry spans.
|
|
22
23
|
*
|
|
23
|
-
* Keys beginning with `ash.` are reserved for framework-owned
|
|
24
|
+
* Keys beginning with `ash.` are reserved for framework-owned context
|
|
24
25
|
* and are ignored when returned from authored instrumentation.
|
|
25
26
|
*/
|
|
26
|
-
export type
|
|
27
|
+
export type InstrumentationRuntimeContext = JsonObject;
|
|
27
28
|
/**
|
|
28
29
|
* Base channel metadata shape used by framework channel kinds.
|
|
29
30
|
*/
|
|
@@ -91,12 +92,12 @@ export interface InstrumentationModelInput {
|
|
|
91
92
|
readonly messages: readonly ModelMessage[];
|
|
92
93
|
}
|
|
93
94
|
/**
|
|
94
|
-
* Input passed to `
|
|
95
|
+
* Input passed to `events["step.started"]`.
|
|
95
96
|
*
|
|
96
97
|
* The callback runs after Ash has built the final model input for this
|
|
97
98
|
* model-call attempt and before the AI SDK model call is constructed.
|
|
98
99
|
*/
|
|
99
|
-
export interface
|
|
100
|
+
export interface InstrumentationStepStartedEventInput {
|
|
100
101
|
readonly channel: InstrumentationChannel;
|
|
101
102
|
readonly modelInput: InstrumentationModelInput;
|
|
102
103
|
readonly session: InstrumentationSession;
|
|
@@ -104,14 +105,23 @@ export interface InstrumentationStepStartedMetadataInput {
|
|
|
104
105
|
readonly turn: InstrumentationTurn;
|
|
105
106
|
}
|
|
106
107
|
/**
|
|
107
|
-
*
|
|
108
|
+
* Result returned by `events["step.started"]`.
|
|
108
109
|
*/
|
|
109
|
-
export interface
|
|
110
|
+
export interface InstrumentationStepStartedEventResult {
|
|
110
111
|
/**
|
|
111
|
-
*
|
|
112
|
+
* Additional runtime context merged into AI SDK telemetry spans.
|
|
113
|
+
*/
|
|
114
|
+
readonly runtimeContext: InstrumentationRuntimeContext;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Event hooks accepted by {@link defineInstrumentation}.
|
|
118
|
+
*/
|
|
119
|
+
export interface InstrumentationEvents {
|
|
120
|
+
/**
|
|
121
|
+
* Per-attempt runtime context resolved before the model call so child spans
|
|
112
122
|
* created by the AI SDK inherit the returned values.
|
|
113
123
|
*/
|
|
114
|
-
readonly "step.started"?: (input:
|
|
124
|
+
readonly "step.started"?: (input: InstrumentationStepStartedEventInput) => InstrumentationStepStartedEventResult | undefined;
|
|
115
125
|
}
|
|
116
126
|
/**
|
|
117
127
|
* Authored instrumentation settings accepted by `defineInstrumentation`.
|
|
@@ -127,9 +137,9 @@ export interface InstrumentationDefinition {
|
|
|
127
137
|
*/
|
|
128
138
|
readonly functionId?: string;
|
|
129
139
|
/**
|
|
130
|
-
*
|
|
140
|
+
* Instrumentation event hooks.
|
|
131
141
|
*/
|
|
132
|
-
readonly
|
|
142
|
+
readonly events?: InstrumentationEvents;
|
|
133
143
|
/**
|
|
134
144
|
* Whether to record full model inputs in telemetry spans.
|
|
135
145
|
*
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Schedule authoring helpers for `agent/schedules/*` files.
|
|
3
3
|
*/
|
|
4
|
-
export { defineSchedule, type ScheduleDefinition, type ScheduleHandlerArgs, type ScheduleRunHandler, type
|
|
4
|
+
export { defineSchedule, type ScheduleDefinition, type ScheduleHandlerArgs, type ScheduleRunHandler, type TypedReceiveTarget, } from "#public/definitions/schedule.js";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Tool authoring helpers for `agent/tools/*.ts` files.
|
|
3
3
|
*/
|
|
4
|
-
export { type CompactionInput, type CompactionHookResult, type DisabledToolSentinel, defineDynamic, defineTool, disableTool, isDisabledToolSentinel, type NeedsApprovalContext, type
|
|
4
|
+
export { type CompactionInput, type CompactionHookResult, type DisabledToolSentinel, defineDynamic, defineTool, disableTool, isDisabledToolSentinel, type NeedsApprovalContext, type ToolDefinition, type ToolContext, type ToolModelOutput, } from "#public/definitions/tool.js";
|
|
5
5
|
export type { DynamicToolEntry, DynamicEvents, DynamicToolEvents, DynamicResolveContext, DynamicSentinel, DynamicToolSet, DynamicToolResult, } from "#shared/dynamic-tool-definition.js";
|
|
6
6
|
export { type SessionContext } from "#public/definitions/callback-context.js";
|
|
7
7
|
export { toolResultFrom, type MatchedConnectionResult, type MatchedToolResult, type ToolResultFromFn, } from "#public/tool-result-narrowing.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{SandboxKey}from"#context/keys.js";import{loadContext}from"#context/container.js";import{loadSkillFromSandbox}from"#runtime/skills/sandbox-access.js";async function executeLoadSkillTool(t){let n=loadContext().get(SandboxKey);if(n===void 0)throw Error(`The load_skill tool requires sandbox access on the runtime context. Ensure the step is running inside a managed runtime context with sandbox support.`);let{skill:r}=t;return await loadSkillFromSandbox(n,r)}const SKILL_TOOL_DEFINITION={description:[`Load the full instructions for one available skill by name or id.`,`Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill.`,`Loading adds the skill instructions to the current turn.`,`Choose the "skill" value from the Available skills block.`].join(` `),execute:e=>executeLoadSkillTool(e),inputSchema:{additionalProperties:!1,properties:{skill:{description:`Available skill name or id.`,type:`string`}},required:[`skill`],type:`object`},logicalPath:`ash:framework/load-skill`,name:`load_skill`,
|
|
1
|
+
import{SandboxKey}from"#context/keys.js";import{loadContext}from"#context/container.js";import{loadSkillFromSandbox}from"#runtime/skills/sandbox-access.js";async function executeLoadSkillTool(t){let n=loadContext().get(SandboxKey);if(n===void 0)throw Error(`The load_skill tool requires sandbox access on the runtime context. Ensure the step is running inside a managed runtime context with sandbox support.`);let{skill:r}=t;return await loadSkillFromSandbox(n,r)}const SKILL_TOOL_DEFINITION={description:[`Load the full instructions for one available skill by name or id.`,`Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill.`,`Loading adds the skill instructions to the current turn.`,`Choose the "skill" value from the Available skills block.`].join(` `),execute:e=>executeLoadSkillTool(e),inputSchema:{additionalProperties:!1,properties:{skill:{description:`Available skill name or id.`,type:`string`}},required:[`skill`],type:`object`},logicalPath:`ash:framework/load-skill`,name:`load_skill`,sourceId:`ash:load-skill-tool`,sourceKind:`module`};export{SKILL_TOOL_DEFINITION};
|
|
@@ -5,7 +5,7 @@ import type { ResolvedToolDefinition } from "#runtime/types.js";
|
|
|
5
5
|
* Resolves one compiled authored tool into a runtime-owned definition
|
|
6
6
|
* with live callbacks reattached from the authored module.
|
|
7
7
|
*
|
|
8
|
-
* Optional hooks (`onCompact`, `needsApproval`,
|
|
8
|
+
* Optional hooks (`onCompact`, `needsApproval`,
|
|
9
9
|
* plus an optional Standard Schema `inputSchema`) are extracted when
|
|
10
10
|
* declared and validated to have the expected shape; any type mismatch
|
|
11
11
|
* raises a {@link ResolveAgentError} so typos surface at resolve time
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{expectFunction,expectObjectRecord}from"#internal/authored-module.js";import{toErrorMessage}from"#shared/errors.js";import{ResolveAgentError,loadResolvedModuleExport}from"#runtime/resolve-helpers.js";import{registerDefinitionSource,stampDefinitionKey}from"#public/tool-result-narrowing.js";async function resolveToolDefinition(a,o
|
|
1
|
+
import{expectFunction,expectObjectRecord}from"#internal/authored-module.js";import{toErrorMessage}from"#shared/errors.js";import{ResolveAgentError,loadResolvedModuleExport}from"#runtime/resolve-helpers.js";import{registerDefinitionSource,stampDefinitionKey}from"#public/tool-result-narrowing.js";async function resolveToolDefinition(i,a,o){try{let n=expectObjectRecord(await loadResolvedModuleExport({definition:i,kindLabel:`tool`,moduleMap:a,nodeId:o}),describe(i,`to return an object`)),r={kind:`tool`,logicalPath:i.logicalPath,name:i.name},s=`tool-source:${i.sourceId}`;stampDefinitionKey(n,s),registerDefinitionSource(s,r),registerDefinitionSource(`tool:${n.description}`,r);let c=expectFunction(n.execute,describe(i,`to provide an execute function`));return{description:i.description,execute:c,exportName:i.exportName,inputSchema:i.inputSchema,logicalPath:i.logicalPath,name:i.name,sourceId:i.sourceId,sourceKind:`module`,...extractOptionalHooks(n,i)}}catch(e){throw e instanceof ResolveAgentError?e:new ResolveAgentError(`Failed to attach the tool execute function from "${i.logicalPath}": ${toErrorMessage(e)}`,{logicalPath:i.logicalPath,sourceId:i.sourceId})}}function extractOptionalHooks(t,n){let r={};return t.onCompact!==void 0&&(r.onCompact=expectFunction(t.onCompact,describe(n,`to provide an onCompact function`))),t.needsApproval!==void 0&&(r.needsApproval=expectFunction(t.needsApproval,describe(n,`to provide a needsApproval function`))),t.toModelOutput!==void 0&&(r.toModelOutput=expectFunction(t.toModelOutput,describe(n,`to provide a toModelOutput function`))),t.inputSchema!==void 0&&isFlexibleSchema(t.inputSchema)&&(r.inputStandardSchema=t.inputSchema),r}function describe(e,t){return`Expected the tool export "${e.exportName??`default`}" from "${e.logicalPath}" ${t}.`}function isFlexibleSchema(e){return typeof e==`object`&&!!e&&`~standard`in e&&typeof e[`~standard`]==`object`}export{resolveToolDefinition};
|
|
@@ -7,7 +7,7 @@ import type { ChannelMethod, RouteContext } from "#public/definitions/channel.js
|
|
|
7
7
|
import type { RouteHandler } from "#channel/routes.js";
|
|
8
8
|
import type { OutboundAuthFn } from "#public/agents/auth.js";
|
|
9
9
|
import type { StreamEventHook } from "#public/definitions/hook.js";
|
|
10
|
-
import type { CompactionInput, CompactionHookResult, NeedsApprovalContext, ToolModelOutput
|
|
10
|
+
import type { CompactionInput, CompactionHookResult, NeedsApprovalContext, ToolModelOutput } from "#public/definitions/tool.js";
|
|
11
11
|
import type { AuthorizationDefinition, HeadersDefinition, ToolFilterDefinition } from "#runtime/connections/types.js";
|
|
12
12
|
import type { CompiledWorkspaceResourceRoot } from "#compiler/manifest.js";
|
|
13
13
|
import type { WorkspaceRuntimeSpec } from "#runtime/workspace/types.js";
|
|
@@ -125,14 +125,6 @@ export type ResolvedToolDefinition = Readonly<Optional<InternalToolDefinitionWit
|
|
|
125
125
|
* than blanket.
|
|
126
126
|
*/
|
|
127
127
|
readonly approvalKey?: (toolInput: Readonly<Record<string, unknown>>) => string;
|
|
128
|
-
/**
|
|
129
|
-
* Optional retention policy for old results of this tool. Controls the
|
|
130
|
-
* behavior of the reactive pruning system when this tool's results age
|
|
131
|
-
* outside the protection window. See {@link ToolRetentionPolicy}.
|
|
132
|
-
*
|
|
133
|
-
* When omitted, the default `"auto"` policy applies.
|
|
134
|
-
*/
|
|
135
|
-
readonly retentionPolicy?: ToolRetentionPolicy;
|
|
136
128
|
/**
|
|
137
129
|
* Optional compaction hook. The execution layer invokes this for every
|
|
138
130
|
* resolved tool that declares it after the harness compacts message
|
package/package.json
CHANGED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
declare const receiveArgsMarker: unique symbol;
|
|
2
|
-
/**
|
|
3
|
-
* Structural marker attached by channel factories (e.g. `slackChannel`,
|
|
4
|
-
* `twilioChannel`) to advertise the args type their `receive()` accepts.
|
|
5
|
-
* `receive(channel, args)` helpers and the route-handler
|
|
6
|
-
* `args.receive(channel, ...)` use this marker to infer typed args
|
|
7
|
-
* from a plain channel import.
|
|
8
|
-
*/
|
|
9
|
-
export interface TypedReceiveRoute<TArgs = Record<string, unknown>> {
|
|
10
|
-
readonly [receiveArgsMarker]?: TArgs;
|
|
11
|
-
}
|
|
12
|
-
/**
|
|
13
|
-
* Extracts the receive-args type from a channel value, falling back to
|
|
14
|
-
* `Record<string, unknown>` when the channel does not advertise one.
|
|
15
|
-
*/
|
|
16
|
-
export type InferReceiveArgs<TChannel> = TChannel extends TypedReceiveRoute<infer TArgs> ? TArgs : Record<string, unknown>;
|
|
17
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
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{parseJsonValue}from"#shared/json.js";import{contextStorage}from"#context/container.js";const log=createLogger(`harness.instrumentation-metadata`);function buildTelemetryRuntimeContext(e){if(e.authored===void 0)return;let t=resolveStepStartedMetadata(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 filterAuthoredMetadata(e,t){let n={};for(let[r,i]of Object.entries(e)){if(r.startsWith(`ash.`)){log.warn(`ignoring reserved instrumentation metadata key`,{key:r,source:t});continue}if(typeof i!=`string`){log.warn(`ignoring non-string instrumentation metadata value`,{key:r,source:t,valueType:typeof i});continue}n[r]=i}return Object.keys(n).length>0?n:void 0}function resolveStepStartedMetadata(e){let t=e.authored?.metadata?.[`step.started`];if(t===void 0)return;let n=`metadata["step.started"]`,i=resolveInstrumentationProjection({invoke:()=>t(buildInstrumentationStepStartedInput(e)),log,source:n});return i===void 0?void 0:filterAuthoredMetadata(i,n)}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};
|
|
File without changes
|