experimental-ash 0.43.0 → 0.45.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/bin/ash.js +1 -0
  3. package/dist/docs/internals/mechanical-invariants.md +16 -0
  4. package/dist/docs/public/advanced/instrumentation.md +71 -21
  5. package/dist/docs/public/advanced/typescript-api.md +1 -1
  6. package/dist/docs/public/sandbox.md +38 -0
  7. package/dist/src/channel/compiled-channel.d.ts +4 -1
  8. package/dist/src/channel/compiled-channel.js +1 -1
  9. package/dist/src/channel/routes.d.ts +8 -10
  10. package/dist/src/compiled/.vendor-stamp.json +2 -2
  11. package/dist/src/compiled/@vercel/sandbox/index.d.ts +24 -19
  12. package/dist/src/compiled/@vercel/sandbox/index.js +5 -5
  13. package/dist/src/compiled/@vercel/sandbox/network-policy.d.ts +161 -0
  14. package/dist/src/compiled/@vercel/sandbox/package.json +1 -1
  15. package/dist/src/compiled/@workflow/core/runtime.js +13 -13
  16. package/dist/src/compiled/_chunks/node/{auth-CVVvWjaK.js → auth-BsyzphzW.js} +1 -1
  17. package/dist/src/compiled/_chunks/node/{version-nR4RSpFw.js → version-BGue04qw.js} +1 -1
  18. package/dist/src/compiled/just-bash/index.d.ts +23 -2
  19. package/dist/src/compiled/just-bash/network/types.d.ts +155 -0
  20. package/dist/src/compiler/artifacts.d.ts +1 -0
  21. package/dist/src/compiler/artifacts.js +1 -1
  22. package/dist/src/compiler/channel-instrumentation-types.d.ts +8 -0
  23. package/dist/src/compiler/channel-instrumentation-types.js +2 -0
  24. package/dist/src/context/dynamic-skill-lifecycle.d.ts +4 -3
  25. package/dist/src/context/dynamic-skill-lifecycle.js +1 -1
  26. package/dist/src/context/keys.d.ts +11 -4
  27. package/dist/src/execution/sandbox/bindings/local.js +1 -1
  28. package/dist/src/execution/sandbox/bindings/vercel.js +1 -1
  29. package/dist/src/execution/sandbox/session.d.ts +6 -1
  30. package/dist/src/execution/sandbox/session.js +1 -1
  31. package/dist/src/harness/tool-loop.js +1 -1
  32. package/dist/src/internal/application/package.js +1 -1
  33. package/dist/src/packages/ash-scaffold/src/channels.js +1 -1
  34. package/dist/src/packages/ash-scaffold/src/web-template.js +1 -0
  35. package/dist/src/public/channels/discord/discordChannel.d.ts +5 -2
  36. package/dist/src/public/channels/index.d.ts +1 -1
  37. package/dist/src/public/channels/slack/slackChannel.d.ts +6 -8
  38. package/dist/src/public/channels/teams/teamsChannel.d.ts +5 -2
  39. package/dist/src/public/channels/telegram/telegramChannel.d.ts +5 -2
  40. package/dist/src/public/channels/twilio/twilioChannel.d.ts +6 -3
  41. package/dist/src/public/definitions/defineChannel.d.ts +6 -3
  42. package/dist/src/public/definitions/instrumentation.d.ts +1 -152
  43. package/dist/src/public/definitions/instrumentation.js +1 -1
  44. package/dist/src/public/definitions/sandbox.d.ts +1 -1
  45. package/dist/src/public/instrumentation/index.d.ts +178 -1
  46. package/dist/src/public/instrumentation/index.js +1 -1
  47. package/dist/src/public/sandbox/index.d.ts +1 -0
  48. package/dist/src/runtime/resolve-channel.js +1 -1
  49. package/dist/src/shared/sandbox-network-policy.d.ts +23 -0
  50. package/dist/src/shared/sandbox-network-policy.js +1 -0
  51. package/dist/src/shared/sandbox-session.d.ts +36 -0
  52. package/dist/src/shared/skill-package.d.ts +7 -0
  53. package/dist/src/shared/skill-package.js +1 -1
  54. package/package.json +2 -2
@@ -1 +1 @@
1
- function defineInstrumentation(e){return e}export{defineInstrumentation};
1
+ export*from"#public/instrumentation/index.js";export{};
@@ -1,6 +1,6 @@
1
1
  import type { Optional } from "#shared/optional.js";
