experimental-ash 0.16.3 → 0.18.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 +76 -0
- package/dist/docs/internals/schedule.md +37 -26
- package/dist/docs/public/channels/README.md +71 -0
- package/dist/docs/public/schedules.md +77 -49
- package/dist/src/channel/cross-channel-receive.d.ts +61 -0
- package/dist/src/channel/cross-channel-receive.js +50 -0
- package/dist/src/channel/receive-args.d.ts +17 -0
- package/dist/src/channel/receive-args.js +1 -0
- package/dist/src/channel/routes.d.ts +9 -0
- package/dist/src/channel/schedule.d.ts +45 -32
- package/dist/src/channel/schedule.js +56 -50
- package/dist/src/chunks/authored-module-loader-XcFLnl49.js +2 -0
- package/dist/src/chunks/{dev-authored-source-watcher-Tu9dhx5K.js → dev-authored-source-watcher-CG6kri3T.js} +1 -1
- package/dist/src/chunks/{host-C83crl7k.js → host-CIU0NATc.js} +6 -6
- package/dist/src/chunks/paths-CvbqpwTh.js +88 -0
- package/dist/src/chunks/{prewarm-CdxOi2uE.js → prewarm-C_Vd0JR7.js} +2 -2
- package/dist/src/cli/commands/info.js +1 -1
- package/dist/src/cli/run.js +1 -1
- package/dist/src/compiled/.vendor-stamp.json +1 -1
- package/dist/src/compiled/@vercel/sandbox/index.d.ts +8 -1
- package/dist/src/compiler/manifest.d.ts +6 -24
- package/dist/src/compiler/manifest.js +2 -8
- package/dist/src/compiler/normalize-channel.d.ts +0 -8
- package/dist/src/compiler/normalize-channel.js +0 -27
- package/dist/src/compiler/normalize-manifest.js +2 -10
- package/dist/src/compiler/normalize-schedule.d.ts +6 -12
- package/dist/src/compiler/normalize-schedule.js +9 -32
- package/dist/src/evals/cli/eval.js +1 -1
- package/dist/src/evals/runner/discover.js +1 -1
- package/dist/src/execution/sandbox/bindings/vercel.d.ts +2 -2
- package/dist/src/execution/sandbox/bindings/vercel.js +8 -1
- package/dist/src/internal/application/package.js +1 -1
- package/dist/src/internal/authored-definition/core.d.ts +3 -2
- package/dist/src/internal/authored-definition/core.js +20 -10
- package/dist/src/internal/authored-module-loader.d.ts +0 -6
- package/dist/src/internal/authored-module-loader.js +11 -72
- package/dist/src/internal/nitro/routes/agent-info/build-agent-info-response.js +3 -1
- package/dist/src/internal/nitro/routes/channel-dispatch.js +3 -0
- package/dist/src/internal/nitro/routes/runtime-stack.d.ts +0 -11
- package/dist/src/internal/nitro/routes/runtime-stack.js +0 -25
- package/dist/src/internal/nitro/routes/schedule-task.d.ts +3 -3
- package/dist/src/internal/nitro/routes/schedule-task.js +41 -11
- package/dist/src/public/channels/slack/index.d.ts +1 -1
- package/dist/src/public/channels/slack/slackChannel.d.ts +20 -1
- package/dist/src/public/channels/slack/slackChannel.js +25 -3
- package/dist/src/public/channels/twilio/twilioChannel.d.ts +2 -1
- package/dist/src/public/definitions/sandbox.d.ts +3 -3
- package/dist/src/public/definitions/schedule.d.ts +47 -50
- package/dist/src/public/definitions/schedule.js +10 -25
- package/dist/src/public/helpers/markdown.d.ts +6 -6
- package/dist/src/public/helpers/markdown.js +8 -8
- package/dist/src/public/sandbox/backends/vercel.d.ts +5 -5
- package/dist/src/public/sandbox/backends/vercel.js +3 -3
- package/dist/src/public/sandbox/index.d.ts +2 -2
- package/dist/src/public/sandbox/vercel-sandbox.d.ts +13 -0
- package/dist/src/public/schedules/index.d.ts +1 -1
- package/dist/src/public/schedules/index.js +1 -1
- package/dist/src/runtime/resolve-channel.js +1 -0
- package/dist/src/runtime/schedules/resolve-schedule.js +5 -5
- package/dist/src/runtime/types.d.ts +15 -10
- package/dist/src/shared/sandbox-backend.d.ts +7 -7
- package/dist/src/shared/sandbox-definition.d.ts +7 -12
- package/package.json +1 -1
- package/dist/src/chunks/authored-module-loader-Pt_g8xX2.js +0 -3
- package/dist/src/chunks/paths-KCBJzXn2.js +0 -88
|
@@ -1,27 +1,57 @@
|
|
|
1
|
+
import { expectScheduleRun, ScheduleDispatcher } from "#channel/schedule.js";
|
|
2
|
+
import { createWorkflowRuntime } from "#execution/workflow-runtime.js";
|
|
3
|
+
import { loadResolvedModuleExport } from "#runtime/resolve-helpers.js";
|
|
1
4
|
import { loadResolvedCompiledScheduleByTaskName } from "#runtime/schedules/resolve-schedule.js";
|
|
5
|
+
import { getCompiledRuntimeAgentBundle } from "#runtime/sessions/compiled-agent-cache.js";
|
|
2
6
|
import { resolveNitroCompiledArtifactsSource } from "#internal/nitro/routes/runtime-artifacts.js";
|
|
3
|
-
import { resolveNitroScheduleDispatcher } from "#internal/nitro/routes/runtime-stack.js";
|
|
4
7
|
/**
|
|
5
8
|
* Dispatches one Ash authored schedule via the execution engine.
|
|
6
9
|
*
|
|
7
10
|
* Fire-and-forget: the workflow runtime owns terminal completion and
|
|
8
|
-
* its own failure observability. The task return value reports
|
|
9
|
-
*
|
|
11
|
+
* its own failure observability. The task return value reports which
|
|
12
|
+
* session ids the handler started so Nitro / dev tools can correlate.
|
|
10
13
|
*/
|
|
11
14
|
export async function dispatchScheduleTask(taskName, config) {
|
|
12
15
|
const compiledArtifactsSource = resolveNitroCompiledArtifactsSource(config);
|
|
13
16
|
const schedule = await loadResolvedCompiledScheduleByTaskName(taskName, {
|
|
14
17
|
compiledArtifactsSource,
|
|
15
18
|
});
|
|
16
|
-
const
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
};
|
|
22
|
-
const
|
|
19
|
+
const bundle = await getCompiledRuntimeAgentBundle({ compiledArtifactsSource });
|
|
20
|
+
const runtime = createWorkflowRuntime({ compiledArtifactsSource });
|
|
21
|
+
const dispatcher = new ScheduleDispatcher({
|
|
22
|
+
runtime,
|
|
23
|
+
channels: bundle.graph.root.channels,
|
|
24
|
+
});
|
|
25
|
+
const dispatchInput = { scheduleId: schedule.name };
|
|
26
|
+
if (schedule.hasRun) {
|
|
27
|
+
dispatchInput.run = await loadScheduleRun(schedule, bundle.moduleMap);
|
|
28
|
+
}
|
|
29
|
+
if (schedule.markdown !== undefined) {
|
|
30
|
+
dispatchInput.markdown = schedule.markdown;
|
|
31
|
+
}
|
|
32
|
+
const result = await dispatcher.trigger(dispatchInput);
|
|
33
|
+
if (result.waitUntilTasks.length > 0) {
|
|
34
|
+
await Promise.allSettled(result.waitUntilTasks);
|
|
35
|
+
}
|
|
23
36
|
return {
|
|
24
37
|
scheduleId: schedule.name,
|
|
25
|
-
|
|
38
|
+
sessionIds: result.sessions.map((session) => session.id),
|
|
26
39
|
};
|
|
27
40
|
}
|
|
41
|
+
async function loadScheduleRun(schedule, moduleMap) {
|
|
42
|
+
if (schedule.sourceKind !== "module") {
|
|
43
|
+
throw new Error(`Schedule "${schedule.name}" claims hasRun but is not a module-backed schedule.`);
|
|
44
|
+
}
|
|
45
|
+
const moduleSchedule = schedule;
|
|
46
|
+
const exportValue = await loadResolvedModuleExport({
|
|
47
|
+
definition: {
|
|
48
|
+
exportName: moduleSchedule.exportName,
|
|
49
|
+
logicalPath: moduleSchedule.logicalPath,
|
|
50
|
+
sourceId: moduleSchedule.sourceId,
|
|
51
|
+
},
|
|
52
|
+
kindLabel: "schedule",
|
|
53
|
+
moduleMap,
|
|
54
|
+
nodeId: undefined,
|
|
55
|
+
});
|
|
56
|
+
return expectScheduleRun(exportValue, moduleSchedule.logicalPath, moduleSchedule.exportName);
|
|
57
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { slackChannel, type SlackApiResponse, type SlackBotToken, type SlackChannel, type SlackChannelConfig, type SlackChannelCredentials, type SlackChannelEvents, type SlackChannelState, type SlackContext, type SlackEventContext, type SlackHandle, type SlackInboundResult, type SlackInboundResultOrPromise, type SlackInteractionAction, type SlackMentionResult, type SlackMentionResultOrPromise, type SlackReceiveArgs, type SlackThread, type SlackWebhookVerifier, } from "#public/channels/slack/slackChannel.js";
|
|
1
|
+
export { slackChannel, type SlackApiResponse, type SlackBotToken, type SlackChannel, type SlackChannelConfig, type SlackChannelCredentials, type SlackChannelEvents, type SlackChannelState, type SlackContext, type SlackEventContext, type SlackHandle, type SlackInboundResult, type SlackInboundResultOrPromise, type SlackInitialPost, type SlackInteractionAction, type SlackMentionResult, type SlackMentionResultOrPromise, type SlackReceiveArgs, type SlackThread, type SlackWebhookVerifier, } from "#public/channels/slack/slackChannel.js";
|
|
2
2
|
export type { SlackAttachment, SlackAuthor, SlackInboundContext, SlackMessage, } from "#public/channels/slack/inbound.js";
|
|
3
3
|
export type { SlackPostInput, SlackPostedMessage, SlackThreadMessage, SlackUploadFilesOptions, SlackUploadFilesResult, } from "#public/channels/slack/api.js";
|
|
4
4
|
export { cardToBlocks, cardToFallbackText, type BlockKitBlock, } from "#public/channels/slack/blocks.js";
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import type { TypedReceiveRoute } from "#channel/receive-args.js";
|
|
1
2
|
import type { SessionAuthContext } from "#channel/types.js";
|
|
3
|
+
import type { CardElement } from "#compiled/chat/index.js";
|
|
2
4
|
import type { HandleMessageStreamEvent } from "#protocol/message.js";
|
|
3
5
|
import { type SlackBotToken, type SlackHandle, type SlackThread } from "#public/channels/slack/api.js";
|
|
4
6
|
import { type SlackMessage } from "#public/channels/slack/inbound.js";
|
|
@@ -94,6 +96,23 @@ export interface SlackChannelCredentials {
|
|
|
94
96
|
export interface SlackReceiveArgs {
|
|
95
97
|
readonly channelId: string;
|
|
96
98
|
readonly threadTs?: string;
|
|
99
|
+
/**
|
|
100
|
+
* Optional message posted into the Slack channel before the agent
|
|
101
|
+
* runs. The post becomes the thread root and the agent's first turn
|
|
102
|
+
* lands threaded under it. Useful for cross-channel handoffs where
|
|
103
|
+
* the caller wants a visible "this is why we're here" anchor before
|
|
104
|
+
* the model speaks. Mutually exclusive with {@link threadTs}.
|
|
105
|
+
*/
|
|
106
|
+
readonly initialPost?: SlackInitialPost;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Pre-agent post issued by `slackChannel().receive` when the caller
|
|
110
|
+
* provides `args.initialPost`. The shape mirrors `ctx.thread.post`'s
|
|
111
|
+
* card variant so the same `Card({...})` construction can be reused.
|
|
112
|
+
*/
|
|
113
|
+
export interface SlackInitialPost {
|
|
114
|
+
readonly card: CardElement;
|
|
115
|
+
readonly fallbackText?: string;
|
|
97
116
|
}
|
|
98
117
|
export interface SlackInteractionAction {
|
|
99
118
|
readonly actionId: string;
|
|
@@ -260,7 +279,7 @@ export interface SlackChannelConfig {
|
|
|
260
279
|
* default-export a `slackChannel(...)` call under `declaration: true`
|
|
261
280
|
* without TypeScript falling back to an internal path for `Channel`.
|
|
262
281
|
*/
|
|
263
|
-
export interface SlackChannel extends Channel<SlackChannelState> {
|
|
282
|
+
export interface SlackChannel extends Channel<SlackChannelState>, TypedReceiveRoute<SlackReceiveArgs> {
|
|
264
283
|
}
|
|
265
284
|
/**
|
|
266
285
|
* Slack channel factory. Wires up the Slack webhook route, mention
|
|
@@ -72,11 +72,33 @@ export function slackChannel(config = {}) {
|
|
|
72
72
|
}),
|
|
73
73
|
],
|
|
74
74
|
async receive(input, { send }) {
|
|
75
|
-
const
|
|
76
|
-
|
|
75
|
+
const receiveArgs = input.args;
|
|
76
|
+
const channelId = receiveArgs.channelId;
|
|
77
|
+
if (!channelId || typeof channelId !== "string") {
|
|
77
78
|
throw new Error("slackChannel().receive requires args.channelId.");
|
|
78
79
|
}
|
|
79
|
-
const
|
|
80
|
+
const requestedThreadTs = typeof receiveArgs.threadTs === "string" ? receiveArgs.threadTs : "";
|
|
81
|
+
const initialPost = receiveArgs.initialPost;
|
|
82
|
+
if (initialPost && requestedThreadTs.length > 0) {
|
|
83
|
+
throw new Error("slackChannel().receive: `threadTs` and `initialPost` are mutually exclusive.");
|
|
84
|
+
}
|
|
85
|
+
let threadTs = requestedThreadTs;
|
|
86
|
+
if (initialPost) {
|
|
87
|
+
const { thread } = buildSlackBinding({
|
|
88
|
+
botToken: config.credentials?.botToken,
|
|
89
|
+
channelId,
|
|
90
|
+
threadTs: "",
|
|
91
|
+
teamId: undefined,
|
|
92
|
+
});
|
|
93
|
+
const postInput = {
|
|
94
|
+
card: initialPost.card,
|
|
95
|
+
};
|
|
96
|
+
if (initialPost.fallbackText !== undefined) {
|
|
97
|
+
postInput.fallbackText = initialPost.fallbackText;
|
|
98
|
+
}
|
|
99
|
+
const posted = await thread.post(postInput);
|
|
100
|
+
threadTs = posted.id;
|
|
101
|
+
}
|
|
80
102
|
return send(input.message, {
|
|
81
103
|
auth: input.auth,
|
|
82
104
|
continuationToken: encodeSlackContinuationToken(channelId, threadTs),
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { TypedReceiveRoute } from "#channel/receive-args.js";
|
|
1
2
|
import type { SessionAuthContext } from "#channel/types.js";
|
|
2
3
|
import type { HandleMessageStreamEvent } from "#protocol/message.js";
|
|
3
4
|
import { type TwilioApiOptions, type TwilioApiResponse, type TwilioCredentials } from "#public/channels/twilio/api.js";
|
|
@@ -172,7 +173,7 @@ export interface TwilioSendMessageOptions {
|
|
|
172
173
|
readonly statusCallbackUrl?: string;
|
|
173
174
|
}
|
|
174
175
|
/** Concrete return type of {@link twilioChannel}. */
|
|
175
|
-
export interface TwilioChannel extends Channel<TwilioChannelState> {
|
|
176
|
+
export interface TwilioChannel extends Channel<TwilioChannelState>, TypedReceiveRoute<TwilioReceiveArgs> {
|
|
176
177
|
}
|
|
177
178
|
/** Twilio channel factory for SMS and speech-transcribed inbound calls. */
|
|
178
179
|
export declare function twilioChannel(config: TwilioChannelConfig): TwilioChannel;
|
|
@@ -2,9 +2,9 @@ import type { Optional } from "#shared/optional.js";
|
|
|
2
2
|
import type { SandboxSession } from "#shared/sandbox-session.js";
|
|
3
3
|
import type { SandboxDefinition as SharedSandboxDefinition } from "#shared/sandbox-definition.js";
|
|
4
4
|
export type { SandboxCommandOptions, SandboxCommandResult, SandboxReadTextFileOptions, SandboxSession, SandboxWriteTextFileOptions, } from "#shared/sandbox-session.js";
|
|
5
|
-
export type {
|
|
6
|
-
export type SandboxDefinition<S extends SandboxSession = SandboxSession,
|
|
5
|
+
export type { SandboxBootstrapUseFn, SandboxSessionUseFn, SandboxBootstrapContext, SandboxSessionContext, } from "#shared/sandbox-definition.js";
|
|
6
|
+
export type SandboxDefinition<S extends SandboxSession = SandboxSession, BO = Record<string, never>, SO = Record<string, never>> = Optional<SharedSandboxDefinition<S, BO, SO>, "backend">;
|
|
7
7
|
/**
|
|
8
8
|
* Defines a sandbox configuration.
|
|
9
9
|
*/
|
|
10
|
-
export declare function defineSandbox<S extends SandboxSession = SandboxSession,
|
|
10
|
+
export declare function defineSandbox<S extends SandboxSession = SandboxSession, BO = Record<string, never>, SO = Record<string, never>>(definition: SandboxDefinition<S, BO, SO>): SandboxDefinition<S, BO, SO>;
|
|
@@ -1,77 +1,75 @@
|
|
|
1
|
-
|
|
1
|
+
import type { CrossChannelReceiveFn } from "#channel/cross-channel-receive.js";
|
|
2
|
+
import type { SessionAuthContext } from "#channel/types.js";
|
|
3
|
+
export type { InferReceiveArgs, TypedReceiveRoute } from "#channel/receive-args.js";
|
|
2
4
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* Accepts both old Route-style and new CompiledChannel values.
|
|
5
|
+
* Arguments handed to {@link ScheduleDefinition.run}. Tight subset of
|
|
6
|
+
* `RouteHandlerArgs` — schedules can hand work off to a channel via
|
|
7
|
+
* {@link receive} and extend the task lifetime via {@link waitUntil}.
|
|
8
|
+
* No `send` because schedules have no current channel.
|
|
8
9
|
*/
|
|
9
|
-
export interface
|
|
10
|
-
|
|
10
|
+
export interface ScheduleHandlerArgs {
|
|
11
|
+
/**
|
|
12
|
+
* Starts a session on another channel — same contract as a route
|
|
13
|
+
* handler's `args.receive(channel, ...)`.
|
|
14
|
+
*/
|
|
15
|
+
readonly receive: CrossChannelReceiveFn;
|
|
16
|
+
/**
|
|
17
|
+
* Extends the cron task's lifetime past handler return so background
|
|
18
|
+
* work (the parked workflow session, in-flight fetches, etc.) is
|
|
19
|
+
* awaited before the Nitro task settles.
|
|
20
|
+
*/
|
|
21
|
+
readonly waitUntil: (task: Promise<unknown>) => void;
|
|
22
|
+
/**
|
|
23
|
+
* Pre-built APP auth context. Pass this to `receive(channel, { auth })`
|
|
24
|
+
* for schedules that run on behalf of the agent itself.
|
|
25
|
+
*/
|
|
26
|
+
readonly appAuth: SessionAuthContext;
|
|
11
27
|
}
|
|
12
28
|
/**
|
|
13
|
-
*
|
|
14
|
-
*/
|
|
15
|
-
type InferReceiveArgs<TChannel> = TChannel extends TypedReceiveRoute<infer TArgs> ? TArgs : Record<string, unknown>;
|
|
16
|
-
/**
|
|
17
|
-
* A channel reference with typed args, produced by {@link receive}.
|
|
18
|
-
* Carries the route/channel object for identity resolution at compile time
|
|
19
|
-
* and the args for the compiled manifest.
|
|
29
|
+
* Handler invoked when the cron fires.
|
|
20
30
|
*/
|
|
21
|
-
export
|
|
22
|
-
readonly __route: any;
|
|
23
|
-
readonly args: TArgs;
|
|
24
|
-
}
|
|
31
|
+
export type ScheduleRunHandler = (args: ScheduleHandlerArgs) => Promise<void> | void;
|
|
25
32
|
/**
|
|
26
|
-
*
|
|
27
|
-
* module and pass it with the args — TypeScript infers the correct
|
|
28
|
-
* args type from the channel's route type.
|
|
29
|
-
*
|
|
30
|
-
* Accepts both old Route-style channels and new CompiledChannel values.
|
|
33
|
+
* Public definition for a schedule authored in TypeScript.
|
|
31
34
|
*
|
|
32
|
-
*
|
|
33
|
-
* ```ts
|
|
34
|
-
* import slack from "../../channels/slack.js";
|
|
35
|
+
* Provide exactly one of `markdown` or `run`:
|
|
35
36
|
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
* }
|
|
40
|
-
*
|
|
41
|
-
*/
|
|
42
|
-
export declare function receive<TChannel extends TypedReceiveRoute<any> | {
|
|
43
|
-
readonly __kind: any;
|
|
44
|
-
}>(channel: TChannel, args: InferReceiveArgs<TChannel>): ChannelReceiveRef<InferReceiveArgs<TChannel>>;
|
|
45
|
-
/**
|
|
46
|
-
* Public definition for a schedule authored in TypeScript.
|
|
37
|
+
* - `markdown` — fire-and-forget agent invocation. The framework runs
|
|
38
|
+
* the agent on the prompt and discards the output. Equivalent to the
|
|
39
|
+
* markdown form (`<name>.md`).
|
|
40
|
+
* - `run` — full handler. Receives `{ receive, waitUntil, appAuth }`
|
|
41
|
+
* and decides what to do.
|
|
47
42
|
*
|
|
48
43
|
* Identity is derived from the file path under `agent/schedules/`;
|
|
49
|
-
* authored definitions do not carry a `name` field.
|
|
50
|
-
* `channel` delivers the agent's output to a typed channel target;
|
|
51
|
-
* when omitted, the agent runs and the output is discarded (the
|
|
52
|
-
* agent is still free to call tools, log, or hit backends).
|
|
44
|
+
* authored definitions do not carry a `name` field.
|
|
53
45
|
*/
|
|
54
46
|
export interface ScheduleDefinition {
|
|
55
|
-
cron: string;
|
|
56
|
-
markdown
|
|
57
|
-
|
|
47
|
+
readonly cron: string;
|
|
48
|
+
readonly markdown?: string;
|
|
49
|
+
readonly run?: ScheduleRunHandler;
|
|
58
50
|
}
|
|
59
51
|
/**
|
|
60
52
|
* Defines a schedule in TypeScript. Export as the default from
|
|
61
53
|
* `agent/schedules/<name>.ts`.
|
|
62
54
|
*
|
|
63
|
-
* @example
|
|
55
|
+
* @example Hand off to Slack:
|
|
64
56
|
* ```ts
|
|
57
|
+
* import { defineSchedule } from "experimental-ash/schedules";
|
|
65
58
|
* import slack from "../channels/slack.js";
|
|
66
59
|
*
|
|
67
60
|
* export default defineSchedule({
|
|
68
61
|
* cron: "0 9 * * 1-5",
|
|
69
|
-
*
|
|
70
|
-
*
|
|
62
|
+
* async run({ receive, waitUntil, appAuth }) {
|
|
63
|
+
* waitUntil(receive(slack, {
|
|
64
|
+
* message: "Post the daily standup summary.",
|
|
65
|
+
* args: { channelId: "C0123ABC" },
|
|
66
|
+
* auth: appAuth,
|
|
67
|
+
* }));
|
|
68
|
+
* },
|
|
71
69
|
* });
|
|
72
70
|
* ```
|
|
73
71
|
*
|
|
74
|
-
* @example
|
|
72
|
+
* @example Fire-and-forget:
|
|
75
73
|
* ```ts
|
|
76
74
|
* export default defineSchedule({
|
|
77
75
|
* cron: "* / 5 * * * *",
|
|
@@ -80,4 +78,3 @@ export interface ScheduleDefinition {
|
|
|
80
78
|
* ```
|
|
81
79
|
*/
|
|
82
80
|
export declare function defineSchedule<TSchedule extends ScheduleDefinition>(definition: TSchedule): TSchedule;
|
|
83
|
-
export {};
|
|
@@ -1,40 +1,25 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Creates a typed channel reference for a schedule. Import the channel
|
|
3
|
-
* module and pass it with the args — TypeScript infers the correct
|
|
4
|
-
* args type from the channel's route type.
|
|
5
|
-
*
|
|
6
|
-
* Accepts both old Route-style channels and new CompiledChannel values.
|
|
7
|
-
*
|
|
8
|
-
* @example
|
|
9
|
-
* ```ts
|
|
10
|
-
* import slack from "../../channels/slack.js";
|
|
11
|
-
*
|
|
12
|
-
* export default defineSchedule({
|
|
13
|
-
* cron: "0 9 * * 1-5",
|
|
14
|
-
* channel: receive(slack, { channelId: "C0123ABC" }),
|
|
15
|
-
* });
|
|
16
|
-
* ```
|
|
17
|
-
*/
|
|
18
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
19
|
-
export function receive(channel, args) {
|
|
20
|
-
return { __route: channel, args };
|
|
21
|
-
}
|
|
22
1
|
/**
|
|
23
2
|
* Defines a schedule in TypeScript. Export as the default from
|
|
24
3
|
* `agent/schedules/<name>.ts`.
|
|
25
4
|
*
|
|
26
|
-
* @example
|
|
5
|
+
* @example Hand off to Slack:
|
|
27
6
|
* ```ts
|
|
7
|
+
* import { defineSchedule } from "experimental-ash/schedules";
|
|
28
8
|
* import slack from "../channels/slack.js";
|
|
29
9
|
*
|
|
30
10
|
* export default defineSchedule({
|
|
31
11
|
* cron: "0 9 * * 1-5",
|
|
32
|
-
*
|
|
33
|
-
*
|
|
12
|
+
* async run({ receive, waitUntil, appAuth }) {
|
|
13
|
+
* waitUntil(receive(slack, {
|
|
14
|
+
* message: "Post the daily standup summary.",
|
|
15
|
+
* args: { channelId: "C0123ABC" },
|
|
16
|
+
* auth: appAuth,
|
|
17
|
+
* }));
|
|
18
|
+
* },
|
|
34
19
|
* });
|
|
35
20
|
* ```
|
|
36
21
|
*
|
|
37
|
-
* @example
|
|
22
|
+
* @example Fire-and-forget:
|
|
38
23
|
* ```ts
|
|
39
24
|
* export default defineSchedule({
|
|
40
25
|
* cron: "* / 5 * * * *",
|
|
@@ -55,12 +55,12 @@ export interface LowerSkillMarkdownInput {
|
|
|
55
55
|
*/
|
|
56
56
|
/**
|
|
57
57
|
* Lowers an authored schedule markdown file into the shared public
|
|
58
|
-
* definition shape. The frontmatter must contain `cron
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
* `
|
|
62
|
-
* identity is path-derived, so the lowered
|
|
63
|
-
* `name`.
|
|
58
|
+
* definition shape. The frontmatter must contain `cron`. The body
|
|
59
|
+
* becomes the schedule's `markdown` (the fire-and-forget prompt the
|
|
60
|
+
* agent runs when the cron fires). Markdown-form schedules cannot
|
|
61
|
+
* declare a `run` handler — use the `.ts` form for handler-based
|
|
62
|
+
* schedules. Schedule identity is path-derived, so the lowered
|
|
63
|
+
* definition never carries a `name`.
|
|
64
64
|
*/
|
|
65
65
|
export declare function lowerScheduleMarkdown(source: string): ScheduleDefinition;
|
|
66
66
|
export declare function lowerSkillMarkdown(source: string, input?: LowerSkillMarkdownInput): SkillDefinition;
|
|
@@ -56,20 +56,20 @@ export function lowerInstructionsMarkdown(markdown) {
|
|
|
56
56
|
*/
|
|
57
57
|
/**
|
|
58
58
|
* Lowers an authored schedule markdown file into the shared public
|
|
59
|
-
* definition shape. The frontmatter must contain `cron
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
* `
|
|
63
|
-
* identity is path-derived, so the lowered
|
|
64
|
-
* `name`.
|
|
59
|
+
* definition shape. The frontmatter must contain `cron`. The body
|
|
60
|
+
* becomes the schedule's `markdown` (the fire-and-forget prompt the
|
|
61
|
+
* agent runs when the cron fires). Markdown-form schedules cannot
|
|
62
|
+
* declare a `run` handler — use the `.ts` form for handler-based
|
|
63
|
+
* schedules. Schedule identity is path-derived, so the lowered
|
|
64
|
+
* definition never carries a `name`.
|
|
65
65
|
*/
|
|
66
66
|
export function lowerScheduleMarkdown(source) {
|
|
67
67
|
const document = parseMarkdownDocument(source);
|
|
68
68
|
if (!document.hasFrontmatter) {
|
|
69
69
|
throw new Error('Schedule markdown must start with YAML frontmatter declaring "cron".');
|
|
70
70
|
}
|
|
71
|
-
if ("
|
|
72
|
-
throw new Error('Markdown-form schedules do not support the "
|
|
71
|
+
if ("run" in document.frontmatter) {
|
|
72
|
+
throw new Error('Markdown-form schedules do not support the "run" frontmatter key. Use a TypeScript schedule (`<name>.ts`) to author a handler.');
|
|
73
73
|
}
|
|
74
74
|
const rawDefinition = {
|
|
75
75
|
...document.frontmatter,
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import type { SandboxBackend } from "#public/definitions/sandbox-backend.js";
|
|
2
|
-
import type { VercelSandbox, VercelSandboxSessionUseOptions } from "#public/sandbox/vercel-sandbox.js";
|
|
2
|
+
import type { VercelSandbox, VercelSandboxBootstrapUseOptions, VercelSandboxSessionUseOptions } from "#public/sandbox/vercel-sandbox.js";
|
|
3
3
|
/**
|
|
4
4
|
* Constructs the built-in Vercel sandbox backend.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
* `
|
|
8
|
-
*
|
|
6
|
+
* `bootstrap({ use })` applies its options to the template via
|
|
7
|
+
* `sandbox.update(...)`; those settings persist into the snapshot.
|
|
8
|
+
* `onSession({ use })` applies its options to the live session via
|
|
9
9
|
* `VercelSandbox.update()`.
|
|
10
10
|
*/
|
|
11
|
-
export declare function vercelBackend(): SandboxBackend<VercelSandbox, VercelSandboxSessionUseOptions>;
|
|
11
|
+
export declare function vercelBackend(): SandboxBackend<VercelSandbox, VercelSandboxBootstrapUseOptions, VercelSandboxSessionUseOptions>;
|
|
@@ -2,9 +2,9 @@ import { createVercelSandboxBackend } from "#execution/sandbox/bindings/vercel.j
|
|
|
2
2
|
/**
|
|
3
3
|
* Constructs the built-in Vercel sandbox backend.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
* `
|
|
7
|
-
*
|
|
5
|
+
* `bootstrap({ use })` applies its options to the template via
|
|
6
|
+
* `sandbox.update(...)`; those settings persist into the snapshot.
|
|
7
|
+
* `onSession({ use })` applies its options to the live session via
|
|
8
8
|
* `VercelSandbox.update()`.
|
|
9
9
|
*/
|
|
10
10
|
export function vercelBackend() {
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
* `agent/sandbox/sandbox.ts` when paired with a `workspace/` folder).
|
|
4
4
|
*/
|
|
5
5
|
export { getSandbox } from "#context/accessors.js";
|
|
6
|
-
export { defineSandbox, type SandboxBootstrapContext, type SandboxBootstrapUseFn, type
|
|
6
|
+
export { defineSandbox, type SandboxBootstrapContext, type SandboxBootstrapUseFn, type SandboxCommandOptions, type SandboxCommandResult, type SandboxDefinition, type SandboxReadTextFileOptions, type SandboxSession, type SandboxSessionContext, type SandboxSessionUseFn, type SandboxWriteTextFileOptions, } from "#public/definitions/sandbox.js";
|
|
7
7
|
export type { SandboxBackend, SandboxBackendCreateInput, SandboxBackendHandle, SandboxBackendPrewarmInput, SandboxBackendRuntimeContext, SandboxBackendSessionState, SandboxSeedFile, } from "#public/definitions/sandbox-backend.js";
|
|
8
8
|
export { SandboxTemplateNotProvisionedError } from "#public/definitions/sandbox-backend.js";
|
|
9
9
|
export { defaultBackend } from "#public/sandbox/backends/default.js";
|
|
10
10
|
export { localBackend } from "#public/sandbox/backends/local.js";
|
|
11
11
|
export { vercelBackend } from "#public/sandbox/backends/vercel.js";
|
|
12
|
-
export type { VercelSandbox, VercelSandboxSessionUseOptions, } from "#public/sandbox/vercel-sandbox.js";
|
|
12
|
+
export type { VercelSandbox, VercelSandboxBootstrapUseOptions, VercelSandboxSessionUseOptions, } from "#public/sandbox/vercel-sandbox.js";
|
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
import type { SandboxUpdateParams } from "#compiled/@vercel/sandbox/index.js";
|
|
2
2
|
import type { SandboxSession } from "#public/definitions/sandbox.js";
|
|
3
|
+
/**
|
|
4
|
+
* Options accepted by the Vercel backend's `bootstrap({ use })` hook.
|
|
5
|
+
* Aliases the Vercel SDK's `SandboxUpdateParams` because bootstrap
|
|
6
|
+
* applies its options to the template via `sandbox.update(...)` after
|
|
7
|
+
* `Sandbox.create()` and before the snapshot is captured. The Vercel
|
|
8
|
+
* SDK persists `update`-d settings on the sandbox so they survive into
|
|
9
|
+
* the snapshot, which becomes the seed for every later session.
|
|
10
|
+
*
|
|
11
|
+
* Today this is the same shape as
|
|
12
|
+
* {@link VercelSandboxSessionUseOptions}; both are exposed as separate
|
|
13
|
+
* named aliases so future divergence is non-breaking.
|
|
14
|
+
*/
|
|
15
|
+
export type VercelSandboxBootstrapUseOptions = SandboxUpdateParams;
|
|
3
16
|
/**
|
|
4
17
|
* Options accepted by the Vercel backend's `onSession({ use })` hook
|
|
5
18
|
* and `VercelSandbox.update()`. Aliases the Vercel SDK's
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Schedule authoring helpers for `agent/schedules/*` files.
|
|
3
3
|
*/
|
|
4
|
-
export { type
|
|
4
|
+
export { defineSchedule, type ScheduleDefinition, type ScheduleHandlerArgs, type ScheduleRunHandler, type TypedReceiveRoute, } from "#public/definitions/schedule.js";
|
|
@@ -19,18 +19,18 @@ export class ResolveScheduleError extends Error {
|
|
|
19
19
|
*/
|
|
20
20
|
export async function resolveSchedules(input) {
|
|
21
21
|
return [...input.manifest.schedules].map((schedule) => {
|
|
22
|
-
const
|
|
22
|
+
const base = {
|
|
23
23
|
cron: schedule.cron,
|
|
24
|
+
hasRun: schedule.hasRun,
|
|
24
25
|
logicalPath: schedule.logicalPath,
|
|
25
|
-
markdown: schedule.markdown,
|
|
26
26
|
name: schedule.name,
|
|
27
27
|
sourceId: schedule.sourceId,
|
|
28
28
|
sourceKind: schedule.sourceKind,
|
|
29
29
|
};
|
|
30
|
-
if (schedule.
|
|
31
|
-
return { ...
|
|
30
|
+
if (schedule.markdown !== undefined) {
|
|
31
|
+
return { ...base, markdown: schedule.markdown };
|
|
32
32
|
}
|
|
33
|
-
return
|
|
33
|
+
return base;
|
|
34
34
|
});
|
|
35
35
|
}
|
|
36
36
|
/**
|
|
@@ -43,20 +43,17 @@ export type ResolvedSkillDefinition = Readonly<InternalSkillDefinition & (Omit<M
|
|
|
43
43
|
/**
|
|
44
44
|
* Runtime-owned authored schedule definition resolved from compiler artifacts.
|
|
45
45
|
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
46
|
+
* A schedule has exactly one of `markdown` (fire-and-forget agent run)
|
|
47
|
+
* or `hasRun: true` (authored handler). For the handler form the
|
|
48
|
+
* runtime loads the schedule's module and invokes `definition.run` with
|
|
49
|
+
* a {@link ScheduleHandlerArgs}-shaped argument; for the markdown form
|
|
50
|
+
* the dispatcher synthesizes a channel-less SCHEDULE_ADAPTER run.
|
|
51
51
|
*/
|
|
52
52
|
export type ResolvedSchedule = Readonly<SourceRef & {
|
|
53
|
-
readonly channel?: {
|
|
54
|
-
readonly name: string;
|
|
55
|
-
readonly args: Readonly<Record<string, unknown>>;
|
|
56
|
-
};
|
|
57
53
|
readonly cron: string;
|
|
58
54
|
readonly name: string;
|
|
59
|
-
readonly markdown
|
|
55
|
+
readonly markdown?: string;
|
|
56
|
+
readonly hasRun: boolean;
|
|
60
57
|
readonly sourceKind: "markdown" | "module";
|
|
61
58
|
} & (Omit<MarkdownSourceRef<undefined>, "definition"> | ModuleSourceRef)>;
|
|
62
59
|
/**
|
|
@@ -187,6 +184,14 @@ export interface ResolvedChannelDefinition extends ResolvedModuleSourceRef {
|
|
|
187
184
|
* accepting a different shape.
|
|
188
185
|
*/
|
|
189
186
|
readonly receive?: CompiledChannel["receive"];
|
|
187
|
+
/**
|
|
188
|
+
* Reference to the authored {@link CompiledChannel} value the channel
|
|
189
|
+
* module exported. Preserved so callers of `args.receive(channel, …)`
|
|
190
|
+
* can identify a target by the same imported reference. `undefined`
|
|
191
|
+
* for framework-internal channels constructed without going through
|
|
192
|
+
* `defineChannel`.
|
|
193
|
+
*/
|
|
194
|
+
readonly definition?: CompiledChannel;
|
|
190
195
|
/**
|
|
191
196
|
* New-style route handler from CompiledChannel. When present, the
|
|
192
197
|
* dispatch layer uses this instead of `fetch`.
|
|
@@ -7,9 +7,9 @@ import type { SandboxSession } from "#shared/sandbox-session.js";
|
|
|
7
7
|
* runtime orchestrator can persist reconnect metadata and release
|
|
8
8
|
* resources.
|
|
9
9
|
*/
|
|
10
|
-
export interface SandboxBackendHandle<S extends SandboxSession = SandboxSession,
|
|
10
|
+
export interface SandboxBackendHandle<S extends SandboxSession = SandboxSession, SO = Record<string, never>> {
|
|
11
11
|
readonly session: SandboxSession;
|
|
12
|
-
readonly useSessionFn: SandboxSessionUseFn<S,
|
|
12
|
+
readonly useSessionFn: SandboxSessionUseFn<S, SO>;
|
|
13
13
|
captureState(): Promise<SandboxBackendSessionState>;
|
|
14
14
|
dispose(): Promise<void>;
|
|
15
15
|
}
|
|
@@ -79,9 +79,9 @@ export interface SandboxBackendCreateInput {
|
|
|
79
79
|
* `bootstrap` hook and `seedFiles`, then `backend.create(...)` opens
|
|
80
80
|
* durable sessions from that snapshot.
|
|
81
81
|
*/
|
|
82
|
-
export interface SandboxBackendPrewarmInput {
|
|
82
|
+
export interface SandboxBackendPrewarmInput<BO = Record<string, never>> {
|
|
83
83
|
readonly templateKey: string;
|
|
84
|
-
readonly bootstrap?: (input: SandboxBootstrapContext) => void | Promise<void>;
|
|
84
|
+
readonly bootstrap?: (input: SandboxBootstrapContext<BO>) => void | Promise<void>;
|
|
85
85
|
readonly runtimeContext: SandboxBackendRuntimeContext;
|
|
86
86
|
readonly seedFiles: ReadonlyArray<SandboxSeedFile>;
|
|
87
87
|
}
|
|
@@ -97,7 +97,7 @@ export interface SandboxBackendPrewarmInput {
|
|
|
97
97
|
* Each backend owns the full template-then-session lifecycle internally;
|
|
98
98
|
* callers only need a single `create` call.
|
|
99
99
|
*/
|
|
100
|
-
export interface SandboxBackend<S extends SandboxSession = SandboxSession,
|
|
100
|
+
export interface SandboxBackend<S extends SandboxSession = SandboxSession, BO = Record<string, never>, SO = Record<string, never>> {
|
|
101
101
|
/**
|
|
102
102
|
* Stable identifier for this backend implementation.
|
|
103
103
|
*
|
|
@@ -113,12 +113,12 @@ export interface SandboxBackend<S extends SandboxSession = SandboxSession, O = R
|
|
|
113
113
|
* {@link SandboxTemplateNotProvisionedError} when the requested
|
|
114
114
|
* template is missing.
|
|
115
115
|
*/
|
|
116
|
-
create(input: SandboxBackendCreateInput): Promise<SandboxBackendHandle<S,
|
|
116
|
+
create(input: SandboxBackendCreateInput): Promise<SandboxBackendHandle<S, SO>>;
|
|
117
117
|
/**
|
|
118
118
|
* Build-time prewarm hook. Ash invokes this for every authored
|
|
119
119
|
* sandbox in the compiled graph before serving traffic so the backend
|
|
120
120
|
* can capture a reusable template snapshot. Idempotent against an
|
|
121
121
|
* existing snapshot keyed by `templateKey`.
|
|
122
122
|
*/
|
|
123
|
-
prewarm(input: SandboxBackendPrewarmInput): Promise<void>;
|
|
123
|
+
prewarm(input: SandboxBackendPrewarmInput<BO>): Promise<void>;
|
|
124
124
|
}
|