experimental-ash 0.2.0-alpha.21 → 0.2.0-alpha.22
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/dist/src/channel/slack-channel.d.ts +14 -7
- package/dist/src/channel/slack-channel.d.ts.map +1 -1
- package/dist/src/channel/slack-channel.js +21 -11
- package/dist/src/channel/slack-channel.js.map +1 -1
- package/dist/src/channel/types.d.ts +14 -14
- package/dist/src/channel/types.d.ts.map +1 -1
- package/dist/src/channel/types.js +6 -0
- package/dist/src/channel/types.js.map +1 -1
- package/dist/src/context/accessors.d.ts +24 -10
- package/dist/src/context/accessors.d.ts.map +1 -1
- package/dist/src/context/accessors.js +34 -16
- package/dist/src/context/accessors.js.map +1 -1
- package/dist/src/context/container.d.ts +43 -20
- package/dist/src/context/container.d.ts.map +1 -1
- package/dist/src/context/container.js +54 -22
- package/dist/src/context/container.js.map +1 -1
- package/dist/src/context/key.d.ts +68 -39
- package/dist/src/context/key.d.ts.map +1 -1
- package/dist/src/context/key.js +49 -62
- package/dist/src/context/key.js.map +1 -1
- package/dist/src/context/keys.d.ts +6 -6
- package/dist/src/context/keys.d.ts.map +1 -1
- package/dist/src/context/keys.js +7 -9
- package/dist/src/context/keys.js.map +1 -1
- package/dist/src/context/node.d.ts +2 -2
- package/dist/src/context/node.d.ts.map +1 -1
- package/dist/src/context/node.js +1 -1
- package/dist/src/context/node.js.map +1 -1
- package/dist/src/context/provider.d.ts +24 -11
- package/dist/src/context/provider.d.ts.map +1 -1
- package/dist/src/context/providers/connection.d.ts +2 -2
- package/dist/src/context/providers/connection.d.ts.map +1 -1
- package/dist/src/context/providers/connection.js +1 -1
- package/dist/src/context/providers/connection.js.map +1 -1
- package/dist/src/context/providers/sandbox.d.ts +2 -2
- package/dist/src/context/providers/sandbox.d.ts.map +1 -1
- package/dist/src/context/providers/sandbox.js +2 -2
- package/dist/src/context/providers/sandbox.js.map +1 -1
- package/dist/src/context/providers/session.d.ts +2 -2
- package/dist/src/context/providers/session.d.ts.map +1 -1
- package/dist/src/context/providers/session.js +4 -4
- package/dist/src/context/providers/session.js.map +1 -1
- package/dist/src/context/providers/skill.d.ts +2 -2
- package/dist/src/context/providers/skill.d.ts.map +1 -1
- package/dist/src/context/providers/skill.js +2 -2
- package/dist/src/context/providers/skill.js.map +1 -1
- package/dist/src/context/run-step.d.ts +9 -8
- package/dist/src/context/run-step.d.ts.map +1 -1
- package/dist/src/context/run-step.js +32 -22
- package/dist/src/context/run-step.js.map +1 -1
- package/dist/src/context/seed-keys.d.ts +7 -7
- package/dist/src/context/seed-keys.d.ts.map +1 -1
- package/dist/src/context/seed-keys.js +7 -19
- package/dist/src/context/seed-keys.js.map +1 -1
- package/dist/src/context/serialize.d.ts +10 -9
- package/dist/src/context/serialize.d.ts.map +1 -1
- package/dist/src/context/serialize.js +18 -30
- package/dist/src/context/serialize.js.map +1 -1
- package/dist/src/execution/continuous-entry.d.ts +8 -6
- package/dist/src/execution/continuous-entry.d.ts.map +1 -1
- package/dist/src/execution/continuous-entry.js +27 -25
- package/dist/src/execution/continuous-entry.js.map +1 -1
- package/dist/src/execution/continuous-runtime.d.ts.map +1 -1
- package/dist/src/execution/continuous-runtime.js +1 -4
- package/dist/src/execution/continuous-runtime.js.map +1 -1
- package/dist/src/execution/node-step.js +1 -0
- package/dist/src/execution/node-step.js.map +1 -1
- package/dist/src/execution/runtime-context.d.ts +3 -3
- package/dist/src/execution/runtime-context.d.ts.map +1 -1
- package/dist/src/execution/runtime-context.js +3 -3
- package/dist/src/execution/runtime-context.js.map +1 -1
- package/dist/src/execution/sandboxes/read-file-tool.js +2 -2
- package/dist/src/execution/sandboxes/read-file-tool.js.map +1 -1
- package/dist/src/execution/sandboxes/require-sandbox.js +2 -2
- package/dist/src/execution/sandboxes/require-sandbox.js.map +1 -1
- package/dist/src/execution/sandboxes/write-file-tool.d.ts.map +1 -1
- package/dist/src/execution/sandboxes/write-file-tool.js +3 -3
- package/dist/src/execution/sandboxes/write-file-tool.js.map +1 -1
- package/dist/src/execution/subagent-tool.js +6 -6
- package/dist/src/execution/subagent-tool.js.map +1 -1
- package/dist/src/execution/tool-compaction.d.ts +3 -1
- package/dist/src/execution/tool-compaction.d.ts.map +1 -1
- package/dist/src/execution/tool-compaction.js +19 -8
- package/dist/src/execution/tool-compaction.js.map +1 -1
- package/dist/src/execution/types.d.ts +1 -0
- package/dist/src/execution/types.d.ts.map +1 -1
- package/dist/src/execution/workflow-entry.d.ts +5 -4
- package/dist/src/execution/workflow-entry.d.ts.map +1 -1
- package/dist/src/execution/workflow-entry.js +8 -3
- package/dist/src/execution/workflow-entry.js.map +1 -1
- package/dist/src/execution/workflow-runtime.d.ts.map +1 -1
- package/dist/src/execution/workflow-runtime.js +2 -3
- package/dist/src/execution/workflow-runtime.js.map +1 -1
- package/dist/src/execution/workflow-steps.d.ts +2 -2
- package/dist/src/execution/workflow-steps.d.ts.map +1 -1
- package/dist/src/execution/workflow-steps.js +42 -17
- package/dist/src/execution/workflow-steps.js.map +1 -1
- package/dist/src/harness/emission.d.ts +1 -1
- package/dist/src/harness/emission.js +3 -3
- package/dist/src/harness/emission.js.map +1 -1
- package/dist/src/harness/execute-tool.d.ts +1 -0
- package/dist/src/harness/execute-tool.d.ts.map +1 -1
- package/dist/src/harness/execute-tool.js.map +1 -1
- package/dist/src/harness/input-requests.d.ts +2 -22
- package/dist/src/harness/input-requests.d.ts.map +1 -1
- package/dist/src/harness/input-requests.js +47 -41
- package/dist/src/harness/input-requests.js.map +1 -1
- package/dist/src/harness/tool-loop.d.ts.map +1 -1
- package/dist/src/harness/tool-loop.js +19 -1
- package/dist/src/harness/tool-loop.js.map +1 -1
- package/dist/src/harness/tools.js +5 -1
- package/dist/src/harness/tools.js.map +1 -1
- package/dist/src/harness/types.d.ts +3 -15
- package/dist/src/harness/types.d.ts.map +1 -1
- package/dist/src/harness/types.js +1 -6
- package/dist/src/harness/types.js.map +1 -1
- package/dist/src/internal/application/package.js +1 -1
- package/dist/src/internal/authored-definition/connection.d.ts.map +1 -1
- package/dist/src/internal/authored-definition/connection.js +14 -1
- package/dist/src/internal/authored-definition/connection.js.map +1 -1
- package/dist/src/internal/nitro/routes/runtime-stack.d.ts +3 -3
- package/dist/src/internal/nitro/routes/runtime-stack.js +3 -3
- package/dist/src/public/channels/slack/index.d.ts +0 -5
- package/dist/src/public/channels/slack/index.d.ts.map +1 -1
- package/dist/src/public/channels/slack/index.js.map +1 -1
- package/dist/src/public/definitions/connections/mcp.d.ts +13 -0
- package/dist/src/public/definitions/connections/mcp.d.ts.map +1 -1
- package/dist/src/public/definitions/connections/mcp.js.map +1 -1
- package/dist/src/public/definitions/tool.d.ts +10 -6
- package/dist/src/public/definitions/tool.d.ts.map +1 -1
- package/dist/src/public/definitions/tool.js.map +1 -1
- package/dist/src/public/index.d.ts +2 -1
- package/dist/src/public/index.d.ts.map +1 -1
- package/dist/src/public/index.js +1 -1
- package/dist/src/public/index.js.map +1 -1
- package/dist/src/public/tools/defaults.d.ts +4 -4
- package/dist/src/public/tools/defaults.js +4 -4
- package/dist/src/runtime/connections/registry.d.ts +6 -0
- package/dist/src/runtime/connections/registry.d.ts.map +1 -1
- package/dist/src/runtime/connections/registry.js +8 -0
- package/dist/src/runtime/connections/registry.js.map +1 -1
- package/dist/src/runtime/connections/types.d.ts +2 -0
- package/dist/src/runtime/connections/types.d.ts.map +1 -1
- package/dist/src/runtime/framework-tools/connection-execute.d.ts.map +1 -1
- package/dist/src/runtime/framework-tools/connection-execute.js +17 -6
- package/dist/src/runtime/framework-tools/connection-execute.js.map +1 -1
- package/dist/src/runtime/framework-tools/connection-search.d.ts +2 -2
- package/dist/src/runtime/framework-tools/connection-search.d.ts.map +1 -1
- package/dist/src/runtime/framework-tools/connection-search.js +4 -4
- package/dist/src/runtime/framework-tools/connection-search.js.map +1 -1
- package/dist/src/runtime/framework-tools/file-state.d.ts +5 -7
- package/dist/src/runtime/framework-tools/file-state.d.ts.map +1 -1
- package/dist/src/runtime/framework-tools/file-state.js +4 -6
- package/dist/src/runtime/framework-tools/file-state.js.map +1 -1
- package/dist/src/runtime/framework-tools/skill.js +3 -3
- package/dist/src/runtime/framework-tools/skill.js.map +1 -1
- package/dist/src/runtime/framework-tools/todo.js +3 -3
- package/dist/src/runtime/framework-tools/todo.js.map +1 -1
- package/dist/src/runtime/prompt/connections.d.ts.map +1 -1
- package/dist/src/runtime/prompt/connections.js +3 -2
- package/dist/src/runtime/prompt/connections.js.map +1 -1
- package/dist/src/runtime/resolve-agent-graph.js +3 -2
- package/dist/src/runtime/resolve-agent-graph.js.map +1 -1
- package/dist/src/runtime/resolve-connection.d.ts.map +1 -1
- package/dist/src/runtime/resolve-connection.js +3 -0
- package/dist/src/runtime/resolve-connection.js.map +1 -1
- package/dist/src/runtime/sessions/auth.d.ts +1 -1
- package/dist/src/runtime/types.d.ts +13 -0
- package/dist/src/runtime/types.d.ts.map +1 -1
- package/docs/internals/context.md +257 -81
- package/docs/public/README.md +17 -19
- package/docs/public/channels/README.md +9 -3
- package/docs/public/session-context.md +95 -49
- package/docs/public/tools.md +25 -17
- package/docs/public/typescript-api.md +2 -6
- package/package.json +1 -1
- package/dist/src/context/durable-context.d.ts +0 -18
- package/dist/src/context/durable-context.d.ts.map +0 -1
- package/dist/src/context/durable-context.js +0 -49
- package/dist/src/context/durable-context.js.map +0 -1
- package/dist/src/execution/step-context.d.ts +0 -27
- package/dist/src/execution/step-context.d.ts.map +0 -1
- package/dist/src/execution/step-context.js +0 -54
- package/dist/src/execution/step-context.js.map +0 -1
- package/docs/public/migration-guide.md +0 -71
|
@@ -1,126 +1,302 @@
|
|
|
1
1
|
# Unified Context
|
|
2
2
|
|
|
3
|
-
Ash uses
|
|
4
|
-
|
|
5
|
-
for
|
|
3
|
+
Ash uses a single `AshContext` container, bound by one `AsyncLocalStorage` instance, to carry all
|
|
4
|
+
runtime state through the execution stack. There are no secondary `AsyncLocalStorage` bindings, no
|
|
5
|
+
custom dehydration protocols, and no out-of-band parameter passing for contextual data.
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## Files To Read
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
- `packages/ash/src/context/key.ts`
|
|
10
|
+
- `packages/ash/src/context/container.ts`
|
|
11
|
+
- `packages/ash/src/context/provider.ts`
|
|
12
|
+
- `packages/ash/src/context/keys.ts`
|
|
13
|
+
- `packages/ash/src/context/serialize.ts`
|
|
14
|
+
- `packages/ash/src/context/run-step.ts`
|
|
15
|
+
- `packages/ash/src/context/accessors.ts`
|
|
16
|
+
- `packages/ash/src/context/providers/session.ts`
|
|
17
|
+
- `packages/ash/src/context/providers/sandbox.ts`
|
|
18
|
+
- `packages/ash/src/context/providers/skill.ts`
|
|
10
19
|
|
|
11
|
-
|
|
12
|
-
- `RuntimeContextKey<T>`: private framework-only runtime values. These seed one step and may be
|
|
13
|
-
reconstructed on the next step.
|
|
20
|
+
## Core Primitives
|
|
14
21
|
|
|
15
|
-
|
|
22
|
+
### ContextKey
|
|
23
|
+
|
|
24
|
+
A typed key identifies a named context slot. Durable values written through `set()` are
|
|
25
|
+
serialized at `"use step"` boundaries. Virtual provider output may also be read through the same
|
|
26
|
+
key during a step.
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
class ContextKey<T> {
|
|
30
|
+
readonly name: string;
|
|
31
|
+
readonly codec?: {
|
|
32
|
+
serialize(value: T): unknown;
|
|
33
|
+
deserialize(data: unknown): T | Promise<T>;
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Type Hierarchy
|
|
39
|
+
|
|
40
|
+
The context type system is a single inheritance chain defined in `key.ts`:
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
ContextReader { get, require, has }
|
|
44
|
+
↑ extends
|
|
45
|
+
ContextAccessor { set, ensure }
|
|
46
|
+
↑ extends
|
|
47
|
+
AshContext { entries() }
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
- `ContextReader` — read-only view for providers and codec deserialization
|
|
51
|
+
- `ContextAccessor` — read/write view for channels (`onDeliver`) and authored code
|
|
52
|
+
- `AshContext` — full container with the `entries()` iterator used by the serialization layer
|
|
53
|
+
|
|
54
|
+
### AshContext / ContextContainer
|
|
55
|
+
|
|
56
|
+
The container interface. One instance per execution scope. Reads see the virtual provider overlay
|
|
57
|
+
first, then the durable map. Writes always target the durable map.
|
|
16
58
|
|
|
17
59
|
```ts
|
|
18
60
|
interface AshContext {
|
|
19
|
-
get<T>(key:
|
|
20
|
-
|
|
21
|
-
has<T>(key:
|
|
22
|
-
set<T>(key:
|
|
23
|
-
|
|
61
|
+
get<T>(key: ContextKey<T>): T | undefined;
|
|
62
|
+
require<T>(key: ContextKey<T>): T;
|
|
63
|
+
has<T>(key: ContextKey<T>): boolean;
|
|
64
|
+
set<T>(key: ContextKey<T>, value: T): T;
|
|
65
|
+
set<T>(key: ContextKey<T>, updater: (current: T | undefined) => T): T;
|
|
66
|
+
ensure<T>(key: ContextKey<T>, create: () => T): T;
|
|
24
67
|
}
|
|
25
68
|
```
|
|
26
69
|
|
|
27
|
-
|
|
28
|
-
`getSkill` all delegate to this container.
|
|
70
|
+
The concrete implementation class is `ContextContainer` (in `container.ts`).
|
|
29
71
|
|
|
30
|
-
|
|
72
|
+
### contextStorage
|
|
31
73
|
|
|
32
|
-
|
|
74
|
+
The single `AsyncLocalStorage<AshContext>` instance. Only `runStep` and the runtime entry points
|
|
75
|
+
call `contextStorage.run(...)`. Everything else reads via `loadContext()`.
|
|
33
76
|
|
|
34
|
-
|
|
77
|
+
### loadContext()
|
|
35
78
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
3. Seed internal framework bookkeeping from `session.internal`.
|
|
39
|
-
4. Apply deliver-time auth and run `channel.onDeliver(...)`.
|
|
40
|
-
5. Run providers and step code inside `contextStorage.run(...)`.
|
|
41
|
-
6. Commit the full durable context bag back to `session.context`.
|
|
79
|
+
Returns the active `AshContext` from `contextStorage`, or throws. Authored code (tools, steps,
|
|
80
|
+
model callbacks) uses this implicitly through the public accessors.
|
|
42
81
|
|
|
43
|
-
|
|
82
|
+
## Key Categories
|
|
44
83
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
84
|
+
### Seed keys
|
|
85
|
+
|
|
86
|
+
Set by the runtime entry point with live values. Serialized and deserialized at durable step
|
|
87
|
+
boundaries via their codec.
|
|
49
88
|
|
|
50
|
-
|
|
89
|
+
| Key | Type | Codec |
|
|
90
|
+
|-----|------|-------|
|
|
91
|
+
| `AuthKey` | `SessionAuthContext \| null` | none (JSON-safe) |
|
|
92
|
+
| `InitiatorAuthKey` | `SessionAuthContext \| null` | none |
|
|
93
|
+
| `SessionIdKey` | `string` | none |
|
|
94
|
+
| `RunIdKey` | `string` | none |
|
|
95
|
+
| `ContinuationTokenKey` | `string` | none |
|
|
96
|
+
| `ModeKey` | `RunMode` | none |
|
|
97
|
+
| `NodeSelectorKey` | `string` | none — points at the active graph node (root or a delegated subagent selector) |
|
|
98
|
+
| `ParentSessionKey` | `SessionParent` | none — set only on delegated child contexts to carry parent lineage |
|
|
99
|
+
| `ChannelKey` | `Channel` | serializes kind + state, deserializes by hydrating the correct channel class |
|
|
100
|
+
| `BundleKey` | `CompiledBundle` | serializes to `compiledArtifactsSource`, deserializes via `getCompiledRuntimeAgentBundle` |
|
|
51
101
|
|
|
52
|
-
|
|
53
|
-
|
|
102
|
+
Keys self-register in a global registry at construction time. The serialization
|
|
103
|
+
layer uses this registry to resolve string names back to typed keys — there is no
|
|
104
|
+
explicit seed key list.
|
|
54
105
|
|
|
55
|
-
|
|
106
|
+
### Virtual keys
|
|
56
107
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
- run mode
|
|
60
|
-
- parent lineage
|
|
61
|
-
- compiled bundle
|
|
62
|
-
- serialized channel
|
|
108
|
+
Created by providers during `runStep`. Never serialized — providers reconstruct them each step
|
|
109
|
+
from durable context and the harness session.
|
|
63
110
|
|
|
64
|
-
|
|
65
|
-
|
|
111
|
+
| Key | Type | Provider |
|
|
112
|
+
|-----|------|----------|
|
|
113
|
+
| `SessionKey` | `Session` | `sessionProvider` |
|
|
114
|
+
| `SandboxKey` | `SandboxAccess` | `sandboxProvider` |
|
|
115
|
+
| `SkillKey` | `SkillAccess` | `skillProvider` |
|
|
66
116
|
|
|
67
117
|
## Providers
|
|
68
118
|
|
|
69
|
-
|
|
119
|
+
Public channel-scoped providers implement `ContextProvider<T>`:
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
interface ContextProvider<T> {
|
|
123
|
+
readonly key: ContextKey<T>;
|
|
124
|
+
create(ctx: ContextReader): T | undefined | Promise<T | undefined>;
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Public providers are derive-only. They run after `onDeliver()` and after the framework providers,
|
|
129
|
+
so they can rebuild live step-local values from the visible context. Returning `undefined` means
|
|
130
|
+
the provider is not active for this step.
|
|
131
|
+
|
|
132
|
+
Framework internals use a superset contract (`FrameworkContextProvider`) that also receives the
|
|
133
|
+
current harness session and may commit provider-owned session data after the step.
|
|
134
|
+
|
|
135
|
+
Provider ordering matters. The framework providers are baked into `runStep` in dependency order:
|
|
136
|
+
|
|
137
|
+
1. `sessionProvider` — depends only on durable seed keys
|
|
138
|
+
2. `sandboxProvider` — depends on `BundleKey` and `SessionIdKey`
|
|
139
|
+
3. `skillProvider` — depends on `BundleKey`
|
|
140
|
+
4. channel-declared public providers — depend on whatever context is visible after `onDeliver()`
|
|
141
|
+
|
|
142
|
+
There is no separate provider list to import — `runStep` knows its providers internally.
|
|
143
|
+
|
|
144
|
+
## Step Runner
|
|
145
|
+
|
|
146
|
+
`runStep` orchestrates the provider lifecycle around a step callback:
|
|
147
|
+
|
|
148
|
+
1. Clears the previous step's virtual overlay.
|
|
149
|
+
2. Builds framework virtual providers in order.
|
|
150
|
+
3. Builds any channel-scoped public providers declared on the active channel class.
|
|
151
|
+
4. Runs the callback inside `contextStorage.run(ctx, ...)` so authored code can read the context.
|
|
152
|
+
5. After the callback completes, calls framework provider `commit` hooks for provider-owned
|
|
153
|
+
session state.
|
|
154
|
+
|
|
155
|
+
## Serialization At Step Boundaries
|
|
156
|
+
|
|
157
|
+
`serializeContext` and `deserializeContext` handle durable step boundaries generically.
|
|
158
|
+
`serializeContext` iterates all entries in the context, calling each codec-backed key's codec
|
|
159
|
+
when present. `deserializeContext` iterates the plain JSON record
|
|
160
|
+
and resolves each string name back to its registered `ContextKey` via the global key registry.
|
|
161
|
+
Keys without a codec are stored as-is (they must be JSON-safe).
|
|
162
|
+
|
|
163
|
+
The workflow runtime serializes the durable context once at `start()` time, then threads the latest
|
|
164
|
+
serialized context forward after every durable step. Each `"use step"` boundary deserializes the
|
|
165
|
+
durable map back, `onDeliver()` mutates it for the turn, and providers reconstruct virtual values.
|
|
166
|
+
|
|
167
|
+
The channel codec is owned by the `ChannelKey` definition in `context/keys.ts`.
|
|
168
|
+
Channel classes themselves have no knowledge of serialization.
|
|
169
|
+
|
|
170
|
+
## Channel Context
|
|
171
|
+
|
|
172
|
+
Auth is separate from the channel — it lives on `RunInput.auth` and `DeliverInput.auth`. There is
|
|
173
|
+
no `ContextUpdater` callback, no `withAuth`/`withContext` wrappers, and no deliver-updater-registry
|
|
174
|
+
side-channel.
|
|
175
|
+
|
|
176
|
+
### Auth
|
|
177
|
+
|
|
178
|
+
Auth lives on the run and deliver inputs, not on the channel itself:
|
|
179
|
+
|
|
180
|
+
- `RunInput.auth` — the caller for the current run
|
|
181
|
+
- `RunInput.initiatorAuth` — the caller that started the durable session (defaults to `auth` on root
|
|
182
|
+
runs; subagent passes the parent's initiator)
|
|
183
|
+
|
|
184
|
+
The runtime reads `RunInput.auth` and `RunInput.initiatorAuth` when seeding `AuthKey` and
|
|
185
|
+
`InitiatorAuthKey`. On `deliver()`, the runtime updates `AuthKey` from `DeliverInput.auth` so that
|
|
186
|
+
`session.auth.current` reflects the follow-up caller while `session.auth.initiator` stays the same.
|
|
187
|
+
|
|
188
|
+
### Custom context via `Channel.onDeliver`
|
|
189
|
+
|
|
190
|
+
Channels set custom durable context keys inside `onDeliver(ctx, payload)`. The method receives a
|
|
191
|
+
narrow `ContextAccessor` as its first argument (typed `get` / `require` / `has` /
|
|
192
|
+
`set` / `ensure` with `ContextKey`) and returns a
|
|
193
|
+
`Promise<StepInput>`. This runs once per turn — both the initial `run()` and each `deliver()` —
|
|
194
|
+
after auth keys are seeded. Channel code stays workflow-agnostic because it only touches the
|
|
195
|
+
accessor.
|
|
196
|
+
|
|
197
|
+
```ts
|
|
198
|
+
class TenantChannel extends Channel {
|
|
199
|
+
static readonly kind = "tenant-channel";
|
|
200
|
+
|
|
201
|
+
private readonly inner: Channel;
|
|
202
|
+
private readonly tenantId: string;
|
|
203
|
+
|
|
204
|
+
constructor(state: { readonly tenantId: string }) {
|
|
205
|
+
super();
|
|
206
|
+
this.tenantId = state.tenantId;
|
|
207
|
+
this.inner = new HttpChannel();
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
async onEvent(event) {
|
|
211
|
+
return await this.inner.onEvent(event);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
async onDeliver(ctx, payload) {
|
|
215
|
+
ctx.set(TenantKey, this.tenantId);
|
|
216
|
+
return await this.inner.onDeliver(ctx, payload);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
serialize() {
|
|
220
|
+
return { tenantId: this.tenantId };
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### Deliver path
|
|
226
|
+
|
|
227
|
+
On `deliver()`, the channel is deserialized from the session (it was serialized at `run()` time via
|
|
228
|
+
the `ChannelKey` codec). The runtime applies `DeliverInput.auth` to update the auth key, then calls
|
|
229
|
+
`channel.onDeliver(ctx, payload)` on the deserialized channel. No process-local user registration or
|
|
230
|
+
function stashing is needed because the runtime rebuilds the channel from the compiled bundle's
|
|
231
|
+
channel registry plus the serialized channel state.
|
|
232
|
+
|
|
233
|
+
### Files
|
|
70
234
|
|
|
71
|
-
- `
|
|
72
|
-
- `
|
|
73
|
-
|
|
235
|
+
- `packages/ash/src/channel/types.ts` — `Channel`, `ContextAccessor` types
|
|
236
|
+
- `packages/ash/src/execution/runtime-context.ts` — `buildRunContext` (reads `RunInput.auth`,
|
|
237
|
+
seeds auth keys)
|
|
238
|
+
- `packages/ash/src/execution/workflow-steps.ts` — deserializes channel, applies deliver auth
|
|
239
|
+
- `packages/ash/src/execution/continuous-entry.ts` — applies deliver auth on in-memory channel
|
|
74
240
|
|
|
75
|
-
|
|
241
|
+
## Integration Points
|
|
76
242
|
|
|
77
|
-
|
|
243
|
+
### Runtime entry (workflow)
|
|
78
244
|
|
|
79
|
-
-
|
|
80
|
-
|
|
81
|
-
|
|
245
|
+
`workflow-runtime.ts` builds a `ContextContainer` via `buildRunContext` (for `run()`) or
|
|
246
|
+
`createDelegatedChildContext` (for `delegate()`), sets seed keys, serializes, and passes
|
|
247
|
+
`serializedContext` to `workflowEntry` via `start()`. The ambient runtime is registered at
|
|
248
|
+
this point so durable steps can later recover it via `tryGetAmbientWorkflowRuntime()`.
|
|
82
249
|
|
|
83
|
-
|
|
250
|
+
### Runtime entry (continuous)
|
|
84
251
|
|
|
85
|
-
`
|
|
252
|
+
`continuous-runtime.ts` builds a `ContextContainer` via the same two helpers and passes the
|
|
253
|
+
live context directly to `runStep` (no serialization needed since there are no durable step
|
|
254
|
+
boundaries). `RuntimeKey` is attached to the context once at run creation.
|
|
86
255
|
|
|
87
|
-
|
|
88
|
-
2. hydrate durable context
|
|
89
|
-
3. seed pending input request bookkeeping
|
|
90
|
-
4. apply deliver-time auth
|
|
91
|
-
5. call `channel.onDeliver(ctx, payload)`
|
|
92
|
-
6. assert that serialized channel state did not change
|
|
256
|
+
### Delegation
|
|
93
257
|
|
|
94
|
-
|
|
95
|
-
|
|
258
|
+
`runtime.delegate(input)` starts a child run rooted at a non-root graph node. Its
|
|
259
|
+
`DelegateInput` extends `RunInput` and adds `parent: SessionParent` and
|
|
260
|
+
`target: { selector }`. `createDelegatedChildContext` seeds the child context with the
|
|
261
|
+
forwarded initiator auth, the delegate's current auth, the parent lineage, the child node
|
|
262
|
+
selector, and the child's own run/session identifiers. The subagent tool wrapper in
|
|
263
|
+
`execution/subagent-tool.ts` is the sole in-tree caller of `delegate()`.
|
|
96
264
|
|
|
97
|
-
|
|
265
|
+
### Durable step boundary
|
|
98
266
|
|
|
99
|
-
|
|
267
|
+
`workflow-steps.ts` deserializes the context from the serialized record, attaches the ambient
|
|
268
|
+
workflow runtime via `RuntimeKey` (when one is registered), resolves the active graph node
|
|
269
|
+
from `NodeSelectorKey`, and calls `runStep` with the framework providers.
|
|
100
270
|
|
|
101
|
-
|
|
102
|
-
If the serialized shape changes, the runtime throws with guidance to move that data into:
|
|
271
|
+
### Tool executors
|
|
103
272
|
|
|
104
|
-
|
|
105
|
-
|
|
273
|
+
Tool executors read from the container via `loadContext()`. Sandbox tools read `SandboxKey`;
|
|
274
|
+
the `load_skill` action reads `SkillKey`. Authored tools access context through the public
|
|
275
|
+
accessors (`getSession`, `getSandbox`, etc.).
|
|
106
276
|
|
|
107
|
-
|
|
277
|
+
### Public API
|
|
108
278
|
|
|
109
|
-
|
|
110
|
-
- continuation token ownership
|
|
111
|
-
- per-turn context seeding
|
|
112
|
-
- delivery policy
|
|
279
|
+
The public accessors in `context/accessors.ts` delegate to `loadContext()`:
|
|
113
280
|
|
|
114
|
-
|
|
281
|
+
- `getContext(key)` returns `T | undefined`
|
|
282
|
+
- `requireContext(key)` returns `T` or throws
|
|
283
|
+
- `hasContext(key)` returns `boolean`
|
|
284
|
+
- `setContext(key, value | updater)` returns `T`
|
|
285
|
+
- `ensureContext(key, factory)` returns `T`
|
|
115
286
|
|
|
116
|
-
|
|
287
|
+
- `getSession()` reads `SessionKey`
|
|
288
|
+
- `getSandbox(name)` reads `SandboxKey`
|
|
289
|
+
- `getSkill(identifier)` reads `SkillKey`
|
|
117
290
|
|
|
118
|
-
|
|
291
|
+
## What Was Removed
|
|
119
292
|
|
|
120
|
-
|
|
121
|
-
durable step
|
|
122
|
-
- continuous runtime also rebuilds a fresh step context on every step, even though the process is
|
|
123
|
-
still live
|
|
293
|
+
The unified context replaced these six prior mechanisms:
|
|
124
294
|
|
|
125
|
-
|
|
126
|
-
|
|
295
|
+
- `execution/runtime-context.ts` — execution-layer `AsyncLocalStorage` and `RuntimeContext`
|
|
296
|
+
- `runtime/session-context.ts` — runtime-layer `AsyncLocalStorage` and `AuthoredRuntimeContext`
|
|
297
|
+
- `execution/framework-context.ts` — `FrameworkContext`, `Dehydratable`, channel emitter
|
|
298
|
+
dehydration
|
|
299
|
+
- `execution/context.ts` — `StepExecutionContext`, `ManagedRuntimeContext`, incremental context
|
|
300
|
+
assembly
|
|
301
|
+
- The `Dehydratable` interface on channel emitters (replaced by key codecs)
|
|
302
|
+
- Separate `session` parameter threading through tool executors and action handlers
|
package/docs/public/README.md
CHANGED
|
@@ -19,22 +19,21 @@ Read in this order:
|
|
|
19
19
|
2. [project-layout.md](./project-layout.md)
|
|
20
20
|
3. [agent-ts.md](./agent-ts.md)
|
|
21
21
|
4. [typescript-api.md](./typescript-api.md)
|
|
22
|
-
5. [
|
|
23
|
-
6. [
|
|
24
|
-
7. [
|
|
25
|
-
8. [
|
|
26
|
-
9. [
|
|
27
|
-
10. [
|
|
28
|
-
11. [
|
|
29
|
-
12. [
|
|
30
|
-
13. [
|
|
31
|
-
14. [
|
|
32
|
-
15. [
|
|
33
|
-
16. [
|
|
34
|
-
17. [
|
|
35
|
-
18. [
|
|
36
|
-
19. [
|
|
37
|
-
20. [cli-build-and-debugging.md](./cli-build-and-debugging.md)
|
|
22
|
+
5. [context-control.md](./context-control.md)
|
|
23
|
+
6. [skills.md](./skills.md)
|
|
24
|
+
7. [tools.md](./tools.md)
|
|
25
|
+
8. [workspace.md](./workspace.md)
|
|
26
|
+
9. [sandboxes.md](./sandboxes.md)
|
|
27
|
+
10. [channels/README.md](./channels/README.md)
|
|
28
|
+
11. [human-in-the-loop.md](./human-in-the-loop.md)
|
|
29
|
+
12. [session-context.md](./session-context.md)
|
|
30
|
+
13. [runs-and-streaming.md](./runs-and-streaming.md)
|
|
31
|
+
14. [subagents.md](./subagents.md)
|
|
32
|
+
15. [schedules.md](./schedules.md)
|
|
33
|
+
16. [evals.md](./evals.md)
|
|
34
|
+
17. [auth-and-route-protection.md](./auth-and-route-protection.md)
|
|
35
|
+
18. [vercel-deployment.md](./vercel-deployment.md)
|
|
36
|
+
19. [cli-build-and-debugging.md](./cli-build-and-debugging.md)
|
|
38
37
|
|
|
39
38
|
## The Public Mental Model
|
|
40
39
|
|
|
@@ -57,11 +56,10 @@ Ash then gives you:
|
|
|
57
56
|
- a stable HTTP message route
|
|
58
57
|
- optional channel webhook routes
|
|
59
58
|
- a reconnectable session stream
|
|
60
|
-
- durable session
|
|
59
|
+
- durable session state across turns
|
|
61
60
|
- a shared runtime workspace
|
|
62
61
|
- optional isolated sandboxes
|
|
63
|
-
- typed runtime helpers such as `getSession()`, `
|
|
64
|
-
`getSandbox()`, and `getSkill()`
|
|
62
|
+
- typed runtime helpers such as `getSession()`, `getSandbox()`, and `getSkill()`
|
|
65
63
|
|
|
66
64
|
## The Runtime Shape
|
|
67
65
|
|
|
@@ -9,7 +9,6 @@ Channels are the transport layer in Ash's channel-harness-runtime split. A chann
|
|
|
9
9
|
- deriving or resuming the stable `continuationToken`
|
|
10
10
|
- applying route auth and network policy
|
|
11
11
|
- deciding how runtime events are delivered back to the platform
|
|
12
|
-
- seeding per-turn durable context inside `onDeliver(...)`
|
|
13
12
|
|
|
14
13
|
The runtime and harness still own the model turn, tool execution, compaction, and session
|
|
15
14
|
persistence.
|
|
@@ -87,8 +86,15 @@ Use a custom channel when you want:
|
|
|
87
86
|
- transport-specific delivery behavior
|
|
88
87
|
- custom request parsing before the runtime turn starts
|
|
89
88
|
|
|
90
|
-
|
|
91
|
-
|
|
89
|
+
When you extend `SlackChannel`, keep context flow in two stages:
|
|
90
|
+
|
|
91
|
+
- write durable serializable facts in `onDeliver()`
|
|
92
|
+
- declare live step-local values on `static readonly contextProviders = [...]`
|
|
93
|
+
- read those values from tools and other authored step code with the unified context helpers
|
|
94
|
+
|
|
95
|
+
`contextProviders` is discovered from the channel class, not the channel instance, and providers run
|
|
96
|
+
after `onDeliver()` on each step. See [`session-context.md`](../session-context.md) for the full
|
|
97
|
+
pattern.
|
|
92
98
|
|
|
93
99
|
## What To Read Next
|
|
94
100
|
|