2
2
  import type { SandboxDefinition as SharedSandboxDefinition } from "#shared/sandbox-definition.js";
3
- export type { SandboxCommandResult, SandboxProcess, SandboxReadBinaryFileOptions, SandboxReadFileOptions, SandboxReadTextFileOptions, SandboxRunOptions, SandboxSession, SandboxSpawnOptions, SandboxWriteBinaryFileOptions, SandboxWriteFileOptions, SandboxWriteTextFileOptions, } from "#shared/sandbox-session.js";
3
+ export type { SandboxCommandResult, SandboxProcess, SandboxReadBinaryFileOptions, SandboxReadFileOptions, SandboxRemovePathOptions, SandboxReadTextFileOptions, SandboxRunOptions, SandboxSession, SandboxSpawnOptions, SandboxWriteBinaryFileOptions, SandboxWriteFileOptions, SandboxWriteTextFileOptions, } from "#shared/sandbox-session.js";
4
4
  export type { SandboxBootstrapUseFn, SandboxSessionUseFn, SandboxBootstrapContext, SandboxSessionContext, } from "#shared/sandbox-definition.js";
5
5
  export type SandboxDefinition<BO = Record<string, never>, SO = Record<string, never>> = Optional<SharedSandboxDefinition<BO, SO>, "backend">;
