experimental-ash 0.40.0 → 0.41.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 +20 -0
- package/dist/docs/public/tools.md +165 -0
- package/dist/src/compiled/.vendor-stamp.json +1 -1
- package/dist/src/compiled/@workflow/core/index.js +1 -1
- package/dist/src/compiled/@workflow/core/runtime.js +1 -1
- package/dist/src/compiled/_chunks/workflow/{resume-hook-D6dFbxMV.js → resume-hook-B2kqAsX6.js} +1 -1
- package/dist/src/compiler/manifest.d.ts +16 -2
- package/dist/src/compiler/manifest.js +1 -1
- package/dist/src/compiler/module-map.js +1 -1
- package/dist/src/compiler/normalize-agent-config.js +1 -1
- package/dist/src/compiler/normalize-manifest.js +1 -1
- package/dist/src/compiler/normalize-tool.d.ts +7 -3
- package/dist/src/compiler/normalize-tool.js +1 -1
- package/dist/src/context/dynamic-tool-lifecycle.d.ts +35 -0
- package/dist/src/context/dynamic-tool-lifecycle.js +1 -0
- package/dist/src/context/hook-lifecycle.js +1 -1
- package/dist/src/context/keys.d.ts +37 -0
- package/dist/src/context/keys.js +1 -1
- package/dist/src/context/providers/sandbox.js +1 -1
- package/dist/src/execution/dispatch-runtime-actions-step.js +1 -1
- package/dist/src/execution/node-step.js +1 -1
- package/dist/src/execution/workflow-steps.js +1 -1
- package/dist/src/harness/attachment-staging.js +1 -1
- package/dist/src/harness/code-mode.d.ts +1 -1
- package/dist/src/harness/code-mode.js +1 -1
- package/dist/src/harness/tool-loop.js +1 -1
- package/dist/src/harness/types.d.ts +7 -0
- package/dist/src/internal/application/package.js +1 -1
- package/dist/src/internal/authored-definition/core.js +1 -1
- package/dist/src/internal/authored-definition/schema-backed.d.ts +9 -3
- package/dist/src/internal/authored-definition/schema-backed.js +1 -1
- package/dist/src/internal/authored-module.d.ts +4 -0
- package/dist/src/internal/authored-module.js +1 -1
- package/dist/src/internal/nitro/host/create-application-nitro.js +1 -1
- package/dist/src/internal/workflow-bundle/dynamic-tool-transform.d.ts +39 -0
- package/dist/src/internal/workflow-bundle/dynamic-tool-transform.js +4 -0
- package/dist/src/packages/ash-scaffold/src/channels.js +1 -1
- package/dist/src/public/definitions/agent.d.ts +1 -1
- package/dist/src/public/definitions/defineChannel.js +1 -1
- package/dist/src/public/definitions/tool.d.ts +67 -0
- package/dist/src/public/definitions/tool.js +1 -1
- package/dist/src/public/index.d.ts +1 -1
- package/dist/src/public/tools/index.d.ts +2 -1
- package/dist/src/public/tools/index.js +1 -1
- package/dist/src/runtime/framework-tools/connection-search.js +1 -1
- package/dist/src/runtime/framework-tools/connection-tools.js +1 -1
- package/dist/src/runtime/resolve-agent-graph.js +1 -1
- package/dist/src/runtime/resolve-agent.js +1 -1
- package/dist/src/runtime/resolve-dynamic-tool.d.ts +12 -0
- package/dist/src/runtime/resolve-dynamic-tool.js +1 -0
- package/dist/src/runtime/types.d.ts +12 -0
- package/dist/src/shared/agent-definition.d.ts +25 -0
- package/dist/src/shared/code-mode.d.ts +14 -1
- package/dist/src/shared/code-mode.js +1 -1
- package/dist/src/shared/dynamic-tool-definition.d.ts +132 -0
- package/dist/src/shared/dynamic-tool-definition.js +1 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# experimental-ash
|
|
2
2
|
|
|
3
|
+
## 0.41.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- d4dd396: Move code mode from the `CODE_MODE` environment variable to a per-agent
|
|
8
|
+
`experimental.codeMode` flag on `defineAgent({ experimental: { codeMode: true } })`.
|
|
9
|
+
Each agent and subagent can now opt in independently. For backwards
|
|
10
|
+
compatibility, agents that omit the flag fall back to the
|
|
11
|
+
`ASH_EXPERIMENTAL_CODE_MODE` environment variable (`"1"` enables it); the
|
|
12
|
+
previous `CODE_MODE` variable is no longer read.
|
|
13
|
+
- 09beab5: Add `defineTools` and `defineTool` dynamic overloads for runtime tool resolution.
|
|
14
|
+
|
|
15
|
+
Tools can now be resolved dynamically at runtime based on session context via `defineTools({ events: { "session.started": handler } })` and `defineTool({ events: { "session.started": handler } })`. Session-scoped resolvers run once and are cached; step-scoped resolvers re-resolve before every model call. Dynamic tools appear in the model's tool list identically to static tools.
|
|
16
|
+
|
|
17
|
+
## 0.40.1
|
|
18
|
+
|
|
19
|
+
### Patch Changes
|
|
20
|
+
|
|
21
|
+
- 35ca14b: Fix cross-deployment turn workflows so forwarded parent streams resolve the parent run's encryption key instead of the latest deployment's key.
|
|
22
|
+
|
|
3
23
|
## 0.40.0
|
|
4
24
|
|
|
5
25
|
### Minor Changes
|
|
@@ -368,6 +368,171 @@ full API.
|
|
|
368
368
|
Note: `retentionPolicy` and `onCompact` operate on the projected output (what the model sees),
|
|
369
369
|
not the full `execute` return.
|
|
370
370
|
|
|
371
|
+
## Dynamic Tools
|
|
372
|
+
|
|
373
|
+
Dynamic tools resolve at runtime based on session context — tenant configuration, feature flags, user roles, or external data. Use `defineTools` with an `events` object to produce tools dynamically.
|
|
374
|
+
|
|
375
|
+
### Single Dynamic Tool
|
|
376
|
+
|
|
377
|
+
`agent/tools/analytics.ts`
|
|
378
|
+
|
|
379
|
+
```ts
|
|
380
|
+
import { defineTool } from "experimental-ash/tools";
|
|
381
|
+
import { z } from "zod";
|
|
382
|
+
|
|
383
|
+
export default defineTool({
|
|
384
|
+
events: {
|
|
385
|
+
"session.started": async (event, ctx) => {
|
|
386
|
+
const flags = await fetchFeatureFlags(ctx.session.id);
|
|
387
|
+
if (!flags.advancedAnalytics) return null;
|
|
388
|
+
|
|
389
|
+
return {
|
|
390
|
+
description: "Run an advanced analytics query.",
|
|
391
|
+
inputSchema: z.object({ query: z.string() }),
|
|
392
|
+
async execute(input) {
|
|
393
|
+
return runAnalytics(input.query);
|
|
394
|
+
},
|
|
395
|
+
};
|
|
396
|
+
},
|
|
397
|
+
},
|
|
398
|
+
});
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
Return `null` to produce no tool. The tool name is the file slug (`analytics`), identical to a static `defineTool`.
|
|
402
|
+
|
|
403
|
+
### Multiple Dynamic Tools
|
|
404
|
+
|
|
405
|
+
`agent/tools/tenant.ts`
|
|
406
|
+
|
|
407
|
+
```ts
|
|
408
|
+
import { defineTools, tool } from "experimental-ash/tools";
|
|
409
|
+
import { z } from "zod";
|
|
410
|
+
|
|
411
|
+
export default defineTools({
|
|
412
|
+
events: {
|
|
413
|
+
"session.started": async (event, ctx) => {
|
|
414
|
+
const tenant = await fetchTenant(ctx.session.id);
|
|
415
|
+
if (!tenant) return null;
|
|
416
|
+
|
|
417
|
+
return {
|
|
418
|
+
export: tool({
|
|
419
|
+
description: `Export ${tenant.name} data`,
|
|
420
|
+
inputSchema: z.object({ format: z.enum(["csv", "json"]) }),
|
|
421
|
+
async execute(input) {
|
|
422
|
+
// input.format is typed as "csv" | "json"
|
|
423
|
+
return callTenantApi(tenant.apiUrl, "export", input.format);
|
|
424
|
+
},
|
|
425
|
+
}),
|
|
426
|
+
query: tool({
|
|
427
|
+
description: `Query ${tenant.name} data`,
|
|
428
|
+
inputSchema: z.object({ sql: z.string() }),
|
|
429
|
+
async execute(input) {
|
|
430
|
+
// input.sql is typed as string
|
|
431
|
+
return callTenantApi(tenant.apiUrl, "query", input.sql);
|
|
432
|
+
},
|
|
433
|
+
}),
|
|
434
|
+
};
|
|
435
|
+
},
|
|
436
|
+
},
|
|
437
|
+
});
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
Each entry must be wrapped with `tool()` — it captures the schema type so `execute(input)` is inferred from `inputSchema`, matching the AI SDK's `tool()` pattern.
|
|
441
|
+
|
|
442
|
+
### Tool Naming
|
|
443
|
+
|
|
444
|
+
Dynamic tool names are derived from the file path and the definition function:
|
|
445
|
+
|
|
446
|
+
| Definition | File | Entry keys | Tool name(s) |
|
|
447
|
+
| ------------- | -------------------------- | ----------------- | --------------------------------- |
|
|
448
|
+
| `defineTool` | `agent/tools/analytics.ts` | _(single entry)_ | `analytics` |
|
|
449
|
+
| `defineTools` | `agent/tools/tenant.ts` | `export`, `query` | `tenant__export`, `tenant__query` |
|
|
450
|
+
| `defineTools` | `agent/tools/search.ts` | `run` | `search__run` |
|
|
451
|
+
|
|
452
|
+
`defineTool` always produces one tool named after the file slug — identical to static tools. `defineTools` always uses `slug__key`, even when the resolver returns a single entry. This keeps names stable: adding a second entry to a `defineTools` file does not rename the first.
|
|
453
|
+
|
|
454
|
+
### Event Scopes
|
|
455
|
+
|
|
456
|
+
The `events` object uses the same event names as hook stream events:
|
|
457
|
+
|
|
458
|
+
| Event | When the resolver runs | I/O behavior |
|
|
459
|
+
| ----------------- | ----------------------- | ----------------------------------- |
|
|
460
|
+
| `session.started` | Once per session | Runs once; results cached durably |
|
|
461
|
+
| `turn.started` | Once per turn | Runs once per turn; cached for turn |
|
|
462
|
+
| `step.started` | Before every model call | Runs every step; no caching |
|
|
463
|
+
|
|
464
|
+
Session-scoped resolvers can do I/O (fetch from a database, call an API). The framework's bundler transform ensures the I/O executes once per session — subsequent workflow steps reconstruct execute functions from stored closure variables without re-running the resolver. Resolvers run concurrently — if multiple dynamic tool files declare the same event, their I/O overlaps.
|
|
465
|
+
|
|
466
|
+
### Multiple Events
|
|
467
|
+
|
|
468
|
+
A single file can declare handlers for multiple events. The result of the most recently fired handler owns the tool set for that file:
|
|
469
|
+
|
|
470
|
+
```ts
|
|
471
|
+
import { defineTools, tool } from "experimental-ash/tools";
|
|
472
|
+
|
|
473
|
+
export default defineTools({
|
|
474
|
+
events: {
|
|
475
|
+
"session.started": async (event, ctx) => {
|
|
476
|
+
return { query: tool({ description: "Query", inputSchema: {}, execute: async () => {} }) };
|
|
477
|
+
},
|
|
478
|
+
"turn.started": async (event, ctx) => {
|
|
479
|
+
// Turn handler replaces session handler's tools for this file
|
|
480
|
+
return { search: tool({ description: "Search", inputSchema: {}, execute: async () => {} }) };
|
|
481
|
+
},
|
|
482
|
+
},
|
|
483
|
+
});
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
### Type Inference
|
|
487
|
+
|
|
488
|
+
`inputSchema` accepts Zod, Standard Schema, or plain JSON Schema — same as static `defineTool`. When using Zod, the `tool()` wrapper infers the `execute` input type automatically:
|
|
489
|
+
|
|
490
|
+
```ts
|
|
491
|
+
return {
|
|
492
|
+
query: tool({
|
|
493
|
+
inputSchema: z.object({ city: z.string() }),
|
|
494
|
+
async execute(input) {
|
|
495
|
+
// input.city is typed as string
|
|
496
|
+
return fetchWeather(input.city);
|
|
497
|
+
},
|
|
498
|
+
}),
|
|
499
|
+
};
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
The `execute` signature matches static tools: `execute(input: TInput, ctx: ToolContext)`.
|
|
503
|
+
|
|
504
|
+
### Helper Functions
|
|
505
|
+
|
|
506
|
+
`execute` can live inside helper functions, `.map()` callbacks, or IIFEs — the bundler transform follows nested scopes and captures all referenced variables:
|
|
507
|
+
|
|
508
|
+
```ts
|
|
509
|
+
import { defineTools, tool } from "experimental-ash/tools";
|
|
510
|
+
import { z } from "zod";
|
|
511
|
+
|
|
512
|
+
export default defineTools({
|
|
513
|
+
events: {
|
|
514
|
+
"session.started": async (event, ctx) => {
|
|
515
|
+
const tenant = await fetchTenant(ctx.session.id);
|
|
516
|
+
|
|
517
|
+
function buildTool(action: string) {
|
|
518
|
+
const endpoint = `${tenant.apiUrl}/${action}`;
|
|
519
|
+
return tool({
|
|
520
|
+
description: `${action} for ${tenant.name}`,
|
|
521
|
+
inputSchema: z.object({ query: z.string() }),
|
|
522
|
+
async execute(input) {
|
|
523
|
+
return callApi(endpoint, input.query);
|
|
524
|
+
},
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
return { query: buildTool("query"), export: buildTool("export") };
|
|
529
|
+
},
|
|
530
|
+
},
|
|
531
|
+
});
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
**Limitation:** `execute` must be an inline function — a function expression, arrow function, or method shorthand written directly as the property value. Assigning a variable (`execute: myFn`) or a call result (`execute: makeFn()`) is not detected by the transform; the tool will work on the first workflow step but will not survive replay across step boundaries.
|
|
535
|
+
|
|
371
536
|
## What To Read Next
|
|
372
537
|
|
|
373
538
|
- [Session Context](./session-context.md)
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{i as e,s as t}from"../../_chunks/workflow/dist-C4EHshZE.js";import{c as n,et as r,nt as i,s as a,tt as o}from"../../_chunks/workflow/symbols-DygIC1sj.js";import{Bt as s,C as c,I as l,R as u,Rt as d,Vt as f,m as p,n as m,x as h,zt as g}from"../../_chunks/workflow/resume-hook-
|
|
1
|
+
import{i as e,s as t}from"../../_chunks/workflow/dist-C4EHshZE.js";import{c as n,et as r,nt as i,s as a,tt as o}from"../../_chunks/workflow/symbols-DygIC1sj.js";import{Bt as s,C as c,I as l,R as u,Rt as d,Vt as f,m as p,n as m,x as h,zt as g}from"../../_chunks/workflow/resume-hook-B2kqAsX6.js";import{n as _,t as v}from"../../_chunks/workflow/sleep-kQ0UwHn0.js";function y(e){o(`createHook()`,`https://workflow-sdk.dev/docs/api-reference/workflow/create-hook`,y)}function b(e){o(`createWebhook()`,`https://workflow-sdk.dev/docs/api-reference/workflow/create-webhook`,b)}function x({schema:e}={}){function t(e){o(`defineHook().create()`,`https://workflow-sdk.dev/docs/api-reference/workflow/define-hook`,t)}return{create:t,async resume(t,n){if(!e?.[`~standard`])return await m(t,n);let r=e[`~standard`].validate(n);if(r instanceof Promise&&(r=await r),r.issues){let e=r.issues.map(e=>{let t=e.path?.map(e=>String(typeof e==`object`&&e?e.key:e)).join(`.`);return t?` at "${t}": ${e.message}`:` ${e.message}`});throw Error(`Hook payload did not match the defined schema:\n${e.join(`
|
|
2
2
|
`)}`)}return await m(t,r.value)}}}const S=Symbol.for(`@workflow/setAttributes//unsupportedWorldWarned`);async function C(t,n={}){let r=u.getStore()?.workflowMetadata?.workflowRunId;if(!r)throw new e(`experimental_setAttributes() must be called from a 'use workflow' or 'use step' function. Calling it from plain host code is not supported.`);let i=_(t,n);if(i.length===0)return;let a=await d();if(typeof a.runs.experimentalSetAttributes!=`function`){let e=globalThis;if(!e[S]){e[S]=!0;let t=`name`in a&&typeof a.name==`string`?a.name:``,n=t?` (${t})`:``;console.warn(`[workflow] setAttributes: the current world implementation${n} does not implement experimentalSetAttributes; this call (and any subsequent setAttributes calls in this process) is a no-op. Attributes will become available once the world adapter adds support.`)}return}await a.runs.experimentalSetAttributes(r,i,n.allowReservedAttributes===!0?{allowReservedAttributes:!0}:{})}function w(){let e=u.getStore();return e||r(`getStepMetadata()`,`https://workflow-sdk.dev/docs/api-reference/workflow/get-step-metadata`,w),e.stepMetadata}function T(){let e=u.getStore();return e||i(`getWorkflowMetadata()`,`https://workflow-sdk.dev/docs/api-reference/workflow/get-workflow-metadata`,T),e.workflowMetadata}function E(e={}){let t=u.getStore();t||i(`getWritable()`,`https://workflow-sdk.dev/docs/api-reference/workflow/get-writable`,E);let{namespace:r}=e,o=t.workflowMetadata.workflowRunId,d=l(o,r),m=t.writables??=new Map,_=m.get(d);if(_)return _.writable;let v=c(h(globalThis,t.ops,o,t.encryptionKey),t.encryptionKey),y=new p(o,d),b=g();return t.ops.push(b.promise),s(v.readable,y,b).catch(()=>{}),f(v.writable,b),Object.defineProperty(v.writable,a,{value:d,writable:!1}),Object.defineProperty(v.writable,n,{value:o,writable:!1}),m.set(d,{writable:v.writable,state:b}),v.writable}export{e as FatalError,t as RetryableError,y as createHook,b as createWebhook,x as defineHook,C as experimental_setAttributes,w as getStepMetadata,T as getWorkflowMetadata,E as getWritable,v as sleep};
|