@agentuity/runtime 2.0.11 → 3.0.0-alpha.1
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/index.d.ts +37 -65
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +59 -61
- package/dist/index.js.map +1 -1
- package/package.json +9 -38
- package/src/index.ts +58 -259
- package/AGENTS.md +0 -116
- package/dist/_config.d.ts +0 -100
- package/dist/_config.d.ts.map +0 -1
- package/dist/_config.js +0 -147
- package/dist/_config.js.map +0 -1
- package/dist/_context.d.ts +0 -80
- package/dist/_context.d.ts.map +0 -1
- package/dist/_context.js +0 -160
- package/dist/_context.js.map +0 -1
- package/dist/_events.d.ts +0 -64
- package/dist/_events.d.ts.map +0 -1
- package/dist/_events.js +0 -92
- package/dist/_events.js.map +0 -1
- package/dist/_globals.d.ts +0 -58
- package/dist/_globals.d.ts.map +0 -1
- package/dist/_globals.js +0 -71
- package/dist/_globals.js.map +0 -1
- package/dist/_idle.d.ts +0 -7
- package/dist/_idle.d.ts.map +0 -1
- package/dist/_idle.js +0 -10
- package/dist/_idle.js.map +0 -1
- package/dist/_metadata.d.ts +0 -117
- package/dist/_metadata.d.ts.map +0 -1
- package/dist/_metadata.js +0 -268
- package/dist/_metadata.js.map +0 -1
- package/dist/_process-protection.d.ts +0 -27
- package/dist/_process-protection.d.ts.map +0 -1
- package/dist/_process-protection.js +0 -56
- package/dist/_process-protection.js.map +0 -1
- package/dist/_server.d.ts +0 -50
- package/dist/_server.d.ts.map +0 -1
- package/dist/_server.js +0 -89
- package/dist/_server.js.map +0 -1
- package/dist/_services.d.ts +0 -25
- package/dist/_services.d.ts.map +0 -1
- package/dist/_services.js +0 -286
- package/dist/_services.js.map +0 -1
- package/dist/_standalone.d.ts +0 -212
- package/dist/_standalone.d.ts.map +0 -1
- package/dist/_standalone.js +0 -556
- package/dist/_standalone.js.map +0 -1
- package/dist/_tokens.d.ts +0 -12
- package/dist/_tokens.d.ts.map +0 -1
- package/dist/_tokens.js +0 -97
- package/dist/_tokens.js.map +0 -1
- package/dist/_util.d.ts +0 -16
- package/dist/_util.d.ts.map +0 -1
- package/dist/_util.js +0 -54
- package/dist/_util.js.map +0 -1
- package/dist/_validation.d.ts +0 -89
- package/dist/_validation.d.ts.map +0 -1
- package/dist/_validation.js +0 -29
- package/dist/_validation.js.map +0 -1
- package/dist/_waituntil.d.ts +0 -32
- package/dist/_waituntil.d.ts.map +0 -1
- package/dist/_waituntil.js +0 -156
- package/dist/_waituntil.js.map +0 -1
- package/dist/agent.d.ts +0 -1262
- package/dist/agent.d.ts.map +0 -1
- package/dist/agent.js +0 -981
- package/dist/agent.js.map +0 -1
- package/dist/app.d.ts +0 -514
- package/dist/app.d.ts.map +0 -1
- package/dist/app.js +0 -228
- package/dist/app.js.map +0 -1
- package/dist/bootstrap.d.ts +0 -44
- package/dist/bootstrap.d.ts.map +0 -1
- package/dist/bootstrap.js +0 -259
- package/dist/bootstrap.js.map +0 -1
- package/dist/bun-s3-patch.d.ts +0 -37
- package/dist/bun-s3-patch.d.ts.map +0 -1
- package/dist/bun-s3-patch.js +0 -142
- package/dist/bun-s3-patch.js.map +0 -1
- package/dist/cors.d.ts +0 -42
- package/dist/cors.d.ts.map +0 -1
- package/dist/cors.js +0 -117
- package/dist/cors.js.map +0 -1
- package/dist/dev-patches/aisdk.d.ts +0 -17
- package/dist/dev-patches/aisdk.d.ts.map +0 -1
- package/dist/dev-patches/aisdk.js +0 -160
- package/dist/dev-patches/aisdk.js.map +0 -1
- package/dist/dev-patches/gateway.d.ts +0 -16
- package/dist/dev-patches/gateway.d.ts.map +0 -1
- package/dist/dev-patches/gateway.js +0 -54
- package/dist/dev-patches/gateway.js.map +0 -1
- package/dist/dev-patches/index.d.ts +0 -21
- package/dist/dev-patches/index.d.ts.map +0 -1
- package/dist/dev-patches/index.js +0 -33
- package/dist/dev-patches/index.js.map +0 -1
- package/dist/dev-patches/otel-llm.d.ts +0 -12
- package/dist/dev-patches/otel-llm.d.ts.map +0 -1
- package/dist/dev-patches/otel-llm.js +0 -352
- package/dist/dev-patches/otel-llm.js.map +0 -1
- package/dist/devmode.d.ts +0 -3
- package/dist/devmode.d.ts.map +0 -1
- package/dist/devmode.js +0 -167
- package/dist/devmode.js.map +0 -1
- package/dist/eval.d.ts +0 -91
- package/dist/eval.d.ts.map +0 -1
- package/dist/eval.js +0 -16
- package/dist/eval.js.map +0 -1
- package/dist/handlers/_route-meta.d.ts +0 -22
- package/dist/handlers/_route-meta.d.ts.map +0 -1
- package/dist/handlers/_route-meta.js +0 -25
- package/dist/handlers/_route-meta.js.map +0 -1
- package/dist/handlers/cron.d.ts +0 -73
- package/dist/handlers/cron.d.ts.map +0 -1
- package/dist/handlers/cron.js +0 -43
- package/dist/handlers/cron.js.map +0 -1
- package/dist/handlers/index.d.ts +0 -6
- package/dist/handlers/index.d.ts.map +0 -1
- package/dist/handlers/index.js +0 -6
- package/dist/handlers/index.js.map +0 -1
- package/dist/handlers/sse.d.ts +0 -163
- package/dist/handlers/sse.d.ts.map +0 -1
- package/dist/handlers/sse.js +0 -175
- package/dist/handlers/sse.js.map +0 -1
- package/dist/handlers/stream.d.ts +0 -52
- package/dist/handlers/stream.d.ts.map +0 -1
- package/dist/handlers/stream.js +0 -108
- package/dist/handlers/stream.js.map +0 -1
- package/dist/handlers/webrtc.d.ts +0 -49
- package/dist/handlers/webrtc.d.ts.map +0 -1
- package/dist/handlers/webrtc.js +0 -109
- package/dist/handlers/webrtc.js.map +0 -1
- package/dist/handlers/websocket.d.ts +0 -88
- package/dist/handlers/websocket.d.ts.map +0 -1
- package/dist/handlers/websocket.js +0 -161
- package/dist/handlers/websocket.js.map +0 -1
- package/dist/logger/console.d.ts +0 -70
- package/dist/logger/console.d.ts.map +0 -1
- package/dist/logger/console.js +0 -278
- package/dist/logger/console.js.map +0 -1
- package/dist/logger/index.d.ts +0 -3
- package/dist/logger/index.d.ts.map +0 -1
- package/dist/logger/index.js +0 -3
- package/dist/logger/index.js.map +0 -1
- package/dist/logger/internal.d.ts +0 -79
- package/dist/logger/internal.d.ts.map +0 -1
- package/dist/logger/internal.js +0 -133
- package/dist/logger/internal.js.map +0 -1
- package/dist/logger/logger.d.ts +0 -41
- package/dist/logger/logger.d.ts.map +0 -1
- package/dist/logger/logger.js +0 -2
- package/dist/logger/logger.js.map +0 -1
- package/dist/logger/user.d.ts +0 -8
- package/dist/logger/user.d.ts.map +0 -1
- package/dist/logger/user.js +0 -7
- package/dist/logger/user.js.map +0 -1
- package/dist/logger/util.d.ts +0 -11
- package/dist/logger/util.d.ts.map +0 -1
- package/dist/logger/util.js +0 -77
- package/dist/logger/util.js.map +0 -1
- package/dist/middleware.d.ts +0 -105
- package/dist/middleware.d.ts.map +0 -1
- package/dist/middleware.js +0 -763
- package/dist/middleware.js.map +0 -1
- package/dist/otel/config.d.ts +0 -19
- package/dist/otel/config.d.ts.map +0 -1
- package/dist/otel/config.js +0 -26
- package/dist/otel/config.js.map +0 -1
- package/dist/otel/console.d.ts +0 -33
- package/dist/otel/console.d.ts.map +0 -1
- package/dist/otel/console.js +0 -86
- package/dist/otel/console.js.map +0 -1
- package/dist/otel/exporters/index.d.ts +0 -4
- package/dist/otel/exporters/index.d.ts.map +0 -1
- package/dist/otel/exporters/index.js +0 -4
- package/dist/otel/exporters/index.js.map +0 -1
- package/dist/otel/exporters/jsonl-log-exporter.d.ts +0 -36
- package/dist/otel/exporters/jsonl-log-exporter.d.ts.map +0 -1
- package/dist/otel/exporters/jsonl-log-exporter.js +0 -103
- package/dist/otel/exporters/jsonl-log-exporter.js.map +0 -1
- package/dist/otel/exporters/jsonl-metric-exporter.d.ts +0 -40
- package/dist/otel/exporters/jsonl-metric-exporter.d.ts.map +0 -1
- package/dist/otel/exporters/jsonl-metric-exporter.js +0 -104
- package/dist/otel/exporters/jsonl-metric-exporter.js.map +0 -1
- package/dist/otel/exporters/jsonl-trace-exporter.d.ts +0 -36
- package/dist/otel/exporters/jsonl-trace-exporter.d.ts.map +0 -1
- package/dist/otel/exporters/jsonl-trace-exporter.js +0 -111
- package/dist/otel/exporters/jsonl-trace-exporter.js.map +0 -1
- package/dist/otel/fetch.d.ts +0 -12
- package/dist/otel/fetch.d.ts.map +0 -1
- package/dist/otel/fetch.js +0 -82
- package/dist/otel/fetch.js.map +0 -1
- package/dist/otel/http.d.ts +0 -16
- package/dist/otel/http.d.ts.map +0 -1
- package/dist/otel/http.js +0 -44
- package/dist/otel/http.js.map +0 -1
- package/dist/otel/logger.d.ts +0 -37
- package/dist/otel/logger.d.ts.map +0 -1
- package/dist/otel/logger.js +0 -265
- package/dist/otel/logger.js.map +0 -1
- package/dist/otel/otel.d.ts +0 -68
- package/dist/otel/otel.d.ts.map +0 -1
- package/dist/otel/otel.js +0 -245
- package/dist/otel/otel.js.map +0 -1
- package/dist/otel/tracestate.d.ts +0 -44
- package/dist/otel/tracestate.d.ts.map +0 -1
- package/dist/otel/tracestate.js +0 -84
- package/dist/otel/tracestate.js.map +0 -1
- package/dist/router.d.ts +0 -66
- package/dist/router.d.ts.map +0 -1
- package/dist/router.js +0 -44
- package/dist/router.js.map +0 -1
- package/dist/services/evalrun/composite.d.ts +0 -21
- package/dist/services/evalrun/composite.d.ts.map +0 -1
- package/dist/services/evalrun/composite.js +0 -26
- package/dist/services/evalrun/composite.js.map +0 -1
- package/dist/services/evalrun/http.d.ts +0 -24
- package/dist/services/evalrun/http.d.ts.map +0 -1
- package/dist/services/evalrun/http.js +0 -115
- package/dist/services/evalrun/http.js.map +0 -1
- package/dist/services/evalrun/index.d.ts +0 -5
- package/dist/services/evalrun/index.d.ts.map +0 -1
- package/dist/services/evalrun/index.js +0 -5
- package/dist/services/evalrun/index.js.map +0 -1
- package/dist/services/evalrun/json.d.ts +0 -21
- package/dist/services/evalrun/json.d.ts.map +0 -1
- package/dist/services/evalrun/json.js +0 -38
- package/dist/services/evalrun/json.js.map +0 -1
- package/dist/services/evalrun/local.d.ts +0 -19
- package/dist/services/evalrun/local.d.ts.map +0 -1
- package/dist/services/evalrun/local.js +0 -22
- package/dist/services/evalrun/local.js.map +0 -1
- package/dist/services/local/_db.d.ts +0 -4
- package/dist/services/local/_db.d.ts.map +0 -1
- package/dist/services/local/_db.js +0 -281
- package/dist/services/local/_db.js.map +0 -1
- package/dist/services/local/_router.d.ts +0 -3
- package/dist/services/local/_router.d.ts.map +0 -1
- package/dist/services/local/_router.js +0 -28
- package/dist/services/local/_router.js.map +0 -1
- package/dist/services/local/_util.d.ts +0 -18
- package/dist/services/local/_util.d.ts.map +0 -1
- package/dist/services/local/_util.js +0 -44
- package/dist/services/local/_util.js.map +0 -1
- package/dist/services/local/email.d.ts +0 -24
- package/dist/services/local/email.d.ts.map +0 -1
- package/dist/services/local/email.js +0 -58
- package/dist/services/local/email.js.map +0 -1
- package/dist/services/local/index.d.ts +0 -10
- package/dist/services/local/index.d.ts.map +0 -1
- package/dist/services/local/index.js +0 -10
- package/dist/services/local/index.js.map +0 -1
- package/dist/services/local/keyvalue.d.ts +0 -17
- package/dist/services/local/keyvalue.d.ts.map +0 -1
- package/dist/services/local/keyvalue.js +0 -133
- package/dist/services/local/keyvalue.js.map +0 -1
- package/dist/services/local/queue.d.ts +0 -10
- package/dist/services/local/queue.d.ts.map +0 -1
- package/dist/services/local/queue.js +0 -96
- package/dist/services/local/queue.js.map +0 -1
- package/dist/services/local/stream.d.ts +0 -12
- package/dist/services/local/stream.d.ts.map +0 -1
- package/dist/services/local/stream.js +0 -266
- package/dist/services/local/stream.js.map +0 -1
- package/dist/services/local/task.d.ts +0 -55
- package/dist/services/local/task.d.ts.map +0 -1
- package/dist/services/local/task.js +0 -1248
- package/dist/services/local/task.js.map +0 -1
- package/dist/services/local/vector.d.ts +0 -17
- package/dist/services/local/vector.d.ts.map +0 -1
- package/dist/services/local/vector.js +0 -303
- package/dist/services/local/vector.js.map +0 -1
- package/dist/services/sandbox/http.d.ts +0 -23
- package/dist/services/sandbox/http.d.ts.map +0 -1
- package/dist/services/sandbox/http.js +0 -327
- package/dist/services/sandbox/http.js.map +0 -1
- package/dist/services/sandbox/index.d.ts +0 -2
- package/dist/services/sandbox/index.d.ts.map +0 -1
- package/dist/services/sandbox/index.js +0 -2
- package/dist/services/sandbox/index.js.map +0 -1
- package/dist/services/session/composite.d.ts +0 -21
- package/dist/services/session/composite.d.ts.map +0 -1
- package/dist/services/session/composite.js +0 -26
- package/dist/services/session/composite.js.map +0 -1
- package/dist/services/session/http.d.ts +0 -34
- package/dist/services/session/http.d.ts.map +0 -1
- package/dist/services/session/http.js +0 -124
- package/dist/services/session/http.js.map +0 -1
- package/dist/services/session/index.d.ts +0 -5
- package/dist/services/session/index.d.ts.map +0 -1
- package/dist/services/session/index.js +0 -5
- package/dist/services/session/index.js.map +0 -1
- package/dist/services/session/json.d.ts +0 -22
- package/dist/services/session/json.d.ts.map +0 -1
- package/dist/services/session/json.js +0 -35
- package/dist/services/session/json.js.map +0 -1
- package/dist/services/session/local.d.ts +0 -19
- package/dist/services/session/local.d.ts.map +0 -1
- package/dist/services/session/local.js +0 -23
- package/dist/services/session/local.js.map +0 -1
- package/dist/services/thread/local.d.ts +0 -20
- package/dist/services/thread/local.d.ts.map +0 -1
- package/dist/services/thread/local.js +0 -158
- package/dist/services/thread/local.js.map +0 -1
- package/dist/session.d.ts +0 -734
- package/dist/session.d.ts.map +0 -1
- package/dist/session.js +0 -1140
- package/dist/session.js.map +0 -1
- package/dist/signature.d.ts +0 -22
- package/dist/signature.d.ts.map +0 -1
- package/dist/signature.js +0 -63
- package/dist/signature.js.map +0 -1
- package/dist/validator.d.ts +0 -142
- package/dist/validator.d.ts.map +0 -1
- package/dist/validator.js +0 -149
- package/dist/validator.js.map +0 -1
- package/dist/version-check.d.ts +0 -20
- package/dist/version-check.d.ts.map +0 -1
- package/dist/version-check.js +0 -157
- package/dist/version-check.js.map +0 -1
- package/dist/web.d.ts +0 -8
- package/dist/web.d.ts.map +0 -1
- package/dist/web.js +0 -67
- package/dist/web.js.map +0 -1
- package/dist/webrtc-signaling.d.ts +0 -80
- package/dist/webrtc-signaling.d.ts.map +0 -1
- package/dist/webrtc-signaling.js +0 -237
- package/dist/webrtc-signaling.js.map +0 -1
- package/dist/workbench.d.ts +0 -17
- package/dist/workbench.d.ts.map +0 -1
- package/dist/workbench.js +0 -605
- package/dist/workbench.js.map +0 -1
- package/src/_config.ts +0 -163
- package/src/_context.ts +0 -240
- package/src/_events.ts +0 -142
- package/src/_globals.ts +0 -92
- package/src/_idle.ts +0 -10
- package/src/_metadata.ts +0 -407
- package/src/_process-protection.ts +0 -71
- package/src/_server.ts +0 -109
- package/src/_services.ts +0 -379
- package/src/_standalone.ts +0 -710
- package/src/_tokens.ts +0 -114
- package/src/_util.ts +0 -62
- package/src/_validation.ts +0 -119
- package/src/_waituntil.ts +0 -188
- package/src/agent.ts +0 -2739
- package/src/app.ts +0 -769
- package/src/bootstrap.ts +0 -321
- package/src/bun-s3-patch.ts +0 -224
- package/src/cors.ts +0 -137
- package/src/dev-patches/aisdk.ts +0 -169
- package/src/dev-patches/gateway.ts +0 -68
- package/src/dev-patches/index.ts +0 -37
- package/src/dev-patches/otel-llm.ts +0 -405
- package/src/devmode.ts +0 -171
- package/src/eval.ts +0 -109
- package/src/globals.d.ts +0 -28
- package/src/handlers/_route-meta.ts +0 -33
- package/src/handlers/cron.ts +0 -141
- package/src/handlers/index.ts +0 -18
- package/src/handlers/sse.ts +0 -358
- package/src/handlers/stream.ts +0 -121
- package/src/handlers/webrtc.ts +0 -125
- package/src/handlers/websocket.ts +0 -203
- package/src/logger/console.ts +0 -323
- package/src/logger/index.ts +0 -2
- package/src/logger/internal.ts +0 -165
- package/src/logger/logger.ts +0 -44
- package/src/logger/user.ts +0 -15
- package/src/logger/util.ts +0 -80
- package/src/middleware.ts +0 -1095
- package/src/otel/config.ts +0 -47
- package/src/otel/console.ts +0 -91
- package/src/otel/exporters/README.md +0 -217
- package/src/otel/exporters/index.ts +0 -3
- package/src/otel/exporters/jsonl-log-exporter.ts +0 -113
- package/src/otel/exporters/jsonl-metric-exporter.ts +0 -120
- package/src/otel/exporters/jsonl-trace-exporter.ts +0 -121
- package/src/otel/fetch.ts +0 -105
- package/src/otel/http.ts +0 -53
- package/src/otel/logger.ts +0 -293
- package/src/otel/otel.ts +0 -354
- package/src/otel/tracestate.ts +0 -108
- package/src/router.ts +0 -75
- package/src/services/evalrun/composite.ts +0 -34
- package/src/services/evalrun/http.ts +0 -167
- package/src/services/evalrun/index.ts +0 -4
- package/src/services/evalrun/json.ts +0 -46
- package/src/services/evalrun/local.ts +0 -28
- package/src/services/local/README.md +0 -1576
- package/src/services/local/_db.ts +0 -353
- package/src/services/local/_router.ts +0 -40
- package/src/services/local/_util.ts +0 -55
- package/src/services/local/email.ts +0 -91
- package/src/services/local/index.ts +0 -9
- package/src/services/local/keyvalue.ts +0 -174
- package/src/services/local/queue.ts +0 -145
- package/src/services/local/stream.ts +0 -358
- package/src/services/local/task.ts +0 -1711
- package/src/services/local/vector.ts +0 -438
- package/src/services/sandbox/http.ts +0 -522
- package/src/services/sandbox/index.ts +0 -1
- package/src/services/session/composite.ts +0 -33
- package/src/services/session/http.ts +0 -167
- package/src/services/session/index.ts +0 -4
- package/src/services/session/json.ts +0 -42
- package/src/services/session/local.ts +0 -33
- package/src/services/thread/local.ts +0 -199
- package/src/session.ts +0 -1960
- package/src/signature.ts +0 -82
- package/src/validator.ts +0 -283
- package/src/version-check.ts +0 -184
- package/src/web.ts +0 -76
- package/src/webrtc-signaling.ts +0 -288
- package/src/workbench.ts +0 -725
package/src/middleware.ts
DELETED
|
@@ -1,1095 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Middleware factories for Vite-native architecture
|
|
3
|
-
* Extracted from _server.ts to be used by generated entry files
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { createMiddleware } from 'hono/factory';
|
|
7
|
-
import { cors } from 'hono/cors';
|
|
8
|
-
import { compress } from 'hono/compress';
|
|
9
|
-
import { setSignedCookie } from 'hono/cookie';
|
|
10
|
-
import type { Env, CompressionConfig, CorsConfig } from './app';
|
|
11
|
-
import { createTrustedCorsOrigin } from './cors';
|
|
12
|
-
import type { Logger } from './logger';
|
|
13
|
-
|
|
14
|
-
import { generateId } from './session';
|
|
15
|
-
import { runInHTTPContext } from './_context';
|
|
16
|
-
import { DURATION_HEADER, TOKENS_HEADER } from './_tokens';
|
|
17
|
-
import { extractTraceContextFromRequest } from './otel/http';
|
|
18
|
-
import { enrichContextWithTraceState } from './otel/tracestate';
|
|
19
|
-
import { context, SpanKind, SpanStatusCode, trace, propagation } from '@opentelemetry/api';
|
|
20
|
-
import type { Meter, Tracer } from '@opentelemetry/api';
|
|
21
|
-
import * as runtimeConfig from './_config';
|
|
22
|
-
import { getSessionEventProvider } from './_services';
|
|
23
|
-
import { internal } from './logger/internal';
|
|
24
|
-
import { STREAM_DONE_PROMISE_KEY, IS_STREAMING_RESPONSE_KEY } from './handlers/sse';
|
|
25
|
-
import { WS_DONE_PROMISE_KEY } from './handlers/websocket';
|
|
26
|
-
import { loadBuildMetadata } from './_metadata';
|
|
27
|
-
|
|
28
|
-
const SESSION_HEADER = 'x-session-id';
|
|
29
|
-
const THREAD_HEADER = 'x-thread-id';
|
|
30
|
-
const DEPLOYMENT_HEADER = 'x-deployment';
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Paths that should skip OTEL session event tracking.
|
|
34
|
-
* These routes still get thread/session setup but won't create session start/complete events.
|
|
35
|
-
*/
|
|
36
|
-
const OTEL_SESSION_EVENT_SKIP_PATHS = new Set([
|
|
37
|
-
'/_agentuity/workbench/ws',
|
|
38
|
-
'/_agentuity/workbench/sample',
|
|
39
|
-
'/_agentuity/workbench/state',
|
|
40
|
-
'/_agentuity/workbench/metadata.json',
|
|
41
|
-
'/_agentuity/webanalytics/analytics.js',
|
|
42
|
-
'/_agentuity/webanalytics/session.js',
|
|
43
|
-
]);
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Paths that should skip thread/session setup entirely.
|
|
47
|
-
* These are lightweight endpoints that don't need any context.
|
|
48
|
-
*/
|
|
49
|
-
const OTEL_FULL_SKIP_PATHS = new Set([
|
|
50
|
-
'/_agentuity/workbench/metadata.json',
|
|
51
|
-
'/_agentuity/webanalytics/analytics.js',
|
|
52
|
-
'/_agentuity/webanalytics/session.js',
|
|
53
|
-
]);
|
|
54
|
-
|
|
55
|
-
export const AGENT_CONTEXT_PROPERTIES = [
|
|
56
|
-
'logger',
|
|
57
|
-
'tracer',
|
|
58
|
-
'sessionId',
|
|
59
|
-
'kv',
|
|
60
|
-
'stream',
|
|
61
|
-
'vector',
|
|
62
|
-
'sandbox',
|
|
63
|
-
'queue',
|
|
64
|
-
'email',
|
|
65
|
-
'schedule',
|
|
66
|
-
'task',
|
|
67
|
-
'state',
|
|
68
|
-
'thread',
|
|
69
|
-
'session',
|
|
70
|
-
'config',
|
|
71
|
-
'app',
|
|
72
|
-
] as const;
|
|
73
|
-
|
|
74
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
75
|
-
function installContextPropertyHelpers(c: any): void {
|
|
76
|
-
for (const property of AGENT_CONTEXT_PROPERTIES) {
|
|
77
|
-
if (Object.hasOwn(c, property)) {
|
|
78
|
-
continue;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
Object.defineProperty(c, property, {
|
|
82
|
-
get() {
|
|
83
|
-
throw new Error(
|
|
84
|
-
`In route handlers, use c.var.${property} instead of c.${property}. ` +
|
|
85
|
-
`The property '${property}' is available on AgentContext (for agent handlers) ` +
|
|
86
|
-
`but must be accessed via c.var in HonoContext (route handlers).`
|
|
87
|
-
);
|
|
88
|
-
},
|
|
89
|
-
configurable: true,
|
|
90
|
-
enumerable: false,
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export interface MiddlewareConfig {
|
|
96
|
-
logger: Logger;
|
|
97
|
-
tracer: Tracer;
|
|
98
|
-
meter: Meter;
|
|
99
|
-
corsOptions?: Parameters<typeof cors>[0];
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Create base middleware that sets up context variables
|
|
104
|
-
*/
|
|
105
|
-
export function createBaseMiddleware(config: MiddlewareConfig) {
|
|
106
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
107
|
-
return createMiddleware<Env<any>>(async (c, next) => {
|
|
108
|
-
c.set('logger', config.logger);
|
|
109
|
-
c.set('tracer', config.tracer);
|
|
110
|
-
c.set('meter', config.meter);
|
|
111
|
-
|
|
112
|
-
// Import services dynamically to avoid circular deps
|
|
113
|
-
const { getServices } = await import('./_services');
|
|
114
|
-
const services = getServices();
|
|
115
|
-
c.set('kv', services.kv);
|
|
116
|
-
c.set('stream', services.stream);
|
|
117
|
-
c.set('vector', services.vector);
|
|
118
|
-
c.set('sandbox', services.sandbox);
|
|
119
|
-
c.set('queue', services.queue);
|
|
120
|
-
c.set('email', services.email);
|
|
121
|
-
c.set('schedule', services.schedule);
|
|
122
|
-
c.set('task', services.task);
|
|
123
|
-
|
|
124
|
-
installContextPropertyHelpers(c);
|
|
125
|
-
|
|
126
|
-
const isWebSocket = c.req.header('upgrade')?.toLowerCase() === 'websocket';
|
|
127
|
-
const skipLogging = c.req.path.startsWith('/_agentuity/');
|
|
128
|
-
const started = performance.now();
|
|
129
|
-
|
|
130
|
-
if (!skipLogging) {
|
|
131
|
-
config.logger.debug('%s %s started', c.req.method, c.req.path);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
await runInHTTPContext(c, next);
|
|
135
|
-
|
|
136
|
-
if (!isWebSocket) {
|
|
137
|
-
const endTime = performance.now();
|
|
138
|
-
const duration = ((endTime - started) / 1000).toFixed(1);
|
|
139
|
-
c.header(DURATION_HEADER, `${duration}s`);
|
|
140
|
-
|
|
141
|
-
// Set deployment header for all routes
|
|
142
|
-
const deploymentId = runtimeConfig.getDeploymentId();
|
|
143
|
-
if (deploymentId) {
|
|
144
|
-
c.header(DEPLOYMENT_HEADER, deploymentId);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
if (!skipLogging && !isWebSocket) {
|
|
149
|
-
config.logger.debug(
|
|
150
|
-
'%s %s completed (%d) in %sms',
|
|
151
|
-
c.req.method,
|
|
152
|
-
c.req.path,
|
|
153
|
-
c.res.status,
|
|
154
|
-
Number(performance.now() - started).toFixed(2)
|
|
155
|
-
);
|
|
156
|
-
}
|
|
157
|
-
});
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Create CORS middleware with lazy config resolution.
|
|
162
|
-
*
|
|
163
|
-
* Handles Cross-Origin Resource Sharing (CORS) headers for API routes.
|
|
164
|
-
* Config is resolved at request time, allowing it to be set via createApp().
|
|
165
|
-
* Static options passed here take precedence over app config.
|
|
166
|
-
*
|
|
167
|
-
* Default behavior:
|
|
168
|
-
* - Reflects the request origin (allows any origin)
|
|
169
|
-
* - Allows common headers: Content-Type, Authorization, Accept, Origin, X-Requested-With
|
|
170
|
-
* - Allows all standard HTTP methods
|
|
171
|
-
* - Enables credentials
|
|
172
|
-
* - Sets max-age to 600 seconds (10 minutes)
|
|
173
|
-
*
|
|
174
|
-
* @param staticOptions - Optional static CORS options that override app config
|
|
175
|
-
*
|
|
176
|
-
* @example
|
|
177
|
-
* ```typescript
|
|
178
|
-
* // Use with default settings
|
|
179
|
-
* app.use('/api/*', createCorsMiddleware());
|
|
180
|
-
*
|
|
181
|
-
* // Or configure via createApp
|
|
182
|
-
* const app = await createApp({
|
|
183
|
-
* cors: {
|
|
184
|
-
* origin: 'https://example.com',
|
|
185
|
-
* allowHeaders: ['Content-Type', 'Authorization', 'X-Custom-Header'],
|
|
186
|
-
* maxAge: 3600,
|
|
187
|
-
* }
|
|
188
|
-
* });
|
|
189
|
-
*
|
|
190
|
-
* // Or pass static options directly (overrides app config)
|
|
191
|
-
* app.use('/api/*', createCorsMiddleware({
|
|
192
|
-
* origin: ['https://app.example.com', 'https://admin.example.com'],
|
|
193
|
-
* credentials: true,
|
|
194
|
-
* }));
|
|
195
|
-
* ```
|
|
196
|
-
*/
|
|
197
|
-
export function createCorsMiddleware(staticOptions?: CorsConfig) {
|
|
198
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
199
|
-
return createMiddleware<Env<any>>(async (c, next) => {
|
|
200
|
-
const corsOptions = { ...staticOptions };
|
|
201
|
-
|
|
202
|
-
// Extract Agentuity-specific options
|
|
203
|
-
const { sameOrigin, allowedOrigins, ...honoCorsOptions } = corsOptions;
|
|
204
|
-
|
|
205
|
-
// Determine origin handler based on sameOrigin setting
|
|
206
|
-
let originHandler: NonNullable<Parameters<typeof cors>[0]>['origin'];
|
|
207
|
-
if (sameOrigin) {
|
|
208
|
-
// Use trusted origins (env vars + allowedOrigins + same-origin)
|
|
209
|
-
originHandler = createTrustedCorsOrigin({ allowedOrigins });
|
|
210
|
-
} else if (honoCorsOptions.origin !== undefined) {
|
|
211
|
-
// Use explicitly provided origin
|
|
212
|
-
originHandler = honoCorsOptions.origin;
|
|
213
|
-
} else {
|
|
214
|
-
// Default: reflect any origin (backwards compatible)
|
|
215
|
-
originHandler = (origin: string) => origin;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
// Required headers that must always be allowed/exposed for runtime functionality
|
|
219
|
-
const requiredAllowHeaders = [THREAD_HEADER];
|
|
220
|
-
const requiredExposeHeaders = [
|
|
221
|
-
TOKENS_HEADER,
|
|
222
|
-
DURATION_HEADER,
|
|
223
|
-
THREAD_HEADER,
|
|
224
|
-
SESSION_HEADER,
|
|
225
|
-
DEPLOYMENT_HEADER,
|
|
226
|
-
];
|
|
227
|
-
|
|
228
|
-
// Default headers to allow (used if none specified)
|
|
229
|
-
const defaultAllowHeaders = [
|
|
230
|
-
'Content-Type',
|
|
231
|
-
'Authorization',
|
|
232
|
-
'Accept',
|
|
233
|
-
'Origin',
|
|
234
|
-
'X-Requested-With',
|
|
235
|
-
];
|
|
236
|
-
|
|
237
|
-
// Default headers to expose (used if none specified)
|
|
238
|
-
const defaultExposeHeaders = ['Content-Length'];
|
|
239
|
-
|
|
240
|
-
const finalAllowHeaders = [
|
|
241
|
-
...(honoCorsOptions.allowHeaders ?? defaultAllowHeaders),
|
|
242
|
-
...requiredAllowHeaders,
|
|
243
|
-
];
|
|
244
|
-
|
|
245
|
-
const corsMiddleware = cors({
|
|
246
|
-
...honoCorsOptions,
|
|
247
|
-
origin: originHandler,
|
|
248
|
-
// Always include required headers, merge with user-provided or defaults
|
|
249
|
-
allowHeaders: finalAllowHeaders,
|
|
250
|
-
allowMethods: honoCorsOptions.allowMethods ?? [
|
|
251
|
-
'POST',
|
|
252
|
-
'GET',
|
|
253
|
-
'OPTIONS',
|
|
254
|
-
'HEAD',
|
|
255
|
-
'PUT',
|
|
256
|
-
'DELETE',
|
|
257
|
-
'PATCH',
|
|
258
|
-
],
|
|
259
|
-
// Always include required headers, merge with user-provided or defaults
|
|
260
|
-
exposeHeaders: [
|
|
261
|
-
...(honoCorsOptions.exposeHeaders ?? defaultExposeHeaders),
|
|
262
|
-
...requiredExposeHeaders,
|
|
263
|
-
],
|
|
264
|
-
maxAge: honoCorsOptions.maxAge ?? 600,
|
|
265
|
-
credentials: honoCorsOptions.credentials ?? originHandler !== '*',
|
|
266
|
-
});
|
|
267
|
-
|
|
268
|
-
return corsMiddleware(c, next);
|
|
269
|
-
});
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
/**
|
|
273
|
-
* Create OpenTelemetry middleware for session/thread tracking
|
|
274
|
-
* This is the critical middleware that creates AgentContext
|
|
275
|
-
*/
|
|
276
|
-
export function createOtelMiddleware() {
|
|
277
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
278
|
-
return createMiddleware<Env<any>>(async (c, next) => {
|
|
279
|
-
// Skip thread/session setup entirely for lightweight endpoints
|
|
280
|
-
if (OTEL_FULL_SKIP_PATHS.has(c.req.path)) {
|
|
281
|
-
return next();
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
// Check if we should skip session events (but still set up thread/session)
|
|
285
|
-
const skipSessionEvents = OTEL_SESSION_EVENT_SKIP_PATHS.has(c.req.path);
|
|
286
|
-
|
|
287
|
-
// Import providers dynamically to avoid circular deps
|
|
288
|
-
const { getThreadProvider, getSessionProvider } = await import('./_services');
|
|
289
|
-
const WaitUntilHandler = (await import('./_waituntil')).default;
|
|
290
|
-
|
|
291
|
-
const extractedContext = extractTraceContextFromRequest(c.req.raw);
|
|
292
|
-
const method = c.req.method;
|
|
293
|
-
const url = new URL(c.req.url);
|
|
294
|
-
const threadProvider = getThreadProvider();
|
|
295
|
-
const sessionProvider = getSessionProvider();
|
|
296
|
-
|
|
297
|
-
await context.with(extractedContext, async (): Promise<void> => {
|
|
298
|
-
const tracer = trace.getTracer('http-server');
|
|
299
|
-
|
|
300
|
-
// Build enriched traceState BEFORE span creation so the
|
|
301
|
-
// recording span inherits it and it gets exported to OTLP.
|
|
302
|
-
// Previously, traceState was set on a NonRecordingSpan *after*
|
|
303
|
-
// the recording span was created, which meant it was propagated
|
|
304
|
-
// to outbound requests but never appeared in the exported span
|
|
305
|
-
// data (ClickHouse TraceState column was empty).
|
|
306
|
-
const projectId = runtimeConfig.getProjectId();
|
|
307
|
-
const orgId = runtimeConfig.getOrganizationId();
|
|
308
|
-
const deploymentId = runtimeConfig.getDeploymentId();
|
|
309
|
-
const isDevMode = runtimeConfig.isDevMode();
|
|
310
|
-
|
|
311
|
-
internal.info(
|
|
312
|
-
'[session] config: orgId=%s, projectId=%s, deploymentId=%s, isDevMode=%s',
|
|
313
|
-
orgId ?? 'NOT SET (AGENTUITY_CLOUD_ORG_ID)',
|
|
314
|
-
projectId ?? 'NOT SET (AGENTUITY_CLOUD_PROJECT_ID)',
|
|
315
|
-
deploymentId ?? 'none',
|
|
316
|
-
isDevMode
|
|
317
|
-
);
|
|
318
|
-
|
|
319
|
-
const enrichedContext = enrichContextWithTraceState(context.active(), {
|
|
320
|
-
pid: projectId,
|
|
321
|
-
oid: orgId,
|
|
322
|
-
did: deploymentId,
|
|
323
|
-
d: isDevMode ? '1' : undefined,
|
|
324
|
-
});
|
|
325
|
-
|
|
326
|
-
await tracer.startActiveSpan(
|
|
327
|
-
`${method} ${url.pathname}`,
|
|
328
|
-
{
|
|
329
|
-
kind: SpanKind.SERVER,
|
|
330
|
-
attributes: {
|
|
331
|
-
'http.method': method,
|
|
332
|
-
'http.host': url.host,
|
|
333
|
-
'http.user_agent': c.req.header('user-agent') || '',
|
|
334
|
-
'http.path': url.pathname,
|
|
335
|
-
},
|
|
336
|
-
},
|
|
337
|
-
enrichedContext,
|
|
338
|
-
async (span): Promise<void> => {
|
|
339
|
-
// Track request duration from the SDK's perspective
|
|
340
|
-
const requestStartTime = performance.now();
|
|
341
|
-
const sctx = span.spanContext();
|
|
342
|
-
const sessionId = sctx?.traceId ? `sess_${sctx.traceId}` : generateId('sess');
|
|
343
|
-
|
|
344
|
-
const thread = await threadProvider.restore(c);
|
|
345
|
-
const session = await sessionProvider.restore(thread, sessionId);
|
|
346
|
-
const handler = new WaitUntilHandler(tracer);
|
|
347
|
-
const isWsUpgrade = c.req.header('upgrade')?.toLowerCase() === 'websocket';
|
|
348
|
-
|
|
349
|
-
c.set('sessionId', sessionId);
|
|
350
|
-
c.set('thread', thread);
|
|
351
|
-
c.set('session', session);
|
|
352
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
353
|
-
(c as any).set('waitUntilHandler', handler);
|
|
354
|
-
const agentIds = new Set<string>();
|
|
355
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
356
|
-
(c as any).set('agentIds', agentIds);
|
|
357
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
358
|
-
(c as any).set('trigger', isWsUpgrade ? 'websocket' : 'api');
|
|
359
|
-
|
|
360
|
-
// Send session start event (so evalruns can reference this session)
|
|
361
|
-
// The provider decides whether to send based on available data (orgId, projectId, etc.)
|
|
362
|
-
// Skip for workbench routes that don't need session tracking
|
|
363
|
-
const sessionEventProvider = getSessionEventProvider();
|
|
364
|
-
if (sessionEventProvider && !skipSessionEvents) {
|
|
365
|
-
try {
|
|
366
|
-
// Look up routeId from build metadata by matching method and path
|
|
367
|
-
// We need to do this here because the router wrapper hasn't run yet
|
|
368
|
-
const metadata = loadBuildMetadata();
|
|
369
|
-
const methodUpper = c.req.method.toUpperCase();
|
|
370
|
-
|
|
371
|
-
// Normalize paths: trim trailing slashes for consistent matching
|
|
372
|
-
const normalizePath = (p: string) => {
|
|
373
|
-
const decoded = decodeURIComponent(p);
|
|
374
|
-
return decoded.endsWith('/') && decoded.length > 1
|
|
375
|
-
? decoded.slice(0, -1)
|
|
376
|
-
: decoded;
|
|
377
|
-
};
|
|
378
|
-
const requestPath = normalizePath(c.req.path);
|
|
379
|
-
|
|
380
|
-
// Helper to check if requestPath ends with routePath at a segment boundary
|
|
381
|
-
// e.g., "/api/translate" matches "/translate" but "/api/translate-v2" does not
|
|
382
|
-
const matchesAtSegmentBoundary = (reqPath: string, routePath: string) => {
|
|
383
|
-
if (reqPath === routePath) return true;
|
|
384
|
-
if (!reqPath.endsWith(routePath)) return false;
|
|
385
|
-
// Check that the character before the match is a path separator
|
|
386
|
-
const charBeforeMatch = reqPath[reqPath.length - routePath.length - 1];
|
|
387
|
-
return charBeforeMatch === '/';
|
|
388
|
-
};
|
|
389
|
-
|
|
390
|
-
// Try matching by exact normalized path first
|
|
391
|
-
let route = metadata?.routes?.find(
|
|
392
|
-
(r) =>
|
|
393
|
-
r.method.toUpperCase() === methodUpper &&
|
|
394
|
-
normalizePath(r.path) === requestPath
|
|
395
|
-
);
|
|
396
|
-
// Fall back to segment-boundary matching (handles /api/translate matching /translate)
|
|
397
|
-
if (!route) {
|
|
398
|
-
route = metadata?.routes?.find(
|
|
399
|
-
(r) =>
|
|
400
|
-
r.method.toUpperCase() === methodUpper &&
|
|
401
|
-
matchesAtSegmentBoundary(requestPath, normalizePath(r.path))
|
|
402
|
-
);
|
|
403
|
-
}
|
|
404
|
-
const routeId = route?.id || '';
|
|
405
|
-
|
|
406
|
-
await sessionEventProvider.start({
|
|
407
|
-
id: sessionId,
|
|
408
|
-
threadId: thread.id,
|
|
409
|
-
orgId: orgId || '',
|
|
410
|
-
projectId: projectId || '',
|
|
411
|
-
deploymentId: deploymentId || undefined,
|
|
412
|
-
devmode: isDevMode,
|
|
413
|
-
trigger: isWsUpgrade ? 'websocket' : 'api',
|
|
414
|
-
routeId,
|
|
415
|
-
environment: runtimeConfig.getEnvironment(),
|
|
416
|
-
url: c.req.path,
|
|
417
|
-
method: c.req.method,
|
|
418
|
-
});
|
|
419
|
-
} catch (_ex) {
|
|
420
|
-
// Silently ignore session start errors - don't block request
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
// Factor out finalization logic so it can run synchronously or deferred
|
|
425
|
-
const finalizeSession = async (statusCode?: number, error?: string) => {
|
|
426
|
-
internal.info(
|
|
427
|
-
'[session] saving session %s (thread: %s) (error: %s)',
|
|
428
|
-
sessionId,
|
|
429
|
-
thread.id,
|
|
430
|
-
error
|
|
431
|
-
);
|
|
432
|
-
await sessionProvider.save(session);
|
|
433
|
-
internal.info('[session] session saved, now saving thread');
|
|
434
|
-
await threadProvider.save(thread);
|
|
435
|
-
internal.info('[session] thread saved');
|
|
436
|
-
|
|
437
|
-
// Send session complete event (skip for workbench routes)
|
|
438
|
-
if (sessionEventProvider && !skipSessionEvents) {
|
|
439
|
-
try {
|
|
440
|
-
const userData = session.serializeUserData();
|
|
441
|
-
internal.info(
|
|
442
|
-
'[session] sending session complete event, userData: %s',
|
|
443
|
-
userData ? `${userData.length} bytes` : 'none'
|
|
444
|
-
);
|
|
445
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
446
|
-
const agentIdsSet = (c as any).get('agentIds') as Set<string> | undefined;
|
|
447
|
-
const agentIds = agentIdsSet ? [...agentIdsSet].filter(Boolean) : undefined;
|
|
448
|
-
internal.info('[session] agentIds: %o', agentIds);
|
|
449
|
-
const isEmpty = await thread.empty();
|
|
450
|
-
await sessionEventProvider.complete({
|
|
451
|
-
id: sessionId,
|
|
452
|
-
threadId: isEmpty ? null : thread.id,
|
|
453
|
-
statusCode: statusCode ?? c.res?.status ?? 200,
|
|
454
|
-
error,
|
|
455
|
-
agentIds: agentIds?.length ? agentIds : undefined,
|
|
456
|
-
userData,
|
|
457
|
-
});
|
|
458
|
-
internal.info('[session] session complete event sent');
|
|
459
|
-
} catch (ex) {
|
|
460
|
-
internal.info(
|
|
461
|
-
'[session] session complete event failed: %s',
|
|
462
|
-
ex instanceof Error ? ex.message : ex
|
|
463
|
-
);
|
|
464
|
-
// Silently ignore session complete errors - don't block response
|
|
465
|
-
}
|
|
466
|
-
}
|
|
467
|
-
};
|
|
468
|
-
|
|
469
|
-
// Track state for finalization
|
|
470
|
-
let responseStatus = 200;
|
|
471
|
-
let errorMessage: string | undefined;
|
|
472
|
-
let handlerDurationMs = 0;
|
|
473
|
-
// Track whether span should be ended in finally block (false for streaming - ended in waitUntil)
|
|
474
|
-
let shouldEndSpanInFinally = true;
|
|
475
|
-
|
|
476
|
-
try {
|
|
477
|
-
internal.info(
|
|
478
|
-
'[request] %s %s - handler starting (session: %s)',
|
|
479
|
-
method,
|
|
480
|
-
url.pathname,
|
|
481
|
-
sessionId
|
|
482
|
-
);
|
|
483
|
-
|
|
484
|
-
await next();
|
|
485
|
-
|
|
486
|
-
// Capture timing immediately after next() returns - this is when the handler completed
|
|
487
|
-
// This is the HTTP response time we want to report (excludes waitUntil/finalization)
|
|
488
|
-
handlerDurationMs = performance.now() - requestStartTime;
|
|
489
|
-
|
|
490
|
-
internal.info(
|
|
491
|
-
'[request] %s %s - handler completed in %sms (session: %s)',
|
|
492
|
-
method,
|
|
493
|
-
url.pathname,
|
|
494
|
-
handlerDurationMs.toFixed(2),
|
|
495
|
-
sessionId
|
|
496
|
-
);
|
|
497
|
-
|
|
498
|
-
// Check if this is a streaming response that needs deferred finalization
|
|
499
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
500
|
-
const streamDone = (c as any).get(STREAM_DONE_PROMISE_KEY) as
|
|
501
|
-
| Promise<void>
|
|
502
|
-
| undefined;
|
|
503
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
504
|
-
const isStreaming = Boolean((c as any).get(IS_STREAMING_RESPONSE_KEY));
|
|
505
|
-
|
|
506
|
-
// Check if this is a WebSocket response that needs deferred finalization
|
|
507
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
508
|
-
const wsDone = (c as any).get(WS_DONE_PROMISE_KEY) as Promise<void> | undefined;
|
|
509
|
-
|
|
510
|
-
// Check if Hono caught an error (c.error is set by Hono's error handler)
|
|
511
|
-
// or if the response status indicates an error
|
|
512
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
513
|
-
const honoError = (c as any).error as Error | undefined;
|
|
514
|
-
responseStatus = c.res?.status ?? 200;
|
|
515
|
-
const isError = honoError || responseStatus >= 500;
|
|
516
|
-
|
|
517
|
-
internal.info(
|
|
518
|
-
'[request] %s %s - status: %d, streaming: %s, websocket: %s, error: %s (session: %s)',
|
|
519
|
-
method,
|
|
520
|
-
url.pathname,
|
|
521
|
-
responseStatus,
|
|
522
|
-
isStreaming,
|
|
523
|
-
Boolean(wsDone),
|
|
524
|
-
isError,
|
|
525
|
-
sessionId
|
|
526
|
-
);
|
|
527
|
-
|
|
528
|
-
if (isError) {
|
|
529
|
-
// Capture error message for finalization
|
|
530
|
-
errorMessage = honoError
|
|
531
|
-
? (honoError.stack ?? honoError.message)
|
|
532
|
-
: `HTTP ${responseStatus}`;
|
|
533
|
-
span.setStatus({
|
|
534
|
-
code: SpanStatusCode.ERROR,
|
|
535
|
-
message: honoError?.message ?? errorMessage,
|
|
536
|
-
});
|
|
537
|
-
if (honoError) {
|
|
538
|
-
span.recordException(honoError);
|
|
539
|
-
}
|
|
540
|
-
} else {
|
|
541
|
-
span.setStatus({ code: SpanStatusCode.OK });
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
// For streaming responses, defer everything until stream completes
|
|
545
|
-
if (isStreaming && streamDone) {
|
|
546
|
-
internal.info(
|
|
547
|
-
'[request] %s %s - streaming response, deferring finalization (session: %s)',
|
|
548
|
-
method,
|
|
549
|
-
url.pathname,
|
|
550
|
-
sessionId
|
|
551
|
-
);
|
|
552
|
-
|
|
553
|
-
// For streaming, we end the span inside waitUntil after setting attributes
|
|
554
|
-
shouldEndSpanInFinally = false;
|
|
555
|
-
|
|
556
|
-
// Capture pending promises BEFORE adding finalization waitUntil to avoid deadlock
|
|
557
|
-
const pendingPromises = handler.getPendingSnapshot();
|
|
558
|
-
const hasPendingTasks = pendingPromises.length > 0;
|
|
559
|
-
|
|
560
|
-
if (hasPendingTasks) {
|
|
561
|
-
internal.info(
|
|
562
|
-
'[request] %s %s - %d pending waitUntil tasks to wait for after stream (session: %s)',
|
|
563
|
-
method,
|
|
564
|
-
url.pathname,
|
|
565
|
-
pendingPromises.length,
|
|
566
|
-
sessionId
|
|
567
|
-
);
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
// Capture values needed for span attributes (responseStatus already captured above)
|
|
571
|
-
const capturedResponseStatus = responseStatus;
|
|
572
|
-
const capturedErrorMessage = errorMessage;
|
|
573
|
-
|
|
574
|
-
// Use waitUntil to handle stream completion and finalization
|
|
575
|
-
// This runs AFTER the response is sent to the client
|
|
576
|
-
// Note: We intentionally do NOT use noSpan here - the waitUntil span helps
|
|
577
|
-
// track the streaming finalization work in telemetry
|
|
578
|
-
handler.waitUntil(async () => {
|
|
579
|
-
// Track if stream ended with error so we can update finalization status
|
|
580
|
-
let streamError: unknown = undefined;
|
|
581
|
-
|
|
582
|
-
try {
|
|
583
|
-
await streamDone;
|
|
584
|
-
internal.info(
|
|
585
|
-
'[request] %s %s - stream completed (session: %s)',
|
|
586
|
-
method,
|
|
587
|
-
url.pathname,
|
|
588
|
-
sessionId
|
|
589
|
-
);
|
|
590
|
-
} catch (ex) {
|
|
591
|
-
streamError = ex;
|
|
592
|
-
internal.info(
|
|
593
|
-
'[request] %s %s - stream ended with error: %s (session: %s)',
|
|
594
|
-
method,
|
|
595
|
-
url.pathname,
|
|
596
|
-
ex,
|
|
597
|
-
sessionId
|
|
598
|
-
);
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
// Record duration now that stream is complete - set attributes BEFORE ending span
|
|
602
|
-
const streamDurationMs = performance.now() - requestStartTime;
|
|
603
|
-
const durationNs = Math.round(streamDurationMs * 1_000_000);
|
|
604
|
-
internal.info(
|
|
605
|
-
'[request] %s %s - recording stream duration: %sms (session: %s)',
|
|
606
|
-
method,
|
|
607
|
-
url.pathname,
|
|
608
|
-
streamDurationMs.toFixed(2),
|
|
609
|
-
sessionId
|
|
610
|
-
);
|
|
611
|
-
|
|
612
|
-
// Determine final status - use stream error if present
|
|
613
|
-
const finalStatus = streamError ? 500 : capturedResponseStatus;
|
|
614
|
-
const finalErrorMessage = streamError
|
|
615
|
-
? streamError instanceof Error
|
|
616
|
-
? (streamError.stack ?? streamError.message)
|
|
617
|
-
: String(streamError)
|
|
618
|
-
: capturedErrorMessage;
|
|
619
|
-
|
|
620
|
-
try {
|
|
621
|
-
// Wait for pending tasks (evals, etc.) captured BEFORE this waitUntil was added
|
|
622
|
-
if (hasPendingTasks) {
|
|
623
|
-
internal.info(
|
|
624
|
-
'[request] %s %s - waiting for %d pending waitUntil tasks (session: %s)',
|
|
625
|
-
method,
|
|
626
|
-
url.pathname,
|
|
627
|
-
pendingPromises.length,
|
|
628
|
-
sessionId
|
|
629
|
-
);
|
|
630
|
-
const logger = c.get('logger');
|
|
631
|
-
await handler.waitForPromises(pendingPromises, logger, sessionId);
|
|
632
|
-
internal.info(
|
|
633
|
-
'[request] %s %s - all waitUntil tasks complete (session: %s)',
|
|
634
|
-
method,
|
|
635
|
-
url.pathname,
|
|
636
|
-
sessionId
|
|
637
|
-
);
|
|
638
|
-
}
|
|
639
|
-
|
|
640
|
-
// Finalize session after stream completes and evals finish
|
|
641
|
-
await finalizeSession(
|
|
642
|
-
finalStatus >= 500 ? finalStatus : undefined,
|
|
643
|
-
finalErrorMessage
|
|
644
|
-
);
|
|
645
|
-
internal.info(
|
|
646
|
-
'[request] %s %s - stream session finalization complete (session: %s)',
|
|
647
|
-
method,
|
|
648
|
-
url.pathname,
|
|
649
|
-
sessionId
|
|
650
|
-
);
|
|
651
|
-
} finally {
|
|
652
|
-
// Set span attributes and end span AFTER all work is done
|
|
653
|
-
span.setAttribute('@agentuity/request.duration', durationNs);
|
|
654
|
-
span.setAttribute('http.status_code', finalStatus);
|
|
655
|
-
|
|
656
|
-
// Set span status based on whether there was an error
|
|
657
|
-
if (streamError) {
|
|
658
|
-
span.setStatus({
|
|
659
|
-
code: SpanStatusCode.ERROR,
|
|
660
|
-
message: finalErrorMessage ?? 'Stream ended with error',
|
|
661
|
-
});
|
|
662
|
-
if (streamError instanceof Error) {
|
|
663
|
-
span.recordException(streamError);
|
|
664
|
-
}
|
|
665
|
-
} else {
|
|
666
|
-
span.setStatus({ code: SpanStatusCode.OK });
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
span.end();
|
|
670
|
-
internal.info(
|
|
671
|
-
'[request] %s %s - stream span ended (session: %s)',
|
|
672
|
-
method,
|
|
673
|
-
url.pathname,
|
|
674
|
-
sessionId
|
|
675
|
-
);
|
|
676
|
-
// Note: We don't call waitUntilAll() here because this waitUntil callback
|
|
677
|
-
// IS the final cleanup task. Calling waitUntilAll() would deadlock since
|
|
678
|
-
// it would wait for this very promise to complete.
|
|
679
|
-
}
|
|
680
|
-
});
|
|
681
|
-
} else if (wsDone) {
|
|
682
|
-
// WebSocket upgrade — end the span immediately (short-lived upgrade span)
|
|
683
|
-
// Per OTel conventions, spans should be short-lived. The HTTP upgrade
|
|
684
|
-
// request is a discrete event that completes in milliseconds. Keeping
|
|
685
|
-
// a span open for the entire WS connection lifetime (minutes/hours) is
|
|
686
|
-
// non-standard and causes issues with OTel backends.
|
|
687
|
-
internal.info(
|
|
688
|
-
'[request] %s %s - websocket upgrade, ending span immediately (session: %s)',
|
|
689
|
-
method,
|
|
690
|
-
url.pathname,
|
|
691
|
-
sessionId
|
|
692
|
-
);
|
|
693
|
-
|
|
694
|
-
// End the upgrade span now with the upgrade duration
|
|
695
|
-
const upgradeDurationNs = Math.round(handlerDurationMs * 1_000_000);
|
|
696
|
-
span.setAttribute('@agentuity/request.duration', upgradeDurationNs);
|
|
697
|
-
span.setAttribute('http.status_code', responseStatus);
|
|
698
|
-
span.setStatus({ code: SpanStatusCode.OK });
|
|
699
|
-
span.end();
|
|
700
|
-
shouldEndSpanInFinally = false; // already ended
|
|
701
|
-
|
|
702
|
-
// Session finalization still defers until WS close, but without holding a span
|
|
703
|
-
const pendingPromises = handler.getPendingSnapshot();
|
|
704
|
-
const hasPendingTasks = pendingPromises.length > 0;
|
|
705
|
-
|
|
706
|
-
if (hasPendingTasks) {
|
|
707
|
-
internal.info(
|
|
708
|
-
'[request] %s %s - %d pending waitUntil tasks to wait for after websocket close (session: %s)',
|
|
709
|
-
method,
|
|
710
|
-
url.pathname,
|
|
711
|
-
pendingPromises.length,
|
|
712
|
-
sessionId
|
|
713
|
-
);
|
|
714
|
-
}
|
|
715
|
-
|
|
716
|
-
handler.waitUntil(async () => {
|
|
717
|
-
let wsError: unknown = undefined;
|
|
718
|
-
|
|
719
|
-
try {
|
|
720
|
-
await wsDone;
|
|
721
|
-
internal.info(
|
|
722
|
-
'[request] %s %s - websocket closed (session: %s)',
|
|
723
|
-
method,
|
|
724
|
-
url.pathname,
|
|
725
|
-
sessionId
|
|
726
|
-
);
|
|
727
|
-
} catch (ex) {
|
|
728
|
-
wsError = ex;
|
|
729
|
-
internal.info(
|
|
730
|
-
'[request] %s %s - websocket closed with error: %s (session: %s)',
|
|
731
|
-
method,
|
|
732
|
-
url.pathname,
|
|
733
|
-
ex,
|
|
734
|
-
sessionId
|
|
735
|
-
);
|
|
736
|
-
}
|
|
737
|
-
|
|
738
|
-
const finalStatus = wsError ? 500 : responseStatus;
|
|
739
|
-
const finalErrorMessage = wsError
|
|
740
|
-
? wsError instanceof Error
|
|
741
|
-
? (wsError.stack ?? wsError.message)
|
|
742
|
-
: String(wsError)
|
|
743
|
-
: errorMessage;
|
|
744
|
-
|
|
745
|
-
// Wait for pending tasks captured BEFORE this waitUntil was added
|
|
746
|
-
if (hasPendingTasks) {
|
|
747
|
-
internal.info(
|
|
748
|
-
'[request] %s %s - waiting for %d pending waitUntil tasks (session: %s)',
|
|
749
|
-
method,
|
|
750
|
-
url.pathname,
|
|
751
|
-
pendingPromises.length,
|
|
752
|
-
sessionId
|
|
753
|
-
);
|
|
754
|
-
const logger = c.get('logger');
|
|
755
|
-
await handler.waitForPromises(pendingPromises, logger, sessionId);
|
|
756
|
-
internal.info(
|
|
757
|
-
'[request] %s %s - all waitUntil tasks complete (session: %s)',
|
|
758
|
-
method,
|
|
759
|
-
url.pathname,
|
|
760
|
-
sessionId
|
|
761
|
-
);
|
|
762
|
-
}
|
|
763
|
-
|
|
764
|
-
// Finalize session after WebSocket closes
|
|
765
|
-
await finalizeSession(
|
|
766
|
-
finalStatus >= 500 ? finalStatus : undefined,
|
|
767
|
-
finalErrorMessage
|
|
768
|
-
);
|
|
769
|
-
internal.info(
|
|
770
|
-
'[request] %s %s - websocket session finalization complete (session: %s)',
|
|
771
|
-
method,
|
|
772
|
-
url.pathname,
|
|
773
|
-
sessionId
|
|
774
|
-
);
|
|
775
|
-
});
|
|
776
|
-
} else {
|
|
777
|
-
// Non-streaming: record duration immediately
|
|
778
|
-
const durationNs = Math.round(handlerDurationMs * 1_000_000);
|
|
779
|
-
internal.info(
|
|
780
|
-
'[request] %s %s - recording duration: %sms (%dns) (session: %s)',
|
|
781
|
-
method,
|
|
782
|
-
url.pathname,
|
|
783
|
-
handlerDurationMs.toFixed(2),
|
|
784
|
-
durationNs,
|
|
785
|
-
sessionId
|
|
786
|
-
);
|
|
787
|
-
span.setAttribute('@agentuity/request.duration', durationNs);
|
|
788
|
-
span.setAttribute('http.status_code', responseStatus);
|
|
789
|
-
|
|
790
|
-
// Capture pending promises BEFORE adding finalization waitUntil to avoid deadlock.
|
|
791
|
-
// If we called waitUntilAll inside waitUntil, it would wait for itself.
|
|
792
|
-
const pendingPromises = handler.getPendingSnapshot();
|
|
793
|
-
const hasPendingTasks = pendingPromises.length > 0;
|
|
794
|
-
|
|
795
|
-
if (hasPendingTasks) {
|
|
796
|
-
internal.info(
|
|
797
|
-
'[request] %s %s - %d pending waitUntil tasks to wait for (session: %s)',
|
|
798
|
-
method,
|
|
799
|
-
url.pathname,
|
|
800
|
-
pendingPromises.length,
|
|
801
|
-
sessionId
|
|
802
|
-
);
|
|
803
|
-
}
|
|
804
|
-
|
|
805
|
-
// Capture values for use in waitUntil callback
|
|
806
|
-
const capturedResponseStatus = responseStatus;
|
|
807
|
-
const capturedErrorMessage = errorMessage;
|
|
808
|
-
|
|
809
|
-
// Defer session finalization to run AFTER response is sent
|
|
810
|
-
// Use noSpan: true since finalizeSession creates its own Session End span
|
|
811
|
-
handler.waitUntil(
|
|
812
|
-
async () => {
|
|
813
|
-
// Wait for the snapshot of pending tasks (evals, etc.) captured BEFORE this waitUntil was added
|
|
814
|
-
if (hasPendingTasks) {
|
|
815
|
-
internal.info(
|
|
816
|
-
'[request] %s %s - waiting for %d pending waitUntil tasks (session: %s)',
|
|
817
|
-
method,
|
|
818
|
-
url.pathname,
|
|
819
|
-
pendingPromises.length,
|
|
820
|
-
sessionId
|
|
821
|
-
);
|
|
822
|
-
const logger = c.get('logger');
|
|
823
|
-
await handler.waitForPromises(pendingPromises, logger, sessionId);
|
|
824
|
-
internal.info(
|
|
825
|
-
'[request] %s %s - all waitUntil tasks complete (session: %s)',
|
|
826
|
-
method,
|
|
827
|
-
url.pathname,
|
|
828
|
-
sessionId
|
|
829
|
-
);
|
|
830
|
-
}
|
|
831
|
-
|
|
832
|
-
// Finalize session - this is the actual work
|
|
833
|
-
internal.info(
|
|
834
|
-
'[request] %s %s - starting session finalization (session: %s)',
|
|
835
|
-
method,
|
|
836
|
-
url.pathname,
|
|
837
|
-
sessionId
|
|
838
|
-
);
|
|
839
|
-
try {
|
|
840
|
-
await finalizeSession(
|
|
841
|
-
capturedResponseStatus >= 500 ? capturedResponseStatus : undefined,
|
|
842
|
-
capturedErrorMessage
|
|
843
|
-
);
|
|
844
|
-
internal.info(
|
|
845
|
-
'[request] %s %s - session finalization complete (session: %s)',
|
|
846
|
-
method,
|
|
847
|
-
url.pathname,
|
|
848
|
-
sessionId
|
|
849
|
-
);
|
|
850
|
-
} catch (ex) {
|
|
851
|
-
internal.error(
|
|
852
|
-
'[request] %s %s - session finalization failed: %s (session: %s)',
|
|
853
|
-
method,
|
|
854
|
-
url.pathname,
|
|
855
|
-
ex,
|
|
856
|
-
sessionId
|
|
857
|
-
);
|
|
858
|
-
}
|
|
859
|
-
// Note: We don't call waitUntilAll() here because this waitUntil callback
|
|
860
|
-
// IS the final cleanup task. Calling waitUntilAll() would deadlock since
|
|
861
|
-
// it would wait for this very promise to complete.
|
|
862
|
-
},
|
|
863
|
-
{ noSpan: true }
|
|
864
|
-
);
|
|
865
|
-
}
|
|
866
|
-
} catch (ex) {
|
|
867
|
-
// Record request metrics even on exceptions (500 status)
|
|
868
|
-
const exceptionDurationMs = performance.now() - requestStartTime;
|
|
869
|
-
const durationNs = Math.round(exceptionDurationMs * 1_000_000);
|
|
870
|
-
internal.info(
|
|
871
|
-
'[request] %s %s - recording exception duration: %sms (session: %s)',
|
|
872
|
-
method,
|
|
873
|
-
url.pathname,
|
|
874
|
-
exceptionDurationMs.toFixed(2),
|
|
875
|
-
sessionId
|
|
876
|
-
);
|
|
877
|
-
span.setAttribute('@agentuity/request.duration', durationNs);
|
|
878
|
-
span.setAttribute('http.status_code', 500);
|
|
879
|
-
|
|
880
|
-
if (ex instanceof Error) {
|
|
881
|
-
span.recordException(ex);
|
|
882
|
-
}
|
|
883
|
-
errorMessage = ex instanceof Error ? (ex.stack ?? ex.message) : String(ex);
|
|
884
|
-
responseStatus = 500;
|
|
885
|
-
span.setStatus({
|
|
886
|
-
code: SpanStatusCode.ERROR,
|
|
887
|
-
message: ex instanceof Error ? ex.message : String(ex),
|
|
888
|
-
});
|
|
889
|
-
|
|
890
|
-
// Capture error message for use in waitUntil callback
|
|
891
|
-
const capturedErrorMessage = errorMessage;
|
|
892
|
-
|
|
893
|
-
// Capture pending promises BEFORE adding finalization waitUntil to avoid deadlock
|
|
894
|
-
const pendingPromises = handler.getPendingSnapshot();
|
|
895
|
-
const hasPendingTasks = pendingPromises.length > 0;
|
|
896
|
-
|
|
897
|
-
if (hasPendingTasks) {
|
|
898
|
-
internal.info(
|
|
899
|
-
'[request] %s %s - %d pending waitUntil tasks to wait for after error (session: %s)',
|
|
900
|
-
method,
|
|
901
|
-
url.pathname,
|
|
902
|
-
pendingPromises.length,
|
|
903
|
-
sessionId
|
|
904
|
-
);
|
|
905
|
-
}
|
|
906
|
-
|
|
907
|
-
// Still defer finalization even on error
|
|
908
|
-
// Use noSpan: true since finalizeSession creates its own Session End span
|
|
909
|
-
handler.waitUntil(
|
|
910
|
-
async () => {
|
|
911
|
-
// Wait for pending tasks (evals, etc.) captured BEFORE this waitUntil was added
|
|
912
|
-
if (hasPendingTasks) {
|
|
913
|
-
internal.info(
|
|
914
|
-
'[request] %s %s - waiting for %d pending waitUntil tasks (session: %s)',
|
|
915
|
-
method,
|
|
916
|
-
url.pathname,
|
|
917
|
-
pendingPromises.length,
|
|
918
|
-
sessionId
|
|
919
|
-
);
|
|
920
|
-
const logger = c.get('logger');
|
|
921
|
-
await handler.waitForPromises(pendingPromises, logger, sessionId);
|
|
922
|
-
internal.info(
|
|
923
|
-
'[request] %s %s - all waitUntil tasks complete (session: %s)',
|
|
924
|
-
method,
|
|
925
|
-
url.pathname,
|
|
926
|
-
sessionId
|
|
927
|
-
);
|
|
928
|
-
}
|
|
929
|
-
|
|
930
|
-
try {
|
|
931
|
-
await finalizeSession(500, capturedErrorMessage);
|
|
932
|
-
} catch (finalizeEx) {
|
|
933
|
-
internal.error(
|
|
934
|
-
'[request] %s %s - error session finalization failed: %s (session: %s)',
|
|
935
|
-
method,
|
|
936
|
-
url.pathname,
|
|
937
|
-
finalizeEx,
|
|
938
|
-
sessionId
|
|
939
|
-
);
|
|
940
|
-
}
|
|
941
|
-
// Note: We don't call waitUntilAll() here because this waitUntil callback
|
|
942
|
-
// IS the final cleanup task. Calling waitUntilAll() would deadlock since
|
|
943
|
-
// it would wait for this very promise to complete.
|
|
944
|
-
},
|
|
945
|
-
{ noSpan: true }
|
|
946
|
-
);
|
|
947
|
-
|
|
948
|
-
throw ex;
|
|
949
|
-
} finally {
|
|
950
|
-
// Set response headers - this is the only thing that should block the response
|
|
951
|
-
const headers: Record<string, string> = {};
|
|
952
|
-
propagation.inject(context.active(), headers);
|
|
953
|
-
for (const key of Object.keys(headers)) {
|
|
954
|
-
c.header(key, headers[key]);
|
|
955
|
-
}
|
|
956
|
-
const traceId = sctx?.traceId || sessionId.replace(/^sess_/, '');
|
|
957
|
-
c.header(SESSION_HEADER, `sess_${traceId}`);
|
|
958
|
-
|
|
959
|
-
internal.info(
|
|
960
|
-
'[request] %s %s - response ready, duration: %sms (session: %s)',
|
|
961
|
-
method,
|
|
962
|
-
url.pathname,
|
|
963
|
-
handlerDurationMs.toFixed(2),
|
|
964
|
-
sessionId
|
|
965
|
-
);
|
|
966
|
-
|
|
967
|
-
// Only end span here for non-streaming responses
|
|
968
|
-
// For streaming, span is ended in the waitUntil callback after setting duration attributes
|
|
969
|
-
if (shouldEndSpanInFinally) {
|
|
970
|
-
span.end();
|
|
971
|
-
}
|
|
972
|
-
}
|
|
973
|
-
}
|
|
974
|
-
);
|
|
975
|
-
});
|
|
976
|
-
});
|
|
977
|
-
}
|
|
978
|
-
|
|
979
|
-
/**
|
|
980
|
-
* Create compression middleware with lazy config resolution.
|
|
981
|
-
*
|
|
982
|
-
* Compresses response bodies using gzip or deflate based on the Accept-Encoding header.
|
|
983
|
-
* Config is resolved at request time, allowing it to be set via createApp().
|
|
984
|
-
*
|
|
985
|
-
* @param staticConfig - Optional static config that overrides app config
|
|
986
|
-
*
|
|
987
|
-
* @example
|
|
988
|
-
* ```typescript
|
|
989
|
-
* // Use with default settings
|
|
990
|
-
* app.use('*', createCompressionMiddleware());
|
|
991
|
-
*
|
|
992
|
-
* // Or configure via createApp
|
|
993
|
-
* const app = await createApp({
|
|
994
|
-
* compression: {
|
|
995
|
-
* threshold: 2048,
|
|
996
|
-
* }
|
|
997
|
-
* });
|
|
998
|
-
* ```
|
|
999
|
-
*/
|
|
1000
|
-
export function createCompressionMiddleware(staticConfig?: CompressionConfig | false) {
|
|
1001
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1002
|
-
return createMiddleware<Env<any>>(async (c, next) => {
|
|
1003
|
-
// Check if compression is explicitly disabled
|
|
1004
|
-
if (staticConfig === false || staticConfig?.enabled === false) {
|
|
1005
|
-
return next();
|
|
1006
|
-
}
|
|
1007
|
-
|
|
1008
|
-
const config: CompressionConfig = { ...staticConfig };
|
|
1009
|
-
|
|
1010
|
-
const { enabled = true, threshold = 1024, filter, honoOptions } = config;
|
|
1011
|
-
|
|
1012
|
-
// Skip if explicitly disabled
|
|
1013
|
-
if (!enabled) {
|
|
1014
|
-
return next();
|
|
1015
|
-
}
|
|
1016
|
-
|
|
1017
|
-
// Skip WebSocket upgrade requests
|
|
1018
|
-
const upgrade = c.req.header('upgrade');
|
|
1019
|
-
if (upgrade && upgrade.toLowerCase() === 'websocket') {
|
|
1020
|
-
return next();
|
|
1021
|
-
}
|
|
1022
|
-
|
|
1023
|
-
// Skip if no Accept-Encoding header
|
|
1024
|
-
const acceptEncoding = c.req.header('accept-encoding');
|
|
1025
|
-
if (!acceptEncoding) {
|
|
1026
|
-
return next();
|
|
1027
|
-
}
|
|
1028
|
-
|
|
1029
|
-
// Check custom filter
|
|
1030
|
-
if (filter && !filter(c)) {
|
|
1031
|
-
return next();
|
|
1032
|
-
}
|
|
1033
|
-
|
|
1034
|
-
// Create and run the Hono compress middleware
|
|
1035
|
-
const compressMiddleware = compress({
|
|
1036
|
-
threshold,
|
|
1037
|
-
...honoOptions,
|
|
1038
|
-
});
|
|
1039
|
-
|
|
1040
|
-
await compressMiddleware(c, next);
|
|
1041
|
-
});
|
|
1042
|
-
}
|
|
1043
|
-
|
|
1044
|
-
/**
|
|
1045
|
-
* Create lightweight thread middleware for web routes (analytics).
|
|
1046
|
-
*
|
|
1047
|
-
* Sets thread cookie that persists across page views for client-side analytics.
|
|
1048
|
-
* This middleware does NOT:
|
|
1049
|
-
* - Create or track sessions (no session ID)
|
|
1050
|
-
* - Set session/thread response headers
|
|
1051
|
-
* - Send events to Catalyst sessions table
|
|
1052
|
-
*
|
|
1053
|
-
* This is intentionally separate from createOtelMiddleware to avoid
|
|
1054
|
-
* polluting the sessions table with web browsing activity.
|
|
1055
|
-
*
|
|
1056
|
-
* - Thread cookie (atid_a): Analytics-readable copy, 1-week expiry
|
|
1057
|
-
*/
|
|
1058
|
-
export function createWebSessionMiddleware() {
|
|
1059
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1060
|
-
return createMiddleware<Env<any>>(async (c, next) => {
|
|
1061
|
-
// Import providers dynamically to avoid circular deps
|
|
1062
|
-
const { getThreadProvider } = await import('./_services');
|
|
1063
|
-
|
|
1064
|
-
const secret = getSessionSecret();
|
|
1065
|
-
|
|
1066
|
-
// Use ThreadProvider.restore() to get/create thread (handles header, cookie, generation)
|
|
1067
|
-
const threadProvider = getThreadProvider();
|
|
1068
|
-
const thread = await threadProvider.restore(c);
|
|
1069
|
-
|
|
1070
|
-
// Set thread cookie for analytics
|
|
1071
|
-
// httpOnly: false so beacon script can read it
|
|
1072
|
-
const isSecure = c.req.url.startsWith('https://');
|
|
1073
|
-
await setSignedCookie(c, 'atid_a', thread.id, secret, {
|
|
1074
|
-
httpOnly: false, // Readable by JavaScript for analytics
|
|
1075
|
-
secure: isSecure,
|
|
1076
|
-
sameSite: 'Lax',
|
|
1077
|
-
path: '/',
|
|
1078
|
-
maxAge: 604800, // 1 week
|
|
1079
|
-
});
|
|
1080
|
-
|
|
1081
|
-
// Store in context for handler to access in same request
|
|
1082
|
-
// (cookies aren't readable until the next request)
|
|
1083
|
-
c.set('_webThreadId', thread.id);
|
|
1084
|
-
|
|
1085
|
-
await next();
|
|
1086
|
-
});
|
|
1087
|
-
}
|
|
1088
|
-
|
|
1089
|
-
/**
|
|
1090
|
-
* Get the secret used for signing session/thread cookies.
|
|
1091
|
-
* Uses AGENTUITY_SDK_KEY if available, falls back to 'agentuity'.
|
|
1092
|
-
*/
|
|
1093
|
-
export function getSessionSecret(): string {
|
|
1094
|
-
return process.env.AGENTUITY_SDK_KEY || 'agentuity';
|
|
1095
|
-
}
|