6
6
  /**
@@ -1 +1,178 @@
1
- export * from "#public/definitions/instrumentation.js";
1
+ import type { ExactDefinition } from "#public/definitions/exact.js";
2
+ /**
3
+ * Instrumentation authoring helpers for `agent/instrumentation.ts`.
4
+ */
5
+ import type { ModelMessage, SystemModelMessage } from "ai";
6
+ import type { SessionAuthContext, SessionParent } from "#channel/types.js";
7
+ import type { Channel } from "#public/definitions/defineChannel.js";
8
+ /**
9
+ * Context passed to the {@link InstrumentationDefinition.setup} callback.
10
+ */
11
+ export interface InstrumentationSetupContext {
12
+ /**
13
+ * The agent name declared by `defineAgent`.
14
+ *
15
+ * Use this as the `serviceName` for `registerOTel` instead of
16
+ * hard-coding a string — it prevents copy-paste drift across agents.
17
+ */
18
+ readonly agentName: string;
19
+ }
20
+ /**
21
+ * User-authored metadata attached to AI SDK telemetry spans.
22
+ *
23
+ * Keys beginning with `ash.` are reserved for framework-owned metadata
24
+ * and are ignored when returned from authored instrumentation.
25
+ */
26
+ export type InstrumentationMetadata = Readonly<Record<string, string>>;
27
+ /**
28
+ * Base channel metadata shape used by framework channel kinds.
29
+ */
30
+ export type InstrumentationChannelMetadata = Readonly<Record<string, unknown>>;
31
+ /**
32
+ * Channel metadata projections keyed by instrumentation channel kind.
33
+ *
34
+ * Channel packages and user-authored channels declaration-merge additional
35
+ * `channel:<registered-name>` entries into this map so `input.channel.metadata`
36
+ * narrows after checking `input.channel.kind`.
37
+ */
38
+ export interface ChannelMetadataMap {
39
+ readonly http: InstrumentationChannelMetadata;
40
+ readonly schedule: InstrumentationChannelMetadata;
41
+ readonly subagent: InstrumentationChannelMetadata;
42
+ readonly unknown: InstrumentationChannelMetadata;
43
+ }
44
+ export type InstrumentationChannelKind = keyof ChannelMetadataMap;
45
+ /**
46
+ * Authored channel values keyed by path-derived instrumentation channel kind.
47
+ *
48
+ * Ash generates declaration-merged entries for authored channel files. Use
49
+ * this map through {@link isChannel}; authored code should not merge it by
50
+ * hand except for unusual setups outside the compiler-owned path.
51
+ */
52
+ export interface ChannelReferenceMap {
53
+ }
54
+ /**
55
+ * Channel projection exposed to instrumentation callbacks.
56
+ *
57
+ * `kind` is the channel identity Ash has for the current turn (`"http"`,
58
+ * `"schedule"`, `"subagent"`, or `channel:<name>` for authored channels,
59
+ * where `<name>` is the channel's filename under `agent/channels/`).
60
+ */
61
+ export interface InstrumentationChannelForKind<K extends InstrumentationChannelKind> {
62
+ readonly kind: K;
63
+ readonly metadata: ChannelMetadataMap[K];
64
+ }
65
+ export type InstrumentationChannel = {
66
+ readonly [K in InstrumentationChannelKind]: InstrumentationChannelForKind<K>;
67
+ }[InstrumentationChannelKind];
68
+ type ChannelReferenceKind<TChannel> = {
69
+ readonly [K in keyof ChannelReferenceMap]: [TChannel] extends [ChannelReferenceMap[K]] ? [ChannelReferenceMap[K]] extends [TChannel] ? K : never : never;
70
+ }[keyof ChannelReferenceMap];
71
+ export type InstrumentationChannelForChannel<TChannel> = Extract<InstrumentationChannel, {
72
+ readonly kind: Extract<ChannelReferenceKind<TChannel>, InstrumentationChannelKind>;
73
+ }>;
74
+ export interface InstrumentationSession {
75
+ readonly auth: {
76
+ readonly current: SessionAuthContext | null;
77
+ readonly initiator: SessionAuthContext | null;
78
+ };
79
+ readonly id: string;
80
+ readonly parent?: SessionParent;
81
+ }
82
+ export interface InstrumentationTurn {
83
+ readonly id: string;
84
+ readonly sequence: number;
85
+ }
86
+ export interface InstrumentationStep {
87
+ readonly index: number;
88
+ }
89
+ export interface InstrumentationModelInput {
90
+ readonly instructions: string | readonly SystemModelMessage[] | undefined;
91
+ readonly messages: readonly ModelMessage[];
92
+ }
93
+ /**
94
+ * Input passed to `metadata["step.started"]`.
95
+ *
96
+ * The callback runs after Ash has built the final model input for this
97
+ * model-call attempt and before the AI SDK model call is constructed.
98
+ */
99
+ export interface InstrumentationStepStartedMetadataInput {
100
+ readonly channel: InstrumentationChannel;
101
+ readonly modelInput: InstrumentationModelInput;
102
+ readonly session: InstrumentationSession;
103
+ readonly step: InstrumentationStep;
104
+ readonly turn: InstrumentationTurn;
105
+ }
106
+ /**
107
+ * Metadata hooks accepted by {@link defineInstrumentation}.
108
+ */
109
+ export interface InstrumentationMetadataConfig {
110
+ /**
111
+ * Per-attempt metadata resolved before the model call so child spans
112
+ * created by the AI SDK inherit the returned values.
113
+ */
114
+ readonly "step.started"?: (input: InstrumentationStepStartedMetadataInput) => InstrumentationMetadata;
115
+ }
116
+ /**
117
+ * Authored instrumentation settings accepted by `defineInstrumentation`.
118
+ *
119
+ * The presence of a `defineInstrumentation` export implicitly enables
120
+ * telemetry — there is no separate `isEnabled` toggle.
121
+ */
122
+ export interface InstrumentationDefinition {
123
+ /**
124
+ * Override the default function identifier attached to telemetry spans.
125
+ *
126
+ * When omitted, Ash derives the identifier from the agent name.
127
+ */
128
+ readonly functionId?: string;
129
+ /**
130
+ * Additional metadata merged into AI SDK telemetry spans.
131
+ */
132
+ readonly metadata?: InstrumentationMetadataConfig;
133
+ /**
134
+ * Whether to record full model inputs in telemetry spans.
135
+ *
136
+ * Defaults to `true` when `instrumentation.ts` is present. Set to `false`
137
+ * to disable recording inputs, which may be useful if inputs contain
138
+ * sensitive content or you want to reduce span payload size.
139
+ */
140
+ readonly recordInputs?: boolean;
141
+ /**
142
+ * Whether to record full model outputs in telemetry spans.
143
+ *
144
+ * Defaults to `true` when `instrumentation.ts` is present. Set to `false`
145
+ * to disable recording outputs.
146
+ */
147
+ readonly recordOutputs?: boolean;
148
+ /**
149
+ * Optional setup callback invoked at server startup with the resolved
150
+ * agent name.
151
+ *
152
+ * Use this to call `registerOTel` or any other OTel provider setup.
153
+ * The `context.agentName` value comes from `defineAgent`, so you never
154
+ * need to hard-code a service name.
155
+ */
156
+ readonly setup?: (context: InstrumentationSetupContext) => void;
157
+ }
158
+ /**
159
+ * Defines instrumentation settings for the agent application.
160
+ *
161
+ * Export the result as the default export of `agent/instrumentation.ts`.
162
+ * Ash picks up these settings at server startup and applies them to every
163
+ * AI SDK model call.
164
+ *
165
+ * The `setup` callback is invoked later by the framework with the resolved
166
+ * agent name — it is not called during `defineInstrumentation` itself.
167
+ */
168
+ export declare function defineInstrumentation<T extends InstrumentationDefinition>(definition: ExactDefinition<T, InstrumentationDefinition>): T;
169
+ /**
170
+ * Narrows an instrumentation channel by comparing it to an app-owned channel
171
+ * value imported from `agent/channels/*`.
172
+ *
173
+ * The comparison uses the compiler's path-derived `channel:<slug>` identity.
174
+ * It does not read durable channel state; metadata remains the explicit
175
+ * projection returned by the channel's `metadata(state)` function.
176
+ */
177
+ export declare function isChannel<TChannel extends Channel<any, any, any>>(channel: InstrumentationChannel, target: TChannel): channel is InstrumentationChannelForChannel<TChannel>;
178
+ export {};
@@ -1 +1 @@
1
- export*from"#public/definitions/instrumentation.js";export{};
1
+ import{getChannelInstrumentationKind}from"#channel/compiled-channel.js";function defineInstrumentation(e){return e}function isChannel(t,n){return t.kind===getChannelInstrumentationKind(n)}export{defineInstrumentation,isChannel};
@@ -4,6 +4,7 @@
4
4
  */
