experimental-ash 0.22.2 → 0.23.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 +14 -0
- package/dist/docs/public/faqs.md +67 -0
- package/dist/docs/public/meta.json +1 -0
- package/dist/docs/public/schedules.md +11 -0
- package/dist/docs/public/session-context.md +46 -89
- package/dist/docs/public/skills.md +13 -0
- package/dist/docs/public/subagents.md +12 -6
- package/dist/docs/public/tools.md +9 -13
- package/dist/docs/public/typescript-api.md +2 -2
- package/dist/src/chunks/{client-CKsU8Li3.js → client-nshDsWNF.js} +1 -1
- package/dist/src/chunks/{dev-authored-source-watcher-BLzYWh05.js → dev-authored-source-watcher-d_35Mp8T.js} +1 -1
- package/dist/src/chunks/{host-DREC8e8Z.js → host-tji7W0Nn.js} +2 -2
- package/dist/src/chunks/paths-YoCQlavu.js +89 -0
- package/dist/src/chunks/{prewarm-hz8p2jlZ.js → prewarm-6duWvvb5.js} +1 -1
- package/dist/src/cli/commands/info.js +1 -1
- package/dist/src/cli/dev/repl.js +2 -2
- package/dist/src/cli/run.js +1 -1
- package/dist/src/client/session.js +8 -0
- package/dist/src/client/types.d.ts +12 -1
- package/dist/src/compiled/.vendor-stamp.json +1 -1
- package/dist/src/compiled/@workflow/core/_ms.d.ts +4 -0
- package/dist/src/compiled/@workflow/core/_workflow-serde.d.ts +5 -0
- package/dist/src/compiled/@workflow/core/_workflow-utils.d.ts +8 -0
- package/dist/src/compiled/@workflow/core/_workflow-world.d.ts +59 -0
- package/dist/src/compiled/@workflow/core/capabilities.d.ts +45 -0
- package/dist/src/compiled/@workflow/core/capture-stack.d.ts +16 -0
- package/dist/src/compiled/@workflow/core/class-serialization.d.ts +31 -0
- package/dist/src/compiled/@workflow/core/classify-error.d.ts +19 -0
- package/dist/src/compiled/@workflow/core/context-errors.d.ts +27 -0
- package/dist/src/compiled/@workflow/core/context-violation-error.d.ts +97 -0
- package/dist/src/compiled/@workflow/core/create-hook.d.ts +179 -0
- package/dist/src/compiled/@workflow/core/define-hook.d.ts +68 -0
- package/dist/src/compiled/@workflow/core/describe-error.d.ts +70 -0
- package/dist/src/compiled/@workflow/core/encryption.d.ts +45 -0
- package/dist/src/compiled/@workflow/core/events-consumer.d.ts +64 -0
- package/dist/src/compiled/@workflow/core/flushable-stream.d.ts +82 -0
- package/dist/src/compiled/@workflow/core/global.d.ts +48 -0
- package/dist/src/compiled/@workflow/core/index.d.ts +19 -38
- package/dist/src/compiled/@workflow/core/log-format.d.ts +25 -0
- package/dist/src/compiled/@workflow/core/logger.d.ts +29 -0
- package/dist/src/compiled/@workflow/core/private.d.ts +59 -10
- package/dist/src/compiled/@workflow/core/runtime/constants.d.ts +4 -0
- package/dist/src/compiled/@workflow/core/runtime/get-port-lazy.d.ts +10 -0
- package/dist/src/compiled/@workflow/core/runtime/get-world-lazy.d.ts +32 -0
- package/dist/src/compiled/@workflow/core/runtime/helpers.d.ts +97 -0
- package/dist/src/compiled/@workflow/core/runtime/resume-hook.d.ts +77 -0
- package/dist/src/compiled/@workflow/core/runtime/run.d.ts +134 -0
- package/dist/src/compiled/@workflow/core/runtime/runs.d.ts +50 -0
- package/dist/src/compiled/@workflow/core/runtime/start.d.ts +59 -0
- package/dist/src/compiled/@workflow/core/runtime/step-executor.d.ts +40 -0
- package/dist/src/compiled/@workflow/core/runtime/step-handler.d.ts +2 -0
- package/dist/src/compiled/@workflow/core/runtime/suspension-handler.d.ts +42 -0
- package/dist/src/compiled/@workflow/core/runtime/world-init.d.ts +75 -0
- package/dist/src/compiled/@workflow/core/runtime/world.d.ts +32 -0
- package/dist/src/compiled/@workflow/core/runtime.d.ts +22 -67
- package/dist/src/compiled/@workflow/core/schemas.d.ts +15 -0
- package/dist/src/compiled/@workflow/core/serialization/client.d.ts +17 -0
- package/dist/src/compiled/@workflow/core/serialization/codec-devalue.d.ts +14 -0
- package/dist/src/compiled/@workflow/core/serialization/codec.d.ts +90 -0
- package/dist/src/compiled/@workflow/core/serialization/encryption.d.ts +32 -0
- package/dist/src/compiled/@workflow/core/serialization/errors.d.ts +21 -0
- package/dist/src/compiled/@workflow/core/serialization/format.d.ts +60 -0
- package/dist/src/compiled/@workflow/core/serialization/index.d.ts +18 -0
- package/dist/src/compiled/@workflow/core/serialization/reducers/class.d.ts +11 -0
- package/dist/src/compiled/@workflow/core/serialization/reducers/common.d.ts +16 -0
- package/dist/src/compiled/@workflow/core/serialization/reducers/step-function.d.ts +35 -0
- package/dist/src/compiled/@workflow/core/serialization/step.d.ts +17 -0
- package/dist/src/compiled/@workflow/core/serialization/types.d.ts +201 -0
- package/dist/src/compiled/@workflow/core/serialization/workflow.d.ts +29 -0
- package/dist/src/compiled/@workflow/core/serialization-format.d.ts +171 -0
- package/dist/src/compiled/@workflow/core/serialization.d.ts +329 -0
- package/dist/src/compiled/@workflow/core/sleep.d.ts +33 -0
- package/dist/src/compiled/@workflow/core/source-map.d.ts +10 -0
- package/dist/src/compiled/@workflow/core/step/context-storage.d.ts +13 -0
- package/dist/src/compiled/@workflow/core/step/get-closure-vars.d.ts +9 -0
- package/dist/src/compiled/@workflow/core/step/get-step-metadata.d.ts +42 -0
- package/dist/src/compiled/@workflow/core/step/get-workflow-metadata.d.ts +7 -0
- package/dist/src/compiled/@workflow/core/step/writable-stream.d.ts +22 -0
- package/dist/src/compiled/@workflow/core/step.d.ts +4 -0
- package/dist/src/compiled/@workflow/core/symbols.d.ts +20 -0
- package/dist/src/compiled/@workflow/core/telemetry/semantic-conventions.d.ts +283 -0
- package/dist/src/compiled/@workflow/core/telemetry.d.ts +53 -0
- package/dist/src/compiled/@workflow/core/types.d.ts +14 -0
- package/dist/src/compiled/@workflow/core/util.d.ts +40 -0
- package/dist/src/compiled/@workflow/core/version.d.ts +2 -0
- package/dist/src/compiled/@workflow/core/vm/index.d.ts +17 -0
- package/dist/src/compiled/@workflow/core/vm/uint8array-base64.d.ts +21 -0
- package/dist/src/compiled/@workflow/core/vm/uuid.d.ts +10 -0
- package/dist/src/compiled/@workflow/core/workflow/abort-controller.d.ts +65 -0
- package/dist/src/compiled/@workflow/core/workflow/create-hook.d.ts +7 -0
- package/dist/src/compiled/@workflow/core/workflow/define-hook.d.ts +10 -0
- package/dist/src/compiled/@workflow/core/workflow/get-workflow-metadata.d.ts +32 -0
- package/dist/src/compiled/@workflow/core/workflow/hook.d.ts +4 -0
- package/dist/src/compiled/@workflow/core/workflow/index.d.ts +11 -0
- package/dist/src/compiled/@workflow/core/workflow/sleep.d.ts +4 -0
- package/dist/src/compiled/@workflow/core/workflow/world-init-stub.d.ts +15 -0
- package/dist/src/compiled/@workflow/core/workflow/writable-stream.d.ts +3 -0
- package/dist/src/compiled/@workflow/core/workflow.d.ts +1 -38
- package/dist/src/evals/cli/eval.js +1 -1
- package/dist/src/internal/application/package.js +1 -1
- package/dist/src/protocol/message.d.ts +6 -1
- package/dist/src/public/channels/ash.js +50 -3
- package/dist/src/public/context/index.d.ts +4 -7
- package/dist/src/public/context/index.js +4 -5
- package/dist/src/public/definitions/state.d.ts +33 -0
- package/dist/src/public/definitions/state.js +34 -0
- package/dist/src/public/next/index.d.ts +7 -0
- package/dist/src/public/next/index.js +2 -0
- package/dist/src/public/next/vercel-json.d.ts +1 -0
- package/dist/src/public/next/vercel-json.js +1 -0
- package/dist/src/react/index.d.ts +1 -1
- package/dist/src/react/use-ash-agent.d.ts +8 -0
- package/dist/src/react/use-ash-agent.js +26 -4
- package/dist/src/services/dev-client.d.ts +4 -1
- package/dist/src/services/dev-client.js +1 -0
- package/package.json +1 -1
- package/dist/src/chunks/paths-C6sp4T2U.js +0 -88
|
@@ -1 +1 @@
|
|
|
1
|
-
import{n as e}from"../../chunks/paths-
|
|
1
|
+
import{n as e}from"../../chunks/paths-YoCQlavu.js";import{loadDevelopmentEnvironmentFiles as t}from"../../cli/dev/environment.js";import{a as n,n as r,t as i}from"../../chunks/client-nshDsWNF.js";import{n as a}from"../../chunks/host-tji7W0Nn.js";import{discoverAndImportSuites as o,discoverSuiteFiles as s,importSuiteFile as c}from"../runner/discover.js";import{executeSuite as l}from"../runner/execute-suite.js";import{ConsoleReporter as u}from"../runner/reporters/console.js";var d=n();function f(e,t){e.command(`eval`).description(`Run eval suites against an Ash agent.`).option(`--suite <id...>`,`Suite IDs to run (repeatable)`).option(`--all`,`Run all discovered suites`).option(`--url <url>`,`Remote agent URL (skip local host startup)`).option(`--timeout <ms>`,`Per-case timeout in milliseconds`).option(`--max-concurrency <n>`,`Max concurrent case executions per suite`).option(`--json`,`Output results as JSON`).option(`--list-suites`,`List discovered suites and exit`).option(`--skip-report`,`Skip suite-defined reporters (e.g. Braintrust)`).action(async e=>{await p(e,t)})}async function p(n,r){let i=e();if(t(i),n.listSuites){await y(i,r);return}let s=n.suite,c=await o(i,s);if(c.length===0){s&&s.length>0?r.error(`No suites found matching: ${s.join(`, `)}`):r.error(`No eval suites found. Create suite files under evals/ with the *.eval.ts extension.`),process.exitCode=1;return}let u,d;n.url?d={kind:`remote`,url:n.url}:(u=await a(i,{host:`127.0.0.1`,port:0}),d={kind:`local`,url:u.url});let f=m(d);try{let e=[];for(let t of c){let r=_(t,n),a=v(r,{json:n.json===!0,skipReport:n.skipReport===!0}),o=await l({suite:r,target:d,reporters:a,appRoot:i,client:f});e.push(o)}n.json&&r.log(JSON.stringify(e,null,2)),e.some(e=>e.errored>0)&&(process.exitCode=1)}finally{u&&await u.close()}process.exit(process.exitCode??0)}function m(e){if(e.kind===`local`)return new i({host:e.url});let t={},n=process.env.VERCEL_AUTOMATION_BYPASS_SECRET?.trim();return n&&(t[r]=n),new i({auth:h(),headers:Object.keys(t).length>0?t:void 0,host:e.url})}function h(){let e=process.env.ASH_EVAL_AUTH_TOKEN?.trim();return e?{bearer:e}:{bearer:g}}async function g(){try{let e=(await(0,d.getVercelOidcToken)()).trim();if(e.length>0)return e}catch{}return process.env.VERCEL_OIDC_TOKEN?.trim()??``}function _(e,t){let n=t.maxConcurrency?Number.parseInt(t.maxConcurrency,10):void 0,r=t.timeout?Number.parseInt(t.timeout,10):void 0;if(n===void 0&&r===void 0)return e;let i={...e};return n!==void 0&&(i.maxConcurrency=n),r!==void 0&&(i.timeoutMs=r),i}function v(e,t){let n=t.json?[]:[new u];return!t.skipReport&&e.reporters&&n.push(...e.reporters),n}async function y(e,t){let n=await s(e);if(n.length===0){t.log(`No eval suites found.`);return}t.log(`Found ${n.length} eval suite file(s):\n`);for(let r of n){let n=await c(e,r);t.log(` ${n.id}${n.description?` - ${n.description}`:``}`)}}export{f as registerEvalCommand,p as runEvalCommand};
|
|
@@ -6,7 +6,7 @@ import { ASH_PACKAGE_NAME } from "#package-name.js";
|
|
|
6
6
|
let cachedPackageInfo;
|
|
7
7
|
// The package build stamps the published version into `dist` so bundled
|
|
8
8
|
// deployments can still report package metadata without resolving package.json.
|
|
9
|
-
const BUNDLED_FALLBACK_PACKAGE_VERSION = "0.
|
|
9
|
+
const BUNDLED_FALLBACK_PACKAGE_VERSION = "0.23.0";
|
|
10
10
|
const BUNDLED_FALLBACK_PACKAGE_VERSION_PLACEHOLDER = "__ASH_PACKAGE_VERSION_PLACEHOLDER__";
|
|
11
11
|
const WORKFLOW_MODULE_ALIASES = {
|
|
12
12
|
"workflow/api": "src/compiled/@workflow/core/runtime.js",
|
|
@@ -76,20 +76,25 @@ export interface RuntimeIdentity {
|
|
|
76
76
|
* `message` is either a plain text string or an AI SDK `UserContent`
|
|
77
77
|
* array (mixing `text`, `image`, and `file` parts). Clients pass
|
|
78
78
|
* multimodal attachments with the same shape AI SDK's `useChat`
|
|
79
|
-
* `sendMessage({ files })` produces.
|
|
79
|
+
* `sendMessage({ files })` produces. `clientContext` is one-turn
|
|
80
|
+
* client/page context; the channel converts it into internal model context.
|
|
80
81
|
*/
|
|
81
82
|
export type HandleMessageRequestBody = {
|
|
82
83
|
readonly message: string | UserContent;
|
|
84
|
+
readonly clientContext?: string | readonly string[] | JsonObject;
|
|
83
85
|
} | {
|
|
84
86
|
readonly continuationToken: string;
|
|
85
87
|
readonly message: string | UserContent;
|
|
88
|
+
readonly clientContext?: string | readonly string[] | JsonObject;
|
|
86
89
|
} | {
|
|
87
90
|
readonly continuationToken: string;
|
|
88
91
|
readonly inputResponses: readonly InputResponse[];
|
|
92
|
+
readonly clientContext?: string | readonly string[] | JsonObject;
|
|
89
93
|
} | {
|
|
90
94
|
readonly continuationToken: string;
|
|
91
95
|
readonly inputResponses: readonly InputResponse[];
|
|
92
96
|
readonly message: string | UserContent;
|
|
97
|
+
readonly clientContext?: string | readonly string[] | JsonObject;
|
|
93
98
|
};
|
|
94
99
|
/**
|
|
95
100
|
* JSON response returned when the message route accepts a start or resume
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import {} from "ai";
|
|
1
2
|
import { ASH_MESSAGE_STREAM_CONTENT_TYPE, ASH_MESSAGE_STREAM_FORMAT, ASH_MESSAGE_STREAM_VERSION, ASH_SESSION_ID_HEADER, ASH_STREAM_FORMAT_HEADER, ASH_STREAM_VERSION_HEADER, } from "#protocol/message.js";
|
|
2
3
|
import { isInputResponse } from "#runtime/input/types.js";
|
|
3
4
|
import { createUnauthorizedResponse } from "#public/channels/auth.js";
|
|
4
5
|
import { collectUploadPolicyViolations, formatUploadPolicyViolation, mergeUploadPolicy, } from "#public/channels/upload-policy.js";
|
|
5
6
|
import { defineChannel, POST, GET } from "#public/definitions/defineChannel.js";
|
|
7
|
+
import { parseJsonObject } from "#shared/json.js";
|
|
6
8
|
export function ashChannel(input) {
|
|
7
9
|
const uploadPolicy = mergeUploadPolicy(input.uploadPolicy);
|
|
8
10
|
return defineChannel({
|
|
@@ -29,7 +31,7 @@ export function ashChannel(input) {
|
|
|
29
31
|
if (policyRejection !== null)
|
|
30
32
|
return policyRejection;
|
|
31
33
|
const token = `ash:${crypto.randomUUID()}`;
|
|
32
|
-
const session = await send(body
|
|
34
|
+
const session = await send(createSendPayload(body), {
|
|
33
35
|
auth: sessionAuth,
|
|
34
36
|
continuationToken: token,
|
|
35
37
|
});
|
|
@@ -79,6 +81,7 @@ export function ashChannel(input) {
|
|
|
79
81
|
const session = await send({
|
|
80
82
|
inputResponses: body.inputResponses,
|
|
81
83
|
message: body.message,
|
|
84
|
+
modelContext: body.modelContext,
|
|
82
85
|
}, {
|
|
83
86
|
auth: sessionAuth,
|
|
84
87
|
continuationToken: body.continuationToken,
|
|
@@ -137,10 +140,13 @@ function parseCreateBody(payload) {
|
|
|
137
140
|
const message = parseMessageField(payload.message);
|
|
138
141
|
if (message instanceof Response)
|
|
139
142
|
return message;
|
|
143
|
+
const modelContext = parseClientContextField(payload.clientContext);
|
|
144
|
+
if (modelContext instanceof Response)
|
|
145
|
+
return modelContext;
|
|
140
146
|
if (message === undefined) {
|
|
141
147
|
return Response.json({ error: "Missing or empty 'message' field.", ok: false }, { status: 400 });
|
|
142
148
|
}
|
|
143
|
-
return { message };
|
|
149
|
+
return { message, modelContext };
|
|
144
150
|
}
|
|
145
151
|
function parseContinueBody(payload) {
|
|
146
152
|
const continuationToken = typeof payload.continuationToken === "string" && payload.continuationToken.length > 0
|
|
@@ -155,13 +161,22 @@ function parseContinueBody(payload) {
|
|
|
155
161
|
const inputResponses = parseInputResponses(payload.inputResponses);
|
|
156
162
|
if (inputResponses instanceof Response)
|
|
157
163
|
return inputResponses;
|
|
164
|
+
const modelContext = parseClientContextField(payload.clientContext);
|
|
165
|
+
if (modelContext instanceof Response)
|
|
166
|
+
return modelContext;
|
|
158
167
|
if (message === undefined && inputResponses === undefined) {
|
|
159
168
|
return Response.json({
|
|
160
169
|
error: "Expected a non-empty 'message', a non-empty 'inputResponses' array, or both.",
|
|
161
170
|
ok: false,
|
|
162
171
|
}, { status: 400 });
|
|
163
172
|
}
|
|
164
|
-
return { message, continuationToken, inputResponses };
|
|
173
|
+
return { message, continuationToken, inputResponses, modelContext };
|
|
174
|
+
}
|
|
175
|
+
function createSendPayload(body) {
|
|
176
|
+
if (body.modelContext === undefined) {
|
|
177
|
+
return body.message;
|
|
178
|
+
}
|
|
179
|
+
return { message: body.message, modelContext: body.modelContext };
|
|
165
180
|
}
|
|
166
181
|
function parseMessageField(value) {
|
|
167
182
|
if (value === undefined)
|
|
@@ -255,6 +270,38 @@ function parseInputResponses(value) {
|
|
|
255
270
|
}
|
|
256
271
|
return inputResponses;
|
|
257
272
|
}
|
|
273
|
+
const CLIENT_CONTEXT_PREFIX = "Ephemeral client context:\n";
|
|
274
|
+
function parseClientContextField(value) {
|
|
275
|
+
if (value === undefined)
|
|
276
|
+
return undefined;
|
|
277
|
+
if (typeof value === "string") {
|
|
278
|
+
return value.length > 0 ? [toClientContextMessage(value)] : undefined;
|
|
279
|
+
}
|
|
280
|
+
if (Array.isArray(value)) {
|
|
281
|
+
if (value.length === 0)
|
|
282
|
+
return undefined;
|
|
283
|
+
if (!value.every((entry) => typeof entry === "string" && entry.length > 0)) {
|
|
284
|
+
return Response.json({ error: "Expected 'clientContext' array entries to be non-empty strings.", ok: false }, { status: 400 });
|
|
285
|
+
}
|
|
286
|
+
return value.map((entry) => toClientContextMessage(entry));
|
|
287
|
+
}
|
|
288
|
+
if (value === null || typeof value !== "object") {
|
|
289
|
+
return Response.json({
|
|
290
|
+
error: "Expected 'clientContext' to be a string, string array, or JSON object.",
|
|
291
|
+
ok: false,
|
|
292
|
+
}, { status: 400 });
|
|
293
|
+
}
|
|
294
|
+
try {
|
|
295
|
+
const json = parseJsonObject(value);
|
|
296
|
+
return [toClientContextMessage(JSON.stringify(json))];
|
|
297
|
+
}
|
|
298
|
+
catch {
|
|
299
|
+
return Response.json({ error: "Expected 'clientContext' to be a JSON-serializable object.", ok: false }, { status: 400 });
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
function toClientContextMessage(content) {
|
|
303
|
+
return { role: "user", content: `${CLIENT_CONTEXT_PREFIX}${content}` };
|
|
304
|
+
}
|
|
258
305
|
function parseStartIndex(request) {
|
|
259
306
|
const raw = new URL(request.url).searchParams.get("startIndex");
|
|
260
307
|
if (raw === null)
|
|
@@ -2,15 +2,12 @@
|
|
|
2
2
|
* Runtime context helpers for authored code.
|
|
3
3
|
*
|
|
4
4
|
* These APIs work only inside active authored runtime execution such as
|
|
5
|
-
* tools and other Ash-invoked callbacks.
|
|
6
|
-
* `AshContext` container bound via `AsyncLocalStorage`.
|
|
5
|
+
* tools and other Ash-invoked callbacks.
|
|
7
6
|
*
|
|
8
7
|
* @example
|
|
9
8
|
* ```ts
|
|
10
|
-
* import {
|
|
9
|
+
* import { defineState, getSession } from "experimental-ash/context";
|
|
11
10
|
* ```
|
|
12
11
|
*/
|
|
13
|
-
export {
|
|
14
|
-
export type
|
|
15
|
-
export { type ContextAccessor, ContextKey, type ContextKeyOptions } from "#context/key.js";
|
|
16
|
-
export type { ContextProvider, ContextReader } from "#context/provider.js";
|
|
12
|
+
export { getSession, type Session, type SessionAuth, type SessionAuthContext, type SessionParent, type SessionTurn, } from "#context/accessors.js";
|
|
13
|
+
export { defineState, type StateHandle } from "#public/definitions/state.js";
|
|
@@ -2,13 +2,12 @@
|
|
|
2
2
|
* Runtime context helpers for authored code.
|
|
3
3
|
*
|
|
4
4
|
* These APIs work only inside active authored runtime execution such as
|
|
5
|
-
* tools and other Ash-invoked callbacks.
|
|
6
|
-
* `AshContext` container bound via `AsyncLocalStorage`.
|
|
5
|
+
* tools and other Ash-invoked callbacks.
|
|
7
6
|
*
|
|
8
7
|
* @example
|
|
9
8
|
* ```ts
|
|
10
|
-
* import {
|
|
9
|
+
* import { defineState, getSession } from "experimental-ash/context";
|
|
11
10
|
* ```
|
|
12
11
|
*/
|
|
13
|
-
export {
|
|
14
|
-
export {
|
|
12
|
+
export { getSession, } from "#context/accessors.js";
|
|
13
|
+
export { defineState } from "#public/definitions/state.js";
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed handle returned by {@link defineState}. Provides read and
|
|
3
|
+
* update operations over a named context slot.
|
|
4
|
+
*
|
|
5
|
+
* All operations require an active Ash context (ALS scope) and throw
|
|
6
|
+
* when called outside one.
|
|
7
|
+
*/
|
|
8
|
+
export interface StateHandle<T> {
|
|
9
|
+
/** Read the current value. Returns `initial()` on first access within a context. */
|
|
10
|
+
get(): T;
|
|
11
|
+
/** Update the value with a function that receives the current value. */
|
|
12
|
+
update(fn: (current: T) => T): void;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Creates a typed, named state slot backed by a durable `ContextKey`.
|
|
16
|
+
*
|
|
17
|
+
* All operations require an active Ash context — calling `get()` or
|
|
18
|
+
* `update()` outside of tools, hooks, or other framework-managed
|
|
19
|
+
* code throws.
|
|
20
|
+
*
|
|
21
|
+
* State is durable: values survive across workflow step boundaries.
|
|
22
|
+
* To reset per-turn, use `update(() => freshValue)` in a lifecycle
|
|
23
|
+
* hook.
|
|
24
|
+
*
|
|
25
|
+
* ```ts
|
|
26
|
+
* const budget = defineState("my-agent.budget", () => ({ count: 0, cap: 25 }));
|
|
27
|
+
*
|
|
28
|
+
* // In a tool or hook:
|
|
29
|
+
* budget.update((s) => ({ ...s, count: s.count + 1 }));
|
|
30
|
+
* const current = budget.get();
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export declare function defineState<T>(name: string, initial: () => T): StateHandle<T>;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { ContextKey } from "#context/key.js";
|
|
2
|
+
import { loadContext } from "#context/container.js";
|
|
3
|
+
/**
|
|
4
|
+
* Creates a typed, named state slot backed by a durable `ContextKey`.
|
|
5
|
+
*
|
|
6
|
+
* All operations require an active Ash context — calling `get()` or
|
|
7
|
+
* `update()` outside of tools, hooks, or other framework-managed
|
|
8
|
+
* code throws.
|
|
9
|
+
*
|
|
10
|
+
* State is durable: values survive across workflow step boundaries.
|
|
11
|
+
* To reset per-turn, use `update(() => freshValue)` in a lifecycle
|
|
12
|
+
* hook.
|
|
13
|
+
*
|
|
14
|
+
* ```ts
|
|
15
|
+
* const budget = defineState("my-agent.budget", () => ({ count: 0, cap: 25 }));
|
|
16
|
+
*
|
|
17
|
+
* // In a tool or hook:
|
|
18
|
+
* budget.update((s) => ({ ...s, count: s.count + 1 }));
|
|
19
|
+
* const current = budget.get();
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export function defineState(name, initial) {
|
|
23
|
+
const key = new ContextKey(name);
|
|
24
|
+
return {
|
|
25
|
+
get() {
|
|
26
|
+
return loadContext().ensure(key, initial);
|
|
27
|
+
},
|
|
28
|
+
update(fn) {
|
|
29
|
+
const ctx = loadContext();
|
|
30
|
+
const current = ctx.ensure(key, initial);
|
|
31
|
+
ctx.set(key, fn(current));
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
}
|
|
@@ -45,6 +45,13 @@ export interface WithAshOptions {
|
|
|
45
45
|
* absolute. Defaults to the Next.js app root.
|
|
46
46
|
*/
|
|
47
47
|
readonly ashRoot?: string;
|
|
48
|
+
/**
|
|
49
|
+
* Build command for the generated Ash Vercel service. Defaults to `ash build`.
|
|
50
|
+
*
|
|
51
|
+
* Use this when the Ash service needs project-specific prework before the
|
|
52
|
+
* framework build, without changing the Next.js service build command.
|
|
53
|
+
*/
|
|
54
|
+
readonly ashBuildCommand?: string;
|
|
48
55
|
/**
|
|
49
56
|
* Set to `false` to skip creating or updating `vercel.json`.
|
|
50
57
|
*
|
|
@@ -9,6 +9,7 @@ import { ensureAshVercelJson } from "./vercel-json.js";
|
|
|
9
9
|
export const ASH_NEXT_SERVICE_PREFIX = "/_ash_internal/ash";
|
|
10
10
|
const ASH_NEXT_PRODUCTION_ORIGIN_ENV = "ASH_NEXT_PRODUCTION_ORIGIN";
|
|
11
11
|
const ASH_NEXT_PRODUCTION_PORT_ENV = "ASH_NEXT_PRODUCTION_PORT";
|
|
12
|
+
const DEFAULT_ASH_BUILD_COMMAND = "ash build";
|
|
12
13
|
const DEFAULT_ASH_NEXT_PRODUCTION_PORT = 4274;
|
|
13
14
|
const ASH_PROXY_REWRITE_SOURCES = [`${ASH_ROUTE_PREFIX}/:path+`];
|
|
14
15
|
function resolveApplicationRoot(appPath) {
|
|
@@ -129,6 +130,7 @@ export function withAsh(configOrFunction, options = {}) {
|
|
|
129
130
|
const configuredVercel = shouldConfigureVercelJson
|
|
130
131
|
? await ensureAshVercelJson({
|
|
131
132
|
appRoot,
|
|
133
|
+
ashBuildCommand: options.ashBuildCommand ?? DEFAULT_ASH_BUILD_COMMAND,
|
|
132
134
|
nextRoot,
|
|
133
135
|
servicePrefix: servicePrefixInput,
|
|
134
136
|
})
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { useAshAgent, type UseAshAgentHelpers, type UseAshAgentOptions, type UseAshAgentSnapshot, type UseAshAgentStatus, } from "#react/use-ash-agent.js";
|
|
1
|
+
export { useAshAgent, type PrepareSend, type UseAshAgentHelpers, type UseAshAgentOptions, type UseAshAgentSnapshot, type UseAshAgentStatus, } from "#react/use-ash-agent.js";
|
|
2
2
|
export { type AshAgentReducer, type AshAgentReducerEvent, type ClientInputRespondedEvent, type ClientMessageFailedEvent, type ClientMessageSubmittedEvent, } from "#client/reducer.js";
|
|
3
3
|
export { defaultMessageReducer, type AshMessageData, type AshDynamicToolPart, type AshMessageInputRequest, type AshMessage, type AshMessageMetadata, type AshMessagePart, type AshMessageToolMetadata, } from "#client/message-reducer.js";
|
|
@@ -4,6 +4,13 @@ import { type AshMessageData } from "#client/message-reducer.js";
|
|
|
4
4
|
import type { HandleMessageStreamEvent } from "#protocol/message.js";
|
|
5
5
|
import type { ClientAuth, HeadersValue, SendMessageOptions, SendTurnInput, SessionState } from "#client/types.js";
|
|
6
6
|
export type UseAshAgentStatus = "error" | "ready" | "streaming" | "submitted";
|
|
7
|
+
/**
|
|
8
|
+
* Prepares one outbound turn immediately before the client sends it.
|
|
9
|
+
*
|
|
10
|
+
* Use this to attach fresh, one-turn client state such as page context via
|
|
11
|
+
* `clientContext`.
|
|
12
|
+
*/
|
|
13
|
+
export type PrepareSend = (input: SendTurnInput) => SendTurnInput | Promise<SendTurnInput>;
|
|
7
14
|
/**
|
|
8
15
|
* Current projected state for an Ash agent session.
|
|
9
16
|
*/
|
|
@@ -22,6 +29,7 @@ export interface UseAshAgentCallbacks<TData> {
|
|
|
22
29
|
readonly onEvent?: (event: HandleMessageStreamEvent) => void;
|
|
23
30
|
readonly onFinish?: (snapshot: UseAshAgentSnapshot<TData>) => void;
|
|
24
31
|
readonly onSessionChange?: (session: SessionState) => void;
|
|
32
|
+
readonly prepareSend?: PrepareSend;
|
|
25
33
|
}
|
|
26
34
|
/**
|
|
27
35
|
* Snapshot plus commands returned by `useAshAgent`.
|
|
@@ -26,6 +26,7 @@ export function useAshAgent(options = {}) {
|
|
|
26
26
|
onEvent: options.onEvent,
|
|
27
27
|
onFinish: options.onFinish,
|
|
28
28
|
onSessionChange: options.onSessionChange,
|
|
29
|
+
prepareSend: options.prepareSend,
|
|
29
30
|
});
|
|
30
31
|
const subscribe = useCallback((onStoreChange) => store.subscribe(onStoreChange), [store]);
|
|
31
32
|
const snapshot = useSyncExternalStore(subscribe, () => store.snapshot, () => store.snapshot);
|
|
@@ -102,11 +103,16 @@ class UseAshAgentStore {
|
|
|
102
103
|
this.#abortController = abortController;
|
|
103
104
|
this.#error = undefined;
|
|
104
105
|
this.#status = "submitted";
|
|
105
|
-
this.#projectOptimisticMessage(input);
|
|
106
|
-
this.#projectInputResponses(input);
|
|
107
106
|
this.#publish();
|
|
108
107
|
try {
|
|
109
|
-
const
|
|
108
|
+
const preparedInput = (await this.#callbacks.prepareSend?.(input)) ?? input;
|
|
109
|
+
if (!this.#isCurrentOperation(operationId)) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
this.#projectOptimisticMessage(preparedInput);
|
|
113
|
+
this.#projectInputResponses(preparedInput);
|
|
114
|
+
this.#publish();
|
|
115
|
+
const response = await this.#session.send(preparedInput, {
|
|
110
116
|
...options,
|
|
111
117
|
signal: createAbortSignal(options?.signal, abortController.signal),
|
|
112
118
|
});
|
|
@@ -195,7 +201,7 @@ class UseAshAgentStore {
|
|
|
195
201
|
const pending = {
|
|
196
202
|
createdAt: Date.now(),
|
|
197
203
|
id,
|
|
198
|
-
message: input.message,
|
|
204
|
+
message: summarizeUserContent(input.message),
|
|
199
205
|
};
|
|
200
206
|
this.#pendingMessageSubmission = pending;
|
|
201
207
|
this.#appendProjectionEvent({
|
|
@@ -312,6 +318,22 @@ function createSubmissionId() {
|
|
|
312
318
|
function createAbortSignal(first, second) {
|
|
313
319
|
return first ? AbortSignal.any([first, second]) : second;
|
|
314
320
|
}
|
|
321
|
+
function summarizeUserContent(message) {
|
|
322
|
+
if (typeof message === "string") {
|
|
323
|
+
return message;
|
|
324
|
+
}
|
|
325
|
+
const parts = [];
|
|
326
|
+
for (const part of message) {
|
|
327
|
+
if (part.type === "text") {
|
|
328
|
+
parts.push(part.text);
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
if (part.type === "file") {
|
|
332
|
+
parts.push(part.filename ? `[file: ${part.filename}]` : "[file]");
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
return parts.join("\n");
|
|
336
|
+
}
|
|
315
337
|
function isAbortError(error) {
|
|
316
338
|
return error instanceof Error && error.name === "AbortError";
|
|
317
339
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import type { UserContent } from "ai";
|
|
1
2
|
import { type SessionState } from "#client/index.js";
|
|
2
3
|
import type { HandleMessageStreamEvent } from "#protocol/message.js";
|
|
3
4
|
import type { InputResponse } from "#runtime/input/types.js";
|
|
5
|
+
import type { JsonObject } from "#shared/json.js";
|
|
4
6
|
export type { DevelopmentRequestHeaders } from "#services/dev-client/request-headers.js";
|
|
5
7
|
export { extractCompletedMessage } from "#services/dev-client/stream.js";
|
|
6
8
|
/**
|
|
@@ -41,8 +43,9 @@ export interface DevelopmentMessageClient {
|
|
|
41
43
|
* Sends one turn payload through the active development session.
|
|
42
44
|
*/
|
|
43
45
|
send(input: {
|
|
46
|
+
readonly clientContext?: string | readonly string[] | JsonObject;
|
|
44
47
|
readonly inputResponses?: readonly InputResponse[];
|
|
45
|
-
readonly message?: string;
|
|
48
|
+
readonly message?: string | UserContent;
|
|
46
49
|
onEvent?(event: HandleMessageStreamEvent): void;
|
|
47
50
|
onResponseStart?(response: {
|
|
48
51
|
sessionId?: string;
|