experimental-ash 0.61.0 → 0.63.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 +72 -0
- package/dist/docs/public/advanced/hooks.mdx +8 -2
- package/dist/docs/public/advanced/runs-and-streaming.md +6 -3
- package/dist/docs/public/advanced/typescript-api.md +32 -0
- package/dist/docs/public/channels/custom.mdx +23 -0
- package/dist/docs/public/frontend/README.md +8 -4
- package/dist/docs/public/frontend/meta.json +9 -1
- package/dist/docs/public/frontend/nextjs.md +4 -4
- package/dist/docs/public/frontend/nuxt.md +168 -0
- package/dist/docs/public/frontend/sveltekit.md +177 -0
- package/dist/docs/public/frontend/use-ash-agent-svelte.md +185 -0
- package/dist/docs/public/frontend/use-ash-agent-vue.md +236 -0
- package/dist/docs/public/frontend/use-ash-agent.md +14 -14
- package/dist/docs/public/getting-started.mdx +2 -0
- package/dist/skills/ash-add-agent/SKILL.md +29 -17
- package/dist/skills/ash-add-next/SKILL.md +58 -8
- package/dist/src/channel/websocket-upgrade-server.d.ts +26 -0
- package/dist/src/channel/websocket-upgrade-server.js +1 -0
- package/dist/src/chunks/use-ash-agent-BQJLh7KU.js +1224 -0
- package/dist/src/chunks/use-ash-agent-CRWVA4i-.js +1192 -0
- package/dist/src/client/ash-agent-store.d.ts +61 -0
- package/dist/src/client/ash-agent-store.js +2 -0
- package/dist/src/client/index.d.ts +2 -0
- package/dist/src/client/index.js +1 -1
- package/dist/src/compiled/.vendor-stamp.json +9 -9
- package/dist/src/compiled/@ai-sdk/anthropic/_provider-utils.d.ts +1 -1
- package/dist/src/compiled/@ai-sdk/anthropic/index.d.ts +3 -3
- package/dist/src/compiled/@ai-sdk/anthropic/index.js +2 -2
- package/dist/src/compiled/@ai-sdk/google/index.d.ts +52 -2
- package/dist/src/compiled/@ai-sdk/google/index.js +6 -6
- package/dist/src/compiled/@ai-sdk/mcp/index.js +1 -1
- package/dist/src/compiled/@ai-sdk/openai/index.d.ts +32 -2
- package/dist/src/compiled/@ai-sdk/openai/index.js +2 -2
- package/dist/src/compiled/@ai-sdk/provider/index.d.ts +507 -1
- package/dist/src/compiled/@chat-adapter/slack/index.js +25 -25
- package/dist/src/compiled/@workflow/core/events-consumer.d.ts +8 -0
- package/dist/src/compiled/@workflow/core/index.js +2 -2
- package/dist/src/compiled/@workflow/core/runtime/constants.d.ts +1 -0
- package/dist/src/compiled/@workflow/core/runtime.js +29 -29
- package/dist/src/compiled/@workflow/core/version.d.ts +1 -1
- package/dist/src/compiled/@workflow/core/workflow.js +1 -1
- package/dist/src/compiled/@workflow/errors/error-codes.d.ts +2 -0
- package/dist/src/compiled/@workflow/errors/index.d.ts +14 -0
- package/dist/src/compiled/@workflow/errors/index.js +1 -1
- package/dist/src/compiled/@workflow/world/queue.d.ts +8 -0
- package/dist/src/compiled/_chunks/workflow/{dist-Chj-QcBs.js → dist-gEXVSMPU.js} +1 -1
- package/dist/src/compiled/_chunks/workflow/dist-zpK2YVVA.js +3 -0
- package/dist/src/compiled/_chunks/workflow/resume-hook-BFK9mgsb.js +12 -0
- package/dist/src/compiled/_chunks/workflow/{sleep-Bg0t23kF.js → sleep-CeJckNg2.js} +1 -1
- package/dist/src/compiled/_chunks/workflow/{symbols-u476uwyR.js → symbols-BWCAoPHE.js} +1 -1
- package/dist/src/compiler/manifest.d.ts +12 -0
- package/dist/src/compiler/manifest.js +1 -1
- package/dist/src/compiler/normalize-connection.d.ts +10 -2
- package/dist/src/compiler/normalize-connection.js +1 -1
- package/dist/src/execution/sandbox/bindings/local.js +1 -1
- package/dist/src/execution/sandbox/bindings/vercel.js +1 -1
- package/dist/src/internal/application/package.d.ts +1 -0
- package/dist/src/internal/application/package.js +1 -1
- package/dist/src/internal/authored-definition/connection.d.ts +9 -0
- package/dist/src/internal/authored-definition/connection.js +1 -1
- package/dist/src/internal/nitro/host/build-application.js +1 -1
- package/dist/src/internal/nitro/host/build-vercel-agent-summary.js +1 -1
- package/dist/src/internal/nitro/host/channel-routes.js +2 -2
- package/dist/src/internal/vercel-agent-summary.d.ts +6 -4
- package/dist/src/internal/workflow-bundle/ash-service-route-output.js +11 -1
- package/dist/src/packages/ash-scaffold/src/channels.js +1 -1
- package/dist/src/public/channels/auth.d.ts +1 -1
- package/dist/src/public/channels/index.d.ts +1 -0
- package/dist/src/public/channels/index.js +1 -1
- package/dist/src/public/connections/index.d.ts +3 -2
- package/dist/src/public/connections/index.js +1 -1
- package/dist/src/public/definitions/connections/mcp.d.ts +4 -12
- package/dist/src/public/definitions/connections/mcp.js +1 -1
- package/dist/src/public/definitions/connections/openapi.d.ts +100 -0
- package/dist/src/public/definitions/connections/openapi.js +1 -0
- package/dist/src/public/definitions/connections/protocol.d.ts +12 -0
- package/dist/src/public/definitions/connections/protocol.js +1 -0
- package/dist/src/public/next/index.d.ts +6 -6
- package/dist/src/public/next/index.js +1 -1
- package/dist/src/public/next/{vercel-json.d.ts → vercel-output-config.d.ts} +3 -3
- package/dist/src/public/next/vercel-output-config.js +1 -0
- package/dist/src/public/nuxt/dev-server.d.ts +24 -0
- package/dist/src/public/nuxt/dev-server.js +1 -0
- package/dist/src/public/nuxt/index.d.ts +1 -0
- package/dist/src/public/nuxt/index.js +1 -0
- package/dist/src/public/nuxt/module.d.ts +31 -0
- package/dist/src/public/nuxt/module.js +1 -0
- package/dist/src/public/nuxt/routing.d.ts +55 -0
- package/dist/src/public/nuxt/routing.js +1 -0
- package/dist/src/public/nuxt/vercel-json.d.ts +17 -0
- package/dist/src/public/{next → nuxt}/vercel-json.js +1 -1
- package/dist/src/public/sveltekit/dev-server.d.ts +24 -0
- package/dist/src/public/sveltekit/dev-server.js +1 -0
- package/dist/src/public/sveltekit/index.d.ts +39 -0
- package/dist/src/public/sveltekit/index.js +1 -0
- package/dist/src/public/sveltekit/routing.d.ts +32 -0
- package/dist/src/public/sveltekit/routing.js +1 -0
- package/dist/src/public/sveltekit/vercel-json.d.ts +17 -0
- package/dist/src/public/sveltekit/vercel-json.js +1 -0
- package/dist/src/react/use-ash-agent.d.ts +5 -27
- package/dist/src/react/use-ash-agent.js +1 -2
- package/dist/src/runtime/connections/openapi-client.d.ts +43 -0
- package/dist/src/runtime/connections/openapi-client.js +1 -0
- package/dist/src/runtime/connections/openapi-operations.d.ts +30 -0
- package/dist/src/runtime/connections/openapi-operations.js +1 -0
- package/dist/src/runtime/connections/openapi-schema.d.ts +39 -0
- package/dist/src/runtime/connections/openapi-schema.js +1 -0
- package/dist/src/runtime/connections/openapi-security.d.ts +41 -0
- package/dist/src/runtime/connections/openapi-security.js +1 -0
- package/dist/src/runtime/connections/openapi-spec.d.ts +20 -0
- package/dist/src/runtime/connections/openapi-spec.js +1 -0
- package/dist/src/runtime/connections/registry.d.ts +5 -7
- package/dist/src/runtime/connections/registry.js +1 -1
- package/dist/src/runtime/connections/types.d.ts +23 -0
- package/dist/src/runtime/resolve-connection.js +1 -1
- package/dist/src/runtime/types.d.ts +15 -1
- package/dist/src/shared/sandbox-session.d.ts +1 -1
- package/dist/src/shared/vercel-output-directory.d.ts +2 -0
- package/dist/src/shared/vercel-output-directory.js +1 -0
- package/dist/src/svelte/index.d.ts +3 -0
- package/dist/src/svelte/index.js +3 -0
- package/dist/src/svelte/use-ash-agent.d.ts +80 -0
- package/dist/src/svelte/use-ash-agent.js +3 -0
- package/dist/src/vue/index.d.ts +3 -0
- package/dist/src/vue/index.js +3 -0
- package/dist/src/vue/use-ash-agent.d.ts +78 -0
- package/dist/src/vue/use-ash-agent.js +3 -0
- package/package.json +59 -14
- package/dist/src/compiled/_chunks/workflow/dist-C4EHshZE.js +0 -3
- package/dist/src/compiled/_chunks/workflow/resume-hook-BlALLgSA.js +0 -12
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "useAshAgent (Svelte)"
|
|
3
|
+
description: "Svelte 5 binding that drives an Ash agent session from the browser."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
`useAshAgent()` is a Svelte 5 binding that opens a long-lived Ash session in
|
|
7
|
+
the browser, lets the user send turns, and projects every stream event into
|
|
8
|
+
rune-friendly reactive data for your template.
|
|
9
|
+
|
|
10
|
+
The simplest usage:
|
|
11
|
+
|
|
12
|
+
```svelte
|
|
13
|
+
<script lang="ts">
|
|
14
|
+
import { useAshAgent } from "experimental-ash/svelte";
|
|
15
|
+
|
|
16
|
+
const agent = useAshAgent();
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
{#each agent.data.messages as message}
|
|
20
|
+
<p>{message.role}: {JSON.stringify(message.parts)}</p>
|
|
21
|
+
{/each}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## What It Returns
|
|
25
|
+
|
|
26
|
+
`useAshAgent()` returns an object with reactive properties and action methods:
|
|
27
|
+
|
|
28
|
+
| Property | Type | Description |
|
|
29
|
+
| ------------- | -------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
30
|
+
| `data` | `TData` | Projected state built by reducing every stream event through the reducer. With the default reducer this is `AshMessageData` containing a `messages` array. |
|
|
31
|
+
| `status` | `UseAshAgentStatus` | `"ready"`, `"submitted"`, `"streaming"`, or `"error"` |
|
|
32
|
+
| `error` | `Error \| undefined` | Last transport-level error |
|
|
33
|
+
| `events` | `readonly HandleMessageStreamEvent[]` | Raw server events for this session |
|
|
34
|
+
| `session` | `SessionState` | Snapshot of session state |
|
|
35
|
+
| `send` | `(input, options?) => Promise<void>` | Send a turn with full options |
|
|
36
|
+
| `sendMessage` | `(message, options?) => Promise<void>` | Shorthand: send a plain text turn |
|
|
37
|
+
| `stop` | `() => void` | Abort the in-flight request |
|
|
38
|
+
| `reset` | `() => void` | Clear state and start a new session |
|
|
39
|
+
|
|
40
|
+
The state fields are reactive getters. Read them directly from templates,
|
|
41
|
+
`$derived`, or `$effect`; do not prefix them with `$` like Svelte stores.
|
|
42
|
+
|
|
43
|
+
## Send A Message
|
|
44
|
+
|
|
45
|
+
```svelte
|
|
46
|
+
<script lang="ts">
|
|
47
|
+
import { useAshAgent } from "experimental-ash/svelte";
|
|
48
|
+
|
|
49
|
+
const agent = useAshAgent();
|
|
50
|
+
let message = $state("");
|
|
51
|
+
|
|
52
|
+
async function handleSubmit() {
|
|
53
|
+
const text = message.trim();
|
|
54
|
+
if (!text) return;
|
|
55
|
+
message = "";
|
|
56
|
+
await agent.sendMessage(text);
|
|
57
|
+
}
|
|
58
|
+
</script>
|
|
59
|
+
|
|
60
|
+
<form onsubmit={(event) => {
|
|
61
|
+
event.preventDefault();
|
|
62
|
+
void handleSubmit();
|
|
63
|
+
}}>
|
|
64
|
+
<input bind:value={message} placeholder="Type a message..." />
|
|
65
|
+
<button type="submit">Send</button>
|
|
66
|
+
</form>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Send Attachments
|
|
70
|
+
|
|
71
|
+
Use `send()` for structured input. Attachments use the AI SDK `UserContent`
|
|
72
|
+
format:
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
await agent.send({
|
|
76
|
+
message: [
|
|
77
|
+
{ type: "text", text: "Describe this image." },
|
|
78
|
+
{ type: "file", data: fileBytes, mimeType: "image/png" },
|
|
79
|
+
],
|
|
80
|
+
});
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Respond To Human-In-The-Loop Prompts
|
|
84
|
+
|
|
85
|
+
When the agent requests human input (tool approvals, confirmations), the
|
|
86
|
+
`inputRequest` shows up inside `message.parts` on `dynamic-tool` parts. Send
|
|
87
|
+
`inputResponses` back:
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
await agent.send({
|
|
91
|
+
inputResponses: [{ requestId: inputRequest.requestId, optionId: "approve" }],
|
|
92
|
+
});
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Stop And Reset
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
agent.stop(); // abort the in-flight stream
|
|
99
|
+
agent.reset(); // wipe state and create a new session
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Resumable Sessions
|
|
103
|
+
|
|
104
|
+
Pass `initialSession` to restore a session from a prior page load:
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
const agent = useAshAgent({
|
|
108
|
+
initialSession: savedSessionState,
|
|
109
|
+
initialEvents: savedEvents,
|
|
110
|
+
});
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Add Per-Turn Client Context
|
|
114
|
+
|
|
115
|
+
Use `prepareSend` to attach short-lived page state to each turn:
|
|
116
|
+
|
|
117
|
+
```ts
|
|
118
|
+
const agent = useAshAgent({
|
|
119
|
+
prepareSend(input) {
|
|
120
|
+
return {
|
|
121
|
+
...input,
|
|
122
|
+
clientContext: {
|
|
123
|
+
pathname: window.location.pathname,
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Custom Host And Auth
|
|
131
|
+
|
|
132
|
+
Use `host` for non-standard routing, and `auth` or `headers` for credentials:
|
|
133
|
+
|
|
134
|
+
```ts
|
|
135
|
+
const agent = useAshAgent({
|
|
136
|
+
host: "https://agent.example.com",
|
|
137
|
+
headers: async () => ({
|
|
138
|
+
authorization: `Bearer ${await getAccessToken()}`,
|
|
139
|
+
}),
|
|
140
|
+
});
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Custom Reducer
|
|
144
|
+
|
|
145
|
+
Pass a custom `reducer` to control how stream events project into `data`:
|
|
146
|
+
|
|
147
|
+
```ts
|
|
148
|
+
import { useAshAgent, type AshAgentReducer } from "experimental-ash/svelte";
|
|
149
|
+
|
|
150
|
+
interface MyData {
|
|
151
|
+
turns: number;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const reducer: AshAgentReducer<MyData> = {
|
|
155
|
+
initial: () => ({ turns: 0 }),
|
|
156
|
+
reduce: (data, event) => {
|
|
157
|
+
if (event.type === "turn.completed") {
|
|
158
|
+
return { turns: data.turns + 1 };
|
|
159
|
+
}
|
|
160
|
+
return data;
|
|
161
|
+
},
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
const agent = useAshAgent({ reducer });
|
|
165
|
+
// agent.data is MyData
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Lifecycle Callbacks
|
|
169
|
+
|
|
170
|
+
```ts
|
|
171
|
+
const agent = useAshAgent({
|
|
172
|
+
onError: (error) => console.error(error),
|
|
173
|
+
onEvent: (event) => console.log(event.type),
|
|
174
|
+
onFinish: (snapshot) => console.log("done", snapshot.status),
|
|
175
|
+
onSessionChange: (session) => {
|
|
176
|
+
localStorage.setItem("ash-session", JSON.stringify(session));
|
|
177
|
+
},
|
|
178
|
+
});
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## What To Read Next
|
|
182
|
+
|
|
183
|
+
- [SvelteKit](./sveltekit.md) - framework setup
|
|
184
|
+
- [Sessions And Streaming](../advanced/runs-and-streaming.md) - under the hood
|
|
185
|
+
- [TypeScript API](../advanced/typescript-api.md) - client exports
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "useAshAgent (Vue)"
|
|
3
|
+
description: "Vue composable that drives an Ash agent session from the browser."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
`useAshAgent()` is a Vue composable that opens a long-lived Ash session
|
|
7
|
+
in the browser, lets the user send turns, and projects every stream event
|
|
8
|
+
into reactive data for your template.
|
|
9
|
+
|
|
10
|
+
The simplest usage:
|
|
11
|
+
|
|
12
|
+
```vue
|
|
13
|
+
<script setup lang="ts">
|
|
14
|
+
import { useAshAgent } from "experimental-ash/vue";
|
|
15
|
+
|
|
16
|
+
const { data } = useAshAgent();
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
<template>
|
|
20
|
+
<div v-for="message in data.messages" :key="message.id">
|
|
21
|
+
<p>{{ message.role }}: {{ message.parts }}</p>
|
|
22
|
+
</div>
|
|
23
|
+
</template>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## What It Returns
|
|
27
|
+
|
|
28
|
+
`useAshAgent()` returns an object of computed refs and action methods:
|
|
29
|
+
|
|
30
|
+
| Property | Type | Description |
|
|
31
|
+
| ------------- | -------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
32
|
+
| `data` | `ComputedRef<TData>` | Projected state built by reducing every stream event through the reducer. With the default reducer this is `AshMessageData` containing a `messages` array. |
|
|
33
|
+
| `status` | `ComputedRef<UseAshAgentStatus>` | `"ready"`, `"submitted"`, `"streaming"`, or `"error"` |
|
|
34
|
+
| `error` | `ComputedRef<Error \| undefined>` | Last transport-level error |
|
|
35
|
+
| `events` | `ComputedRef<readonly HandleMessageStreamEvent[]>` | Raw server events for this session |
|
|
36
|
+
| `session` | `ComputedRef<SessionState>` | Snapshot of session state |
|
|
37
|
+
| `send` | `(input, options?) => Promise<void>` | Send a turn with full options |
|
|
38
|
+
| `sendMessage` | `(message, options?) => Promise<void>` | Shorthand: send a plain text turn |
|
|
39
|
+
| `stop` | `() => void` | Abort the in-flight request |
|
|
40
|
+
| `reset` | `() => void` | Clear state and start a new session |
|
|
41
|
+
|
|
42
|
+
The state properties (`data`, `status`, `error`, `events`, `session`) are
|
|
43
|
+
`ComputedRef`s; the rest are methods. Destructure whatever you need — refs keep
|
|
44
|
+
their reactivity through destructuring — and read them with `.value` in
|
|
45
|
+
`<script>` or unwrapped (no `.value`) in `<template>`.
|
|
46
|
+
|
|
47
|
+
## Send A Message
|
|
48
|
+
|
|
49
|
+
```vue
|
|
50
|
+
<script setup lang="ts">
|
|
51
|
+
import { useAshAgent } from "experimental-ash/vue";
|
|
52
|
+
|
|
53
|
+
const { sendMessage } = useAshAgent();
|
|
54
|
+
const message = ref("");
|
|
55
|
+
|
|
56
|
+
async function handleSubmit() {
|
|
57
|
+
const text = message.value.trim();
|
|
58
|
+
if (!text) return;
|
|
59
|
+
message.value = "";
|
|
60
|
+
await sendMessage(text);
|
|
61
|
+
}
|
|
62
|
+
</script>
|
|
63
|
+
|
|
64
|
+
<template>
|
|
65
|
+
<form @submit.prevent="handleSubmit">
|
|
66
|
+
<input v-model="message" placeholder="Type a message..." />
|
|
67
|
+
<button type="submit">Send</button>
|
|
68
|
+
</form>
|
|
69
|
+
</template>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Send Attachments
|
|
73
|
+
|
|
74
|
+
Use `send()` for structured input. Attachments use the AI SDK `UserContent`
|
|
75
|
+
format:
|
|
76
|
+
|
|
77
|
+
```vue
|
|
78
|
+
<script setup lang="ts">
|
|
79
|
+
import { useAshAgent } from "experimental-ash/vue";
|
|
80
|
+
|
|
81
|
+
const { send } = useAshAgent();
|
|
82
|
+
|
|
83
|
+
async function onFileChange(event: Event) {
|
|
84
|
+
const file = (event.target as HTMLInputElement).files?.[0];
|
|
85
|
+
if (!file) return;
|
|
86
|
+
await send({
|
|
87
|
+
message: [
|
|
88
|
+
{ type: "text", text: "Describe this image." },
|
|
89
|
+
{ type: "file", data: new Uint8Array(await file.arrayBuffer()), mediaType: file.type },
|
|
90
|
+
],
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
</script>
|
|
94
|
+
|
|
95
|
+
<template>
|
|
96
|
+
<input type="file" accept="image/*" @change="onFileChange" />
|
|
97
|
+
</template>
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Respond To Human-In-The-Loop Prompts
|
|
101
|
+
|
|
102
|
+
When the agent requests human input (tool approvals, confirmations),
|
|
103
|
+
the `inputRequest` shows up inside `message.parts` on `dynamic-tool` parts.
|
|
104
|
+
Send `inputResponses` back:
|
|
105
|
+
|
|
106
|
+
```vue
|
|
107
|
+
<script setup lang="ts">
|
|
108
|
+
import { useAshAgent, type AshDynamicToolPart } from "experimental-ash/vue";
|
|
109
|
+
|
|
110
|
+
const props = defineProps<{ part: AshDynamicToolPart }>();
|
|
111
|
+
|
|
112
|
+
const { send } = useAshAgent();
|
|
113
|
+
|
|
114
|
+
const inputRequest = computed(() => props.part.toolMetadata?.ash?.inputRequest);
|
|
115
|
+
|
|
116
|
+
function respond(optionId: string) {
|
|
117
|
+
const request = inputRequest.value;
|
|
118
|
+
if (!request) return;
|
|
119
|
+
void send({ inputResponses: [{ requestId: request.requestId, optionId }] });
|
|
120
|
+
}
|
|
121
|
+
</script>
|
|
122
|
+
|
|
123
|
+
<template>
|
|
124
|
+
<div v-if="inputRequest">
|
|
125
|
+
<p>{{ inputRequest.prompt }}</p>
|
|
126
|
+
<button v-for="option in inputRequest.options" :key="option.id" @click="respond(option.id)">
|
|
127
|
+
{{ option.label }}
|
|
128
|
+
</button>
|
|
129
|
+
</div>
|
|
130
|
+
</template>
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Stop And Reset
|
|
134
|
+
|
|
135
|
+
Abort the in-flight stream, or wipe state and start a fresh session:
|
|
136
|
+
|
|
137
|
+
```vue
|
|
138
|
+
<script setup lang="ts">
|
|
139
|
+
import { useAshAgent } from "experimental-ash/vue";
|
|
140
|
+
|
|
141
|
+
const { status, stop, reset } = useAshAgent();
|
|
142
|
+
|
|
143
|
+
const isBusy = computed(() => status.value === "submitted" || status.value === "streaming");
|
|
144
|
+
</script>
|
|
145
|
+
|
|
146
|
+
<template>
|
|
147
|
+
<button :disabled="!isBusy" @click="stop()">Stop</button>
|
|
148
|
+
<button @click="reset()">Reset</button>
|
|
149
|
+
</template>
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Resumable Sessions
|
|
153
|
+
|
|
154
|
+
Pass `initialSession` to restore a session from a prior page load:
|
|
155
|
+
|
|
156
|
+
```ts
|
|
157
|
+
const agent = useAshAgent({
|
|
158
|
+
initialSession: savedSessionState,
|
|
159
|
+
initialEvents: savedEvents,
|
|
160
|
+
});
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Add Per-Turn Client Context
|
|
164
|
+
|
|
165
|
+
Use `prepareSend` to attach short-lived page state to each turn:
|
|
166
|
+
|
|
167
|
+
```ts
|
|
168
|
+
const agent = useAshAgent({
|
|
169
|
+
prepareSend(input) {
|
|
170
|
+
return {
|
|
171
|
+
...input,
|
|
172
|
+
clientContext: {
|
|
173
|
+
pathname: window.location.pathname,
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
},
|
|
177
|
+
});
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Custom Host And Auth
|
|
181
|
+
|
|
182
|
+
Use `host` for non-standard routing, and `auth` or `headers` for
|
|
183
|
+
credentials:
|
|
184
|
+
|
|
185
|
+
```ts
|
|
186
|
+
const agent = useAshAgent({
|
|
187
|
+
host: "https://agent.example.com",
|
|
188
|
+
headers: async () => ({
|
|
189
|
+
authorization: `Bearer ${await getAccessToken()}`,
|
|
190
|
+
}),
|
|
191
|
+
});
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## Custom Reducer
|
|
195
|
+
|
|
196
|
+
Pass a custom `reducer` to control how stream events project into `data`:
|
|
197
|
+
|
|
198
|
+
```ts
|
|
199
|
+
import { useAshAgent, type AshAgentReducer } from "experimental-ash/vue";
|
|
200
|
+
|
|
201
|
+
interface MyData {
|
|
202
|
+
turns: number;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const reducer: AshAgentReducer<MyData> = {
|
|
206
|
+
initial: () => ({ turns: 0 }),
|
|
207
|
+
reduce: (data, event) => {
|
|
208
|
+
if (event.type === "turn.completed") {
|
|
209
|
+
return { turns: data.turns + 1 };
|
|
210
|
+
}
|
|
211
|
+
return data;
|
|
212
|
+
},
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
const { data } = useAshAgent({ reducer });
|
|
216
|
+
// data.value is MyData
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## Lifecycle Callbacks
|
|
220
|
+
|
|
221
|
+
```ts
|
|
222
|
+
const agent = useAshAgent({
|
|
223
|
+
onError: (error) => console.error(error),
|
|
224
|
+
onEvent: (event) => console.log(event.type),
|
|
225
|
+
onFinish: (snapshot) => console.log("done", snapshot.status),
|
|
226
|
+
onSessionChange: (session) => {
|
|
227
|
+
localStorage.setItem("ash-session", JSON.stringify(session));
|
|
228
|
+
},
|
|
229
|
+
});
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
## What To Read Next
|
|
233
|
+
|
|
234
|
+
- [Nuxt](./nuxt.md) — module setup
|
|
235
|
+
- [Sessions And Streaming](../advanced/runs-and-streaming.md) — under the hood
|
|
236
|
+
- [TypeScript API](../advanced/typescript-api.md) — client exports
|
|
@@ -60,17 +60,17 @@ your component can call the agent without configuring a separate URL.
|
|
|
60
60
|
|
|
61
61
|
`useAshAgent()` returns the current UI state plus commands:
|
|
62
62
|
|
|
63
|
-
| Field | What it is
|
|
64
|
-
| ------------- |
|
|
65
|
-
| `data` | Projected UI state from the reducer. Defaults to `{ messages }`.
|
|
66
|
-
| `status` | `"ready"`, `"submitted"`, `"streaming"`, or `"error"`. Drives the composer.
|
|
67
|
-
| `error` | The last `Error` thrown, if any.
|
|
68
|
-
| `events` | Raw Ash stream events for this session — see [Sessions And Streaming](../runs-and-streaming.md). |
|
|
69
|
-
| `session` | Serializable session cursor (`sessionId`, `continuationToken`, `streamIndex`).
|
|
70
|
-
| `sendMessage` | Send plain text.
|
|
71
|
-
| `send` | Send the full turn payload (multi-part messages, HITL responses).
|
|
72
|
-
| `stop` | Abort the active request.
|
|
73
|
-
| `reset` | Clear local events, data, errors, and the local session cursor.
|
|
63
|
+
| Field | What it is |
|
|
64
|
+
| ------------- | --------------------------------------------------------------------------------------------------------- |
|
|
65
|
+
| `data` | Projected UI state from the reducer. Defaults to `{ messages }`. |
|
|
66
|
+
| `status` | `"ready"`, `"submitted"`, `"streaming"`, or `"error"`. Drives the composer. |
|
|
67
|
+
| `error` | The last `Error` thrown, if any. |
|
|
68
|
+
| `events` | Raw Ash stream events for this session — see [Sessions And Streaming](../advanced/runs-and-streaming.md). |
|
|
69
|
+
| `session` | Serializable session cursor (`sessionId`, `continuationToken`, `streamIndex`). |
|
|
70
|
+
| `sendMessage` | Send plain text. |
|
|
71
|
+
| `send` | Send the full turn payload (multi-part messages, HITL responses). |
|
|
72
|
+
| `stop` | Abort the active request. |
|
|
73
|
+
| `reset` | Clear local events, data, errors, and the local session cursor. |
|
|
74
74
|
|
|
75
75
|
Most chat UIs only need `data.messages` and `status`. Reach for `events` when
|
|
76
76
|
you want to render lower-level activity (tool calls, reasoning deltas) the
|
|
@@ -286,7 +286,7 @@ const agent = useAshAgent({
|
|
|
286
286
|
```
|
|
287
287
|
|
|
288
288
|
`agent.data.tools` now drives the UI. The reducer sees the full Ash event
|
|
289
|
-
stream — see [Sessions And Streaming](../runs-and-streaming.md) — plus
|
|
289
|
+
stream — see [Sessions And Streaming](../advanced/runs-and-streaming.md) — plus
|
|
290
290
|
client-side projection events (`client.message.submitted`,
|
|
291
291
|
`client.message.failed`, `client.input.responded`) for optimistic updates.
|
|
292
292
|
|
|
@@ -323,6 +323,6 @@ To change credentials without remounting, pass a function to `auth` or
|
|
|
323
323
|
## What To Read Next
|
|
324
324
|
|
|
325
325
|
- [Next.js](./nextjs.md)
|
|
326
|
-
- [Sessions And Streaming](../runs-and-streaming.md)
|
|
326
|
+
- [Sessions And Streaming](../advanced/runs-and-streaming.md)
|
|
327
327
|
- [Human In The Loop](../human-in-the-loop.md)
|
|
328
|
-
- [Hooks](../hooks.
|
|
328
|
+
- [Hooks](../advanced/hooks.mdx)
|
|
@@ -174,3 +174,5 @@ curl -X POST http://127.0.0.1:3000/ash/v1/session/<sessionId> \
|
|
|
174
174
|
- [Skills](./skills.md) for on-demand procedures
|
|
175
175
|
- [Tools](./tools.mdx) for typed integrations
|
|
176
176
|
- [Sessions And Streaming](./runs-and-streaming.md) for the durable session model
|
|
177
|
+
- [Nuxt Integration](./frontend/nuxt.md) for Nuxt apps
|
|
178
|
+
- [`useAshAgent` (Vue)](./frontend/use-ash-agent-vue.md) for Vue composable usage
|
|
@@ -20,7 +20,6 @@ app/
|
|
|
20
20
|
next.config.ts
|
|
21
21
|
package.json
|
|
22
22
|
tsconfig.json
|
|
23
|
-
vercel.json # generated/updated by withAsh()
|
|
24
23
|
```
|
|
25
24
|
|
|
26
25
|
## Steps
|
|
@@ -52,28 +51,43 @@ Use the model requested by the user or the project standard.
|
|
|
52
51
|
|
|
53
52
|
3. Add the Ash HTTP channel:
|
|
54
53
|
|
|
54
|
+
First inspect the existing Next.js app for authentication. Reuse its current provider and session
|
|
55
|
+
model instead of introducing a second auth system. Look for Auth.js, Clerk, application session
|
|
56
|
+
cookies, bearer-token middleware, or another established request-auth helper.
|
|
57
|
+
|
|
55
58
|
```ts
|
|
56
59
|
// agent/channels/ash.ts
|
|
57
60
|
import { ashChannel } from "experimental-ash/channels/ash";
|
|
58
61
|
import { type AuthFn, localDev, vercelOidc } from "experimental-ash/channels/auth";
|
|
59
62
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
}
|
|
67
|
-
|
|
63
|
+
const authenticateUser: AuthFn = async (request) => {
|
|
64
|
+
// Replace this call with the app's existing Auth.js, Clerk, or session helper.
|
|
65
|
+
const user = await authenticate(request);
|
|
66
|
+
if (!user) return null;
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
attributes: {},
|
|
70
|
+
authenticator: "app",
|
|
71
|
+
issuer: "nextjs",
|
|
72
|
+
principalId: user.id,
|
|
73
|
+
principalType: "user",
|
|
68
74
|
};
|
|
69
|
-
}
|
|
75
|
+
};
|
|
70
76
|
|
|
71
77
|
export default ashChannel({
|
|
72
|
-
auth: [localDev(), vercelOidc(),
|
|
78
|
+
auth: [localDev(), vercelOidc(), authenticateUser],
|
|
73
79
|
});
|
|
74
80
|
```
|
|
75
81
|
|
|
76
|
-
An authored `agent/channels/ash.ts` replaces Ash's framework default `ash` channel. Keep
|
|
82
|
+
An authored `agent/channels/ash.ts` replaces Ash's framework default `ash` channel. Keep
|
|
83
|
+
`localDev()` for localhost and the REPL, keep `vercelOidc()` so Vercel services can reach the
|
|
84
|
+
deployed agent, and implement `authenticateUser` with the app's real user auth. If the app already
|
|
85
|
+
uses Auth.js, Clerk, or another session provider, wire that existing provider here.
|
|
86
|
+
|
|
87
|
+
For same-origin cookie auth, the browser already sends the session cookie and the channel should
|
|
88
|
+
validate it from `request`. For bearer or custom-header auth, also configure the frontend's
|
|
89
|
+
`useAshAgent({ auth })` or `useAshAgent({ headers })` call. Do not report the frontend as
|
|
90
|
+
production-ready until unauthenticated requests are rejected.
|
|
77
91
|
|
|
78
92
|
4. Merge package imports only when agent files use `#...` aliases:
|
|
79
93
|
|
|
@@ -149,10 +163,6 @@ Keep an existing `typecheck` unless it excludes `agent/**/*.ts`.
|
|
|
149
163
|
|
|
150
164
|
Add `paths` only if the project uses those aliases.
|
|
151
165
|
|
|
152
|
-
8. Let `withAsh()` generate or update `vercel.json`.
|
|
153
|
-
|
|
154
|
-
After adding `withAsh()`, run `pnpm dev` or the relevant build command. Do not hand-author the Vercel services config unless generation fails or the user explicitly wants manual control. If `vercel.json` is created or changed, review and commit it.
|
|
155
|
-
|
|
156
166
|
## Verify
|
|
157
167
|
|
|
158
168
|
```bash
|
|
@@ -163,4 +173,6 @@ curl http://localhost:3000/ash/v1/health
|
|
|
163
173
|
|
|
164
174
|
Use `pnpm info:ash` or `pnpm dev:ash` to inspect Ash directly.
|
|
165
175
|
|
|
166
|
-
|
|
176
|
+
Verify the selected auth path as well as the health route. Local development may be accepted by
|
|
177
|
+
`localDev()`, but a production-style unauthenticated request must be rejected by the end-user auth
|
|
178
|
+
policy.
|
|
@@ -13,6 +13,8 @@ Convert an Ash-only project into a combined Next.js app plus Ash agent. Preserve
|
|
|
13
13
|
```txt
|
|
14
14
|
agent/
|
|
15
15
|
agent.ts
|
|
16
|
+
channels/
|
|
17
|
+
ash.ts
|
|
16
18
|
instructions.md
|
|
17
19
|
app/
|
|
18
20
|
globals.css
|
|
@@ -22,7 +24,6 @@ next.config.ts
|
|
|
22
24
|
next-env.d.ts
|
|
23
25
|
package.json
|
|
24
26
|
tsconfig.json
|
|
25
|
-
vercel.json # generated/updated by withAsh()
|
|
26
27
|
```
|
|
27
28
|
|
|
28
29
|
## Steps
|
|
@@ -78,7 +79,57 @@ const nextConfig: NextConfig = {};
|
|
|
78
79
|
export default withAsh(nextConfig);
|
|
79
80
|
```
|
|
80
81
|
|
|
81
|
-
4.
|
|
82
|
+
4. Choose how frontend users authenticate to the Ash channel.
|
|
83
|
+
|
|
84
|
+
Inspect `agent/channels/ash.ts` first and preserve any existing auth policy. A channel configured
|
|
85
|
+
only with `localDev()` and `vercelOidc()` does not have end-user authentication. If the project does
|
|
86
|
+
not already have end-user authentication, ask the user which production access model they want
|
|
87
|
+
before adding one:
|
|
88
|
+
|
|
89
|
+
- **Clerk or Auth.js** for a normal signed-in web application.
|
|
90
|
+
- **Existing cookie/session auth** when another application auth system is already planned.
|
|
91
|
+
- **Bearer/JWT auth** when a separate client or identity provider supplies access tokens.
|
|
92
|
+
- **HTTP Basic** for a small private or internal frontend where a shared credential is acceptable.
|
|
93
|
+
- **Local-only for now** when they are only prototyping. Keep `localDev()` and `vercelOidc()`, clearly
|
|
94
|
+
state that browser users cannot access the deployed agent, and do not describe production setup as
|
|
95
|
+
complete.
|
|
96
|
+
|
|
97
|
+
Do not choose Clerk, Auth.js, or another provider on the user's behalf when starting from an
|
|
98
|
+
Ash-only project. Once the user chooses, install and configure that provider using its established
|
|
99
|
+
Next.js pattern, then connect the resulting request identity to `agent/channels/ash.ts`.
|
|
100
|
+
|
|
101
|
+
For cookie or session authentication, use this channel shape:
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
// agent/channels/ash.ts
|
|
105
|
+
import { ashChannel } from "experimental-ash/channels/ash";
|
|
106
|
+
import { type AuthFn, localDev, vercelOidc } from "experimental-ash/channels/auth";
|
|
107
|
+
|
|
108
|
+
const authenticateUser: AuthFn = async (request) => {
|
|
109
|
+
// Implement this with the selected Clerk, Auth.js, or application session API.
|
|
110
|
+
const user = await authenticate(request);
|
|
111
|
+
if (!user) return null;
|
|
112
|
+
|
|
113
|
+
return {
|
|
114
|
+
attributes: {},
|
|
115
|
+
authenticator: "app",
|
|
116
|
+
issuer: "nextjs",
|
|
117
|
+
principalId: user.id,
|
|
118
|
+
principalType: "user",
|
|
119
|
+
};
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
export default ashChannel({
|
|
123
|
+
auth: [localDev(), vercelOidc(), authenticateUser],
|
|
124
|
+
});
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Same-origin cookie sessions need no client credential plumbing. For bearer/JWT or custom-header
|
|
128
|
+
auth, also configure the generated frontend with `useAshAgent({ auth })` or
|
|
129
|
+
`useAshAgent({ headers })`. For HTTP Basic, use the Ash channel auth helper and keep the credential
|
|
130
|
+
outside source control.
|
|
131
|
+
|
|
132
|
+
5. Make Next own the main scripts:
|
|
82
133
|
|
|
83
134
|
```json
|
|
84
135
|
{
|
|
@@ -93,7 +144,7 @@ export default withAsh(nextConfig);
|
|
|
93
144
|
}
|
|
94
145
|
```
|
|
95
146
|
|
|
96
|
-
|
|
147
|
+
6. Use a Next-compatible `tsconfig.json`, keep `.ash/**/*.d.ts` in `include`, and preserve Ash aliases:
|
|
97
148
|
|
|
98
149
|
```json
|
|
99
150
|
{
|
|
@@ -122,11 +173,7 @@ export default withAsh(nextConfig);
|
|
|
122
173
|
|
|
123
174
|
Merge matching `package.json` `imports` when Ash files use `#...`.
|
|
124
175
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
After adding `withAsh()`, run `pnpm dev` or the relevant build command. Do not hand-author the Vercel services config unless generation fails or the user explicitly wants manual control. If `vercel.json` is created or changed, review and commit it.
|
|
128
|
-
|
|
129
|
-
7. Ignore generated artifacts if needed: `.next`, `.ash`, `.output`, `*.tsbuildinfo`. Do not ignore `vercel.json` when it is generated for the app.
|
|
176
|
+
7. Ignore generated artifacts if needed: `.next`, `.vercel`, `.ash`, `.output`, `*.tsbuildinfo`.
|
|
130
177
|
|
|
131
178
|
## Verify
|
|
132
179
|
|
|
@@ -135,3 +182,6 @@ pnpm install
|
|
|
135
182
|
pnpm dev
|
|
136
183
|
curl http://localhost:3000/ash/v1/health
|
|
137
184
|
```
|
|
185
|
+
|
|
186
|
+
Also verify the chosen authentication path. A signed-in or correctly credentialed request should
|
|
187
|
+
reach the Ash session API, while an unauthenticated production-style request should return `401`.
|