5
5
  export { defineSandbox, type SandboxBootstrapContext, type SandboxBootstrapUseFn, type SandboxCommandResult, type SandboxDefinition, type SandboxProcess, type SandboxReadBinaryFileOptions, type SandboxReadFileOptions, type SandboxReadTextFileOptions, type SandboxRunOptions, type SandboxSession, type SandboxSpawnOptions, type SandboxSessionContext, type SandboxSessionUseFn, type SandboxWriteBinaryFileOptions, type SandboxWriteFileOptions, type SandboxWriteTextFileOptions, } from "#public/definitions/sandbox.js";
6
6
  export type { SandboxBackend, SandboxBackendCreateInput, SandboxBackendHandle, SandboxBackendPrewarmInput, SandboxBackendRuntimeContext, SandboxBackendSessionState, SandboxSeedFile, } from "#public/definitions/sandbox-backend.js";
7
+ export type { SandboxNetworkPolicy } from "#shared/sandbox-network-policy.js";
7
8
  export { SandboxTemplateNotProvisionedError } from "#public/definitions/sandbox-backend.js";
8
9
  export { defaultBackend } from "#public/sandbox/backends/default.js";
9
10
  export { localBackend } from "#public/sandbox/backends/local.js";
@@ -1 +1 @@
1
- import{toErrorMessage}from"#shared/errors.js";import{normalizeChannelDefinition}from"#internal/authored-definition/channel.js";import{ResolveAgentError,createResolvedModuleSourceRef,loadResolvedModuleExport}from"#runtime/resolve-helpers.js";import{HTTP_ADAPTER_KIND}from"#channel/http.js";async function resolveChannelDefinition(r,i,a){try{let e=normalizeChannelDefinition(await loadResolvedModuleExport({definition:r,kindLabel:`channel`,moduleMap:i,nodeId:a}),`Expected the channel export "${r.exportName??`default`}" from "${r.logicalPath}" to match the public Ash shape.`),n=createResolvedModuleSourceRef({exportName:r.exportName,logicalPath:r.logicalPath,sourceId:r.sourceId}),o=e.routes.find(e=>e.method.toUpperCase()===r.method.toUpperCase()&&e.path===r.urlPath),s=e.adapter;return s&&s.kind!==HTTP_ADAPTER_KIND&&(s.kind=`channel:${r.name}`),{name:r.name,method:r.method,urlPath:r.urlPath,fetch:async(e,t)=>o?o.handler(e,t):Response.json({error:`No matching route handler.`,ok:!1},{status:404}),handler:o?.handler,receive:e.receive,definition:e,adapter:s,...n}}catch(t){throw t instanceof ResolveAgentError?t:new ResolveAgentError(`Failed to attach the channel definition from "${r.logicalPath}": ${toErrorMessage(t)}`,{logicalPath:r.logicalPath,sourceId:r.sourceId})}}export{resolveChannelDefinition};
1
+ import{setChannelInstrumentationKind}from"#channel/compiled-channel.js";import{toErrorMessage}from"#shared/errors.js";import{normalizeChannelDefinition}from"#internal/authored-definition/channel.js";import{ResolveAgentError,createResolvedModuleSourceRef,loadResolvedModuleExport}from"#runtime/resolve-helpers.js";import{HTTP_ADAPTER_KIND}from"#channel/http.js";async function resolveChannelDefinition(r,i,a){try{let t=normalizeChannelDefinition(await loadResolvedModuleExport({definition:r,kindLabel:`channel`,moduleMap:i,nodeId:a}),`Expected the channel export "${r.exportName??`default`}" from "${r.logicalPath}" to match the public Ash shape.`),n=createResolvedModuleSourceRef({exportName:r.exportName,logicalPath:r.logicalPath,sourceId:r.sourceId}),o=t.routes.find(e=>e.method.toUpperCase()===r.method.toUpperCase()&&e.path===r.urlPath),s=`channel:${r.name}`;setChannelInstrumentationKind(t,s);let c=t.adapter;return c&&c.kind!==HTTP_ADAPTER_KIND&&(c.kind=s),{name:r.name,method:r.method,urlPath:r.urlPath,fetch:async(e,t)=>o?o.handler(e,t):Response.json({error:`No matching route handler.`,ok:!1},{status:404}),handler:o?.handler,receive:t.receive,definition:t,adapter:c,...n}}catch(e){throw e instanceof ResolveAgentError?e:new ResolveAgentError(`Failed to attach the channel definition from "${r.logicalPath}": ${toErrorMessage(e)}`,{logicalPath:r.logicalPath,sourceId:r.sourceId})}}export{resolveChannelDefinition};
@@ -0,0 +1,23 @@
1
+ import type { NetworkPolicy } from "#compiled/@vercel/sandbox/index.js";
2
+ /**
3
+ * Firewall network policy applied to a live sandbox session.
4
+ *
5
+ * Ash-owned alias of the backend network-policy shape. Use it to restrict
6
+ * egress (`"deny-all"`, an allow-list) or to broker credentials onto
7
+ * outgoing requests — a per-domain `transform` injects headers at the
8
+ * firewall so secrets never enter the sandbox process:
9
+ *
10
+ * ```ts
11
+ * const sandbox = await ctx.getSandbox();
12
+ * await sandbox.setNetworkPolicy({
13
+ * allow: {
14
+ * "github.com": [{ transform: [{ headers: { authorization: "Basic …" } }] }],
15
+ * "*": [],
16
+ * },
17
+ * });
18
+ * ```
19
+ *
20
+ * The local backend rejects `setNetworkPolicy`: just-bash cannot update its
21
+ * network policy at run time and runs no binaries to govern.
22
+ */
23
+ export type SandboxNetworkPolicy = NetworkPolicy;
@@ -0,0 +1 @@
1
+ export{};
@@ -1,4 +1,5 @@
1
1
  import type { Experimental_Sandbox as AiSdkSandbox } from "ai";
