experimental-ash 0.7.5 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/README.md +79 -56
- package/dist/docs/public/channels/README.md +33 -23
- package/dist/docs/public/channels/attachments.md +42 -29
- package/dist/src/channel/adapter.d.ts +21 -28
- package/dist/src/channel/compiled-channel.d.ts +1 -1
- package/dist/src/channel/http.d.ts +29 -0
- package/dist/src/channel/http.js +30 -0
- package/dist/src/channel/schedule.d.ts +20 -0
- package/dist/src/channel/schedule.js +22 -1
- package/dist/src/channel/send.d.ts +1 -1
- package/dist/src/channel/send.js +22 -1
- package/dist/src/channel/types.d.ts +1 -1
- package/dist/src/chunks/{client-DBMG7iuf.js → client-BeZ_W7vl.js} +2 -2
- package/dist/src/chunks/{dev-authored-source-watcher-BcN7BUDE.js → dev-authored-source-watcher-BFC_yNcP.js} +1 -1
- package/dist/src/chunks/host-DMccRKcz.js +22 -0
- package/dist/src/chunks/{paths-BYIdCNw9.js → paths-B-aiDznc.js} +26 -26
- package/dist/src/chunks/{prewarm-DXhyk7i9.js → prewarm-CCbReSNm.js} +1 -1
- package/dist/src/chunks/types-MZUhN0Zy.js +1 -0
- package/dist/src/cli/commands/info.js +1 -1
- package/dist/src/cli/dev/environment.d.ts +3 -2
- package/dist/src/cli/dev/repl.js +1 -1
- package/dist/src/cli/run.js +1 -1
- package/dist/src/compiled/.vendor-stamp.json +1 -1
- package/dist/src/compiled/@vercel/sandbox/index.d.ts +37 -3
- package/dist/src/evals/cli/eval.js +1 -1
- package/dist/src/execution/sandbox/bindings/local.d.ts +0 -2
- package/dist/src/execution/sandbox/bindings/local.js +1 -20
- package/dist/src/execution/sandbox/bindings/vercel.d.ts +2 -2
- package/dist/src/execution/sandbox/bindings/vercel.js +1 -12
- package/dist/src/harness/attachment-staging.js +54 -50
- package/dist/src/harness/emission.d.ts +14 -1
- package/dist/src/harness/emission.js +15 -2
- package/dist/src/harness/tool-loop.js +58 -15
- package/dist/src/internal/application/package.js +1 -1
- package/dist/src/internal/attachments/url-refs.d.ts +14 -0
- package/dist/src/internal/attachments/url-refs.js +20 -0
- package/dist/src/internal/nitro/host/configure-nitro-routes.d.ts +0 -1
- package/dist/src/internal/nitro/host/configure-nitro-routes.js +24 -17
- package/dist/src/internal/nitro/host/create-application-nitro.js +1 -16
- package/dist/src/internal/nitro/routes/agent-info/build-agent-info-response.d.ts +87 -0
- package/dist/src/internal/nitro/routes/{home-page/build-home-page-response.js → agent-info/build-agent-info-response.js} +6 -6
- package/dist/src/internal/nitro/routes/{home-page/load-home-page-data.d.ts → agent-info/load-agent-info-data.d.ts} +8 -8
- package/dist/src/internal/nitro/routes/{home-page/load-home-page-data.js → agent-info/load-agent-info-data.js} +7 -8
- package/dist/src/internal/nitro/routes/index.d.ts +10 -5
- package/dist/src/internal/nitro/routes/index.js +225 -18
- package/dist/src/internal/nitro/routes/info.d.ts +14 -0
- package/dist/src/internal/nitro/routes/info.js +50 -0
- package/dist/src/protocol/routes.d.ts +8 -6
- package/dist/src/protocol/routes.js +8 -6
- package/dist/src/public/channels/ash.js +1 -6
- package/dist/src/public/channels/index.d.ts +1 -1
- package/dist/src/public/channels/slack/attachments.d.ts +14 -18
- package/dist/src/public/channels/slack/attachments.js +30 -36
- package/dist/src/public/channels/slack/index.d.ts +0 -1
- package/dist/src/public/channels/slack/slackChannel.js +3 -3
- package/dist/src/public/definitions/defineChannel.d.ts +9 -7
- package/dist/src/public/definitions/defineChannel.js +5 -11
- package/dist/src/public/definitions/sandbox.d.ts +3 -3
- package/dist/src/public/sandbox/backends/vercel.d.ts +2 -2
- package/dist/src/public/sandbox/index.d.ts +2 -2
- package/dist/src/public/sandbox/vercel-sandbox.d.ts +11 -10
- package/dist/src/runtime/channels/registry.js +9 -3
- package/dist/src/runtime/resolve-channel.js +2 -1
- package/dist/src/shared/sandbox-backend.d.ts +4 -4
- package/dist/src/shared/sandbox-definition.d.ts +6 -36
- package/package.json +1 -1
- package/dist/src/chunks/host-33-Sb6vq.js +0 -22
- package/dist/src/chunks/types-D9Uv7nU4.js +0 -1
- package/dist/src/internal/nitro/host/load-home-page-web-assets.d.ts +0 -12
- package/dist/src/internal/nitro/host/load-home-page-web-assets.js +0 -34
- package/dist/src/internal/nitro/routes/home-page/build-home-page-response.d.ts +0 -87
- package/dist/src/internal/nitro/routes/home.d.ts +0 -6
- package/dist/src/internal/nitro/routes/home.js +0 -21
- package/dist/src/internal/nitro/routes/web-ui/assets/index-BQa8fbHJ.js +0 -11
- package/dist/src/internal/nitro/routes/web-ui/assets/style-Kqb6YxTP.css +0 -2
- package/dist/src/internal/nitro/routes/web-ui/index.html +0 -17
- package/dist/src/public/sandboxes/vercel-sandbox.d.ts +0 -41
- package/dist/src/public/sandboxes/vercel-sandbox.js +0 -1
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { buildAgentInfoResponse } from "#internal/nitro/routes/agent-info/build-agent-info-response.js";
|
|
2
|
+
import { loadAgentInfoData, resolveAgentInfoCompiledArtifactsSource, } from "#internal/nitro/routes/agent-info/load-agent-info-data.js";
|
|
3
|
+
import { createUnauthorizedResponse, none, vercelOidc, } from "#public/channels/auth.js";
|
|
4
|
+
/**
|
|
5
|
+
* Resolves the default auth strategy for the agent info endpoint.
|
|
6
|
+
*
|
|
7
|
+
* Mirrors {@link resolveFrameworkAshAuth} (session/stream routes): on
|
|
8
|
+
* Vercel deployments require a valid Vercel OIDC bearer (the always-on
|
|
9
|
+
* same-project bypass lets internal callers in automatically); off
|
|
10
|
+
* Vercel (`ash dev`) accept anonymous requests so local inspection
|
|
11
|
+
* tools and curl-based probes work without setup.
|
|
12
|
+
*/
|
|
13
|
+
function resolveAgentInfoAuth() {
|
|
14
|
+
if (process.env.VERCEL) {
|
|
15
|
+
return vercelOidc();
|
|
16
|
+
}
|
|
17
|
+
return none();
|
|
18
|
+
}
|
|
19
|
+
async function createAgentInfoPayload(input) {
|
|
20
|
+
const data = await loadAgentInfoData({
|
|
21
|
+
compiledArtifactsSource: resolveAgentInfoCompiledArtifactsSource({
|
|
22
|
+
appRoot: input.appRoot,
|
|
23
|
+
}),
|
|
24
|
+
});
|
|
25
|
+
return buildAgentInfoResponse(data);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Builds the package-owned JSON inspection response for the current
|
|
29
|
+
* agent (model id, instructions preview, skills, schedules, subagents,
|
|
30
|
+
* tools, sandbox, endpoint catalog).
|
|
31
|
+
*
|
|
32
|
+
* The route is gated by {@link resolveAgentInfoAuth} so the deployed URL
|
|
33
|
+
* does not expose agent metadata to anonymous callers. The same-project
|
|
34
|
+
* bypass on `vercelOidc()` keeps the route reachable to in-deployment
|
|
35
|
+
* callers (`apps/nextjs-ash`-style consumers) without any additional
|
|
36
|
+
* setup.
|
|
37
|
+
*/
|
|
38
|
+
export async function handleAgentInfoRequest(input, request) {
|
|
39
|
+
const auth = resolveAgentInfoAuth();
|
|
40
|
+
const result = await auth(request);
|
|
41
|
+
if (result === null) {
|
|
42
|
+
return createUnauthorizedResponse({ challenges: [{ scheme: "Bearer" }] });
|
|
43
|
+
}
|
|
44
|
+
return new Response(JSON.stringify(await createAgentInfoPayload(input)), {
|
|
45
|
+
headers: {
|
|
46
|
+
"cache-control": "no-store",
|
|
47
|
+
"content-type": "application/json; charset=utf-8",
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
}
|
|
@@ -8,9 +8,15 @@ export declare const ASH_ROUTE_PREFIX = "/ash/v1";
|
|
|
8
8
|
*/
|
|
9
9
|
export declare const ASH_HEALTH_ROUTE_PATH = "/ash/v1/health";
|
|
10
10
|
/**
|
|
11
|
-
* Stable framework-owned route exposing
|
|
11
|
+
* Stable framework-owned route exposing the JSON inspection payload for
|
|
12
|
+
* the current agent (model id, instructions preview, skills, schedules,
|
|
13
|
+
* subagents, tools, sandbox, and the runtime's own endpoint catalog).
|
|
14
|
+
*
|
|
15
|
+
* Default auth on this route is `vercelOidc()` on Vercel deployments
|
|
16
|
+
* (with the same-project bypass for in-deployment callers) and `none()`
|
|
17
|
+
* off Vercel. See `routes/info.ts` for the handler.
|
|
12
18
|
*/
|
|
13
|
-
export declare const
|
|
19
|
+
export declare const ASH_INFO_ROUTE_PATH = "/ash/v1/info";
|
|
14
20
|
/**
|
|
15
21
|
* Stable framework-owned route for creating a new session.
|
|
16
22
|
*/
|
|
@@ -24,10 +30,6 @@ export declare const ASH_CONTINUE_SESSION_ROUTE_PATTERN = "/ash/v1/session/:sess
|
|
|
24
30
|
* Stable framework-owned message stream route pattern.
|
|
25
31
|
*/
|
|
26
32
|
export declare const ASH_MESSAGE_STREAM_ROUTE_PATTERN = "/ash/v1/session/:sessionId/stream";
|
|
27
|
-
/**
|
|
28
|
-
* Stable framework-owned route prefix for static home-page UI assets.
|
|
29
|
-
*/
|
|
30
|
-
export declare const ASH_HOME_PAGE_UI_ROUTE_PREFIX = "/ash/v1/ui";
|
|
31
33
|
/**
|
|
32
34
|
* Stable framework-owned route pattern for receiving inbound IdP redirects
|
|
33
35
|
* during in-turn interactive connection authorization.
|
|
@@ -8,9 +8,15 @@ export const ASH_ROUTE_PREFIX = "/ash/v1";
|
|
|
8
8
|
*/
|
|
9
9
|
export const ASH_HEALTH_ROUTE_PATH = `${ASH_ROUTE_PREFIX}/health`;
|
|
10
10
|
/**
|
|
11
|
-
* Stable framework-owned route exposing
|
|
11
|
+
* Stable framework-owned route exposing the JSON inspection payload for
|
|
12
|
+
* the current agent (model id, instructions preview, skills, schedules,
|
|
13
|
+
* subagents, tools, sandbox, and the runtime's own endpoint catalog).
|
|
14
|
+
*
|
|
15
|
+
* Default auth on this route is `vercelOidc()` on Vercel deployments
|
|
16
|
+
* (with the same-project bypass for in-deployment callers) and `none()`
|
|
17
|
+
* off Vercel. See `routes/info.ts` for the handler.
|
|
12
18
|
*/
|
|
13
|
-
export const
|
|
19
|
+
export const ASH_INFO_ROUTE_PATH = `${ASH_ROUTE_PREFIX}/info`;
|
|
14
20
|
/**
|
|
15
21
|
* Stable framework-owned route for creating a new session.
|
|
16
22
|
*/
|
|
@@ -24,10 +30,6 @@ export const ASH_CONTINUE_SESSION_ROUTE_PATTERN = `${ASH_ROUTE_PREFIX}/session/:
|
|
|
24
30
|
* Stable framework-owned message stream route pattern.
|
|
25
31
|
*/
|
|
26
32
|
export const ASH_MESSAGE_STREAM_ROUTE_PATTERN = `${ASH_ROUTE_PREFIX}/session/:sessionId/stream`;
|
|
27
|
-
/**
|
|
28
|
-
* Stable framework-owned route prefix for static home-page UI assets.
|
|
29
|
-
*/
|
|
30
|
-
export const ASH_HOME_PAGE_UI_ROUTE_PREFIX = `${ASH_ROUTE_PREFIX}/ui`;
|
|
31
33
|
/**
|
|
32
34
|
* Stable framework-owned route pattern for receiving inbound IdP redirects
|
|
33
35
|
* during in-turn interactive connection authorization.
|
|
@@ -29,12 +29,7 @@ export function ashChannel(input) {
|
|
|
29
29
|
if (policyRejection !== null)
|
|
30
30
|
return policyRejection;
|
|
31
31
|
const token = `ash:${crypto.randomUUID()}`;
|
|
32
|
-
const
|
|
33
|
-
? body.message
|
|
34
|
-
: body.message
|
|
35
|
-
? JSON.stringify(body.message)
|
|
36
|
-
: "";
|
|
37
|
-
const session = await send(messageText, {
|
|
32
|
+
const session = await send(body.message, {
|
|
38
33
|
auth: sessionAuth,
|
|
39
34
|
continuationToken: token,
|
|
40
35
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { defineChannel, POST, GET, PUT, DELETE, type
|
|
1
|
+
export { defineChannel, POST, GET, PUT, DELETE, type Channel, type ChannelConfig, type ChannelEvents, type Session, type SessionHandle, type RouteDefinition, type RouteHandlerArgs, type SendFn, type SendOptions, type SendPayload, type GetSessionFn, } from "#public/definitions/defineChannel.js";
|
|
@@ -1,21 +1,16 @@
|
|
|
1
1
|
import type { FilePart, UserContent } from "ai";
|
|
2
2
|
import type { SlackBotToken } from "#compiled/@chat-adapter/slack/index.js";
|
|
3
3
|
import type { Message } from "#compiled/chat/index.js";
|
|
4
|
-
import type {
|
|
4
|
+
import type { FetchFileResult } from "#channel/adapter.js";
|
|
5
5
|
import type { UploadPolicy } from "#public/channels/upload-policy.js";
|
|
6
|
-
/**
|
|
7
|
-
* Payload an `ash-attachment:` ref carries for Slack file uploads.
|
|
8
|
-
* The Slack resolver reads `params.url` and fetches it with the bot
|
|
9
|
-
* token.
|
|
10
|
-
*/
|
|
11
|
-
export interface SlackAttachmentParams {
|
|
12
|
-
readonly url: string;
|
|
13
|
-
}
|
|
14
6
|
/**
|
|
15
7
|
* Emits one {@link FilePart} per supported attachment in the inbound
|
|
16
|
-
* message, with `data` set to
|
|
17
|
-
* video,
|
|
18
|
-
* single bad upload never blocks the text portion
|
|
8
|
+
* message, with `data` set to a `URL` object pointing at the Slack
|
|
9
|
+
* file. Audio, video, URL-less, and policy-violating attachments are
|
|
10
|
+
* dropped so a single bad upload never blocks the text portion.
|
|
11
|
+
*
|
|
12
|
+
* The `URL` object in `data` is resolved by the channel's `fetchFile`
|
|
13
|
+
* function at staging time inside the workflow step.
|
|
19
14
|
*/
|
|
20
15
|
export declare function collectSlackFileParts(message: Pick<Message, "attachments">, policy: UploadPolicy): FilePart[];
|
|
21
16
|
/**
|
|
@@ -25,11 +20,12 @@ export declare function collectSlackFileParts(message: Pick<Message, "attachment
|
|
|
25
20
|
*/
|
|
26
21
|
export declare function buildSlackTurnMessage(text: string, fileParts: readonly FilePart[]): string | UserContent;
|
|
27
22
|
/**
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
23
|
+
* Creates a `fetchFile` function for the Slack channel.
|
|
24
|
+
*
|
|
25
|
+
* Returns `null` for URLs that don't belong to Slack so they pass
|
|
26
|
+
* through to the model provider unchanged. Fetches Slack file URLs
|
|
27
|
+
* with the bot token.
|
|
32
28
|
*/
|
|
33
|
-
export declare function
|
|
29
|
+
export declare function createSlackFetchFile(input: {
|
|
34
30
|
readonly botToken?: SlackBotToken;
|
|
35
|
-
}):
|
|
31
|
+
}): (url: string) => Promise<FetchFileResult | null>;
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import { AshAttachmentError } from "#internal/attachments/errors.js";
|
|
2
|
-
import { encodeAttachmentRef } from "#internal/attachments/refs.js";
|
|
3
1
|
import { createLogger } from "#internal/logging.js";
|
|
4
2
|
import { evaluateFilePart, formatUploadPolicyViolation } from "#public/channels/upload-policy.js";
|
|
5
3
|
const log = createLogger("slack.attachments");
|
|
6
4
|
/**
|
|
7
5
|
* Emits one {@link FilePart} per supported attachment in the inbound
|
|
8
|
-
* message, with `data` set to
|
|
9
|
-
* video,
|
|
10
|
-
* single bad upload never blocks the text portion
|
|
6
|
+
* message, with `data` set to a `URL` object pointing at the Slack
|
|
7
|
+
* file. Audio, video, URL-less, and policy-violating attachments are
|
|
8
|
+
* dropped so a single bad upload never blocks the text portion.
|
|
9
|
+
*
|
|
10
|
+
* The `URL` object in `data` is resolved by the channel's `fetchFile`
|
|
11
|
+
* function at staging time inside the workflow step.
|
|
11
12
|
*/
|
|
12
13
|
export function collectSlackFileParts(message, policy) {
|
|
13
14
|
const parts = [];
|
|
@@ -31,7 +32,7 @@ function toSlackFilePart(attachment, index) {
|
|
|
31
32
|
return null;
|
|
32
33
|
}
|
|
33
34
|
if (!attachment.url) {
|
|
34
|
-
log.warn("dropped attachment — no url available
|
|
35
|
+
log.warn("dropped attachment — no url available", {
|
|
35
36
|
name: attachment.name,
|
|
36
37
|
});
|
|
37
38
|
return null;
|
|
@@ -40,10 +41,7 @@ function toSlackFilePart(attachment, index) {
|
|
|
40
41
|
type: "file",
|
|
41
42
|
mediaType: attachment.mimeType ?? "application/octet-stream",
|
|
42
43
|
filename: attachment.name ?? `attachment-${index}`,
|
|
43
|
-
data:
|
|
44
|
-
params: { url: attachment.url },
|
|
45
|
-
size: attachment.size,
|
|
46
|
-
}),
|
|
44
|
+
data: new URL(attachment.url),
|
|
47
45
|
};
|
|
48
46
|
}
|
|
49
47
|
/**
|
|
@@ -62,39 +60,35 @@ export function buildSlackTurnMessage(text, fileParts) {
|
|
|
62
60
|
const textPart = { type: "text", text };
|
|
63
61
|
return [textPart, ...fileParts];
|
|
64
62
|
}
|
|
65
|
-
/** Materializes a {@link SlackBotToken} to a string at fetch time
|
|
66
|
-
* so callers can rotate (function-shaped credentials) or read lazily. */
|
|
67
63
|
async function resolveSlackBotToken(token) {
|
|
68
64
|
const source = token ?? process.env.SLACK_BOT_TOKEN;
|
|
69
65
|
if (!source) {
|
|
70
|
-
throw new Error("SLACK_BOT_TOKEN is required to
|
|
66
|
+
throw new Error("SLACK_BOT_TOKEN is required to fetch Slack files.");
|
|
71
67
|
}
|
|
72
68
|
return typeof source === "function" ? await source() : source;
|
|
73
69
|
}
|
|
74
70
|
/**
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
71
|
+
* Creates a `fetchFile` function for the Slack channel.
|
|
72
|
+
*
|
|
73
|
+
* Returns `null` for URLs that don't belong to Slack so they pass
|
|
74
|
+
* through to the model provider unchanged. Fetches Slack file URLs
|
|
75
|
+
* with the bot token.
|
|
79
76
|
*/
|
|
80
|
-
export function
|
|
81
|
-
return {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
mediaType: response.headers.get("content-type") ?? undefined,
|
|
97
|
-
};
|
|
98
|
-
},
|
|
77
|
+
export function createSlackFetchFile(input) {
|
|
78
|
+
return async (url) => {
|
|
79
|
+
if (!url.startsWith("https://files.slack.com/")) {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
const token = await resolveSlackBotToken(input.botToken);
|
|
83
|
+
const response = await fetch(url, {
|
|
84
|
+
headers: { authorization: `Bearer ${token}` },
|
|
85
|
+
});
|
|
86
|
+
if (!response.ok) {
|
|
87
|
+
throw new Error(`Slack file fetch returned HTTP ${response.status} for ${url}.`);
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
bytes: Buffer.from(await response.arrayBuffer()),
|
|
91
|
+
mediaType: response.headers.get("content-type") ?? undefined,
|
|
92
|
+
};
|
|
99
93
|
};
|
|
100
94
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
export { slack, type SlackOptions } from "#public/channels/slack/slack.js";
|
|
2
2
|
export { slackChannel, type SlackApiHandle, type SlackApiResponse, type SlackChannelConfig, type SlackChannelEvents, type SlackChannelCredentials, type SlackChannelState, type SlackContext, type SlackInteractionAction, type SlackReceiveArgs, } from "#public/channels/slack/slackChannel.js";
|
|
3
|
-
export type { SlackAttachmentParams } from "#public/channels/slack/attachments.js";
|
|
4
3
|
export { Actions, Button, Card, CardText, Divider, Fields, Image, LinkButton, Modal, RadioSelect, Section, Select, SelectOption, Table, TextInput, } from "#compiled/chat/index.js";
|
|
5
4
|
export type { AdapterPostableMessage, Attachment, Author, CardElement, FileUpload, Message, PostableMessage, SentMessage, Thread, } from "#compiled/chat/index.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createLogger } from "#internal/logging.js";
|
|
2
|
-
import { buildSlackTurnMessage, collectSlackFileParts,
|
|
2
|
+
import { buildSlackTurnMessage, collectSlackFileParts, createSlackFetchFile, } from "#public/channels/slack/attachments.js";
|
|
3
3
|
import { deriveHitlResponse, isHitlAction, renderInputRequestBlocks, } from "#public/channels/slack/hitl.js";
|
|
4
4
|
import { mergeUploadPolicy } from "#public/channels/upload-policy.js";
|
|
5
5
|
import { defineChannel, POST, } from "#public/definitions/defineChannel.js";
|
|
@@ -109,7 +109,7 @@ function defaultInputRequestedHandler() {
|
|
|
109
109
|
}
|
|
110
110
|
export function slackChannel(config = {}) {
|
|
111
111
|
const uploadPolicy = mergeUploadPolicy(config.uploadPolicy);
|
|
112
|
-
const
|
|
112
|
+
const slackFetchFile = createSlackFetchFile({
|
|
113
113
|
botToken: config.credentials?.botToken,
|
|
114
114
|
});
|
|
115
115
|
let activeSend = null;
|
|
@@ -183,7 +183,7 @@ export function slackChannel(config = {}) {
|
|
|
183
183
|
const inputHandler = config.events?.["input.requested"] ?? defaultInputRequestedHandler();
|
|
184
184
|
return defineChannel({
|
|
185
185
|
state: { serializedThread: null, teamId: null },
|
|
186
|
-
|
|
186
|
+
fetchFile: slackFetchFile,
|
|
187
187
|
context(state) {
|
|
188
188
|
return rebuildSlackContext(state, config.credentials?.botToken);
|
|
189
189
|
},
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { FetchFileResult } from "#channel/adapter.js";
|
|
2
2
|
import { CHANNEL_SENTINEL } from "#channel/compiled-channel.js";
|
|
3
3
|
import type { SessionAuthContext } from "#channel/types.js";
|
|
4
4
|
import type { HandleMessageStreamEvent } from "#protocol/message.js";
|
|
@@ -7,7 +7,6 @@ import type { Session, SessionHandle } from "#channel/session.js";
|
|
|
7
7
|
export type { Session, SessionHandle } from "#channel/session.js";
|
|
8
8
|
export { POST, GET, PUT, DELETE } from "#channel/routes.js";
|
|
9
9
|
export type { RouteDefinition, RouteHandlerArgs, SendFn, SendOptions, SendPayload, GetSessionFn, } from "#channel/routes.js";
|
|
10
|
-
export type { AttachmentResolver } from "#channel/adapter.js";
|
|
11
10
|
type EventData<T extends HandleMessageStreamEvent["type"]> = Extract<HandleMessageStreamEvent, {
|
|
12
11
|
type: T;
|
|
13
12
|
}> extends {
|
|
@@ -43,12 +42,15 @@ export interface ChannelConfig<TState = undefined, TCtx = void> {
|
|
|
43
42
|
}): Promise<Session>;
|
|
44
43
|
readonly events?: ChannelEvents<TCtx>;
|
|
45
44
|
/**
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
45
|
+
* Fetches bytes for a URL encountered in `FilePart.data`.
|
|
46
|
+
*
|
|
47
|
+
* Called by the staging pipeline when it encounters a `URL` object
|
|
48
|
+
* on a `FilePart` (e.g. a Slack file URL placed there by the route
|
|
49
|
+
* handler). Return `null` to let the URL pass through to the model
|
|
50
|
+
* provider unchanged. Return bytes or {@link FetchFileResult} to
|
|
51
|
+
* stage the file to the sandbox.
|
|
50
52
|
*/
|
|
51
|
-
readonly
|
|
53
|
+
readonly fetchFile?: (url: string) => Promise<Buffer | FetchFileResult | null>;
|
|
52
54
|
}
|
|
53
55
|
export interface Channel<TState = undefined> {
|
|
54
56
|
readonly __kind: typeof CHANNEL_SENTINEL;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { CHANNEL_SENTINEL } from "#channel/compiled-channel.js";
|
|
2
2
|
import { defaultDeliverResult } from "#channel/adapter.js";
|
|
3
|
+
import { HTTP_ADAPTER_KIND } from "#channel/http.js";
|
|
3
4
|
export { POST, GET, PUT, DELETE } from "#channel/routes.js";
|
|
4
5
|
export function defineChannel(config) {
|
|
5
6
|
const adapter = buildAdapter(config);
|
|
@@ -14,7 +15,7 @@ export function defineChannel(config) {
|
|
|
14
15
|
function buildAdapter(config) {
|
|
15
16
|
const hasState = config.state != null;
|
|
16
17
|
const hasContext = config.context != null;
|
|
17
|
-
const
|
|
18
|
+
const hasFetchFile = config.fetchFile !== undefined;
|
|
18
19
|
const hasBehavior = hasState || hasContext;
|
|
19
20
|
const eventHandlers = {};
|
|
20
21
|
let hasEventHandlers = false;
|
|
@@ -42,20 +43,13 @@ function buildAdapter(config) {
|
|
|
42
43
|
};
|
|
43
44
|
}
|
|
44
45
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
// registry conflict when the adapter would otherwise reserve a
|
|
48
|
-
// framework kind with no behavior to register against it.
|
|
49
|
-
if (!hasBehavior && !hasEventHandlers && !hasAttachments) {
|
|
50
|
-
return { kind: "http" };
|
|
46
|
+
if (!hasBehavior && !hasEventHandlers && !hasFetchFile) {
|
|
47
|
+
return { kind: HTTP_ADAPTER_KIND };
|
|
51
48
|
}
|
|
52
|
-
// Channels with any authored behavior use a dedicated adapter kind so
|
|
53
|
-
// the registry can rehydrate it across step boundaries without
|
|
54
|
-
// conflicting with framework-reserved kinds.
|
|
55
49
|
return {
|
|
56
50
|
kind: "defineChannel",
|
|
57
51
|
state: hasState ? { ...config.state } : {},
|
|
58
|
-
|
|
52
|
+
fetchFile: config.fetchFile,
|
|
59
53
|
createAdapterContext(base) {
|
|
60
54
|
const state = base.state;
|
|
61
55
|
const channelCtx = hasContext ? config.context(state) : {};
|
|
@@ -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, SandboxReadFileOptions, SandboxSession, } from "#shared/sandbox-session.js";
|
|
5
|
-
export type {
|
|
6
|
-
export type SandboxDefinition<S extends SandboxSession = SandboxSession
|
|
5
|
+
export type { SandboxBootstrapUseOptions, SandboxBootstrapUseFn, SandboxSessionUseFn, SandboxBootstrapContext, SandboxSessionContext, } from "#shared/sandbox-definition.js";
|
|
6
|
+
export type SandboxDefinition<S extends SandboxSession = SandboxSession, O = Record<string, never>> = Optional<SharedSandboxDefinition<S, O>, "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, O = Record<string, never>>(definition: SandboxDefinition<S, O>): SandboxDefinition<S, O>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { SandboxBackend } from "#public/definitions/sandbox-backend.js";
|
|
2
|
-
import type { VercelSandbox } from "#public/sandbox/vercel-sandbox.js";
|
|
2
|
+
import type { VercelSandbox, VercelSandboxSessionUseOptions } from "#public/sandbox/vercel-sandbox.js";
|
|
3
3
|
/**
|
|
4
4
|
* Constructs the built-in Vercel sandbox backend.
|
|
5
5
|
*
|
|
@@ -8,4 +8,4 @@ import type { VercelSandbox } from "#public/sandbox/vercel-sandbox.js";
|
|
|
8
8
|
* timeout) lives in `onSession`'s `create()` call or
|
|
9
9
|
* `VercelSandbox.update()`.
|
|
10
10
|
*/
|
|
11
|
-
export declare function vercelBackend(): SandboxBackend<VercelSandbox>;
|
|
11
|
+
export declare function vercelBackend(): SandboxBackend<VercelSandbox, VercelSandboxSessionUseOptions>;
|
|
@@ -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 SandboxBootstrapUseOptions, type SandboxCommandOptions, type SandboxCommandResult, type SandboxDefinition, type
|
|
6
|
+
export { defineSandbox, type SandboxBootstrapContext, type SandboxBootstrapUseFn, type SandboxBootstrapUseOptions, type SandboxCommandOptions, type SandboxCommandResult, type SandboxDefinition, type SandboxReadFileOptions, type SandboxSession, type SandboxSessionContext, type SandboxSessionUseFn, } 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 } from "#public/sandbox/vercel-sandbox.js";
|
|
12
|
+
export type { VercelSandbox, VercelSandboxSessionUseOptions, } from "#public/sandbox/vercel-sandbox.js";
|
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { SandboxUpdateParams } from "#compiled/@vercel/sandbox/index.js";
|
|
2
|
+
import type { SandboxSession } from "#public/definitions/sandbox.js";
|
|
3
|
+
/**
|
|
4
|
+
* Options accepted by the Vercel backend's `onSession({ use })` hook
|
|
5
|
+
* and `VercelSandbox.update()`. Aliases the Vercel SDK's
|
|
6
|
+
* `SandboxUpdateParams` so the full live-session configuration surface
|
|
7
|
+
* is available to authors.
|
|
8
|
+
*/
|
|
9
|
+
export type VercelSandboxSessionUseOptions = SandboxUpdateParams;
|
|
2
10
|
/**
|
|
3
11
|
* Ash-owned session type returned by the Vercel backend's
|
|
4
12
|
* `onSession({ use })` hook. Extends the narrow
|
|
@@ -9,16 +17,9 @@ export interface VercelSandbox extends SandboxSession {
|
|
|
9
17
|
readonly name: string;
|
|
10
18
|
readonly persistent: boolean;
|
|
11
19
|
readonly status: string;
|
|
12
|
-
readonly networkPolicy:
|
|
20
|
+
readonly networkPolicy: SandboxUpdateParams["networkPolicy"];
|
|
13
21
|
readonly tags: Record<string, string> | undefined;
|
|
14
|
-
update(params:
|
|
15
|
-
networkPolicy?: SandboxNetworkPolicy;
|
|
16
|
-
resources?: {
|
|
17
|
-
vcpus?: number;
|
|
18
|
-
};
|
|
19
|
-
timeout?: number;
|
|
20
|
-
tags?: Record<string, string>;
|
|
21
|
-
}): Promise<void>;
|
|
22
|
+
update(params: VercelSandboxSessionUseOptions): Promise<void>;
|
|
22
23
|
stop(opts?: {
|
|
23
24
|
blocking?: boolean;
|
|
24
25
|
}): Promise<void>;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { getAdapterKind } from "#channel/adapter.js";
|
|
2
|
+
import { HTTP_ADAPTER } from "#channel/http.js";
|
|
3
|
+
import { SCHEDULE_ADAPTER } from "#channel/schedule.js";
|
|
2
4
|
import { SUBAGENT_ADAPTER } from "#execution/subagent-adapter.js";
|
|
3
5
|
import { RuntimeRegistryError } from "#internal/runtime-registry.js";
|
|
4
6
|
/**
|
|
@@ -6,7 +8,11 @@ import { RuntimeRegistryError } from "#internal/runtime-registry.js";
|
|
|
6
8
|
*
|
|
7
9
|
* Framework kinds cannot be shadowed with route-authored behavior.
|
|
8
10
|
*/
|
|
9
|
-
const FRAMEWORK_ADAPTERS = [
|
|
11
|
+
const FRAMEWORK_ADAPTERS = [
|
|
12
|
+
HTTP_ADAPTER,
|
|
13
|
+
SUBAGENT_ADAPTER,
|
|
14
|
+
SCHEDULE_ADAPTER,
|
|
15
|
+
];
|
|
10
16
|
/**
|
|
11
17
|
* Non-event-handler fields on a {@link ChannelAdapter}. Any other
|
|
12
18
|
* key on the adapter object corresponds to a stream event handler
|
|
@@ -20,7 +26,7 @@ const ADAPTER_NON_EVENT_FIELDS = new Set([
|
|
|
20
26
|
"deliver",
|
|
21
27
|
"contextProviders",
|
|
22
28
|
"createAdapterContext",
|
|
23
|
-
"
|
|
29
|
+
"fetchFile",
|
|
24
30
|
]);
|
|
25
31
|
/**
|
|
26
32
|
* Builds the runtime-owned adapter registry from framework adapters plus any
|
|
@@ -109,7 +115,7 @@ function carriesAdapterBehavior(adapter) {
|
|
|
109
115
|
if (adapter.deliver !== undefined) {
|
|
110
116
|
return true;
|
|
111
117
|
}
|
|
112
|
-
if (adapter.
|
|
118
|
+
if (adapter.fetchFile !== undefined) {
|
|
113
119
|
return true;
|
|
114
120
|
}
|
|
115
121
|
if (adapter.createAdapterContext !== undefined) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { HTTP_ADAPTER_KIND } from "#channel/http.js";
|
|
1
2
|
import { normalizeChannelDefinition } from "#internal/authored-definition/channel.js";
|
|
2
3
|
import { toErrorMessage } from "#shared/errors.js";
|
|
3
4
|
import { createResolvedModuleSourceRef, loadResolvedModuleExport, ResolveAgentError, } from "#runtime/resolve-helpers.js";
|
|
@@ -30,7 +31,7 @@ export async function resolveChannelDefinition(definition, moduleMap, nodeId) {
|
|
|
30
31
|
});
|
|
31
32
|
const matchedRoute = channelDefinition.routes.find((r) => r.method.toUpperCase() === definition.method.toUpperCase() && r.path === definition.urlPath);
|
|
32
33
|
const adapter = channelDefinition.adapter;
|
|
33
|
-
if (adapter && adapter.kind !==
|
|
34
|
+
if (adapter && adapter.kind !== HTTP_ADAPTER_KIND) {
|
|
34
35
|
adapter.kind = `channel:${definition.name}`;
|
|
35
36
|
}
|
|
36
37
|
return {
|
|
@@ -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, O = Record<string, never>> {
|
|
11
11
|
readonly session: SandboxSession;
|
|
12
|
-
readonly useSessionFn: SandboxSessionUseFn<S>;
|
|
12
|
+
readonly useSessionFn: SandboxSessionUseFn<S, O>;
|
|
13
13
|
captureState(): Promise<SandboxBackendSessionState>;
|
|
14
14
|
dispose(): Promise<void>;
|
|
15
15
|
}
|
|
@@ -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, O = Record<string, never>> {
|
|
101
101
|
/**
|
|
102
102
|
* Stable identifier for this backend implementation.
|
|
103
103
|
*
|
|
@@ -113,7 +113,7 @@ export interface SandboxBackend<S extends SandboxSession = SandboxSession> {
|
|
|
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, O>>;
|
|
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
|
|
@@ -1,47 +1,17 @@
|
|
|
1
1
|
import type { SandboxSession } from "#shared/sandbox-session.js";
|
|
2
2
|
import type { SandboxBackend } from "#shared/sandbox-backend.js";
|
|
3
|
-
/**
|
|
4
|
-
* A transform applied to network requests matching a domain rule.
|
|
5
|
-
*/
|
|
6
|
-
export interface SandboxNetworkTransformer {
|
|
7
|
-
readonly headers?: Record<string, string>;
|
|
8
|
-
}
|
|
9
|
-
/**
|
|
10
|
-
* A rule applied to requests matching a domain in the network policy.
|
|
11
|
-
*/
|
|
12
|
-
export interface SandboxNetworkPolicyRule {
|
|
13
|
-
readonly transform?: SandboxNetworkTransformer[];
|
|
14
|
-
}
|
|
15
|
-
/**
|
|
16
|
-
* Network policy to define network restrictions for the sandbox.
|
|
17
|
-
*
|
|
18
|
-
* - `"allow-all"`: Full internet access (default). All traffic is allowed.
|
|
19
|
-
* - `"deny-all"`: No internet access. All traffic is denied.
|
|
20
|
-
* - Object: Custom access with explicit allow/deny lists.
|
|
21
|
-
*/
|
|
22
|
-
export type SandboxNetworkPolicy = "allow-all" | "deny-all" | {
|
|
23
|
-
readonly allow?: string[] | Readonly<Record<string, SandboxNetworkPolicyRule[]>>;
|
|
24
|
-
};
|
|
25
3
|
export interface SandboxBootstrapUseOptions {
|
|
26
4
|
readonly runtime?: string;
|
|
27
5
|
readonly ports?: number[];
|
|
28
6
|
readonly env?: Record<string, string>;
|
|
29
7
|
}
|
|
30
|
-
export interface SandboxSessionUseOptions {
|
|
31
|
-
readonly networkPolicy?: SandboxNetworkPolicy;
|
|
32
|
-
readonly resources?: {
|
|
33
|
-
vcpus?: number;
|
|
34
|
-
};
|
|
35
|
-
readonly timeout?: number;
|
|
36
|
-
readonly tags?: Record<string, string>;
|
|
37
|
-
}
|
|
38
8
|
export type SandboxBootstrapUseFn = (options?: SandboxBootstrapUseOptions) => Promise<SandboxSession>;
|
|
39
|
-
export type SandboxSessionUseFn<S extends SandboxSession = SandboxSession
|
|
9
|
+
export type SandboxSessionUseFn<S extends SandboxSession = SandboxSession, O = Record<string, never>> = (options?: O) => Promise<S>;
|
|
40
10
|
export interface SandboxBootstrapContext {
|
|
41
11
|
readonly use: SandboxBootstrapUseFn;
|
|
42
12
|
}
|
|
43
|
-
export interface SandboxSessionContext<S extends SandboxSession = SandboxSession
|
|
44
|
-
readonly use: SandboxSessionUseFn<S>;
|
|
13
|
+
export interface SandboxSessionContext<S extends SandboxSession = SandboxSession, O = Record<string, never>> {
|
|
14
|
+
readonly use: SandboxSessionUseFn<S, O>;
|
|
45
15
|
}
|
|
46
16
|
/**
|
|
47
17
|
* Public sandbox definition authored in `agent/sandbox.ts` (shorthand)
|
|
@@ -56,7 +26,7 @@ export interface SandboxSessionContext<S extends SandboxSession = SandboxSession
|
|
|
56
26
|
* `subagents/<name>/sandbox.ts` (or the folder form) and do not inherit
|
|
57
27
|
* their parent's sandbox (skill seeds differ per agent).
|
|
58
28
|
*/
|
|
59
|
-
export interface SandboxDefinition<S extends SandboxSession = SandboxSession
|
|
29
|
+
export interface SandboxDefinition<S extends SandboxSession = SandboxSession, O = Record<string, never>> {
|
|
60
30
|
/**
|
|
61
31
|
* Backend that runs this sandbox.
|
|
62
32
|
*
|
|
@@ -66,7 +36,7 @@ export interface SandboxDefinition<S extends SandboxSession = SandboxSession> {
|
|
|
66
36
|
* everywhere else. Set `backend` explicitly to pin the sandbox to a
|
|
67
37
|
* specific backend regardless of environment.
|
|
68
38
|
*/
|
|
69
|
-
backend: SandboxBackend<S>;
|
|
39
|
+
backend: SandboxBackend<S, O>;
|
|
70
40
|
bootstrap?(input: SandboxBootstrapContext): Promise<void> | void;
|
|
71
|
-
onSession?(input: SandboxSessionContext<S>): Promise<void> | void;
|
|
41
|
+
onSession?(input: SandboxSessionContext<S, O>): Promise<void> | void;
|
|
72
42
|
}
|