@tanstack/ai 0.42.0 → 0.43.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/README.md +15 -1
- package/dist/esm/activities/chat/adapter.js +23 -16
- package/dist/esm/activities/chat/adapter.js.map +1 -1
- package/dist/esm/activities/chat/agent-loop-strategies.d.ts +5 -36
- package/dist/esm/activities/chat/agent-loop-strategies.js +75 -21
- package/dist/esm/activities/chat/agent-loop-strategies.js.map +1 -1
- package/dist/esm/activities/chat/cancel.d.ts +40 -0
- package/dist/esm/activities/chat/cancel.js +54 -0
- package/dist/esm/activities/chat/cancel.js.map +1 -0
- package/dist/esm/activities/chat/index.d.ts +28 -21
- package/dist/esm/activities/chat/index.js +2100 -1813
- package/dist/esm/activities/chat/index.js.map +1 -1
- package/dist/esm/activities/chat/mcp/manager.d.ts +2 -2
- package/dist/esm/activities/chat/mcp/manager.js +90 -77
- package/dist/esm/activities/chat/mcp/manager.js.map +1 -1
- package/dist/esm/activities/chat/mcp/types.d.ts +2 -2
- package/dist/esm/activities/chat/messages.js +397 -346
- package/dist/esm/activities/chat/messages.js.map +1 -1
- package/dist/esm/activities/chat/middleware/builder.js +17 -15
- package/dist/esm/activities/chat/middleware/builder.js.map +1 -1
- package/dist/esm/activities/chat/middleware/capabilities.js +78 -43
- package/dist/esm/activities/chat/middleware/capabilities.js.map +1 -1
- package/dist/esm/activities/chat/middleware/compose.d.ts +94 -1
- package/dist/esm/activities/chat/middleware/compose.js +623 -531
- package/dist/esm/activities/chat/middleware/compose.js.map +1 -1
- package/dist/esm/activities/chat/middleware/define.js +12 -5
- package/dist/esm/activities/chat/middleware/define.js.map +1 -1
- package/dist/esm/activities/chat/middleware/index.d.ts +5 -1
- package/dist/esm/activities/chat/middleware/locks.d.ts +50 -0
- package/dist/esm/activities/chat/middleware/locks.js +71 -0
- package/dist/esm/activities/chat/middleware/locks.js.map +1 -0
- package/dist/esm/activities/chat/middleware/pending-turn.d.ts +15 -0
- package/dist/esm/activities/chat/middleware/pending-turn.js +35 -0
- package/dist/esm/activities/chat/middleware/pending-turn.js.map +1 -0
- package/dist/esm/activities/chat/middleware/run-disconnect.d.ts +23 -0
- package/dist/esm/activities/chat/middleware/run-disconnect.js +42 -0
- package/dist/esm/activities/chat/middleware/run-disconnect.js.map +1 -0
- package/dist/esm/activities/chat/middleware/run-store.d.ts +283 -0
- package/dist/esm/activities/chat/middleware/run-store.js +176 -0
- package/dist/esm/activities/chat/middleware/run-store.js.map +1 -0
- package/dist/esm/activities/chat/middleware/sandbox-runtime.js +14 -8
- package/dist/esm/activities/chat/middleware/sandbox-runtime.js.map +1 -1
- package/dist/esm/activities/chat/middleware/tool-cache-middleware.js +79 -70
- package/dist/esm/activities/chat/middleware/tool-cache-middleware.js.map +1 -1
- package/dist/esm/activities/chat/middleware/types.d.ts +59 -2
- package/dist/esm/activities/chat/middleware/validate.js +23 -28
- package/dist/esm/activities/chat/middleware/validate.js.map +1 -1
- package/dist/esm/activities/chat/stream/json-parser.js +39 -25
- package/dist/esm/activities/chat/stream/json-parser.js.map +1 -1
- package/dist/esm/activities/chat/stream/message-updaters.js +275 -234
- package/dist/esm/activities/chat/stream/message-updaters.js.map +1 -1
- package/dist/esm/activities/chat/stream/processor.d.ts +24 -4
- package/dist/esm/activities/chat/stream/processor.js +1341 -1542
- package/dist/esm/activities/chat/stream/processor.js.map +1 -1
- package/dist/esm/activities/chat/stream/strategies.js +69 -53
- package/dist/esm/activities/chat/stream/strategies.js.map +1 -1
- package/dist/esm/activities/chat/tools/approval-schema.d.ts +19 -0
- package/dist/esm/activities/chat/tools/approval-schema.js +117 -0
- package/dist/esm/activities/chat/tools/approval-schema.js.map +1 -0
- package/dist/esm/activities/chat/tools/lazy-tool-manager.js +164 -191
- package/dist/esm/activities/chat/tools/lazy-tool-manager.js.map +1 -1
- package/dist/esm/activities/chat/tools/lazy-tools.js +24 -12
- package/dist/esm/activities/chat/tools/lazy-tools.js.map +1 -1
- package/dist/esm/activities/chat/tools/schema-converter.js +293 -146
- package/dist/esm/activities/chat/tools/schema-converter.js.map +1 -1
- package/dist/esm/activities/chat/tools/tool-calls.d.ts +18 -2
- package/dist/esm/activities/chat/tools/tool-calls.js +522 -531
- package/dist/esm/activities/chat/tools/tool-calls.js.map +1 -1
- package/dist/esm/activities/chat/tools/tool-definition.d.ts +75 -16
- package/dist/esm/activities/chat/tools/tool-definition.js +95 -23
- package/dist/esm/activities/chat/tools/tool-definition.js.map +1 -1
- package/dist/esm/activities/error-payload.js +85 -47
- package/dist/esm/activities/error-payload.js.map +1 -1
- package/dist/esm/activities/generateAudio/adapter.js +22 -15
- package/dist/esm/activities/generateAudio/adapter.js.map +1 -1
- package/dist/esm/activities/generateAudio/index.d.ts +4 -0
- package/dist/esm/activities/generateAudio/index.js +141 -105
- package/dist/esm/activities/generateAudio/index.js.map +1 -1
- package/dist/esm/activities/generateImage/adapter.js +22 -15
- package/dist/esm/activities/generateImage/adapter.js.map +1 -1
- package/dist/esm/activities/generateImage/index.d.ts +4 -0
- package/dist/esm/activities/generateImage/index.js +155 -111
- package/dist/esm/activities/generateImage/index.js.map +1 -1
- package/dist/esm/activities/generateSpeech/adapter.js +22 -15
- package/dist/esm/activities/generateSpeech/adapter.js.map +1 -1
- package/dist/esm/activities/generateSpeech/index.d.ts +4 -0
- package/dist/esm/activities/generateSpeech/index.js +159 -110
- package/dist/esm/activities/generateSpeech/index.js.map +1 -1
- package/dist/esm/activities/generateTranscription/adapter.js +22 -15
- package/dist/esm/activities/generateTranscription/adapter.js.map +1 -1
- package/dist/esm/activities/generateTranscription/index.d.ts +4 -0
- package/dist/esm/activities/generateTranscription/index.js +159 -100
- package/dist/esm/activities/generateTranscription/index.js.map +1 -1
- package/dist/esm/activities/generateVideo/adapter.js +36 -29
- package/dist/esm/activities/generateVideo/adapter.js.map +1 -1
- package/dist/esm/activities/generateVideo/index.d.ts +143 -19
- package/dist/esm/activities/generateVideo/index.js +456 -279
- package/dist/esm/activities/generateVideo/index.js.map +1 -1
- package/dist/esm/activities/generateVideo/snap.js +60 -48
- package/dist/esm/activities/generateVideo/snap.js.map +1 -1
- package/dist/esm/activities/index.js +8 -34
- package/dist/esm/activities/middleware/index.d.ts +1 -1
- package/dist/esm/activities/middleware/run.d.ts +10 -0
- package/dist/esm/activities/middleware/run.js +53 -29
- package/dist/esm/activities/middleware/run.js.map +1 -1
- package/dist/esm/activities/middleware/types.d.ts +44 -6
- package/dist/esm/activities/stream-generation-result.d.ts +4 -1
- package/dist/esm/activities/stream-generation-result.js +79 -44
- package/dist/esm/activities/stream-generation-result.js.map +1 -1
- package/dist/esm/activities/summarize/adapter.js +22 -15
- package/dist/esm/activities/summarize/adapter.js.map +1 -1
- package/dist/esm/activities/summarize/chat-stream-summarize.js +252 -202
- package/dist/esm/activities/summarize/chat-stream-summarize.js.map +1 -1
- package/dist/esm/activities/summarize/index.d.ts +27 -0
- package/dist/esm/activities/summarize/index.js +268 -102
- package/dist/esm/activities/summarize/index.js.map +1 -1
- package/dist/esm/adapter-internals.d.ts +2 -1
- package/dist/esm/adapter-internals.js +4 -11
- package/dist/esm/client.d.ts +25 -3
- package/dist/esm/client.js +131 -64
- package/dist/esm/client.js.map +1 -1
- package/dist/esm/custom-events.d.ts +76 -0
- package/dist/esm/custom-events.js +37 -0
- package/dist/esm/custom-events.js.map +1 -0
- package/dist/esm/delivery-detach.d.ts +50 -0
- package/dist/esm/delivery-detach.js +71 -0
- package/dist/esm/delivery-detach.js.map +1 -0
- package/dist/esm/delivery-disconnect.d.ts +62 -0
- package/dist/esm/delivery-disconnect.js +81 -0
- package/dist/esm/delivery-disconnect.js.map +1 -0
- package/dist/esm/extend-adapter.js +19 -17
- package/dist/esm/extend-adapter.js.map +1 -1
- package/dist/esm/index.d.ts +24 -6
- package/dist/esm/index.js +30 -98
- package/dist/esm/interrupt-resume.d.ts +71 -0
- package/dist/esm/interrupt-resume.js +438 -0
- package/dist/esm/interrupt-resume.js.map +1 -0
- package/dist/esm/interrupt-serialization.d.ts +12 -0
- package/dist/esm/interrupt-serialization.js +178 -0
- package/dist/esm/interrupt-serialization.js.map +1 -0
- package/dist/esm/interrupts.d.ts +84 -0
- package/dist/esm/interrupts.js +31 -0
- package/dist/esm/interrupts.js.map +1 -0
- package/dist/esm/locks.d.ts +10 -0
- package/dist/esm/locks.js +2 -0
- package/dist/esm/logger/console-logger.js +101 -78
- package/dist/esm/logger/console-logger.js.map +1 -1
- package/dist/esm/logger/internal-logger.js +104 -89
- package/dist/esm/logger/internal-logger.js.map +1 -1
- package/dist/esm/logger/resolve.js +54 -49
- package/dist/esm/logger/resolve.js.map +1 -1
- package/dist/esm/logger/types.d.ts +1 -1
- package/dist/esm/middlewares/content-guard.js +142 -148
- package/dist/esm/middlewares/content-guard.js.map +1 -1
- package/dist/esm/middlewares/index.js +2 -6
- package/dist/esm/middlewares/otel.js +598 -732
- package/dist/esm/middlewares/otel.js.map +1 -1
- package/dist/esm/middlewares/usage-attributes.js +47 -40
- package/dist/esm/middlewares/usage-attributes.js.map +1 -1
- package/dist/esm/realtime/event-emitter.js +24 -25
- package/dist/esm/realtime/event-emitter.js.map +1 -1
- package/dist/esm/realtime/index.d.ts +5 -9
- package/dist/esm/realtime/index.js +29 -6
- package/dist/esm/realtime/index.js.map +1 -1
- package/dist/esm/scope.d.ts +47 -0
- package/dist/esm/stream-durability.d.ts +171 -0
- package/dist/esm/stream-durability.js +295 -0
- package/dist/esm/stream-durability.js.map +1 -0
- package/dist/esm/stream-to-response.d.ts +178 -13
- package/dist/esm/stream-to-response.js +663 -115
- package/dist/esm/stream-to-response.js.map +1 -1
- package/dist/esm/strip-to-spec-middleware.js +30 -16
- package/dist/esm/strip-to-spec-middleware.js.map +1 -1
- package/dist/esm/system-prompts.js +27 -21
- package/dist/esm/system-prompts.js.map +1 -1
- package/dist/esm/tool-registry.js +72 -45
- package/dist/esm/tool-registry.js.map +1 -1
- package/dist/esm/tools/provider-tool.js +14 -5
- package/dist/esm/tools/provider-tool.js.map +1 -1
- package/dist/esm/types.d.ts +321 -42
- package/dist/esm/types.js +2 -0
- package/dist/esm/utilities/ag-ui-wire.js +79 -93
- package/dist/esm/utilities/ag-ui-wire.js.map +1 -1
- package/dist/esm/utilities/chat-params.d.ts +26 -4
- package/dist/esm/utilities/chat-params.js +218 -92
- package/dist/esm/utilities/chat-params.js.map +1 -1
- package/dist/esm/utilities/errors.js +28 -18
- package/dist/esm/utilities/errors.js.map +1 -1
- package/dist/esm/utilities/media-prompt.js +46 -41
- package/dist/esm/utilities/media-prompt.js.map +1 -1
- package/dist/esm/utilities/numbers.js +13 -10
- package/dist/esm/utilities/numbers.js.map +1 -1
- package/dist/esm/utilities/provider-executed.js +20 -11
- package/dist/esm/utilities/provider-executed.js.map +1 -1
- package/dist/esm/utilities/sampling-keys.js +31 -19
- package/dist/esm/utilities/sampling-keys.js.map +1 -1
- package/dist/esm/utilities/tool-result.js +42 -30
- package/dist/esm/utilities/tool-result.js.map +1 -1
- package/dist/esm/utilities/usage.js +27 -9
- package/dist/esm/utilities/usage.js.map +1 -1
- package/dist/esm/utils.js +26 -18
- package/dist/esm/utils.js.map +1 -1
- package/package.json +10 -6
- package/skills/ai-core/SKILL.md +69 -18
- package/skills/ai-core/adapter-configuration/SKILL.md +44 -21
- package/skills/ai-core/adapter-configuration/references/anthropic-adapter.md +1 -3
- package/skills/ai-core/adapter-configuration/references/byteplus-adapter.md +148 -0
- package/skills/ai-core/adapter-configuration/references/gemini-adapter.md +2 -6
- package/skills/ai-core/adapter-configuration/references/groq-adapter.md +2 -6
- package/skills/ai-core/adapter-configuration/references/openai-adapter.md +1 -3
- package/skills/ai-core/ag-ui-protocol/SKILL.md +1 -1
- package/skills/ai-core/chat-experience/SKILL.md +98 -11
- package/skills/ai-core/client-persistence/SKILL.md +277 -0
- package/skills/ai-core/custom-backend-integration/SKILL.md +1 -1
- package/skills/ai-core/debug-logging/SKILL.md +1 -1
- package/skills/ai-core/locks/SKILL.md +143 -0
- package/skills/ai-core/media-generation/SKILL.md +144 -12
- package/skills/ai-core/middleware/SKILL.md +258 -33
- package/skills/ai-core/structured-outputs/SKILL.md +1 -1
- package/skills/ai-core/tool-calling/SKILL.md +54 -61
- package/src/activities/chat/agent-loop-strategies.ts +5 -39
- package/src/activities/chat/cancel.ts +81 -0
- package/src/activities/chat/index.ts +1091 -200
- package/src/activities/chat/mcp/manager.ts +4 -4
- package/src/activities/chat/mcp/types.ts +2 -2
- package/src/activities/chat/messages.ts +5 -3
- package/src/activities/chat/middleware/builder.ts +1 -1
- package/src/activities/chat/middleware/compose.ts +186 -9
- package/src/activities/chat/middleware/index.ts +26 -0
- package/src/activities/chat/middleware/locks.ts +102 -0
- package/src/activities/chat/middleware/pending-turn.ts +47 -0
- package/src/activities/chat/middleware/run-disconnect.ts +62 -0
- package/src/activities/chat/middleware/run-store.ts +412 -0
- package/src/activities/chat/middleware/types.ts +62 -1
- package/src/activities/chat/stream/processor.ts +189 -5
- package/src/activities/chat/tools/approval-schema.ts +205 -0
- package/src/activities/chat/tools/tool-calls.ts +106 -13
- package/src/activities/chat/tools/tool-definition.ts +210 -39
- package/src/activities/generateAudio/index.ts +20 -3
- package/src/activities/generateImage/index.ts +20 -3
- package/src/activities/generateSpeech/index.ts +25 -3
- package/src/activities/generateTranscription/index.ts +26 -3
- package/src/activities/generateVideo/index.ts +345 -82
- package/src/activities/middleware/index.ts +2 -0
- package/src/activities/middleware/run.ts +31 -0
- package/src/activities/middleware/types.ts +49 -5
- package/src/activities/stream-generation-result.ts +30 -2
- package/src/activities/summarize/chat-stream-summarize.ts +5 -0
- package/src/activities/summarize/index.ts +200 -10
- package/src/adapter-internals.ts +10 -1
- package/src/client.ts +244 -0
- package/src/custom-events.ts +107 -0
- package/src/delivery-detach.ts +72 -0
- package/src/delivery-disconnect.ts +84 -0
- package/src/index.ts +138 -1
- package/src/interrupt-resume.ts +824 -0
- package/src/interrupt-serialization.ts +183 -0
- package/src/interrupts.ts +146 -0
- package/src/locks.ts +17 -0
- package/src/logger/types.ts +1 -1
- package/src/middlewares/otel.ts +1 -0
- package/src/realtime/index.ts +5 -9
- package/src/scope.ts +47 -0
- package/src/stream-durability.ts +598 -0
- package/src/stream-to-response.ts +1051 -95
- package/src/strip-to-spec-middleware.ts +3 -3
- package/src/types.ts +405 -45
- package/src/utilities/chat-params.ts +245 -55
- package/dist/esm/activities/index.js.map +0 -1
- package/dist/esm/adapter-internals.js.map +0 -1
- package/dist/esm/index.js.map +0 -1
- package/dist/esm/middlewares/index.js.map +0 -1
|
@@ -1,537 +1,629 @@
|
|
|
1
1
|
import { aiEventClient } from "@tanstack/ai-event-client";
|
|
2
|
+
//#region src/activities/chat/middleware/compose.ts
|
|
3
|
+
/** Check if a middleware should be skipped for instrumentation events. */
|
|
2
4
|
function shouldSkipInstrumentation(mw) {
|
|
3
|
-
|
|
5
|
+
return mw.name === "devtools" || mw.name === "strip-to-spec";
|
|
4
6
|
}
|
|
7
|
+
/** Build the base context for middleware instrumentation events. */
|
|
5
8
|
function instrumentCtx(ctx) {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
return {
|
|
10
|
+
requestId: ctx.requestId,
|
|
11
|
+
streamId: ctx.streamId,
|
|
12
|
+
clientId: ctx.threadId,
|
|
13
|
+
timestamp: Date.now()
|
|
14
|
+
};
|
|
12
15
|
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Internal middleware runner that manages composed execution of middleware hooks.
|
|
18
|
+
* Created once per chat() invocation.
|
|
19
|
+
*/
|
|
20
|
+
var MiddlewareRunner = class {
|
|
21
|
+
middlewares;
|
|
22
|
+
logger;
|
|
23
|
+
constructor(middlewares, logger) {
|
|
24
|
+
this.middlewares = middlewares;
|
|
25
|
+
this.logger = logger;
|
|
26
|
+
}
|
|
27
|
+
get hasMiddleware() {
|
|
28
|
+
return this.middlewares.length > 0;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Pipe config through all middleware onConfig hooks in order.
|
|
32
|
+
* Each middleware receives the merged config from previous middleware.
|
|
33
|
+
* Partial returns are shallow-merged with the current config.
|
|
34
|
+
*/
|
|
35
|
+
async runOnConfig(ctx, config) {
|
|
36
|
+
let current = config;
|
|
37
|
+
for (const mw of this.middlewares) if (mw.onConfig) {
|
|
38
|
+
const skip = shouldSkipInstrumentation(mw);
|
|
39
|
+
const start = Date.now();
|
|
40
|
+
const result = await mw.onConfig(ctx, current);
|
|
41
|
+
const hasTransform = result !== void 0 && result !== null;
|
|
42
|
+
if (hasTransform) {
|
|
43
|
+
current = {
|
|
44
|
+
...current,
|
|
45
|
+
...result
|
|
46
|
+
};
|
|
47
|
+
if (!skip) this.logger.config(`middleware=${mw.name ?? "unnamed"} keys=${Object.keys(result).join(",")}`, {
|
|
48
|
+
middleware: mw.name ?? "unnamed",
|
|
49
|
+
changes: result
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
if (!skip) {
|
|
53
|
+
const base = instrumentCtx(ctx);
|
|
54
|
+
aiEventClient.emit("middleware:hook:executed", {
|
|
55
|
+
...base,
|
|
56
|
+
middlewareName: mw.name || "unnamed",
|
|
57
|
+
hookName: "onConfig",
|
|
58
|
+
iteration: ctx.iteration,
|
|
59
|
+
duration: Date.now() - start,
|
|
60
|
+
hasTransform
|
|
61
|
+
});
|
|
62
|
+
if (hasTransform) aiEventClient.emit("middleware:config:transformed", {
|
|
63
|
+
...base,
|
|
64
|
+
middlewareName: mw.name || "unnamed",
|
|
65
|
+
iteration: ctx.iteration,
|
|
66
|
+
changes: result
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return current;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Pipe config through all middleware onStructuredOutputConfig hooks in order.
|
|
74
|
+
* Each middleware receives the merged config from previous middleware.
|
|
75
|
+
* Partial returns are shallow-merged with the current config.
|
|
76
|
+
*
|
|
77
|
+
* Called once at the structured-output boundary, before runOnConfig at the
|
|
78
|
+
* same boundary (which receives a ChatMiddlewareConfig view, no outputSchema).
|
|
79
|
+
*/
|
|
80
|
+
async runOnStructuredOutputConfig(ctx, config) {
|
|
81
|
+
let current = config;
|
|
82
|
+
for (const mw of this.middlewares) if (mw.onStructuredOutputConfig) {
|
|
83
|
+
const skip = shouldSkipInstrumentation(mw);
|
|
84
|
+
const start = Date.now();
|
|
85
|
+
const result = await mw.onStructuredOutputConfig(ctx, current);
|
|
86
|
+
const hasTransform = result !== void 0 && result !== null;
|
|
87
|
+
if (hasTransform) {
|
|
88
|
+
current = {
|
|
89
|
+
...current,
|
|
90
|
+
...result
|
|
91
|
+
};
|
|
92
|
+
if (!skip) this.logger.config(`middleware=${mw.name ?? "unnamed"} keys=${Object.keys(result).join(",")}`, {
|
|
93
|
+
middleware: mw.name ?? "unnamed",
|
|
94
|
+
changes: result
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
if (!skip) {
|
|
98
|
+
const base = instrumentCtx(ctx);
|
|
99
|
+
aiEventClient.emit("middleware:hook:executed", {
|
|
100
|
+
...base,
|
|
101
|
+
middlewareName: mw.name || "unnamed",
|
|
102
|
+
hookName: "onStructuredOutputConfig",
|
|
103
|
+
iteration: ctx.iteration,
|
|
104
|
+
duration: Date.now() - start,
|
|
105
|
+
hasTransform
|
|
106
|
+
});
|
|
107
|
+
if (hasTransform) aiEventClient.emit("middleware:config:transformed", {
|
|
108
|
+
...base,
|
|
109
|
+
middlewareName: mw.name || "unnamed",
|
|
110
|
+
iteration: ctx.iteration,
|
|
111
|
+
changes: Object.fromEntries(Object.entries(result))
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return current;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Run all `setup` hooks in array order, then assert every declared `provides`
|
|
119
|
+
* capability was actually provided. Wires the last-wins duplicate-provide
|
|
120
|
+
* warning into the registry. Runs before init `onConfig`.
|
|
121
|
+
*
|
|
122
|
+
* Takes the full `ChatMiddlewareContext` — the same stable context the engine
|
|
123
|
+
* threads through every other hook — because it both forwards `ctx` to each
|
|
124
|
+
* `setup` hook and emits instrumentation events from it.
|
|
125
|
+
*/
|
|
126
|
+
async runSetup(ctx) {
|
|
127
|
+
ctx.capabilities.setOnDuplicate((name) => {
|
|
128
|
+
this.logger.warn(`capability "${name}" was provided more than once; last provider wins`, { capability: name });
|
|
129
|
+
});
|
|
130
|
+
for (const mw of this.middlewares) if (mw.setup) {
|
|
131
|
+
const skip = shouldSkipInstrumentation(mw);
|
|
132
|
+
const start = Date.now();
|
|
133
|
+
await mw.setup(ctx);
|
|
134
|
+
if (!skip) {
|
|
135
|
+
this.logger.middleware(`hook=setup middleware=${mw.name ?? "unnamed"}`, {
|
|
136
|
+
middleware: mw.name ?? "unnamed",
|
|
137
|
+
hook: "setup"
|
|
138
|
+
});
|
|
139
|
+
aiEventClient.emit("middleware:hook:executed", {
|
|
140
|
+
...instrumentCtx(ctx),
|
|
141
|
+
middlewareName: mw.name || "unnamed",
|
|
142
|
+
hookName: "setup",
|
|
143
|
+
iteration: ctx.iteration,
|
|
144
|
+
duration: Date.now() - start,
|
|
145
|
+
hasTransform: false
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
for (const mw of this.middlewares) for (const handle of mw.provides ?? []) if (!ctx.capabilities.has(handle)) throw new Error(`Middleware "${mw.name ?? "unnamed"}" declares it provides "${handle.capabilityName}" but never called provide() in setup().`);
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Call onStart on all middleware in order.
|
|
153
|
+
*/
|
|
154
|
+
async runOnStart(ctx) {
|
|
155
|
+
for (const mw of this.middlewares) if (mw.onStart) {
|
|
156
|
+
const skip = shouldSkipInstrumentation(mw);
|
|
157
|
+
const start = Date.now();
|
|
158
|
+
await mw.onStart(ctx);
|
|
159
|
+
if (!skip) {
|
|
160
|
+
this.logger.middleware(`hook=onStart middleware=${mw.name ?? "unnamed"}`, {
|
|
161
|
+
middleware: mw.name ?? "unnamed",
|
|
162
|
+
hook: "onStart"
|
|
163
|
+
});
|
|
164
|
+
aiEventClient.emit("middleware:hook:executed", {
|
|
165
|
+
...instrumentCtx(ctx),
|
|
166
|
+
middlewareName: mw.name || "unnamed",
|
|
167
|
+
hookName: "onStart",
|
|
168
|
+
iteration: ctx.iteration,
|
|
169
|
+
duration: Date.now() - start,
|
|
170
|
+
hasTransform: false
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Pipe a single chunk through all middleware onChunk hooks in order.
|
|
177
|
+
* Returns the resulting chunks (0..N) to yield to the consumer.
|
|
178
|
+
*
|
|
179
|
+
* - void: pass through unchanged
|
|
180
|
+
* - chunk: replace with this chunk
|
|
181
|
+
* - chunk[]: expand to multiple chunks
|
|
182
|
+
* - null: drop the chunk entirely
|
|
183
|
+
*/
|
|
184
|
+
async runOnChunk(ctx, chunk) {
|
|
185
|
+
let chunks = [chunk];
|
|
186
|
+
for (const mw of this.middlewares) {
|
|
187
|
+
if (!mw.onChunk) continue;
|
|
188
|
+
const skip = shouldSkipInstrumentation(mw);
|
|
189
|
+
const nextChunks = [];
|
|
190
|
+
for (const c of chunks) {
|
|
191
|
+
const chunkType = c.type;
|
|
192
|
+
if (!skip) this.logger.middleware(`hook=onChunk middleware=${mw.name ?? "unnamed"} in=${chunkType}`, {
|
|
193
|
+
middleware: mw.name ?? "unnamed",
|
|
194
|
+
hook: "onChunk",
|
|
195
|
+
in: c
|
|
196
|
+
});
|
|
197
|
+
const result = await mw.onChunk(ctx, c);
|
|
198
|
+
if (result === null) {
|
|
199
|
+
if (!skip) {
|
|
200
|
+
this.logger.middleware(`hook=onChunk middleware=${mw.name ?? "unnamed"} in=${chunkType} out=<dropped>`, {
|
|
201
|
+
middleware: mw.name ?? "unnamed",
|
|
202
|
+
hook: "onChunk",
|
|
203
|
+
dropped: true
|
|
204
|
+
});
|
|
205
|
+
aiEventClient.emit("middleware:chunk:transformed", {
|
|
206
|
+
...instrumentCtx(ctx),
|
|
207
|
+
middlewareName: mw.name || "unnamed",
|
|
208
|
+
originalChunkType: chunkType,
|
|
209
|
+
resultCount: 0,
|
|
210
|
+
wasDropped: true
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
continue;
|
|
214
|
+
} else if (result === void 0) nextChunks.push(c);
|
|
215
|
+
else if (Array.isArray(result)) {
|
|
216
|
+
nextChunks.push(...result);
|
|
217
|
+
if (!skip) {
|
|
218
|
+
this.logger.middleware(`hook=onChunk middleware=${mw.name ?? "unnamed"} in=${chunkType} out=[${result.map((r) => r.type).join(",")}]`, {
|
|
219
|
+
middleware: mw.name ?? "unnamed",
|
|
220
|
+
hook: "onChunk",
|
|
221
|
+
in: c,
|
|
222
|
+
out: result
|
|
223
|
+
});
|
|
224
|
+
aiEventClient.emit("middleware:chunk:transformed", {
|
|
225
|
+
...instrumentCtx(ctx),
|
|
226
|
+
middlewareName: mw.name || "unnamed",
|
|
227
|
+
originalChunkType: chunkType,
|
|
228
|
+
resultCount: result.length,
|
|
229
|
+
wasDropped: false
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
} else {
|
|
233
|
+
nextChunks.push(result);
|
|
234
|
+
if (!skip) {
|
|
235
|
+
this.logger.middleware(`hook=onChunk middleware=${mw.name ?? "unnamed"} in=${chunkType} out=${result.type}`, {
|
|
236
|
+
middleware: mw.name ?? "unnamed",
|
|
237
|
+
hook: "onChunk",
|
|
238
|
+
in: c,
|
|
239
|
+
out: result
|
|
240
|
+
});
|
|
241
|
+
aiEventClient.emit("middleware:chunk:transformed", {
|
|
242
|
+
...instrumentCtx(ctx),
|
|
243
|
+
middlewareName: mw.name || "unnamed",
|
|
244
|
+
originalChunkType: chunkType,
|
|
245
|
+
resultCount: 1,
|
|
246
|
+
wasDropped: false
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
chunks = nextChunks;
|
|
252
|
+
}
|
|
253
|
+
return chunks;
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Dispatch a sandbox file event to every middleware's `sandbox` hooks, in
|
|
257
|
+
* array order: the catch-all `onFile` then the type-specific hook. Errors are
|
|
258
|
+
* logged and swallowed so one bad hook can't break the run.
|
|
259
|
+
*/
|
|
260
|
+
async runSandboxFile(ctx, event) {
|
|
261
|
+
const typed = {
|
|
262
|
+
create: "onFileCreate",
|
|
263
|
+
change: "onFileChange",
|
|
264
|
+
delete: "onFileDelete"
|
|
265
|
+
}[event.type];
|
|
266
|
+
for (const mw of this.middlewares) {
|
|
267
|
+
const hooks = mw.sandbox;
|
|
268
|
+
if (!hooks) continue;
|
|
269
|
+
for (const fn of [hooks.onFile, hooks[typed]]) {
|
|
270
|
+
if (!fn) continue;
|
|
271
|
+
try {
|
|
272
|
+
await fn(ctx, event);
|
|
273
|
+
} catch (error) {
|
|
274
|
+
this.logger.sandbox(`hook=${typed} middleware=${mw.name ?? "unnamed"} threw`, {
|
|
275
|
+
middleware: mw.name ?? "unnamed",
|
|
276
|
+
error
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Run onBeforeToolCall through middleware in order.
|
|
284
|
+
* Returns the first non-void decision, or undefined to continue normally.
|
|
285
|
+
*/
|
|
286
|
+
async runOnBeforeToolCall(ctx, hookCtx) {
|
|
287
|
+
for (const mw of this.middlewares) if (mw.onBeforeToolCall) {
|
|
288
|
+
const skip = shouldSkipInstrumentation(mw);
|
|
289
|
+
const start = Date.now();
|
|
290
|
+
const decision = await mw.onBeforeToolCall(ctx, hookCtx);
|
|
291
|
+
const hasTransform = decision !== void 0 && decision !== null;
|
|
292
|
+
if (!skip) {
|
|
293
|
+
this.logger.middleware(`hook=onBeforeToolCall middleware=${mw.name ?? "unnamed"}`, {
|
|
294
|
+
middleware: mw.name ?? "unnamed",
|
|
295
|
+
hook: "onBeforeToolCall"
|
|
296
|
+
});
|
|
297
|
+
aiEventClient.emit("middleware:hook:executed", {
|
|
298
|
+
...instrumentCtx(ctx),
|
|
299
|
+
middlewareName: mw.name || "unnamed",
|
|
300
|
+
hookName: "onBeforeToolCall",
|
|
301
|
+
iteration: ctx.iteration,
|
|
302
|
+
duration: Date.now() - start,
|
|
303
|
+
hasTransform
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
if (hasTransform) return decision;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Run onAfterToolCall on all middleware in order.
|
|
311
|
+
*/
|
|
312
|
+
async runOnAfterToolCall(ctx, info) {
|
|
313
|
+
for (const mw of this.middlewares) if (mw.onAfterToolCall) {
|
|
314
|
+
const skip = shouldSkipInstrumentation(mw);
|
|
315
|
+
const start = Date.now();
|
|
316
|
+
await mw.onAfterToolCall(ctx, info);
|
|
317
|
+
if (!skip) {
|
|
318
|
+
this.logger.middleware(`hook=onAfterToolCall middleware=${mw.name ?? "unnamed"}`, {
|
|
319
|
+
middleware: mw.name ?? "unnamed",
|
|
320
|
+
hook: "onAfterToolCall"
|
|
321
|
+
});
|
|
322
|
+
aiEventClient.emit("middleware:hook:executed", {
|
|
323
|
+
...instrumentCtx(ctx),
|
|
324
|
+
middlewareName: mw.name || "unnamed",
|
|
325
|
+
hookName: "onAfterToolCall",
|
|
326
|
+
iteration: ctx.iteration,
|
|
327
|
+
duration: Date.now() - start,
|
|
328
|
+
hasTransform: false
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Run onUsage on all middleware in order.
|
|
335
|
+
*/
|
|
336
|
+
async runOnUsage(ctx, usage) {
|
|
337
|
+
for (const mw of this.middlewares) if (mw.onUsage) {
|
|
338
|
+
const skip = shouldSkipInstrumentation(mw);
|
|
339
|
+
const start = Date.now();
|
|
340
|
+
await mw.onUsage(ctx, usage);
|
|
341
|
+
if (!skip) {
|
|
342
|
+
this.logger.middleware(`hook=onUsage middleware=${mw.name ?? "unnamed"}`, {
|
|
343
|
+
middleware: mw.name ?? "unnamed",
|
|
344
|
+
hook: "onUsage"
|
|
345
|
+
});
|
|
346
|
+
aiEventClient.emit("middleware:hook:executed", {
|
|
347
|
+
...instrumentCtx(ctx),
|
|
348
|
+
middlewareName: mw.name || "unnamed",
|
|
349
|
+
hookName: "onUsage",
|
|
350
|
+
iteration: ctx.iteration,
|
|
351
|
+
duration: Date.now() - start,
|
|
352
|
+
hasTransform: false
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Await ONE terminal hook and RETURN its throw instead of letting it escape
|
|
359
|
+
* the caller's loop, logging it on the `errors` channel first so the failure
|
|
360
|
+
* is never invisible. `undefined` means the hook completed.
|
|
361
|
+
*
|
|
362
|
+
* Capturing (rather than swallowing at this level) is what lets isolation and
|
|
363
|
+
* reporting coexist: every caller gives every middleware its turn, and then
|
|
364
|
+
* each decides on its own whether the collected failures are worth telling the
|
|
365
|
+
* caller about. See {@link runOnFinish} vs {@link runOnAbort} /
|
|
366
|
+
* {@link runOnError}.
|
|
367
|
+
*/
|
|
368
|
+
async captureTerminalHook(mw, hookName, invoke) {
|
|
369
|
+
try {
|
|
370
|
+
await invoke();
|
|
371
|
+
return;
|
|
372
|
+
} catch (error) {
|
|
373
|
+
this.logger.errors(`middleware ${hookName} hook failed`, {
|
|
374
|
+
middleware: mw.name ?? "unnamed",
|
|
375
|
+
hook: hookName,
|
|
376
|
+
error
|
|
377
|
+
});
|
|
378
|
+
return {
|
|
379
|
+
middleware: mw.name ?? "unnamed",
|
|
380
|
+
error
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* Run onFinish on all middleware in order.
|
|
386
|
+
*
|
|
387
|
+
* ISOLATED **and** REPORTED. `onFinish` is the only terminal fan-out on the
|
|
388
|
+
* SUCCESS path, and it is where `withPersistence.onFinish` writes the
|
|
389
|
+
* assistant turn through the store. So the two properties are needed together
|
|
390
|
+
* and neither may be traded for the other:
|
|
391
|
+
*
|
|
392
|
+
* - ISOLATION: every middleware's hook runs even if an earlier one threw, so a
|
|
393
|
+
* transient store error cannot skip a later middleware's own bookkeeping.
|
|
394
|
+
* Each failure is captured by {@link captureTerminalHook}, not propagated
|
|
395
|
+
* mid-loop.
|
|
396
|
+
* - REPORTING: after the loop, the failures are rethrown. `chat()`'s catch
|
|
397
|
+
* treats what we throw as a genuine error (it is not a
|
|
398
|
+
* `MiddlewareAbortError`, and `structuralInterruptFailure` does not match
|
|
399
|
+
* it) and rethrows it out of the generator.
|
|
400
|
+
*
|
|
401
|
+
* What that rethrow can and cannot achieve depends on the transport, because
|
|
402
|
+
* this fan-out is awaited AFTER the adapter's `RUN_FINISHED` has already been
|
|
403
|
+
* yielded (`chat()` yields terminal chunks while streaming, then awaits this
|
|
404
|
+
* hook on its way out). The success terminal is therefore already gone; the
|
|
405
|
+
* rethrow can only append to what the consumer saw, never retract it:
|
|
406
|
+
*
|
|
407
|
+
* - NON-DURABLE transport: the throw escapes the generator mid-response, and
|
|
408
|
+
* the SSE / HTTP-stream encoder turns it into a TRAILING `RUN_ERROR` on the
|
|
409
|
+
* wire carrying the store's own message and `code`. `ai-client` surfaces
|
|
410
|
+
* that as an error status, so the user is not told the turn was saved when
|
|
411
|
+
* it was not.
|
|
412
|
+
* - DURABLE transport: the throw reaches the durability sink instead. The
|
|
413
|
+
* terminal was already persisted AND forwarded, so the sink deliberately
|
|
414
|
+
* does NOT append a second, contradictory terminal, and `terminalForwarded`
|
|
415
|
+
* (see `stream-to-response.ts`) suppresses the rethrow to the live consumer.
|
|
416
|
+
* The `RUN_FINISHED` stands and the failure is RECORDED SERVER-SIDE on the
|
|
417
|
+
* sink's `errors` channel. That is the intended outcome, not a gap: the save
|
|
418
|
+
* failed, not the run — the consumer did receive the complete stream, so
|
|
419
|
+
* telling it the run errored would be the lie. What the rethrow buys here is
|
|
420
|
+
* that the sink sees the failure at all; while this loop swallowed, the only
|
|
421
|
+
* trace anywhere was {@link captureTerminalHook}'s log line.
|
|
422
|
+
*
|
|
423
|
+
* Either way, swallowing is the one option ruled out: a failed
|
|
424
|
+
* `messages.append` would otherwise leave a `completed` run record with the
|
|
425
|
+
* assistant turn missing from storage and nothing beyond a middleware log
|
|
426
|
+
* line, and the client would go on to send a history the server has no record
|
|
427
|
+
* of.
|
|
428
|
+
*
|
|
429
|
+
* A single failure is rethrown AS-IS so the store's own error — its message,
|
|
430
|
+
* `cause`, `code` and `instanceof` identity — is what reaches the caller and
|
|
431
|
+
* the wire; wrapping the common case would bury it. Two or more become an
|
|
432
|
+
* `AggregateError` (never a `MiddlewareAbortError`, so it cannot be mistaken
|
|
433
|
+
* for an abort) rather than picking a winner and dropping the rest.
|
|
434
|
+
*/
|
|
435
|
+
async runOnFinish(ctx, info) {
|
|
436
|
+
const failures = [];
|
|
437
|
+
let firstFailure;
|
|
438
|
+
for (const mw of this.middlewares) {
|
|
439
|
+
const hook = mw.onFinish;
|
|
440
|
+
if (hook) {
|
|
441
|
+
const skip = shouldSkipInstrumentation(mw);
|
|
442
|
+
const start = Date.now();
|
|
443
|
+
const failure = await this.captureTerminalHook(mw, "onFinish", () => hook.call(mw, ctx, info));
|
|
444
|
+
if (failure !== void 0) {
|
|
445
|
+
firstFailure ??= failure;
|
|
446
|
+
failures.push(failure);
|
|
447
|
+
continue;
|
|
448
|
+
}
|
|
449
|
+
if (!skip) {
|
|
450
|
+
this.logger.middleware(`hook=onFinish middleware=${mw.name ?? "unnamed"}`, {
|
|
451
|
+
middleware: mw.name ?? "unnamed",
|
|
452
|
+
hook: "onFinish"
|
|
453
|
+
});
|
|
454
|
+
aiEventClient.emit("middleware:hook:executed", {
|
|
455
|
+
...instrumentCtx(ctx),
|
|
456
|
+
middlewareName: mw.name || "unnamed",
|
|
457
|
+
hookName: "onFinish",
|
|
458
|
+
iteration: ctx.iteration,
|
|
459
|
+
duration: Date.now() - start,
|
|
460
|
+
hasTransform: false
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
if (firstFailure !== void 0) throw failures.length === 1 ? firstFailure.error : new AggregateError(failures.map((f) => f.error), `${failures.length} middleware onFinish hooks failed: ` + failures.map((f) => f.middleware).join(", "));
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* Run onAbort on all middleware in order.
|
|
469
|
+
*
|
|
470
|
+
* ISOLATED and DELIBERATELY SWALLOWED. `onAbort` is a pure teardown fan-out
|
|
471
|
+
* released from `chat()`'s `finally`, on a path where the outcome is already
|
|
472
|
+
* decided: the run stopped, and the caller is being told why. A throw here has
|
|
473
|
+
* nothing better to report than the abort reason it would DISPLACE — the
|
|
474
|
+
* `finally` would surface a flaky store's error in place of "client
|
|
475
|
+
* disconnected" — so failures are logged on the `errors` channel and go no
|
|
476
|
+
* further. That is not a silent failure; it is refusing to let teardown
|
|
477
|
+
* rewrite an outcome it did not produce.
|
|
478
|
+
*
|
|
479
|
+
* Isolation matters independently: these hooks release PER-MIDDLEWARE
|
|
480
|
+
* resources (`withSandbox.onAbort` detaches or destroys the sandbox and stamps
|
|
481
|
+
* `detachedSince`; `withPersistence.onAbort` records the run status through the
|
|
482
|
+
* store), so an unguarded loop turns one transient store error into a
|
|
483
|
+
* permanently leaked sandbox for every middleware ordered after it.
|
|
484
|
+
*/
|
|
485
|
+
async runOnAbort(ctx, info) {
|
|
486
|
+
for (const mw of this.middlewares) {
|
|
487
|
+
const hook = mw.onAbort;
|
|
488
|
+
if (hook) {
|
|
489
|
+
const skip = shouldSkipInstrumentation(mw);
|
|
490
|
+
const start = Date.now();
|
|
491
|
+
if (await this.captureTerminalHook(mw, "onAbort", () => hook.call(mw, ctx, info)) === void 0 && !skip) {
|
|
492
|
+
this.logger.middleware(`hook=onAbort middleware=${mw.name ?? "unnamed"}`, {
|
|
493
|
+
middleware: mw.name ?? "unnamed",
|
|
494
|
+
hook: "onAbort"
|
|
495
|
+
});
|
|
496
|
+
aiEventClient.emit("middleware:hook:executed", {
|
|
497
|
+
...instrumentCtx(ctx),
|
|
498
|
+
middlewareName: mw.name || "unnamed",
|
|
499
|
+
hookName: "onAbort",
|
|
500
|
+
iteration: ctx.iteration,
|
|
501
|
+
duration: Date.now() - start,
|
|
502
|
+
hasTransform: false
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
/**
|
|
509
|
+
* Run onError on all middleware in order.
|
|
510
|
+
*
|
|
511
|
+
* ISOLATED and DELIBERATELY SWALLOWED, for the same reason as
|
|
512
|
+
* {@link runOnAbort} and NOT merely because it is teardown: the run has
|
|
513
|
+
* already failed, `info.error` IS that failure, and `chat()` rethrows it to the
|
|
514
|
+
* caller the moment this fan-out returns. A propagated hook throw could only
|
|
515
|
+
* REPLACE the run's real error with a teardown artifact — strictly less
|
|
516
|
+
* information for the caller, who is already learning the run failed. Reporting
|
|
517
|
+
* would buy nothing and cost the diagnosis, so failures are logged on the
|
|
518
|
+
* `errors` channel and stop there.
|
|
519
|
+
*
|
|
520
|
+
* Contrast {@link runOnFinish}, where nothing else is telling the caller
|
|
521
|
+
* anything is wrong — which is why that one reports.
|
|
522
|
+
*/
|
|
523
|
+
async runOnError(ctx, info) {
|
|
524
|
+
for (const mw of this.middlewares) {
|
|
525
|
+
const hook = mw.onError;
|
|
526
|
+
if (hook) {
|
|
527
|
+
const skip = shouldSkipInstrumentation(mw);
|
|
528
|
+
const start = Date.now();
|
|
529
|
+
if (await this.captureTerminalHook(mw, "onError", () => hook.call(mw, ctx, info)) === void 0 && !skip) {
|
|
530
|
+
this.logger.middleware(`hook=onError middleware=${mw.name ?? "unnamed"}`, {
|
|
531
|
+
middleware: mw.name ?? "unnamed",
|
|
532
|
+
hook: "onError"
|
|
533
|
+
});
|
|
534
|
+
aiEventClient.emit("middleware:hook:executed", {
|
|
535
|
+
...instrumentCtx(ctx),
|
|
536
|
+
middlewareName: mw.name || "unnamed",
|
|
537
|
+
hookName: "onError",
|
|
538
|
+
iteration: ctx.iteration,
|
|
539
|
+
duration: Date.now() - start,
|
|
540
|
+
hasTransform: false
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
/**
|
|
547
|
+
* Run onIteration on all middleware in order.
|
|
548
|
+
* Called at the start of each agent loop iteration.
|
|
549
|
+
*/
|
|
550
|
+
async runOnIteration(ctx, info) {
|
|
551
|
+
for (const mw of this.middlewares) if (mw.onIteration) {
|
|
552
|
+
const skip = shouldSkipInstrumentation(mw);
|
|
553
|
+
const start = Date.now();
|
|
554
|
+
await mw.onIteration(ctx, info);
|
|
555
|
+
if (!skip) {
|
|
556
|
+
this.logger.middleware(`hook=onIteration middleware=${mw.name ?? "unnamed"}`, {
|
|
557
|
+
middleware: mw.name ?? "unnamed",
|
|
558
|
+
hook: "onIteration"
|
|
559
|
+
});
|
|
560
|
+
aiEventClient.emit("middleware:hook:executed", {
|
|
561
|
+
...instrumentCtx(ctx),
|
|
562
|
+
middlewareName: mw.name || "unnamed",
|
|
563
|
+
hookName: "onIteration",
|
|
564
|
+
iteration: ctx.iteration,
|
|
565
|
+
duration: Date.now() - start,
|
|
566
|
+
hasTransform: false
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* Run onShouldContinue through middleware in order (AND semantics).
|
|
573
|
+
* Any explicit `false` stops further iterations; `true` / void / undefined pass.
|
|
574
|
+
* Called after `agentLoopStrategy` has already approved continuation.
|
|
575
|
+
*/
|
|
576
|
+
async runOnShouldContinue(ctx, state) {
|
|
577
|
+
for (const mw of this.middlewares) if (mw.onShouldContinue) {
|
|
578
|
+
const skip = shouldSkipInstrumentation(mw);
|
|
579
|
+
const start = Date.now();
|
|
580
|
+
const result = await mw.onShouldContinue(ctx, state);
|
|
581
|
+
if (!skip) {
|
|
582
|
+
this.logger.middleware(`hook=onShouldContinue middleware=${mw.name ?? "unnamed"}`, {
|
|
583
|
+
middleware: mw.name ?? "unnamed",
|
|
584
|
+
hook: "onShouldContinue",
|
|
585
|
+
result
|
|
586
|
+
});
|
|
587
|
+
aiEventClient.emit("middleware:hook:executed", {
|
|
588
|
+
...instrumentCtx(ctx),
|
|
589
|
+
middlewareName: mw.name || "unnamed",
|
|
590
|
+
hookName: "onShouldContinue",
|
|
591
|
+
iteration: ctx.iteration,
|
|
592
|
+
duration: Date.now() - start,
|
|
593
|
+
hasTransform: result === false
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
if (result === false) return false;
|
|
597
|
+
}
|
|
598
|
+
return true;
|
|
599
|
+
}
|
|
600
|
+
/**
|
|
601
|
+
* Run onToolPhaseComplete on all middleware in order.
|
|
602
|
+
* Called after all tool calls in an iteration have been processed.
|
|
603
|
+
*/
|
|
604
|
+
async runOnToolPhaseComplete(ctx, info) {
|
|
605
|
+
for (const mw of this.middlewares) if (mw.onToolPhaseComplete) {
|
|
606
|
+
const skip = shouldSkipInstrumentation(mw);
|
|
607
|
+
const start = Date.now();
|
|
608
|
+
await mw.onToolPhaseComplete(ctx, info);
|
|
609
|
+
if (!skip) {
|
|
610
|
+
this.logger.middleware(`hook=onToolPhaseComplete middleware=${mw.name ?? "unnamed"}`, {
|
|
611
|
+
middleware: mw.name ?? "unnamed",
|
|
612
|
+
hook: "onToolPhaseComplete"
|
|
613
|
+
});
|
|
614
|
+
aiEventClient.emit("middleware:hook:executed", {
|
|
615
|
+
...instrumentCtx(ctx),
|
|
616
|
+
middlewareName: mw.name || "unnamed",
|
|
617
|
+
hookName: "onToolPhaseComplete",
|
|
618
|
+
iteration: ctx.iteration,
|
|
619
|
+
duration: Date.now() - start,
|
|
620
|
+
hasTransform: false
|
|
621
|
+
});
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
}
|
|
536
625
|
};
|
|
537
|
-
//#
|
|
626
|
+
//#endregion
|
|
627
|
+
export { MiddlewareRunner };
|
|
628
|
+
|
|
629
|
+
//# sourceMappingURL=compose.js.map
|