2
+ import type { SandboxNetworkPolicy } from "#shared/sandbox-network-policy.js";
2
3
  /**
3
4
  * Options for running one command in a sandbox. Shape mirrors the AI
4
5
  * SDK {@link AiSdkSandbox} `run` argument so authored code that targets
@@ -49,6 +50,19 @@ export type SandboxWriteBinaryFileOptions = Parameters<AiSdkSandbox["writeBinary
49
50
  * Options for writing one text file to a sandbox.
50
51
  */
51
52
  export type SandboxWriteTextFileOptions = Parameters<AiSdkSandbox["writeTextFile"]>[0];
53
+ /**
54
+ * Options for removing a path from a sandbox.
55
+ *
56
+ * Relative paths resolve from `/workspace`; absolute paths pass through.
57
+ * `force` ignores missing paths. `recursive` permits removing non-empty
58
+ * directories.
59
+ */
60
+ export interface SandboxRemovePathOptions {
61
+ readonly abortSignal?: AbortSignal;
62
+ readonly force?: boolean;
63
+ readonly path: string;
64
+ readonly recursive?: boolean;
65
+ }
52
66
  /**
53
67
  * Public Ash-owned sandbox session exposed to authored lifecycle hooks.
54
68
  *
@@ -83,6 +97,26 @@ export interface SandboxSession extends Pick<AiSdkSandbox, "run" | "spawn" | "re
83
97
  * The read and write methods already apply this internally.
84
98
  */
85
99
  resolvePath(path: string): string;
100
+ /**
101
+ * Applies a firewall network policy to this live sandbox at run time —
102
+ * for changing the policy *during* a turn (e.g. brokering a credential
103
+ * resolved mid-turn, or tightening egress after fetching data). A
104
+ * per-domain `transform` injects headers at the firewall so secrets
105
+ * never enter the sandbox process. The policy takes effect from the time
106
+ * the call resolves, so await it before the egress you want governed.
107
+ *
108
+ * When the policy is known at session start, prefer configuring it up
109
+ * front in the sandbox backend factory or `onSession`'s `use()`. The
110
+ * local backend rejects this call: just-bash cannot update its network
111
+ * policy at run time and runs no binaries to govern.
112
+ */
113
+ setNetworkPolicy(policy: SandboxNetworkPolicy): Promise<void>;
114
+ /**
115
+ * Removes one file or directory from the sandbox filesystem.
116
+ *
117
+ * Relative paths resolve from `/workspace`; absolute paths pass through.
118
+ */
119
+ removePath(options: SandboxRemovePathOptions): Promise<void>;
86
120
  }
87
121
  /**
88
122
  * Internal sandbox session, used to construct the public {@link SandboxSession}.
@@ -103,6 +137,8 @@ export interface InternalSandboxSession extends Pick<AiSdkSandbox, "spawn" | "re
103
137
  * Stable identifier surfaced on the public {@link SandboxSession}.
104
138
  */
105
139
  readonly id: string;
140
+ /** Removes an already-resolved path from the backend filesystem. */
141
+ removePath(options: SandboxRemovePathOptions): Promise<void>;
106
142
  /** Translates a user-facing path to the backend's native path. */
107
143
  resolvePath(path: string): string;
108
144
  }
@@ -36,6 +36,13 @@ export declare function writeSkillPackageToSandbox(input: {
36
36
  readonly sandbox: SandboxSession;
37
37
  readonly skill: MaterializableSkillPackage;
38
38
  }): Promise<void>;
39
+ /**
40
+ * Removes a skill package from the live sandbox.
41
+ */
42
+ export declare function removeSkillPackageFromSandbox(input: {
43
+ readonly sandbox: SandboxSession;
44
+ readonly name: string;
45
+ }): Promise<void>;
39
46
  /**
40
47
  * Validates a runtime-contributed skill name before it becomes one path
41
48
  * segment under `/workspace/skills`.
@@ -1 +1 @@
1
- import{dirname,join}from"node:path";import{mkdir,writeFile}from"node:fs/promises";import{Buffer}from"node:buffer";function normalizeSkillPackage(e){assertSafeSkillPackageName(e.name);let t=[{content:Buffer.from(e.markdown,`utf8`),relativePath:`SKILL.md`}];for(let[n,r]of Object.entries(e.files??{}))assertSafeSkillPackageFilePath(n),t.push({content:contentToBuffer(r),relativePath:n});return t.sort((e,t)=>comparePaths(e.relativePath,t.relativePath)),{description:e.description,files:t,license:e.license,markdown:e.markdown,metadata:e.metadata===void 0?void 0:{...e.metadata},name:e.name}}async function writeSkillPackageDirectory(i){for(let a of i.skill.files){let o=join(i.rootPath,`skills`,i.skill.name,a.relativePath);await mkdir(dirname(o),{recursive:!0}),await writeFile(o,a.content)}}async function writeSkillPackageToSandbox(e){for(let t of e.skill.files)await e.sandbox.writeBinaryFile({content:t.content,path:`/workspace/skills/${e.skill.name}/${t.relativePath}`})}function assertSafeSkillPackageName(e){if(e.length===0||e.trim()!==e||e.startsWith(`.`)||e.includes(`/`)||e.includes(`\\`)||e.includes(`..`)||/^[A-Za-z]:/.test(e))throw Error(`Expected skill name to be a non-empty safe path segment without whitespace, separators, "." prefix, or "..".`)}function assertSafeSkillPackageFilePath(e){if(e===`SKILL.md`)throw Error(`Skill package files must not include "SKILL.md"; Ash generates it.`);if(e.length===0||e.startsWith(`/`)||e.includes(`\\`)||/^[A-Za-z]:/.test(e)||e.split(`/`).some(e=>e.length===0||e===`.`||e===`..`))throw Error(`Expected skill package file paths to be relative POSIX paths.`)}function contentToBuffer(e){return typeof e==`string`?Buffer.from(e,`utf8`):Buffer.from(e)}function comparePaths(e,t){return e<t?-1:+(e>t)}export{assertSafeSkillPackageName,normalizeSkillPackage,writeSkillPackageDirectory,writeSkillPackageToSandbox};
1
+ import{dirname,join}from"node:path";import{mkdir,writeFile}from"node:fs/promises";import{Buffer}from"node:buffer";const WORKSPACE_ROOT=`/workspace`;function normalizeSkillPackage(e){assertSafeSkillPackageName(e.name);let t=[{content:Buffer.from(e.markdown,`utf8`),relativePath:`SKILL.md`}];for(let[n,r]of Object.entries(e.files??{}))assertSafeSkillPackageFilePath(n),t.push({content:contentToBuffer(r),relativePath:n});return t.sort((e,t)=>comparePaths(e.relativePath,t.relativePath)),{description:e.description,files:t,license:e.license,markdown:e.markdown,metadata:e.metadata===void 0?void 0:{...e.metadata},name:e.name}}async function writeSkillPackageDirectory(i){for(let a of i.skill.files){let o=join(i.rootPath,`skills`,i.skill.name,a.relativePath);await mkdir(dirname(o),{recursive:!0}),await writeFile(o,a.content)}}async function writeSkillPackageToSandbox(e){for(let t of e.skill.files)await e.sandbox.writeBinaryFile({content:t.content,path:`${WORKSPACE_ROOT}/skills/${e.skill.name}/${t.relativePath}`})}async function removeSkillPackageFromSandbox(e){assertSafeSkillPackageName(e.name),await e.sandbox.removePath({force:!0,path:`${WORKSPACE_ROOT}/skills/${e.name}`,recursive:!0})}function assertSafeSkillPackageName(e){if(e.length===0||e.startsWith(`.`)||e.includes(`/`)||e.includes(`\\`)||e.includes(`..`)||!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(e)||/^[A-Za-z]:/.test(e))throw Error(`Expected skill name to be a non-empty shell-safe path segment starting with an alphanumeric character and containing only alphanumerics, ".", "_", or "-".`)}function assertSafeSkillPackageFilePath(e){if(e===`SKILL.md`)throw Error(`Skill package files must not include "SKILL.md"; Ash generates it.`);if(e.length===0||e.startsWith(`/`)||e.includes(`\\`)||/^[A-Za-z]:/.test(e)||e.split(`/`).some(e=>e.length===0||e===`.`||e===`..`))throw Error(`Expected skill package file paths to be relative POSIX paths.`)}function contentToBuffer(e){return typeof e==`string`?Buffer.from(e,`utf8`):Buffer.from(e)}function comparePaths(e,t){return e<t?-1:+(e>t)}export{assertSafeSkillPackageName,normalizeSkillPackage,removeSkillPackageFromSandbox,writeSkillPackageDirectory,writeSkillPackageToSandbox};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "experimental-ash",
3
- "version": "0.43.0",
3
+ "version": "0.45.0",
4
4
  "bin": {
5
5
  "ash": "./bin/ash.js",
6
6
  "experimental-ash": "./bin/ash.js"
@@ -188,7 +188,7 @@
188
188
  "@types/react": "19.2.15",
189
189
  "@types/react-test-renderer": "19.1.0",
190
190
  "@vercel/oidc": "3.5.0",
191
- "@vercel/sandbox": "2.0.1",
191
+ "@vercel/sandbox": "2.1.0",
192
192
  "@workflow/core": "5.0.0-beta.10",
193
193
  "@workflow/errors": "5.0.0-beta.6",
194
194
  "@workflow/world": "5.0.0-beta.5",