@copilotkit/runtime 1.72.1-canary.sp-longthread-perf → 1.73.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/dist/agent/index.cjs +48 -8
- package/dist/agent/index.cjs.map +1 -1
- package/dist/agent/index.d.cts +20 -7
- package/dist/agent/index.d.cts.map +1 -1
- package/dist/agent/index.d.mts +18 -5
- package/dist/agent/index.d.mts.map +1 -1
- package/dist/agent/index.mjs +48 -8
- package/dist/agent/index.mjs.map +1 -1
- package/dist/agent/learned-skills.cjs +77 -0
- package/dist/agent/learned-skills.cjs.map +1 -0
- package/dist/agent/learned-skills.d.cts +16 -0
- package/dist/agent/learned-skills.d.cts.map +1 -0
- package/dist/agent/learned-skills.d.mts +16 -0
- package/dist/agent/learned-skills.d.mts.map +1 -0
- package/dist/agent/learned-skills.mjs +75 -0
- package/dist/agent/learned-skills.mjs.map +1 -0
- package/dist/index.d.cts +4 -4
- package/dist/index.d.mts +4 -4
- package/dist/internal/learned-skills.cjs +22 -0
- package/dist/internal/learned-skills.d.cts +10 -0
- package/dist/internal/learned-skills.d.mts +10 -0
- package/dist/internal/learned-skills.mjs +10 -0
- package/dist/lib/index.d.cts +2 -2
- package/dist/lib/index.d.mts +2 -2
- package/dist/lib/integrations/node-http/index.cjs.map +1 -1
- package/dist/lib/integrations/node-http/index.d.cts.map +1 -1
- package/dist/lib/integrations/node-http/index.d.mts.map +1 -1
- package/dist/lib/integrations/node-http/index.mjs.map +1 -1
- package/dist/lib/integrations/shared.cjs.map +1 -1
- package/dist/lib/integrations/shared.d.cts +1 -1
- package/dist/lib/integrations/shared.d.cts.map +1 -1
- package/dist/lib/integrations/shared.d.mts +1 -1
- package/dist/lib/integrations/shared.d.mts.map +1 -1
- package/dist/lib/integrations/shared.mjs.map +1 -1
- package/dist/lib/runtime/agent-integrations/langgraph/agent.d.cts +1 -1
- package/dist/lib/runtime/copilot-runtime.d.cts +2 -2
- package/dist/lib/runtime/copilot-runtime.d.mts +2 -2
- package/dist/package.cjs +9 -3
- package/dist/package.mjs +9 -3
- package/dist/service-adapters/anthropic/anthropic-adapter.cjs.map +1 -1
- package/dist/service-adapters/anthropic/anthropic-adapter.d.cts.map +1 -1
- package/dist/service-adapters/anthropic/anthropic-adapter.d.mts.map +1 -1
- package/dist/service-adapters/anthropic/anthropic-adapter.mjs.map +1 -1
- package/dist/service-adapters/shared/sdk-client-utils.cjs.map +1 -1
- package/dist/service-adapters/shared/sdk-client-utils.d.cts.map +1 -1
- package/dist/service-adapters/shared/sdk-client-utils.d.mts.map +1 -1
- package/dist/service-adapters/shared/sdk-client-utils.mjs.map +1 -1
- package/dist/v2/index.cjs +6 -0
- package/dist/v2/index.d.cts +6 -5
- package/dist/v2/index.d.mts +6 -5
- package/dist/v2/index.mjs +2 -2
- package/dist/v2/runtime/core/channel-manager.d.cts +1 -1
- package/dist/v2/runtime/core/channel-manager.d.mts +1 -1
- package/dist/v2/runtime/core/runtime.cjs +5 -0
- package/dist/v2/runtime/core/runtime.cjs.map +1 -1
- package/dist/v2/runtime/core/runtime.d.cts +18 -3
- package/dist/v2/runtime/core/runtime.d.cts.map +1 -1
- package/dist/v2/runtime/core/runtime.d.mts +17 -2
- package/dist/v2/runtime/core/runtime.d.mts.map +1 -1
- package/dist/v2/runtime/core/runtime.mjs +5 -0
- package/dist/v2/runtime/core/runtime.mjs.map +1 -1
- package/dist/v2/runtime/handlers/handle-suggest.cjs +4 -3
- package/dist/v2/runtime/handlers/handle-suggest.cjs.map +1 -1
- package/dist/v2/runtime/handlers/handle-suggest.mjs +5 -4
- package/dist/v2/runtime/handlers/handle-suggest.mjs.map +1 -1
- package/dist/v2/runtime/handlers/shared/sse-keep-alive.cjs +69 -0
- package/dist/v2/runtime/handlers/shared/sse-keep-alive.cjs.map +1 -0
- package/dist/v2/runtime/handlers/shared/sse-keep-alive.mjs +67 -0
- package/dist/v2/runtime/handlers/shared/sse-keep-alive.mjs.map +1 -0
- package/dist/v2/runtime/handlers/shared/sse-response.cjs +5 -2
- package/dist/v2/runtime/handlers/shared/sse-response.cjs.map +1 -1
- package/dist/v2/runtime/handlers/shared/sse-response.mjs +5 -2
- package/dist/v2/runtime/handlers/shared/sse-response.mjs.map +1 -1
- package/dist/v2/runtime/handlers/sse/connect.cjs +1 -0
- package/dist/v2/runtime/handlers/sse/connect.cjs.map +1 -1
- package/dist/v2/runtime/handlers/sse/connect.mjs +1 -0
- package/dist/v2/runtime/handlers/sse/connect.mjs.map +1 -1
- package/dist/v2/runtime/handlers/sse/run.cjs +1 -0
- package/dist/v2/runtime/handlers/sse/run.cjs.map +1 -1
- package/dist/v2/runtime/handlers/sse/run.mjs +1 -0
- package/dist/v2/runtime/handlers/sse/run.mjs.map +1 -1
- package/dist/v2/runtime/index.d.cts +3 -3
- package/dist/v2/runtime/index.d.mts +3 -3
- package/dist/v2/runtime/intelligence-platform/client.d.cts +1 -1
- package/dist/v2/runtime/intelligence-platform/client.d.mts +1 -1
- package/dist/v2/runtime/intelligence-platform/skill-registry/archive.cjs +132 -0
- package/dist/v2/runtime/intelligence-platform/skill-registry/archive.cjs.map +1 -0
- package/dist/v2/runtime/intelligence-platform/skill-registry/archive.d.cts +11 -0
- package/dist/v2/runtime/intelligence-platform/skill-registry/archive.d.cts.map +1 -0
- package/dist/v2/runtime/intelligence-platform/skill-registry/archive.d.mts +11 -0
- package/dist/v2/runtime/intelligence-platform/skill-registry/archive.d.mts.map +1 -0
- package/dist/v2/runtime/intelligence-platform/skill-registry/archive.mjs +128 -0
- package/dist/v2/runtime/intelligence-platform/skill-registry/archive.mjs.map +1 -0
- package/dist/v2/runtime/intelligence-platform/skill-registry/config.cjs +48 -0
- package/dist/v2/runtime/intelligence-platform/skill-registry/config.cjs.map +1 -0
- package/dist/v2/runtime/intelligence-platform/skill-registry/config.d.cts +27 -0
- package/dist/v2/runtime/intelligence-platform/skill-registry/config.d.cts.map +1 -0
- package/dist/v2/runtime/intelligence-platform/skill-registry/config.d.mts +27 -0
- package/dist/v2/runtime/intelligence-platform/skill-registry/config.d.mts.map +1 -0
- package/dist/v2/runtime/intelligence-platform/skill-registry/config.mjs +48 -0
- package/dist/v2/runtime/intelligence-platform/skill-registry/config.mjs.map +1 -0
- package/dist/v2/runtime/intelligence-platform/skill-registry/errors.cjs +11 -0
- package/dist/v2/runtime/intelligence-platform/skill-registry/errors.cjs.map +1 -0
- package/dist/v2/runtime/intelligence-platform/skill-registry/errors.d.cts +8 -0
- package/dist/v2/runtime/intelligence-platform/skill-registry/errors.d.cts.map +1 -0
- package/dist/v2/runtime/intelligence-platform/skill-registry/errors.d.mts +8 -0
- package/dist/v2/runtime/intelligence-platform/skill-registry/errors.d.mts.map +1 -0
- package/dist/v2/runtime/intelligence-platform/skill-registry/errors.mjs +11 -0
- package/dist/v2/runtime/intelligence-platform/skill-registry/errors.mjs.map +1 -0
- package/dist/v2/runtime/intelligence-platform/skill-registry/index.d.cts +8 -0
- package/dist/v2/runtime/intelligence-platform/skill-registry/index.d.mts +8 -0
- package/dist/v2/runtime/intelligence-platform/skill-registry/registry.cjs +132 -0
- package/dist/v2/runtime/intelligence-platform/skill-registry/registry.cjs.map +1 -0
- package/dist/v2/runtime/intelligence-platform/skill-registry/registry.d.cts +31 -0
- package/dist/v2/runtime/intelligence-platform/skill-registry/registry.d.cts.map +1 -0
- package/dist/v2/runtime/intelligence-platform/skill-registry/registry.d.mts +31 -0
- package/dist/v2/runtime/intelligence-platform/skill-registry/registry.d.mts.map +1 -0
- package/dist/v2/runtime/intelligence-platform/skill-registry/registry.mjs +132 -0
- package/dist/v2/runtime/intelligence-platform/skill-registry/registry.mjs.map +1 -0
- package/dist/v2/runtime/intelligence-platform/skill-registry/skill-content.cjs +42 -0
- package/dist/v2/runtime/intelligence-platform/skill-registry/skill-content.cjs.map +1 -0
- package/dist/v2/runtime/intelligence-platform/skill-registry/skill-content.d.cts +13 -0
- package/dist/v2/runtime/intelligence-platform/skill-registry/skill-content.d.cts.map +1 -0
- package/dist/v2/runtime/intelligence-platform/skill-registry/skill-content.d.mts +13 -0
- package/dist/v2/runtime/intelligence-platform/skill-registry/skill-content.d.mts.map +1 -0
- package/dist/v2/runtime/intelligence-platform/skill-registry/skill-content.mjs +39 -0
- package/dist/v2/runtime/intelligence-platform/skill-registry/skill-content.mjs.map +1 -0
- package/dist/v2/runtime/intelligence-platform/skill-registry/snapshot.cjs +93 -0
- package/dist/v2/runtime/intelligence-platform/skill-registry/snapshot.cjs.map +1 -0
- package/dist/v2/runtime/intelligence-platform/skill-registry/snapshot.d.cts +28 -0
- package/dist/v2/runtime/intelligence-platform/skill-registry/snapshot.d.cts.map +1 -0
- package/dist/v2/runtime/intelligence-platform/skill-registry/snapshot.d.mts +28 -0
- package/dist/v2/runtime/intelligence-platform/skill-registry/snapshot.d.mts.map +1 -0
- package/dist/v2/runtime/intelligence-platform/skill-registry/snapshot.mjs +92 -0
- package/dist/v2/runtime/intelligence-platform/skill-registry/snapshot.mjs.map +1 -0
- package/dist/v2/runtime/runner/agent-runner.d.cts +1 -1
- package/dist/v2/runtime/runner/in-memory.d.cts +1 -1
- package/dist/v2/runtime/runner/index.d.cts +2 -2
- package/dist/v2/runtime/runner/index.d.mts +2 -2
- package/dist/v2/runtime/runner/index.mjs +2 -2
- package/dist/v2/runtime/runner/intelligence.d.cts +1 -1
- package/package.json +9 -3
|
@@ -55,14 +55,15 @@ async function handleSuggestAgent({ runtime, request, agentId }) {
|
|
|
55
55
|
request,
|
|
56
56
|
agentId,
|
|
57
57
|
captureTelemetry: false,
|
|
58
|
+
keepAliveIntervalSeconds: runtime.sseKeepAliveIntervalSeconds,
|
|
58
59
|
observableFactory: () => new rxjs.Observable((subscriber) => {
|
|
59
|
-
const
|
|
60
|
+
const finalizer = (0, _copilotkit_shared.createRunEventFinalizer)();
|
|
60
61
|
let settled = false;
|
|
61
62
|
agent.runAgent(input, { onEvent: ({ event }) => {
|
|
62
|
-
|
|
63
|
+
finalizer.observe(event);
|
|
63
64
|
subscriber.next(event);
|
|
64
65
|
} }).then(() => {
|
|
65
|
-
for (const event of
|
|
66
|
+
for (const event of finalizer.finalize({ stopRequested: false })) subscriber.next(event);
|
|
66
67
|
settled = true;
|
|
67
68
|
subscriber.complete();
|
|
68
69
|
}).catch((error) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handle-suggest.cjs","names":["cloneAgentForRequest","parseRunRequest","mergeForwardableHeaders","resolveForwardHeadersPolicy","createSseEventResponse","Observable"],"sources":["../../../../src/v2/runtime/handlers/handle-suggest.ts"],"sourcesContent":["import type { AbstractAgent, BaseEvent } from \"@ag-ui/client\";\nimport {
|
|
1
|
+
{"version":3,"file":"handle-suggest.cjs","names":["cloneAgentForRequest","parseRunRequest","mergeForwardableHeaders","resolveForwardHeadersPolicy","createSseEventResponse","Observable"],"sources":["../../../../src/v2/runtime/handlers/handle-suggest.ts"],"sourcesContent":["import type { AbstractAgent, BaseEvent } from \"@ag-ui/client\";\nimport { createRunEventFinalizer } from \"@copilotkit/shared\";\nimport { Observable } from \"rxjs\";\nimport type { CopilotRuntimeLike } from \"../core/runtime\";\nimport {\n mergeForwardableHeaders,\n resolveForwardHeadersPolicy,\n} from \"./header-utils\";\nimport { cloneAgentForRequest, parseRunRequest } from \"./shared/agent-utils\";\nimport { createSseEventResponse } from \"./shared/sse-response\";\n\n/**\n * Parameters for {@link handleSuggestAgent}.\n */\ninterface SuggestAgentParameters {\n /** The runtime whose registry the agent is resolved from. */\n runtime: CopilotRuntimeLike;\n /** The incoming `POST /agent/:agentId/suggest` request. */\n request: Request;\n /** The route-resolved agent id to run. */\n agentId: string;\n}\n\n/**\n * Stateless suggestion run.\n *\n * Executes the provider agent **directly** and streams its AG-UI events back as\n * SSE — the same wire format as `/agent/:id/run`, so the client consumes it with\n * the stock `HttpAgent` transport and chips fill in as the provider emits them.\n * It deliberately does not go through `runtime.runner`: the runner's\n * `run()` writes to a module-level store keyed by threadId (backing the SSE\n * runtime's local thread endpoints), which would leak the throwaway suggestion\n * thread. This handler runs the agent's event pipeline **without** that\n * persistence — no thread, lock, gateway, name-gen, or run telemetry — so\n * dynamic suggestions are side-effect-free in every runtime mode.\n *\n * The only per-request configuration it applies is forwarding the request's\n * allowlisted headers (`authorization` + `x-*`) onto the agent clone. It does\n * **not** attach any request middleware — no A2UI, no MCPApps, no\n * OpenGenerativeUI, no Intelligence enterprise-learning tools. The **client**\n * (the core suggestion engine) forces `toolChoice: copilotkitSuggest` in the\n * request body; this handler does not set tool choice itself and relies on it\n * being present in the incoming `input`. Given that forced tool choice, any\n * middleware-injected tools are dead weight, and MCPApps setup can incur a\n * `listTools` network round-trip per suggestion under `available: \"always\"` — a\n * side effect this path must never pay.\n *\n * When the client aborts the HTTP request (via its `AbortController`), the\n * server-side run is cancelled best-effort so an aborted suggestion does not\n * keep running a provider call to completion.\n *\n * @param params - The runtime, request, and resolved agent id.\n * @returns A `text/event-stream` `Response` of the provider agent's AG-UI events\n * on success, or the resolution `Response` (e.g. 404/400) when agent/body\n * resolution fails before streaming begins.\n */\nexport async function handleSuggestAgent({\n runtime,\n request,\n agentId,\n}: SuggestAgentParameters): Promise<Response> {\n const agent = await cloneAgentForRequest(runtime, agentId, request);\n if (agent instanceof Response) {\n return agent;\n }\n\n // Carry the registry key onto the clone so any framework relying on it during\n // the run sees the correct agentId.\n agent.agentId = agentId;\n\n const input = await parseRunRequest(request);\n if (input instanceof Response) {\n return input;\n }\n\n // Forward eligible inbound headers onto the clone under the runtime's resolved\n // forwarding policy (same helper the run handler uses: `authorization` /\n // custom `x-*`, infra/proxy/platform headers denylisted, server-set headers\n // winning on collision — #5712). Unlike a full agent run, the suggest path\n // intentionally attaches no middleware (see the handler docblock): the forced\n // `copilotkitSuggest` tool choice makes any middleware-injected tools dead\n // weight, and MCPApps setup can trigger a `listTools` network round-trip we\n // must never incur per suggestion.\n //\n // `AbstractAgent` doesn't declare `headers` on its base type (concrete\n // framework agents add it), so narrow to the header-carrying shape rather\n // than casting to `any`.\n const headerCarryingAgent = agent as AbstractAgent & {\n headers?: Record<string, string>;\n };\n headerCarryingAgent.headers = mergeForwardableHeaders(\n headerCarryingAgent.headers,\n request,\n runtime.forwardHeadersPolicy ?? resolveForwardHeadersPolicy(undefined),\n );\n\n agent.setMessages(input.messages);\n agent.setState(input.state);\n agent.threadId = input.threadId;\n\n // Stream the provider agent's events over SSE without persistence. This is\n // the runner's event pipeline (`agent.runAgent({ onEvent })` + terminal-event\n // finalization) minus the `GLOBAL_STORE` writes that would leak a thread.\n // `captureTelemetry: false` keeps suggestions out of run telemetry; omitting\n // `debugEventBus` keeps them out of the inspector's run trace.\n return createSseEventResponse({\n request,\n agentId,\n captureTelemetry: false,\n keepAliveIntervalSeconds: runtime.sseKeepAliveIntervalSeconds,\n observableFactory: () =>\n new Observable<BaseEvent>((subscriber) => {\n // Tracks open message/tool lifecycles so the missing terminal events\n // (an unclosed message/tool call, or a `RUN_FINISHED`) can be appended\n // — the same closure the runner applies — so the client sees a\n // well-formed AG-UI sequence. Nothing is persisted here, so only the\n // open ids are kept, never the streamed events themselves.\n const finalizer = createRunEventFinalizer();\n let settled = false;\n\n void agent\n .runAgent(input, {\n onEvent: ({ event }) => {\n finalizer.observe(event);\n subscriber.next(event);\n },\n })\n .then(() => {\n for (const event of finalizer.finalize({ stopRequested: false })) {\n subscriber.next(event);\n }\n settled = true;\n subscriber.complete();\n })\n .catch((error: unknown) => {\n settled = true;\n subscriber.error(error);\n });\n\n // Teardown fires when the response stream is torn down (client abort /\n // disconnect). Cancel the still-running provider call best-effort.\n return () => {\n if (!settled && typeof agent.abortRun === \"function\") {\n try {\n agent.abortRun();\n } catch {\n // best-effort — nothing actionable if aborting the run fails.\n }\n }\n };\n }),\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwDA,eAAsB,mBAAmB,EACvC,SACA,SACA,WAC4C;CAC5C,MAAM,QAAQ,MAAMA,yCAAqB,SAAS,SAAS,QAAQ;AACnE,KAAI,iBAAiB,SACnB,QAAO;AAKT,OAAM,UAAU;CAEhB,MAAM,QAAQ,MAAMC,oCAAgB,QAAQ;AAC5C,KAAI,iBAAiB,SACnB,QAAO;CAeT,MAAM,sBAAsB;AAG5B,qBAAoB,UAAUC,6CAC5B,oBAAoB,SACpB,SACA,QAAQ,wBAAwBC,iDAA4B,OAAU,CACvE;AAED,OAAM,YAAY,MAAM,SAAS;AACjC,OAAM,SAAS,MAAM,MAAM;AAC3B,OAAM,WAAW,MAAM;AAOvB,QAAOC,4CAAuB;EAC5B;EACA;EACA,kBAAkB;EAClB,0BAA0B,QAAQ;EAClC,yBACE,IAAIC,iBAAuB,eAAe;GAMxC,MAAM,6DAAqC;GAC3C,IAAI,UAAU;AAEd,GAAK,MACF,SAAS,OAAO,EACf,UAAU,EAAE,YAAY;AACtB,cAAU,QAAQ,MAAM;AACxB,eAAW,KAAK,MAAM;MAEzB,CAAC,CACD,WAAW;AACV,SAAK,MAAM,SAAS,UAAU,SAAS,EAAE,eAAe,OAAO,CAAC,CAC9D,YAAW,KAAK,MAAM;AAExB,cAAU;AACV,eAAW,UAAU;KACrB,CACD,OAAO,UAAmB;AACzB,cAAU;AACV,eAAW,MAAM,MAAM;KACvB;AAIJ,gBAAa;AACX,QAAI,CAAC,WAAW,OAAO,MAAM,aAAa,WACxC,KAAI;AACF,WAAM,UAAU;YACV;;IAKZ;EACL,CAAC"}
|
|
@@ -2,7 +2,7 @@ import "reflect-metadata";
|
|
|
2
2
|
import { mergeForwardableHeaders, resolveForwardHeadersPolicy } from "./header-utils.mjs";
|
|
3
3
|
import { cloneAgentForRequest, parseRunRequest } from "./shared/agent-utils.mjs";
|
|
4
4
|
import { createSseEventResponse } from "./shared/sse-response.mjs";
|
|
5
|
-
import {
|
|
5
|
+
import { createRunEventFinalizer } from "@copilotkit/shared";
|
|
6
6
|
import { Observable } from "rxjs";
|
|
7
7
|
|
|
8
8
|
//#region src/v2/runtime/handlers/handle-suggest.ts
|
|
@@ -54,14 +54,15 @@ async function handleSuggestAgent({ runtime, request, agentId }) {
|
|
|
54
54
|
request,
|
|
55
55
|
agentId,
|
|
56
56
|
captureTelemetry: false,
|
|
57
|
+
keepAliveIntervalSeconds: runtime.sseKeepAliveIntervalSeconds,
|
|
57
58
|
observableFactory: () => new Observable((subscriber) => {
|
|
58
|
-
const
|
|
59
|
+
const finalizer = createRunEventFinalizer();
|
|
59
60
|
let settled = false;
|
|
60
61
|
agent.runAgent(input, { onEvent: ({ event }) => {
|
|
61
|
-
|
|
62
|
+
finalizer.observe(event);
|
|
62
63
|
subscriber.next(event);
|
|
63
64
|
} }).then(() => {
|
|
64
|
-
for (const event of
|
|
65
|
+
for (const event of finalizer.finalize({ stopRequested: false })) subscriber.next(event);
|
|
65
66
|
settled = true;
|
|
66
67
|
subscriber.complete();
|
|
67
68
|
}).catch((error) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handle-suggest.mjs","names":[],"sources":["../../../../src/v2/runtime/handlers/handle-suggest.ts"],"sourcesContent":["import type { AbstractAgent, BaseEvent } from \"@ag-ui/client\";\nimport {
|
|
1
|
+
{"version":3,"file":"handle-suggest.mjs","names":[],"sources":["../../../../src/v2/runtime/handlers/handle-suggest.ts"],"sourcesContent":["import type { AbstractAgent, BaseEvent } from \"@ag-ui/client\";\nimport { createRunEventFinalizer } from \"@copilotkit/shared\";\nimport { Observable } from \"rxjs\";\nimport type { CopilotRuntimeLike } from \"../core/runtime\";\nimport {\n mergeForwardableHeaders,\n resolveForwardHeadersPolicy,\n} from \"./header-utils\";\nimport { cloneAgentForRequest, parseRunRequest } from \"./shared/agent-utils\";\nimport { createSseEventResponse } from \"./shared/sse-response\";\n\n/**\n * Parameters for {@link handleSuggestAgent}.\n */\ninterface SuggestAgentParameters {\n /** The runtime whose registry the agent is resolved from. */\n runtime: CopilotRuntimeLike;\n /** The incoming `POST /agent/:agentId/suggest` request. */\n request: Request;\n /** The route-resolved agent id to run. */\n agentId: string;\n}\n\n/**\n * Stateless suggestion run.\n *\n * Executes the provider agent **directly** and streams its AG-UI events back as\n * SSE — the same wire format as `/agent/:id/run`, so the client consumes it with\n * the stock `HttpAgent` transport and chips fill in as the provider emits them.\n * It deliberately does not go through `runtime.runner`: the runner's\n * `run()` writes to a module-level store keyed by threadId (backing the SSE\n * runtime's local thread endpoints), which would leak the throwaway suggestion\n * thread. This handler runs the agent's event pipeline **without** that\n * persistence — no thread, lock, gateway, name-gen, or run telemetry — so\n * dynamic suggestions are side-effect-free in every runtime mode.\n *\n * The only per-request configuration it applies is forwarding the request's\n * allowlisted headers (`authorization` + `x-*`) onto the agent clone. It does\n * **not** attach any request middleware — no A2UI, no MCPApps, no\n * OpenGenerativeUI, no Intelligence enterprise-learning tools. The **client**\n * (the core suggestion engine) forces `toolChoice: copilotkitSuggest` in the\n * request body; this handler does not set tool choice itself and relies on it\n * being present in the incoming `input`. Given that forced tool choice, any\n * middleware-injected tools are dead weight, and MCPApps setup can incur a\n * `listTools` network round-trip per suggestion under `available: \"always\"` — a\n * side effect this path must never pay.\n *\n * When the client aborts the HTTP request (via its `AbortController`), the\n * server-side run is cancelled best-effort so an aborted suggestion does not\n * keep running a provider call to completion.\n *\n * @param params - The runtime, request, and resolved agent id.\n * @returns A `text/event-stream` `Response` of the provider agent's AG-UI events\n * on success, or the resolution `Response` (e.g. 404/400) when agent/body\n * resolution fails before streaming begins.\n */\nexport async function handleSuggestAgent({\n runtime,\n request,\n agentId,\n}: SuggestAgentParameters): Promise<Response> {\n const agent = await cloneAgentForRequest(runtime, agentId, request);\n if (agent instanceof Response) {\n return agent;\n }\n\n // Carry the registry key onto the clone so any framework relying on it during\n // the run sees the correct agentId.\n agent.agentId = agentId;\n\n const input = await parseRunRequest(request);\n if (input instanceof Response) {\n return input;\n }\n\n // Forward eligible inbound headers onto the clone under the runtime's resolved\n // forwarding policy (same helper the run handler uses: `authorization` /\n // custom `x-*`, infra/proxy/platform headers denylisted, server-set headers\n // winning on collision — #5712). Unlike a full agent run, the suggest path\n // intentionally attaches no middleware (see the handler docblock): the forced\n // `copilotkitSuggest` tool choice makes any middleware-injected tools dead\n // weight, and MCPApps setup can trigger a `listTools` network round-trip we\n // must never incur per suggestion.\n //\n // `AbstractAgent` doesn't declare `headers` on its base type (concrete\n // framework agents add it), so narrow to the header-carrying shape rather\n // than casting to `any`.\n const headerCarryingAgent = agent as AbstractAgent & {\n headers?: Record<string, string>;\n };\n headerCarryingAgent.headers = mergeForwardableHeaders(\n headerCarryingAgent.headers,\n request,\n runtime.forwardHeadersPolicy ?? resolveForwardHeadersPolicy(undefined),\n );\n\n agent.setMessages(input.messages);\n agent.setState(input.state);\n agent.threadId = input.threadId;\n\n // Stream the provider agent's events over SSE without persistence. This is\n // the runner's event pipeline (`agent.runAgent({ onEvent })` + terminal-event\n // finalization) minus the `GLOBAL_STORE` writes that would leak a thread.\n // `captureTelemetry: false` keeps suggestions out of run telemetry; omitting\n // `debugEventBus` keeps them out of the inspector's run trace.\n return createSseEventResponse({\n request,\n agentId,\n captureTelemetry: false,\n keepAliveIntervalSeconds: runtime.sseKeepAliveIntervalSeconds,\n observableFactory: () =>\n new Observable<BaseEvent>((subscriber) => {\n // Tracks open message/tool lifecycles so the missing terminal events\n // (an unclosed message/tool call, or a `RUN_FINISHED`) can be appended\n // — the same closure the runner applies — so the client sees a\n // well-formed AG-UI sequence. Nothing is persisted here, so only the\n // open ids are kept, never the streamed events themselves.\n const finalizer = createRunEventFinalizer();\n let settled = false;\n\n void agent\n .runAgent(input, {\n onEvent: ({ event }) => {\n finalizer.observe(event);\n subscriber.next(event);\n },\n })\n .then(() => {\n for (const event of finalizer.finalize({ stopRequested: false })) {\n subscriber.next(event);\n }\n settled = true;\n subscriber.complete();\n })\n .catch((error: unknown) => {\n settled = true;\n subscriber.error(error);\n });\n\n // Teardown fires when the response stream is torn down (client abort /\n // disconnect). Cancel the still-running provider call best-effort.\n return () => {\n if (!settled && typeof agent.abortRun === \"function\") {\n try {\n agent.abortRun();\n } catch {\n // best-effort — nothing actionable if aborting the run fails.\n }\n }\n };\n }),\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwDA,eAAsB,mBAAmB,EACvC,SACA,SACA,WAC4C;CAC5C,MAAM,QAAQ,MAAM,qBAAqB,SAAS,SAAS,QAAQ;AACnE,KAAI,iBAAiB,SACnB,QAAO;AAKT,OAAM,UAAU;CAEhB,MAAM,QAAQ,MAAM,gBAAgB,QAAQ;AAC5C,KAAI,iBAAiB,SACnB,QAAO;CAeT,MAAM,sBAAsB;AAG5B,qBAAoB,UAAU,wBAC5B,oBAAoB,SACpB,SACA,QAAQ,wBAAwB,4BAA4B,OAAU,CACvE;AAED,OAAM,YAAY,MAAM,SAAS;AACjC,OAAM,SAAS,MAAM,MAAM;AAC3B,OAAM,WAAW,MAAM;AAOvB,QAAO,uBAAuB;EAC5B;EACA;EACA,kBAAkB;EAClB,0BAA0B,QAAQ;EAClC,yBACE,IAAI,YAAuB,eAAe;GAMxC,MAAM,YAAY,yBAAyB;GAC3C,IAAI,UAAU;AAEd,GAAK,MACF,SAAS,OAAO,EACf,UAAU,EAAE,YAAY;AACtB,cAAU,QAAQ,MAAM;AACxB,eAAW,KAAK,MAAM;MAEzB,CAAC,CACD,WAAW;AACV,SAAK,MAAM,SAAS,UAAU,SAAS,EAAE,eAAe,OAAO,CAAC,CAC9D,YAAW,KAAK,MAAM;AAExB,cAAU;AACV,eAAW,UAAU;KACrB,CACD,OAAO,UAAmB;AACzB,cAAU;AACV,eAAW,MAAM,MAAM;KACvB;AAIJ,gBAAa;AACX,QAAI,CAAC,WAAW,OAAO,MAAM,aAAa,WACxC,KAAI;AACF,WAAM,UAAU;YACV;;IAKZ;EACL,CAAC"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
require("reflect-metadata");
|
|
2
|
+
|
|
3
|
+
//#region src/v2/runtime/handlers/shared/sse-keep-alive.ts
|
|
4
|
+
/**
|
|
5
|
+
* Default silence, in seconds, before an SSE response carries a keep-alive
|
|
6
|
+
* comment. Sits under the tightest common proxy idle timeouts (55–60 s) and
|
|
7
|
+
* beside `lockHeartbeatIntervalSeconds`, which shares the unit and default.
|
|
8
|
+
*/
|
|
9
|
+
const DEFAULT_SSE_KEEP_ALIVE_INTERVAL_SECONDS = 15;
|
|
10
|
+
/**
|
|
11
|
+
* Longest interval the timer can honour. Timers take a signed 32-bit
|
|
12
|
+
* millisecond delay; Node silently replaces anything larger with 1 ms, which
|
|
13
|
+
* would turn a keep-alive into a flood.
|
|
14
|
+
*/
|
|
15
|
+
const MAX_SSE_KEEP_ALIVE_INTERVAL_SECONDS = (2 ** 31 - 1) / 1e3;
|
|
16
|
+
/**
|
|
17
|
+
* Resolves the `sseKeepAliveIntervalSeconds` runtime option once, at
|
|
18
|
+
* construction, so every SSE call site reads one validated value.
|
|
19
|
+
* `undefined` takes the default; `0` disables the keep-alive.
|
|
20
|
+
*/
|
|
21
|
+
function resolveSseKeepAliveIntervalSeconds(value) {
|
|
22
|
+
const seconds = value ?? DEFAULT_SSE_KEEP_ALIVE_INTERVAL_SECONDS;
|
|
23
|
+
if (!Number.isFinite(seconds) || seconds < 0 || seconds > MAX_SSE_KEEP_ALIVE_INTERVAL_SECONDS) throw new RangeError(`sseKeepAliveIntervalSeconds must be between 0 and ${MAX_SSE_KEEP_ALIVE_INTERVAL_SECONDS}, got ${String(value)}`);
|
|
24
|
+
return seconds;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* SSE comment frame. Parsers drop comment lines, so no AG-UI event is
|
|
28
|
+
* invented and the verifier, history and middleware never see it.
|
|
29
|
+
* @see https://html.spec.whatwg.org/multipage/server-sent-events.html#authoring-notes
|
|
30
|
+
*/
|
|
31
|
+
const KEEP_ALIVE_FRAME = new TextEncoder().encode(": keep-alive\n\n");
|
|
32
|
+
/**
|
|
33
|
+
* Keeps a quiet SSE body open by writing a comment after `idleMs` of silence.
|
|
34
|
+
*
|
|
35
|
+
* Any byte from `source` resets the clock, so an active stream adds nothing
|
|
36
|
+
* to the wire. A back-pressured reader skips the tick instead of queueing
|
|
37
|
+
* frames. The timer stops when the pipe settles, which covers both the source
|
|
38
|
+
* completing and the reader cancelling, on every supported Node version.
|
|
39
|
+
*/
|
|
40
|
+
function keepAliveSse(source, idleMs) {
|
|
41
|
+
let timer;
|
|
42
|
+
const stop = () => {
|
|
43
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
44
|
+
timer = void 0;
|
|
45
|
+
};
|
|
46
|
+
const arm = (controller) => {
|
|
47
|
+
stop();
|
|
48
|
+
timer = setTimeout(() => {
|
|
49
|
+
if (controller.desiredSize === 1) controller.enqueue(KEEP_ALIVE_FRAME);
|
|
50
|
+
arm(controller);
|
|
51
|
+
}, idleMs);
|
|
52
|
+
timer.unref?.();
|
|
53
|
+
};
|
|
54
|
+
const transform = new TransformStream({
|
|
55
|
+
start: arm,
|
|
56
|
+
transform(chunk, controller) {
|
|
57
|
+
controller.enqueue(chunk);
|
|
58
|
+
arm(controller);
|
|
59
|
+
},
|
|
60
|
+
flush: stop
|
|
61
|
+
}, void 0, { highWaterMark: 1 });
|
|
62
|
+
source.pipeTo(transform.writable).catch(() => {}).finally(stop);
|
|
63
|
+
return transform.readable;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
//#endregion
|
|
67
|
+
exports.keepAliveSse = keepAliveSse;
|
|
68
|
+
exports.resolveSseKeepAliveIntervalSeconds = resolveSseKeepAliveIntervalSeconds;
|
|
69
|
+
//# sourceMappingURL=sse-keep-alive.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sse-keep-alive.cjs","names":[],"sources":["../../../../../src/v2/runtime/handlers/shared/sse-keep-alive.ts"],"sourcesContent":["/**\n * Default silence, in seconds, before an SSE response carries a keep-alive\n * comment. Sits under the tightest common proxy idle timeouts (55–60 s) and\n * beside `lockHeartbeatIntervalSeconds`, which shares the unit and default.\n */\nexport const DEFAULT_SSE_KEEP_ALIVE_INTERVAL_SECONDS = 15;\n\n/**\n * Longest interval the timer can honour. Timers take a signed 32-bit\n * millisecond delay; Node silently replaces anything larger with 1 ms, which\n * would turn a keep-alive into a flood.\n */\nexport const MAX_SSE_KEEP_ALIVE_INTERVAL_SECONDS = (2 ** 31 - 1) / 1000;\n\n/**\n * Resolves the `sseKeepAliveIntervalSeconds` runtime option once, at\n * construction, so every SSE call site reads one validated value.\n * `undefined` takes the default; `0` disables the keep-alive.\n */\nexport function resolveSseKeepAliveIntervalSeconds(\n value: number | undefined,\n): number {\n const seconds = value ?? DEFAULT_SSE_KEEP_ALIVE_INTERVAL_SECONDS;\n if (\n !Number.isFinite(seconds) ||\n seconds < 0 ||\n seconds > MAX_SSE_KEEP_ALIVE_INTERVAL_SECONDS\n ) {\n throw new RangeError(\n `sseKeepAliveIntervalSeconds must be between 0 and ${MAX_SSE_KEEP_ALIVE_INTERVAL_SECONDS}, got ${String(value)}`,\n );\n }\n return seconds;\n}\n\n/**\n * SSE comment frame. Parsers drop comment lines, so no AG-UI event is\n * invented and the verifier, history and middleware never see it.\n * @see https://html.spec.whatwg.org/multipage/server-sent-events.html#authoring-notes\n */\nconst KEEP_ALIVE_FRAME = new TextEncoder().encode(\": keep-alive\\n\\n\");\n\n/**\n * Keeps a quiet SSE body open by writing a comment after `idleMs` of silence.\n *\n * Any byte from `source` resets the clock, so an active stream adds nothing\n * to the wire. A back-pressured reader skips the tick instead of queueing\n * frames. The timer stops when the pipe settles, which covers both the source\n * completing and the reader cancelling, on every supported Node version.\n */\nexport function keepAliveSse(\n source: ReadableStream<Uint8Array>,\n idleMs: number,\n): ReadableStream<Uint8Array> {\n let timer: ReturnType<typeof setTimeout> | undefined;\n const stop = () => {\n if (timer !== undefined) clearTimeout(timer);\n timer = undefined;\n };\n const arm = (controller: TransformStreamDefaultController<Uint8Array>) => {\n stop();\n timer = setTimeout(() => {\n // `desiredSize` is 1 only while the readable queue is empty; 0 means a\n // slow reader still holds the previous chunk, null means it went away.\n if (controller.desiredSize === 1) controller.enqueue(KEEP_ALIVE_FRAME);\n arm(controller);\n }, idleMs);\n timer.unref?.();\n };\n const transform = new TransformStream<Uint8Array, Uint8Array>(\n {\n start: arm,\n transform(chunk, controller) {\n controller.enqueue(chunk);\n arm(controller);\n },\n flush: stop,\n },\n undefined,\n { highWaterMark: 1 },\n );\n // `flush` stops the timer when the source completes. The pipe settling\n // covers the reader cancelling, on every supported Node version; the source\n // writer surfaces that cancellation to the caller.\n void source\n .pipeTo(transform.writable)\n .catch(() => {})\n .finally(stop);\n return transform.readable;\n}\n"],"mappings":";;;;;;;;AAKA,MAAa,0CAA0C;;;;;;AAOvD,MAAa,uCAAuC,KAAK,KAAK,KAAK;;;;;;AAOnE,SAAgB,mCACd,OACQ;CACR,MAAM,UAAU,SAAS;AACzB,KACE,CAAC,OAAO,SAAS,QAAQ,IACzB,UAAU,KACV,UAAU,oCAEV,OAAM,IAAI,WACR,qDAAqD,oCAAoC,QAAQ,OAAO,MAAM,GAC/G;AAEH,QAAO;;;;;;;AAQT,MAAM,mBAAmB,IAAI,aAAa,CAAC,OAAO,mBAAmB;;;;;;;;;AAUrE,SAAgB,aACd,QACA,QAC4B;CAC5B,IAAI;CACJ,MAAM,aAAa;AACjB,MAAI,UAAU,OAAW,cAAa,MAAM;AAC5C,UAAQ;;CAEV,MAAM,OAAO,eAA6D;AACxE,QAAM;AACN,UAAQ,iBAAiB;AAGvB,OAAI,WAAW,gBAAgB,EAAG,YAAW,QAAQ,iBAAiB;AACtE,OAAI,WAAW;KACd,OAAO;AACV,QAAM,SAAS;;CAEjB,MAAM,YAAY,IAAI,gBACpB;EACE,OAAO;EACP,UAAU,OAAO,YAAY;AAC3B,cAAW,QAAQ,MAAM;AACzB,OAAI,WAAW;;EAEjB,OAAO;EACR,EACD,QACA,EAAE,eAAe,GAAG,CACrB;AAID,CAAK,OACF,OAAO,UAAU,SAAS,CAC1B,YAAY,GAAG,CACf,QAAQ,KAAK;AAChB,QAAO,UAAU"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import "reflect-metadata";
|
|
2
|
+
//#region src/v2/runtime/handlers/shared/sse-keep-alive.ts
|
|
3
|
+
/**
|
|
4
|
+
* Default silence, in seconds, before an SSE response carries a keep-alive
|
|
5
|
+
* comment. Sits under the tightest common proxy idle timeouts (55–60 s) and
|
|
6
|
+
* beside `lockHeartbeatIntervalSeconds`, which shares the unit and default.
|
|
7
|
+
*/
|
|
8
|
+
const DEFAULT_SSE_KEEP_ALIVE_INTERVAL_SECONDS = 15;
|
|
9
|
+
/**
|
|
10
|
+
* Longest interval the timer can honour. Timers take a signed 32-bit
|
|
11
|
+
* millisecond delay; Node silently replaces anything larger with 1 ms, which
|
|
12
|
+
* would turn a keep-alive into a flood.
|
|
13
|
+
*/
|
|
14
|
+
const MAX_SSE_KEEP_ALIVE_INTERVAL_SECONDS = (2 ** 31 - 1) / 1e3;
|
|
15
|
+
/**
|
|
16
|
+
* Resolves the `sseKeepAliveIntervalSeconds` runtime option once, at
|
|
17
|
+
* construction, so every SSE call site reads one validated value.
|
|
18
|
+
* `undefined` takes the default; `0` disables the keep-alive.
|
|
19
|
+
*/
|
|
20
|
+
function resolveSseKeepAliveIntervalSeconds(value) {
|
|
21
|
+
const seconds = value ?? DEFAULT_SSE_KEEP_ALIVE_INTERVAL_SECONDS;
|
|
22
|
+
if (!Number.isFinite(seconds) || seconds < 0 || seconds > MAX_SSE_KEEP_ALIVE_INTERVAL_SECONDS) throw new RangeError(`sseKeepAliveIntervalSeconds must be between 0 and ${MAX_SSE_KEEP_ALIVE_INTERVAL_SECONDS}, got ${String(value)}`);
|
|
23
|
+
return seconds;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* SSE comment frame. Parsers drop comment lines, so no AG-UI event is
|
|
27
|
+
* invented and the verifier, history and middleware never see it.
|
|
28
|
+
* @see https://html.spec.whatwg.org/multipage/server-sent-events.html#authoring-notes
|
|
29
|
+
*/
|
|
30
|
+
const KEEP_ALIVE_FRAME = new TextEncoder().encode(": keep-alive\n\n");
|
|
31
|
+
/**
|
|
32
|
+
* Keeps a quiet SSE body open by writing a comment after `idleMs` of silence.
|
|
33
|
+
*
|
|
34
|
+
* Any byte from `source` resets the clock, so an active stream adds nothing
|
|
35
|
+
* to the wire. A back-pressured reader skips the tick instead of queueing
|
|
36
|
+
* frames. The timer stops when the pipe settles, which covers both the source
|
|
37
|
+
* completing and the reader cancelling, on every supported Node version.
|
|
38
|
+
*/
|
|
39
|
+
function keepAliveSse(source, idleMs) {
|
|
40
|
+
let timer;
|
|
41
|
+
const stop = () => {
|
|
42
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
43
|
+
timer = void 0;
|
|
44
|
+
};
|
|
45
|
+
const arm = (controller) => {
|
|
46
|
+
stop();
|
|
47
|
+
timer = setTimeout(() => {
|
|
48
|
+
if (controller.desiredSize === 1) controller.enqueue(KEEP_ALIVE_FRAME);
|
|
49
|
+
arm(controller);
|
|
50
|
+
}, idleMs);
|
|
51
|
+
timer.unref?.();
|
|
52
|
+
};
|
|
53
|
+
const transform = new TransformStream({
|
|
54
|
+
start: arm,
|
|
55
|
+
transform(chunk, controller) {
|
|
56
|
+
controller.enqueue(chunk);
|
|
57
|
+
arm(controller);
|
|
58
|
+
},
|
|
59
|
+
flush: stop
|
|
60
|
+
}, void 0, { highWaterMark: 1 });
|
|
61
|
+
source.pipeTo(transform.writable).catch(() => {}).finally(stop);
|
|
62
|
+
return transform.readable;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
//#endregion
|
|
66
|
+
export { keepAliveSse, resolveSseKeepAliveIntervalSeconds };
|
|
67
|
+
//# sourceMappingURL=sse-keep-alive.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sse-keep-alive.mjs","names":[],"sources":["../../../../../src/v2/runtime/handlers/shared/sse-keep-alive.ts"],"sourcesContent":["/**\n * Default silence, in seconds, before an SSE response carries a keep-alive\n * comment. Sits under the tightest common proxy idle timeouts (55–60 s) and\n * beside `lockHeartbeatIntervalSeconds`, which shares the unit and default.\n */\nexport const DEFAULT_SSE_KEEP_ALIVE_INTERVAL_SECONDS = 15;\n\n/**\n * Longest interval the timer can honour. Timers take a signed 32-bit\n * millisecond delay; Node silently replaces anything larger with 1 ms, which\n * would turn a keep-alive into a flood.\n */\nexport const MAX_SSE_KEEP_ALIVE_INTERVAL_SECONDS = (2 ** 31 - 1) / 1000;\n\n/**\n * Resolves the `sseKeepAliveIntervalSeconds` runtime option once, at\n * construction, so every SSE call site reads one validated value.\n * `undefined` takes the default; `0` disables the keep-alive.\n */\nexport function resolveSseKeepAliveIntervalSeconds(\n value: number | undefined,\n): number {\n const seconds = value ?? DEFAULT_SSE_KEEP_ALIVE_INTERVAL_SECONDS;\n if (\n !Number.isFinite(seconds) ||\n seconds < 0 ||\n seconds > MAX_SSE_KEEP_ALIVE_INTERVAL_SECONDS\n ) {\n throw new RangeError(\n `sseKeepAliveIntervalSeconds must be between 0 and ${MAX_SSE_KEEP_ALIVE_INTERVAL_SECONDS}, got ${String(value)}`,\n );\n }\n return seconds;\n}\n\n/**\n * SSE comment frame. Parsers drop comment lines, so no AG-UI event is\n * invented and the verifier, history and middleware never see it.\n * @see https://html.spec.whatwg.org/multipage/server-sent-events.html#authoring-notes\n */\nconst KEEP_ALIVE_FRAME = new TextEncoder().encode(\": keep-alive\\n\\n\");\n\n/**\n * Keeps a quiet SSE body open by writing a comment after `idleMs` of silence.\n *\n * Any byte from `source` resets the clock, so an active stream adds nothing\n * to the wire. A back-pressured reader skips the tick instead of queueing\n * frames. The timer stops when the pipe settles, which covers both the source\n * completing and the reader cancelling, on every supported Node version.\n */\nexport function keepAliveSse(\n source: ReadableStream<Uint8Array>,\n idleMs: number,\n): ReadableStream<Uint8Array> {\n let timer: ReturnType<typeof setTimeout> | undefined;\n const stop = () => {\n if (timer !== undefined) clearTimeout(timer);\n timer = undefined;\n };\n const arm = (controller: TransformStreamDefaultController<Uint8Array>) => {\n stop();\n timer = setTimeout(() => {\n // `desiredSize` is 1 only while the readable queue is empty; 0 means a\n // slow reader still holds the previous chunk, null means it went away.\n if (controller.desiredSize === 1) controller.enqueue(KEEP_ALIVE_FRAME);\n arm(controller);\n }, idleMs);\n timer.unref?.();\n };\n const transform = new TransformStream<Uint8Array, Uint8Array>(\n {\n start: arm,\n transform(chunk, controller) {\n controller.enqueue(chunk);\n arm(controller);\n },\n flush: stop,\n },\n undefined,\n { highWaterMark: 1 },\n );\n // `flush` stops the timer when the source completes. The pipe settling\n // covers the reader cancelling, on every supported Node version; the source\n // writer surfaces that cancellation to the caller.\n void source\n .pipeTo(transform.writable)\n .catch(() => {})\n .finally(stop);\n return transform.readable;\n}\n"],"mappings":";;;;;;;AAKA,MAAa,0CAA0C;;;;;;AAOvD,MAAa,uCAAuC,KAAK,KAAK,KAAK;;;;;;AAOnE,SAAgB,mCACd,OACQ;CACR,MAAM,UAAU,SAAS;AACzB,KACE,CAAC,OAAO,SAAS,QAAQ,IACzB,UAAU,KACV,UAAU,oCAEV,OAAM,IAAI,WACR,qDAAqD,oCAAoC,QAAQ,OAAO,MAAM,GAC/G;AAEH,QAAO;;;;;;;AAQT,MAAM,mBAAmB,IAAI,aAAa,CAAC,OAAO,mBAAmB;;;;;;;;;AAUrE,SAAgB,aACd,QACA,QAC4B;CAC5B,IAAI;CACJ,MAAM,aAAa;AACjB,MAAI,UAAU,OAAW,cAAa,MAAM;AAC5C,UAAQ;;CAEV,MAAM,OAAO,eAA6D;AACxE,QAAM;AACN,UAAQ,iBAAiB;AAGvB,OAAI,WAAW,gBAAgB,EAAG,YAAW,QAAQ,iBAAiB;AACtE,OAAI,WAAW;KACd,OAAO;AACV,QAAM,SAAS;;CAEjB,MAAM,YAAY,IAAI,gBACpB;EACE,OAAO;EACP,UAAU,OAAO,YAAY;AAC3B,cAAW,QAAQ,MAAM;AACzB,OAAI,WAAW;;EAEjB,OAAO;EACR,EACD,QACA,EAAE,eAAe,GAAG,CACrB;AAID,CAAK,OACF,OAAO,UAAU,SAAS,CAC1B,YAAY,GAAG,CACf,QAAQ,KAAK;AAChB,QAAO,UAAU"}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
require("reflect-metadata");
|
|
2
2
|
const require_runtime = require('../../../../_virtual/_rolldown/runtime.cjs');
|
|
3
3
|
const require_logger = require('../../../../lib/logger.cjs');
|
|
4
|
+
const require_sse_keep_alive = require('./sse-keep-alive.cjs');
|
|
4
5
|
const require_telemetry_client = require('../../telemetry/telemetry-client.cjs');
|
|
5
6
|
let _ag_ui_encoder = require("@ag-ui/encoder");
|
|
6
7
|
|
|
7
8
|
//#region src/v2/runtime/handlers/shared/sse-response.ts
|
|
8
|
-
function createSseEventResponse({ request, observableFactory, debugEventBus, agentId, debug, logger, telemetry: telemetry$1 = require_telemetry_client.default, executionSeed, captureTelemetry = true, runtimeErrorReporter, startTime }) {
|
|
9
|
+
function createSseEventResponse({ request, observableFactory, debugEventBus, agentId, debug, logger, telemetry: telemetry$1 = require_telemetry_client.default, executionSeed, captureTelemetry = true, runtimeErrorReporter, startTime, keepAliveIntervalSeconds }) {
|
|
10
|
+
const keepAliveSeconds = require_sse_keep_alive.resolveSseKeepAliveIntervalSeconds(keepAliveIntervalSeconds);
|
|
9
11
|
const stream = new TransformStream();
|
|
10
12
|
const writer = stream.writable.getWriter();
|
|
11
13
|
const encoder = new _ag_ui_encoder.EventEncoder();
|
|
@@ -124,7 +126,8 @@ function createSseEventResponse({ request, observableFactory, debugEventBus, age
|
|
|
124
126
|
request.signal.addEventListener("abort", () => {
|
|
125
127
|
subscription?.unsubscribe();
|
|
126
128
|
});
|
|
127
|
-
|
|
129
|
+
const body = keepAliveSeconds > 0 ? require_sse_keep_alive.keepAliveSse(stream.readable, keepAliveSeconds * 1e3) : stream.readable;
|
|
130
|
+
return new Response(body, {
|
|
128
131
|
status: 200,
|
|
129
132
|
headers: {
|
|
130
133
|
"Content-Type": "text/event-stream",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sse-response.cjs","names":["defaultTelemetry","EventEncoder","createLogger"],"sources":["../../../../../src/v2/runtime/handlers/shared/sse-response.ts"],"sourcesContent":["import type { BaseEvent } from \"@ag-ui/client\";\nimport { EventEncoder } from \"@ag-ui/encoder\";\nimport type { Observable, Subscription } from \"rxjs\";\nimport type { ResolvedDebugConfig } from \"@copilotkit/shared\";\nimport { createLogger } from \"../../../../v1-deprecated/lib/logger\";\nimport type { CopilotRuntimeLogger } from \"../../../../v1-deprecated/lib/logger\";\nimport { telemetry as defaultTelemetry } from \"../../telemetry\";\nimport type { TelemetryCapture } from \"../../telemetry/telemetry-client\";\nimport type { AgentExecutionResponseInfo } from \"../../telemetry/events\";\nimport type { DebugEventBus } from \"../../core/debug-event-bus\";\nimport type {\n RuntimeErrorPhase,\n RuntimeErrorReporter,\n} from \"../../core/runtime-error-reporter\";\n\ninterface CreateSseEventResponseParams {\n request: Request;\n observableFactory: () =>\n | Promise<Observable<BaseEvent>>\n | Observable<BaseEvent>;\n debugEventBus?: DebugEventBus;\n agentId?: string;\n debug?: ResolvedDebugConfig;\n /** Pre-created logger instance to avoid creating a new pino logger per request. */\n logger?: CopilotRuntimeLogger;\n /** Runtime-bound telemetry capture. Falls back for external direct callers. */\n telemetry?: TelemetryCapture;\n /**\n * Execution facts known before the stream opens, merged under anything the\n * upstream reports as it runs. Carries `llmHostClass`, which is knowable\n * from the agent's configuration and never appears on a streamed event.\n */\n executionSeed?: AgentExecutionResponseInfo;\n /**\n * Whether to emit `oss.runtime.agent_execution_stream_*` telemetry for this\n * stream. Defaults to `true`. The stateless `/suggest` path sets this to\n * `false`: a suggestion is a side-effect-free structured completion, not a\n * tracked run, and under `available: \"always\"` it fires often enough to flood\n * run telemetry.\n */\n captureTelemetry?: boolean;\n runtimeErrorReporter?: RuntimeErrorReporter;\n startTime?: number;\n}\n\nexport function createSseEventResponse({\n request,\n observableFactory,\n debugEventBus,\n agentId,\n debug,\n logger,\n telemetry = defaultTelemetry,\n executionSeed,\n captureTelemetry = true,\n runtimeErrorReporter,\n startTime,\n}: CreateSseEventResponseParams): Response {\n const stream = new TransformStream();\n const writer = stream.writable.getWriter();\n const encoder = new EventEncoder();\n let streamClosed = false;\n let debugThreadId = \"\";\n let debugRunId = \"\";\n\n const debugLogger = debug?.enabled\n ? (logger ??\n createLogger({ level: \"debug\", component: \"copilotkit-debug\" }))\n : undefined;\n\n const closeStream = async () => {\n if (!streamClosed) {\n try {\n await writer.close();\n streamClosed = true;\n } catch {\n // Stream already closed.\n }\n }\n };\n\n const logError = (error: unknown) => {\n console.error(\"Error running agent:\", error);\n console.error(\n \"Error stack:\",\n error instanceof Error ? error.stack : \"No stack trace\",\n );\n console.error(\"Error details:\", {\n name: error instanceof Error ? error.name : \"Unknown\",\n message: error instanceof Error ? error.message : String(error),\n cause: error instanceof Error ? error.cause : undefined,\n });\n };\n\n let subscription: Subscription | undefined;\n let agentErrorReported = false;\n\n const reportAgentError = (error: unknown, phase: RuntimeErrorPhase) => {\n if (agentErrorReported) return;\n agentErrorReported = true;\n runtimeErrorReporter?.report({\n request,\n error,\n operation: \"agent.run\",\n agentId,\n threadId: debugThreadId || undefined,\n runId: debugRunId || undefined,\n phase,\n startTime,\n });\n };\n\n (async () => {\n const observable = await observableFactory();\n\n if (captureTelemetry) {\n telemetry.capture(\"oss.runtime.agent_execution_stream_started\", {});\n }\n\n if (debug?.lifecycle) {\n debugLogger!.debug(\"SSE stream opened\");\n }\n\n let eventCount = 0;\n let loggedEventCount = 0;\n\n // Provider/model/LangGraph facts, scraped off raw upstream events as\n // they pass and reported once the stream finishes. This moved here from\n // the v1 TelemetryAgentRunner, which wrapped the runner purely to\n // collect it and emitted its own duplicate copy of every stream event\n // to carry it. v2 callers had no equivalent and reported `{}`.\n // Seeded rather than empty: `llmHostClass` comes from the agent's config,\n // not from the wire, so nothing streamed will ever fill it in. Scraped\n // fields still win, since `collectExecutionInfo` writes over this.\n const executionInfo: AgentExecutionResponseInfo = { ...executionSeed };\n\n subscription = observable.subscribe({\n next: async (event) => {\n collectExecutionInfo(event, executionInfo);\n\n // Extract threadId/runId from RUN_STARTED\n if (event.type === \"RUN_STARTED\") {\n const e = event as { threadId?: string; runId?: string };\n debugThreadId = e.threadId ?? \"\";\n debugRunId = e.runId ?? \"\";\n }\n\n if (event.type === \"RUN_ERROR\") {\n const e = event as {\n message?: unknown;\n threadId?: string;\n runId?: string;\n };\n debugThreadId = e.threadId ?? debugThreadId;\n debugRunId = e.runId ?? debugRunId;\n reportAgentError(\n new Error(\n typeof e.message === \"string\"\n ? e.message\n : \"Runner reported a run error\",\n ),\n \"sse.subscription\",\n );\n }\n\n // Broadcast to debug listeners BEFORE the stream-closed gate below.\n // Intentional: debug subscribers (e.g. the VS Code Inspector panel)\n // should still receive trailing events after the SSE client for\n // this request closed its connection — they're independent\n // consumers observing the underlying runtime, not the request's\n // response stream.\n //\n // Wrapped in try/catch so a buggy debug subscriber can't propagate\n // an exception into this observer — if the throw reached the\n // `next` callback it would get routed to `error` by RxJS, closing\n // the SSE stream for an unrelated reason. Log via `logError` and\n // move on.\n if (debugEventBus) {\n try {\n debugEventBus.broadcast(event, {\n agentId: agentId ?? \"\",\n threadId: debugThreadId,\n runId: debugRunId,\n });\n } catch (broadcastError) {\n logError(broadcastError);\n }\n }\n\n if (!request.signal.aborted && !streamClosed) {\n try {\n eventCount++;\n if (debug?.events) {\n loggedEventCount++;\n if (debug.verbose) {\n debugLogger!.debug({ event }, \"Event emitted\");\n } else {\n debugLogger!.debug(\n { type: event.type, ...summarizeEvent(event) },\n \"Event emitted\",\n );\n }\n }\n await writer.write(encoder.encodeBinary(event));\n } catch (error) {\n if (error instanceof Error && error.name === \"AbortError\") {\n streamClosed = true;\n } else {\n // Non-abort write failures (backpressure disconnects,\n // transform-stream exceptions, …) were previously swallowed\n // silently — `streamClosed` stayed `false` and the next\n // event re-attempted a broken writer. Log and mark the\n // stream closed so we stop trying.\n logError(error);\n streamClosed = true;\n }\n }\n }\n },\n error: async (error) => {\n reportAgentError(error, \"sse.subscription\");\n if (captureTelemetry) {\n telemetry.capture(\"oss.runtime.agent_execution_stream_errored\", {\n ...executionInfo,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n if (debug?.lifecycle) {\n debugLogger!.debug(\n { error: error instanceof Error ? error.message : String(error) },\n \"SSE stream errored\",\n );\n }\n logError(error);\n await closeStream();\n },\n complete: async () => {\n if (captureTelemetry) {\n telemetry.capture(\n \"oss.runtime.agent_execution_stream_ended\",\n executionInfo,\n );\n }\n if (debug?.lifecycle) {\n debugLogger!.debug(\n { eventCount, loggedEventCount },\n \"SSE stream completed\",\n );\n }\n await closeStream();\n },\n });\n\n // If the client disconnected before the subscription was created,\n // unsubscribe immediately to avoid leaking the observable.\n if (request.signal.aborted) {\n subscription.unsubscribe();\n }\n })().catch(async (error) => {\n reportAgentError(error, \"sse.factory\");\n logError(error);\n await closeStream();\n });\n\n request.signal.addEventListener(\"abort\", () => {\n subscription?.unsubscribe();\n });\n\n return new Response(stream.readable, {\n status: 200,\n headers: {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n },\n });\n}\n\nfunction summarizeEvent(event: BaseEvent): Record<string, unknown> {\n const e = event as any;\n const summary: Record<string, unknown> = {};\n\n if (e.messageId) summary.messageId = e.messageId;\n if (e.toolCallId) summary.toolCallId = e.toolCallId;\n if (e.toolCallName) summary.toolCallName = e.toolCallName;\n if (e.role) summary.role = e.role;\n if (e.delta != null && typeof e.delta === \"string\")\n summary.deltaLength = e.delta.length;\n if (e.snapshot && typeof e.snapshot === \"object\")\n summary.snapshotKeys = Object.keys(e.snapshot);\n if (e.delta && Array.isArray(e.delta))\n summary.operationCount = e.delta.length;\n if (e.threadId) summary.threadId = e.threadId;\n if (e.runId) summary.runId = e.runId;\n if (e.message) summary.message = e.message;\n if (e.code) summary.code = e.code;\n if (e.stepName) summary.stepName = e.stepName;\n\n return summary;\n}\n\n/**\n * Accumulate provider, model, and LangGraph facts from one upstream event.\n *\n * Mutates rather than returns so the caller keeps one record across the whole\n * stream: these arrive on different events and the last one wins. Only fields\n * the upstream actually sent are set, so an agent that reports none leaves the\n * record empty and the stream events carry `{}` as before.\n *\n * `rawEvent` is the untransformed upstream payload, present on AG-UI events\n * that wrap one. Its shape is the provider's, not ours, hence the narrowing.\n */\nfunction collectExecutionInfo(\n event: BaseEvent,\n into: AgentExecutionResponseInfo,\n): void {\n const rawEvent = (\n event as {\n rawEvent?: {\n metadata?: Record<string, unknown>;\n data?: Record<string, unknown>;\n };\n }\n ).rawEvent;\n if (!rawEvent) return;\n\n const model = (rawEvent.data as { output?: { model?: string } } | undefined)\n ?.output?.model;\n if (model) {\n into.model = model;\n // Carried forward from the v1 implementation, which set both from the\n // same field. The upstream sends no separate provider name.\n into.provider = model;\n }\n\n const metadata = rawEvent.metadata as\n | { langgraph_host?: string; langgraph_version?: string }\n | undefined;\n if (metadata?.langgraph_host) into.langGraphHost = metadata.langgraph_host;\n if (metadata?.langgraph_version) {\n into.langGraphVersion = metadata.langgraph_version;\n }\n}\n"],"mappings":";;;;;;;AA6CA,SAAgB,uBAAuB,EACrC,SACA,mBACA,eACA,SACA,OACA,QACA,yBAAYA,kCACZ,eACA,mBAAmB,MACnB,sBACA,aACyC;CACzC,MAAM,SAAS,IAAI,iBAAiB;CACpC,MAAM,SAAS,OAAO,SAAS,WAAW;CAC1C,MAAM,UAAU,IAAIC,6BAAc;CAClC,IAAI,eAAe;CACnB,IAAI,gBAAgB;CACpB,IAAI,aAAa;CAEjB,MAAM,cAAc,OAAO,UACtB,UACDC,4BAAa;EAAE,OAAO;EAAS,WAAW;EAAoB,CAAC,GAC/D;CAEJ,MAAM,cAAc,YAAY;AAC9B,MAAI,CAAC,aACH,KAAI;AACF,SAAM,OAAO,OAAO;AACpB,kBAAe;UACT;;CAMZ,MAAM,YAAY,UAAmB;AACnC,UAAQ,MAAM,wBAAwB,MAAM;AAC5C,UAAQ,MACN,gBACA,iBAAiB,QAAQ,MAAM,QAAQ,iBACxC;AACD,UAAQ,MAAM,kBAAkB;GAC9B,MAAM,iBAAiB,QAAQ,MAAM,OAAO;GAC5C,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;GAC/D,OAAO,iBAAiB,QAAQ,MAAM,QAAQ;GAC/C,CAAC;;CAGJ,IAAI;CACJ,IAAI,qBAAqB;CAEzB,MAAM,oBAAoB,OAAgB,UAA6B;AACrE,MAAI,mBAAoB;AACxB,uBAAqB;AACrB,wBAAsB,OAAO;GAC3B;GACA;GACA,WAAW;GACX;GACA,UAAU,iBAAiB;GAC3B,OAAO,cAAc;GACrB;GACA;GACD,CAAC;;AAGJ,EAAC,YAAY;EACX,MAAM,aAAa,MAAM,mBAAmB;AAE5C,MAAI,iBACF,aAAU,QAAQ,8CAA8C,EAAE,CAAC;AAGrE,MAAI,OAAO,UACT,aAAa,MAAM,oBAAoB;EAGzC,IAAI,aAAa;EACjB,IAAI,mBAAmB;EAUvB,MAAM,gBAA4C,EAAE,GAAG,eAAe;AAEtE,iBAAe,WAAW,UAAU;GAClC,MAAM,OAAO,UAAU;AACrB,yBAAqB,OAAO,cAAc;AAG1C,QAAI,MAAM,SAAS,eAAe;KAChC,MAAM,IAAI;AACV,qBAAgB,EAAE,YAAY;AAC9B,kBAAa,EAAE,SAAS;;AAG1B,QAAI,MAAM,SAAS,aAAa;KAC9B,MAAM,IAAI;AAKV,qBAAgB,EAAE,YAAY;AAC9B,kBAAa,EAAE,SAAS;AACxB,sBACE,IAAI,MACF,OAAO,EAAE,YAAY,WACjB,EAAE,UACF,8BACL,EACD,mBACD;;AAeH,QAAI,cACF,KAAI;AACF,mBAAc,UAAU,OAAO;MAC7B,SAAS,WAAW;MACpB,UAAU;MACV,OAAO;MACR,CAAC;aACK,gBAAgB;AACvB,cAAS,eAAe;;AAI5B,QAAI,CAAC,QAAQ,OAAO,WAAW,CAAC,aAC9B,KAAI;AACF;AACA,SAAI,OAAO,QAAQ;AACjB;AACA,UAAI,MAAM,QACR,aAAa,MAAM,EAAE,OAAO,EAAE,gBAAgB;UAE9C,aAAa,MACX;OAAE,MAAM,MAAM;OAAM,GAAG,eAAe,MAAM;OAAE,EAC9C,gBACD;;AAGL,WAAM,OAAO,MAAM,QAAQ,aAAa,MAAM,CAAC;aACxC,OAAO;AACd,SAAI,iBAAiB,SAAS,MAAM,SAAS,aAC3C,gBAAe;UACV;AAML,eAAS,MAAM;AACf,qBAAe;;;;GAKvB,OAAO,OAAO,UAAU;AACtB,qBAAiB,OAAO,mBAAmB;AAC3C,QAAI,iBACF,aAAU,QAAQ,8CAA8C;KAC9D,GAAG;KACH,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;KAC9D,CAAC;AAEJ,QAAI,OAAO,UACT,aAAa,MACX,EAAE,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,EAAE,EACjE,qBACD;AAEH,aAAS,MAAM;AACf,UAAM,aAAa;;GAErB,UAAU,YAAY;AACpB,QAAI,iBACF,aAAU,QACR,4CACA,cACD;AAEH,QAAI,OAAO,UACT,aAAa,MACX;KAAE;KAAY;KAAkB,EAChC,uBACD;AAEH,UAAM,aAAa;;GAEtB,CAAC;AAIF,MAAI,QAAQ,OAAO,QACjB,cAAa,aAAa;KAE1B,CAAC,MAAM,OAAO,UAAU;AAC1B,mBAAiB,OAAO,cAAc;AACtC,WAAS,MAAM;AACf,QAAM,aAAa;GACnB;AAEF,SAAQ,OAAO,iBAAiB,eAAe;AAC7C,gBAAc,aAAa;GAC3B;AAEF,QAAO,IAAI,SAAS,OAAO,UAAU;EACnC,QAAQ;EACR,SAAS;GACP,gBAAgB;GAChB,iBAAiB;GACjB,YAAY;GACb;EACF,CAAC;;AAGJ,SAAS,eAAe,OAA2C;CACjE,MAAM,IAAI;CACV,MAAM,UAAmC,EAAE;AAE3C,KAAI,EAAE,UAAW,SAAQ,YAAY,EAAE;AACvC,KAAI,EAAE,WAAY,SAAQ,aAAa,EAAE;AACzC,KAAI,EAAE,aAAc,SAAQ,eAAe,EAAE;AAC7C,KAAI,EAAE,KAAM,SAAQ,OAAO,EAAE;AAC7B,KAAI,EAAE,SAAS,QAAQ,OAAO,EAAE,UAAU,SACxC,SAAQ,cAAc,EAAE,MAAM;AAChC,KAAI,EAAE,YAAY,OAAO,EAAE,aAAa,SACtC,SAAQ,eAAe,OAAO,KAAK,EAAE,SAAS;AAChD,KAAI,EAAE,SAAS,MAAM,QAAQ,EAAE,MAAM,CACnC,SAAQ,iBAAiB,EAAE,MAAM;AACnC,KAAI,EAAE,SAAU,SAAQ,WAAW,EAAE;AACrC,KAAI,EAAE,MAAO,SAAQ,QAAQ,EAAE;AAC/B,KAAI,EAAE,QAAS,SAAQ,UAAU,EAAE;AACnC,KAAI,EAAE,KAAM,SAAQ,OAAO,EAAE;AAC7B,KAAI,EAAE,SAAU,SAAQ,WAAW,EAAE;AAErC,QAAO;;;;;;;;;;;;;AAcT,SAAS,qBACP,OACA,MACM;CACN,MAAM,WACJ,MAMA;AACF,KAAI,CAAC,SAAU;CAEf,MAAM,QAAS,SAAS,MACpB,QAAQ;AACZ,KAAI,OAAO;AACT,OAAK,QAAQ;AAGb,OAAK,WAAW;;CAGlB,MAAM,WAAW,SAAS;AAG1B,KAAI,UAAU,eAAgB,MAAK,gBAAgB,SAAS;AAC5D,KAAI,UAAU,kBACZ,MAAK,mBAAmB,SAAS"}
|
|
1
|
+
{"version":3,"file":"sse-response.cjs","names":["defaultTelemetry","resolveSseKeepAliveIntervalSeconds","EventEncoder","createLogger","keepAliveSse"],"sources":["../../../../../src/v2/runtime/handlers/shared/sse-response.ts"],"sourcesContent":["import type { BaseEvent } from \"@ag-ui/client\";\nimport { EventEncoder } from \"@ag-ui/encoder\";\nimport type { Observable, Subscription } from \"rxjs\";\nimport {\n keepAliveSse,\n resolveSseKeepAliveIntervalSeconds,\n} from \"./sse-keep-alive\";\nimport type { ResolvedDebugConfig } from \"@copilotkit/shared\";\nimport { createLogger } from \"../../../../v1-deprecated/lib/logger\";\nimport type { CopilotRuntimeLogger } from \"../../../../v1-deprecated/lib/logger\";\nimport { telemetry as defaultTelemetry } from \"../../telemetry\";\nimport type { TelemetryCapture } from \"../../telemetry/telemetry-client\";\nimport type { AgentExecutionResponseInfo } from \"../../telemetry/events\";\nimport type { DebugEventBus } from \"../../core/debug-event-bus\";\nimport type {\n RuntimeErrorPhase,\n RuntimeErrorReporter,\n} from \"../../core/runtime-error-reporter\";\n\ninterface CreateSseEventResponseParams {\n request: Request;\n observableFactory: () =>\n | Promise<Observable<BaseEvent>>\n | Observable<BaseEvent>;\n debugEventBus?: DebugEventBus;\n agentId?: string;\n debug?: ResolvedDebugConfig;\n /** Pre-created logger instance to avoid creating a new pino logger per request. */\n logger?: CopilotRuntimeLogger;\n /** Runtime-bound telemetry capture. Falls back for external direct callers. */\n telemetry?: TelemetryCapture;\n /**\n * Execution facts known before the stream opens, merged under anything the\n * upstream reports as it runs. Carries `llmHostClass`, which is knowable\n * from the agent's configuration and never appears on a streamed event.\n */\n executionSeed?: AgentExecutionResponseInfo;\n /**\n * Whether to emit `oss.runtime.agent_execution_stream_*` telemetry for this\n * stream. Defaults to `true`. The stateless `/suggest` path sets this to\n * `false`: a suggestion is a side-effect-free structured completion, not a\n * tracked run, and under `available: \"always\"` it fires often enough to flood\n * run telemetry.\n */\n captureTelemetry?: boolean;\n runtimeErrorReporter?: RuntimeErrorReporter;\n startTime?: number;\n /**\n * Seconds of silence before a `: keep-alive` SSE comment is written.\n * `0` disables the keep-alive. Resolved by\n * {@link resolveSseKeepAliveIntervalSeconds}, which supplies the default\n * and enforces the timer bound for every caller.\n */\n keepAliveIntervalSeconds?: number;\n}\n\nexport function createSseEventResponse({\n request,\n observableFactory,\n debugEventBus,\n agentId,\n debug,\n logger,\n telemetry = defaultTelemetry,\n executionSeed,\n captureTelemetry = true,\n runtimeErrorReporter,\n startTime,\n keepAliveIntervalSeconds,\n}: CreateSseEventResponseParams): Response {\n const keepAliveSeconds = resolveSseKeepAliveIntervalSeconds(\n keepAliveIntervalSeconds,\n );\n const stream = new TransformStream();\n const writer = stream.writable.getWriter();\n const encoder = new EventEncoder();\n let streamClosed = false;\n let debugThreadId = \"\";\n let debugRunId = \"\";\n\n const debugLogger = debug?.enabled\n ? (logger ??\n createLogger({ level: \"debug\", component: \"copilotkit-debug\" }))\n : undefined;\n\n const closeStream = async () => {\n if (!streamClosed) {\n try {\n await writer.close();\n streamClosed = true;\n } catch {\n // Stream already closed.\n }\n }\n };\n\n const logError = (error: unknown) => {\n console.error(\"Error running agent:\", error);\n console.error(\n \"Error stack:\",\n error instanceof Error ? error.stack : \"No stack trace\",\n );\n console.error(\"Error details:\", {\n name: error instanceof Error ? error.name : \"Unknown\",\n message: error instanceof Error ? error.message : String(error),\n cause: error instanceof Error ? error.cause : undefined,\n });\n };\n\n let subscription: Subscription | undefined;\n let agentErrorReported = false;\n\n const reportAgentError = (error: unknown, phase: RuntimeErrorPhase) => {\n if (agentErrorReported) return;\n agentErrorReported = true;\n runtimeErrorReporter?.report({\n request,\n error,\n operation: \"agent.run\",\n agentId,\n threadId: debugThreadId || undefined,\n runId: debugRunId || undefined,\n phase,\n startTime,\n });\n };\n\n (async () => {\n const observable = await observableFactory();\n\n if (captureTelemetry) {\n telemetry.capture(\"oss.runtime.agent_execution_stream_started\", {});\n }\n\n if (debug?.lifecycle) {\n debugLogger!.debug(\"SSE stream opened\");\n }\n\n let eventCount = 0;\n let loggedEventCount = 0;\n\n // Provider/model/LangGraph facts, scraped off raw upstream events as\n // they pass and reported once the stream finishes. This moved here from\n // the v1 TelemetryAgentRunner, which wrapped the runner purely to\n // collect it and emitted its own duplicate copy of every stream event\n // to carry it. v2 callers had no equivalent and reported `{}`.\n // Seeded rather than empty: `llmHostClass` comes from the agent's config,\n // not from the wire, so nothing streamed will ever fill it in. Scraped\n // fields still win, since `collectExecutionInfo` writes over this.\n const executionInfo: AgentExecutionResponseInfo = { ...executionSeed };\n\n subscription = observable.subscribe({\n next: async (event) => {\n collectExecutionInfo(event, executionInfo);\n\n // Extract threadId/runId from RUN_STARTED\n if (event.type === \"RUN_STARTED\") {\n const e = event as { threadId?: string; runId?: string };\n debugThreadId = e.threadId ?? \"\";\n debugRunId = e.runId ?? \"\";\n }\n\n if (event.type === \"RUN_ERROR\") {\n const e = event as {\n message?: unknown;\n threadId?: string;\n runId?: string;\n };\n debugThreadId = e.threadId ?? debugThreadId;\n debugRunId = e.runId ?? debugRunId;\n reportAgentError(\n new Error(\n typeof e.message === \"string\"\n ? e.message\n : \"Runner reported a run error\",\n ),\n \"sse.subscription\",\n );\n }\n\n // Broadcast to debug listeners BEFORE the stream-closed gate below.\n // Intentional: debug subscribers (e.g. the VS Code Inspector panel)\n // should still receive trailing events after the SSE client for\n // this request closed its connection — they're independent\n // consumers observing the underlying runtime, not the request's\n // response stream.\n //\n // Wrapped in try/catch so a buggy debug subscriber can't propagate\n // an exception into this observer — if the throw reached the\n // `next` callback it would get routed to `error` by RxJS, closing\n // the SSE stream for an unrelated reason. Log via `logError` and\n // move on.\n if (debugEventBus) {\n try {\n debugEventBus.broadcast(event, {\n agentId: agentId ?? \"\",\n threadId: debugThreadId,\n runId: debugRunId,\n });\n } catch (broadcastError) {\n logError(broadcastError);\n }\n }\n\n if (!request.signal.aborted && !streamClosed) {\n try {\n eventCount++;\n if (debug?.events) {\n loggedEventCount++;\n if (debug.verbose) {\n debugLogger!.debug({ event }, \"Event emitted\");\n } else {\n debugLogger!.debug(\n { type: event.type, ...summarizeEvent(event) },\n \"Event emitted\",\n );\n }\n }\n await writer.write(encoder.encodeBinary(event));\n } catch (error) {\n if (error instanceof Error && error.name === \"AbortError\") {\n streamClosed = true;\n } else {\n // Non-abort write failures (backpressure disconnects,\n // transform-stream exceptions, …) were previously swallowed\n // silently — `streamClosed` stayed `false` and the next\n // event re-attempted a broken writer. Log and mark the\n // stream closed so we stop trying.\n logError(error);\n streamClosed = true;\n }\n }\n }\n },\n error: async (error) => {\n reportAgentError(error, \"sse.subscription\");\n if (captureTelemetry) {\n telemetry.capture(\"oss.runtime.agent_execution_stream_errored\", {\n ...executionInfo,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n if (debug?.lifecycle) {\n debugLogger!.debug(\n { error: error instanceof Error ? error.message : String(error) },\n \"SSE stream errored\",\n );\n }\n logError(error);\n await closeStream();\n },\n complete: async () => {\n if (captureTelemetry) {\n telemetry.capture(\n \"oss.runtime.agent_execution_stream_ended\",\n executionInfo,\n );\n }\n if (debug?.lifecycle) {\n debugLogger!.debug(\n { eventCount, loggedEventCount },\n \"SSE stream completed\",\n );\n }\n await closeStream();\n },\n });\n\n // If the client disconnected before the subscription was created,\n // unsubscribe immediately to avoid leaking the observable.\n if (request.signal.aborted) {\n subscription.unsubscribe();\n }\n })().catch(async (error) => {\n reportAgentError(error, \"sse.factory\");\n logError(error);\n await closeStream();\n });\n\n request.signal.addEventListener(\"abort\", () => {\n subscription?.unsubscribe();\n });\n\n const body =\n keepAliveSeconds > 0\n ? keepAliveSse(stream.readable, keepAliveSeconds * 1_000)\n : stream.readable;\n\n return new Response(body, {\n status: 200,\n headers: {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n },\n });\n}\n\nfunction summarizeEvent(event: BaseEvent): Record<string, unknown> {\n const e = event as any;\n const summary: Record<string, unknown> = {};\n\n if (e.messageId) summary.messageId = e.messageId;\n if (e.toolCallId) summary.toolCallId = e.toolCallId;\n if (e.toolCallName) summary.toolCallName = e.toolCallName;\n if (e.role) summary.role = e.role;\n if (e.delta != null && typeof e.delta === \"string\")\n summary.deltaLength = e.delta.length;\n if (e.snapshot && typeof e.snapshot === \"object\")\n summary.snapshotKeys = Object.keys(e.snapshot);\n if (e.delta && Array.isArray(e.delta))\n summary.operationCount = e.delta.length;\n if (e.threadId) summary.threadId = e.threadId;\n if (e.runId) summary.runId = e.runId;\n if (e.message) summary.message = e.message;\n if (e.code) summary.code = e.code;\n if (e.stepName) summary.stepName = e.stepName;\n\n return summary;\n}\n\n/**\n * Accumulate provider, model, and LangGraph facts from one upstream event.\n *\n * Mutates rather than returns so the caller keeps one record across the whole\n * stream: these arrive on different events and the last one wins. Only fields\n * the upstream actually sent are set, so an agent that reports none leaves the\n * record empty and the stream events carry `{}` as before.\n *\n * `rawEvent` is the untransformed upstream payload, present on AG-UI events\n * that wrap one. Its shape is the provider's, not ours, hence the narrowing.\n */\nfunction collectExecutionInfo(\n event: BaseEvent,\n into: AgentExecutionResponseInfo,\n): void {\n const rawEvent = (\n event as {\n rawEvent?: {\n metadata?: Record<string, unknown>;\n data?: Record<string, unknown>;\n };\n }\n ).rawEvent;\n if (!rawEvent) return;\n\n const model = (rawEvent.data as { output?: { model?: string } } | undefined)\n ?.output?.model;\n if (model) {\n into.model = model;\n // Carried forward from the v1 implementation, which set both from the\n // same field. The upstream sends no separate provider name.\n into.provider = model;\n }\n\n const metadata = rawEvent.metadata as\n | { langgraph_host?: string; langgraph_version?: string }\n | undefined;\n if (metadata?.langgraph_host) into.langGraphHost = metadata.langgraph_host;\n if (metadata?.langgraph_version) {\n into.langGraphVersion = metadata.langgraph_version;\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import "reflect-metadata";
|
|
2
2
|
import { createLogger } from "../../../../lib/logger.mjs";
|
|
3
|
+
import { keepAliveSse, resolveSseKeepAliveIntervalSeconds } from "./sse-keep-alive.mjs";
|
|
3
4
|
import telemetry from "../../telemetry/telemetry-client.mjs";
|
|
4
5
|
import { EventEncoder } from "@ag-ui/encoder";
|
|
5
6
|
|
|
6
7
|
//#region src/v2/runtime/handlers/shared/sse-response.ts
|
|
7
|
-
function createSseEventResponse({ request, observableFactory, debugEventBus, agentId, debug, logger, telemetry: telemetry$1 = telemetry, executionSeed, captureTelemetry = true, runtimeErrorReporter, startTime }) {
|
|
8
|
+
function createSseEventResponse({ request, observableFactory, debugEventBus, agentId, debug, logger, telemetry: telemetry$1 = telemetry, executionSeed, captureTelemetry = true, runtimeErrorReporter, startTime, keepAliveIntervalSeconds }) {
|
|
9
|
+
const keepAliveSeconds = resolveSseKeepAliveIntervalSeconds(keepAliveIntervalSeconds);
|
|
8
10
|
const stream = new TransformStream();
|
|
9
11
|
const writer = stream.writable.getWriter();
|
|
10
12
|
const encoder = new EventEncoder();
|
|
@@ -123,7 +125,8 @@ function createSseEventResponse({ request, observableFactory, debugEventBus, age
|
|
|
123
125
|
request.signal.addEventListener("abort", () => {
|
|
124
126
|
subscription?.unsubscribe();
|
|
125
127
|
});
|
|
126
|
-
|
|
128
|
+
const body = keepAliveSeconds > 0 ? keepAliveSse(stream.readable, keepAliveSeconds * 1e3) : stream.readable;
|
|
129
|
+
return new Response(body, {
|
|
127
130
|
status: 200,
|
|
128
131
|
headers: {
|
|
129
132
|
"Content-Type": "text/event-stream",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sse-response.mjs","names":["defaultTelemetry"],"sources":["../../../../../src/v2/runtime/handlers/shared/sse-response.ts"],"sourcesContent":["import type { BaseEvent } from \"@ag-ui/client\";\nimport { EventEncoder } from \"@ag-ui/encoder\";\nimport type { Observable, Subscription } from \"rxjs\";\nimport type { ResolvedDebugConfig } from \"@copilotkit/shared\";\nimport { createLogger } from \"../../../../v1-deprecated/lib/logger\";\nimport type { CopilotRuntimeLogger } from \"../../../../v1-deprecated/lib/logger\";\nimport { telemetry as defaultTelemetry } from \"../../telemetry\";\nimport type { TelemetryCapture } from \"../../telemetry/telemetry-client\";\nimport type { AgentExecutionResponseInfo } from \"../../telemetry/events\";\nimport type { DebugEventBus } from \"../../core/debug-event-bus\";\nimport type {\n RuntimeErrorPhase,\n RuntimeErrorReporter,\n} from \"../../core/runtime-error-reporter\";\n\ninterface CreateSseEventResponseParams {\n request: Request;\n observableFactory: () =>\n | Promise<Observable<BaseEvent>>\n | Observable<BaseEvent>;\n debugEventBus?: DebugEventBus;\n agentId?: string;\n debug?: ResolvedDebugConfig;\n /** Pre-created logger instance to avoid creating a new pino logger per request. */\n logger?: CopilotRuntimeLogger;\n /** Runtime-bound telemetry capture. Falls back for external direct callers. */\n telemetry?: TelemetryCapture;\n /**\n * Execution facts known before the stream opens, merged under anything the\n * upstream reports as it runs. Carries `llmHostClass`, which is knowable\n * from the agent's configuration and never appears on a streamed event.\n */\n executionSeed?: AgentExecutionResponseInfo;\n /**\n * Whether to emit `oss.runtime.agent_execution_stream_*` telemetry for this\n * stream. Defaults to `true`. The stateless `/suggest` path sets this to\n * `false`: a suggestion is a side-effect-free structured completion, not a\n * tracked run, and under `available: \"always\"` it fires often enough to flood\n * run telemetry.\n */\n captureTelemetry?: boolean;\n runtimeErrorReporter?: RuntimeErrorReporter;\n startTime?: number;\n}\n\nexport function createSseEventResponse({\n request,\n observableFactory,\n debugEventBus,\n agentId,\n debug,\n logger,\n telemetry = defaultTelemetry,\n executionSeed,\n captureTelemetry = true,\n runtimeErrorReporter,\n startTime,\n}: CreateSseEventResponseParams): Response {\n const stream = new TransformStream();\n const writer = stream.writable.getWriter();\n const encoder = new EventEncoder();\n let streamClosed = false;\n let debugThreadId = \"\";\n let debugRunId = \"\";\n\n const debugLogger = debug?.enabled\n ? (logger ??\n createLogger({ level: \"debug\", component: \"copilotkit-debug\" }))\n : undefined;\n\n const closeStream = async () => {\n if (!streamClosed) {\n try {\n await writer.close();\n streamClosed = true;\n } catch {\n // Stream already closed.\n }\n }\n };\n\n const logError = (error: unknown) => {\n console.error(\"Error running agent:\", error);\n console.error(\n \"Error stack:\",\n error instanceof Error ? error.stack : \"No stack trace\",\n );\n console.error(\"Error details:\", {\n name: error instanceof Error ? error.name : \"Unknown\",\n message: error instanceof Error ? error.message : String(error),\n cause: error instanceof Error ? error.cause : undefined,\n });\n };\n\n let subscription: Subscription | undefined;\n let agentErrorReported = false;\n\n const reportAgentError = (error: unknown, phase: RuntimeErrorPhase) => {\n if (agentErrorReported) return;\n agentErrorReported = true;\n runtimeErrorReporter?.report({\n request,\n error,\n operation: \"agent.run\",\n agentId,\n threadId: debugThreadId || undefined,\n runId: debugRunId || undefined,\n phase,\n startTime,\n });\n };\n\n (async () => {\n const observable = await observableFactory();\n\n if (captureTelemetry) {\n telemetry.capture(\"oss.runtime.agent_execution_stream_started\", {});\n }\n\n if (debug?.lifecycle) {\n debugLogger!.debug(\"SSE stream opened\");\n }\n\n let eventCount = 0;\n let loggedEventCount = 0;\n\n // Provider/model/LangGraph facts, scraped off raw upstream events as\n // they pass and reported once the stream finishes. This moved here from\n // the v1 TelemetryAgentRunner, which wrapped the runner purely to\n // collect it and emitted its own duplicate copy of every stream event\n // to carry it. v2 callers had no equivalent and reported `{}`.\n // Seeded rather than empty: `llmHostClass` comes from the agent's config,\n // not from the wire, so nothing streamed will ever fill it in. Scraped\n // fields still win, since `collectExecutionInfo` writes over this.\n const executionInfo: AgentExecutionResponseInfo = { ...executionSeed };\n\n subscription = observable.subscribe({\n next: async (event) => {\n collectExecutionInfo(event, executionInfo);\n\n // Extract threadId/runId from RUN_STARTED\n if (event.type === \"RUN_STARTED\") {\n const e = event as { threadId?: string; runId?: string };\n debugThreadId = e.threadId ?? \"\";\n debugRunId = e.runId ?? \"\";\n }\n\n if (event.type === \"RUN_ERROR\") {\n const e = event as {\n message?: unknown;\n threadId?: string;\n runId?: string;\n };\n debugThreadId = e.threadId ?? debugThreadId;\n debugRunId = e.runId ?? debugRunId;\n reportAgentError(\n new Error(\n typeof e.message === \"string\"\n ? e.message\n : \"Runner reported a run error\",\n ),\n \"sse.subscription\",\n );\n }\n\n // Broadcast to debug listeners BEFORE the stream-closed gate below.\n // Intentional: debug subscribers (e.g. the VS Code Inspector panel)\n // should still receive trailing events after the SSE client for\n // this request closed its connection — they're independent\n // consumers observing the underlying runtime, not the request's\n // response stream.\n //\n // Wrapped in try/catch so a buggy debug subscriber can't propagate\n // an exception into this observer — if the throw reached the\n // `next` callback it would get routed to `error` by RxJS, closing\n // the SSE stream for an unrelated reason. Log via `logError` and\n // move on.\n if (debugEventBus) {\n try {\n debugEventBus.broadcast(event, {\n agentId: agentId ?? \"\",\n threadId: debugThreadId,\n runId: debugRunId,\n });\n } catch (broadcastError) {\n logError(broadcastError);\n }\n }\n\n if (!request.signal.aborted && !streamClosed) {\n try {\n eventCount++;\n if (debug?.events) {\n loggedEventCount++;\n if (debug.verbose) {\n debugLogger!.debug({ event }, \"Event emitted\");\n } else {\n debugLogger!.debug(\n { type: event.type, ...summarizeEvent(event) },\n \"Event emitted\",\n );\n }\n }\n await writer.write(encoder.encodeBinary(event));\n } catch (error) {\n if (error instanceof Error && error.name === \"AbortError\") {\n streamClosed = true;\n } else {\n // Non-abort write failures (backpressure disconnects,\n // transform-stream exceptions, …) were previously swallowed\n // silently — `streamClosed` stayed `false` and the next\n // event re-attempted a broken writer. Log and mark the\n // stream closed so we stop trying.\n logError(error);\n streamClosed = true;\n }\n }\n }\n },\n error: async (error) => {\n reportAgentError(error, \"sse.subscription\");\n if (captureTelemetry) {\n telemetry.capture(\"oss.runtime.agent_execution_stream_errored\", {\n ...executionInfo,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n if (debug?.lifecycle) {\n debugLogger!.debug(\n { error: error instanceof Error ? error.message : String(error) },\n \"SSE stream errored\",\n );\n }\n logError(error);\n await closeStream();\n },\n complete: async () => {\n if (captureTelemetry) {\n telemetry.capture(\n \"oss.runtime.agent_execution_stream_ended\",\n executionInfo,\n );\n }\n if (debug?.lifecycle) {\n debugLogger!.debug(\n { eventCount, loggedEventCount },\n \"SSE stream completed\",\n );\n }\n await closeStream();\n },\n });\n\n // If the client disconnected before the subscription was created,\n // unsubscribe immediately to avoid leaking the observable.\n if (request.signal.aborted) {\n subscription.unsubscribe();\n }\n })().catch(async (error) => {\n reportAgentError(error, \"sse.factory\");\n logError(error);\n await closeStream();\n });\n\n request.signal.addEventListener(\"abort\", () => {\n subscription?.unsubscribe();\n });\n\n return new Response(stream.readable, {\n status: 200,\n headers: {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n },\n });\n}\n\nfunction summarizeEvent(event: BaseEvent): Record<string, unknown> {\n const e = event as any;\n const summary: Record<string, unknown> = {};\n\n if (e.messageId) summary.messageId = e.messageId;\n if (e.toolCallId) summary.toolCallId = e.toolCallId;\n if (e.toolCallName) summary.toolCallName = e.toolCallName;\n if (e.role) summary.role = e.role;\n if (e.delta != null && typeof e.delta === \"string\")\n summary.deltaLength = e.delta.length;\n if (e.snapshot && typeof e.snapshot === \"object\")\n summary.snapshotKeys = Object.keys(e.snapshot);\n if (e.delta && Array.isArray(e.delta))\n summary.operationCount = e.delta.length;\n if (e.threadId) summary.threadId = e.threadId;\n if (e.runId) summary.runId = e.runId;\n if (e.message) summary.message = e.message;\n if (e.code) summary.code = e.code;\n if (e.stepName) summary.stepName = e.stepName;\n\n return summary;\n}\n\n/**\n * Accumulate provider, model, and LangGraph facts from one upstream event.\n *\n * Mutates rather than returns so the caller keeps one record across the whole\n * stream: these arrive on different events and the last one wins. Only fields\n * the upstream actually sent are set, so an agent that reports none leaves the\n * record empty and the stream events carry `{}` as before.\n *\n * `rawEvent` is the untransformed upstream payload, present on AG-UI events\n * that wrap one. Its shape is the provider's, not ours, hence the narrowing.\n */\nfunction collectExecutionInfo(\n event: BaseEvent,\n into: AgentExecutionResponseInfo,\n): void {\n const rawEvent = (\n event as {\n rawEvent?: {\n metadata?: Record<string, unknown>;\n data?: Record<string, unknown>;\n };\n }\n ).rawEvent;\n if (!rawEvent) return;\n\n const model = (rawEvent.data as { output?: { model?: string } } | undefined)\n ?.output?.model;\n if (model) {\n into.model = model;\n // Carried forward from the v1 implementation, which set both from the\n // same field. The upstream sends no separate provider name.\n into.provider = model;\n }\n\n const metadata = rawEvent.metadata as\n | { langgraph_host?: string; langgraph_version?: string }\n | undefined;\n if (metadata?.langgraph_host) into.langGraphHost = metadata.langgraph_host;\n if (metadata?.langgraph_version) {\n into.langGraphVersion = metadata.langgraph_version;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"sse-response.mjs","names":["defaultTelemetry"],"sources":["../../../../../src/v2/runtime/handlers/shared/sse-response.ts"],"sourcesContent":["import type { BaseEvent } from \"@ag-ui/client\";\nimport { EventEncoder } from \"@ag-ui/encoder\";\nimport type { Observable, Subscription } from \"rxjs\";\nimport {\n keepAliveSse,\n resolveSseKeepAliveIntervalSeconds,\n} from \"./sse-keep-alive\";\nimport type { ResolvedDebugConfig } from \"@copilotkit/shared\";\nimport { createLogger } from \"../../../../v1-deprecated/lib/logger\";\nimport type { CopilotRuntimeLogger } from \"../../../../v1-deprecated/lib/logger\";\nimport { telemetry as defaultTelemetry } from \"../../telemetry\";\nimport type { TelemetryCapture } from \"../../telemetry/telemetry-client\";\nimport type { AgentExecutionResponseInfo } from \"../../telemetry/events\";\nimport type { DebugEventBus } from \"../../core/debug-event-bus\";\nimport type {\n RuntimeErrorPhase,\n RuntimeErrorReporter,\n} from \"../../core/runtime-error-reporter\";\n\ninterface CreateSseEventResponseParams {\n request: Request;\n observableFactory: () =>\n | Promise<Observable<BaseEvent>>\n | Observable<BaseEvent>;\n debugEventBus?: DebugEventBus;\n agentId?: string;\n debug?: ResolvedDebugConfig;\n /** Pre-created logger instance to avoid creating a new pino logger per request. */\n logger?: CopilotRuntimeLogger;\n /** Runtime-bound telemetry capture. Falls back for external direct callers. */\n telemetry?: TelemetryCapture;\n /**\n * Execution facts known before the stream opens, merged under anything the\n * upstream reports as it runs. Carries `llmHostClass`, which is knowable\n * from the agent's configuration and never appears on a streamed event.\n */\n executionSeed?: AgentExecutionResponseInfo;\n /**\n * Whether to emit `oss.runtime.agent_execution_stream_*` telemetry for this\n * stream. Defaults to `true`. The stateless `/suggest` path sets this to\n * `false`: a suggestion is a side-effect-free structured completion, not a\n * tracked run, and under `available: \"always\"` it fires often enough to flood\n * run telemetry.\n */\n captureTelemetry?: boolean;\n runtimeErrorReporter?: RuntimeErrorReporter;\n startTime?: number;\n /**\n * Seconds of silence before a `: keep-alive` SSE comment is written.\n * `0` disables the keep-alive. Resolved by\n * {@link resolveSseKeepAliveIntervalSeconds}, which supplies the default\n * and enforces the timer bound for every caller.\n */\n keepAliveIntervalSeconds?: number;\n}\n\nexport function createSseEventResponse({\n request,\n observableFactory,\n debugEventBus,\n agentId,\n debug,\n logger,\n telemetry = defaultTelemetry,\n executionSeed,\n captureTelemetry = true,\n runtimeErrorReporter,\n startTime,\n keepAliveIntervalSeconds,\n}: CreateSseEventResponseParams): Response {\n const keepAliveSeconds = resolveSseKeepAliveIntervalSeconds(\n keepAliveIntervalSeconds,\n );\n const stream = new TransformStream();\n const writer = stream.writable.getWriter();\n const encoder = new EventEncoder();\n let streamClosed = false;\n let debugThreadId = \"\";\n let debugRunId = \"\";\n\n const debugLogger = debug?.enabled\n ? (logger ??\n createLogger({ level: \"debug\", component: \"copilotkit-debug\" }))\n : undefined;\n\n const closeStream = async () => {\n if (!streamClosed) {\n try {\n await writer.close();\n streamClosed = true;\n } catch {\n // Stream already closed.\n }\n }\n };\n\n const logError = (error: unknown) => {\n console.error(\"Error running agent:\", error);\n console.error(\n \"Error stack:\",\n error instanceof Error ? error.stack : \"No stack trace\",\n );\n console.error(\"Error details:\", {\n name: error instanceof Error ? error.name : \"Unknown\",\n message: error instanceof Error ? error.message : String(error),\n cause: error instanceof Error ? error.cause : undefined,\n });\n };\n\n let subscription: Subscription | undefined;\n let agentErrorReported = false;\n\n const reportAgentError = (error: unknown, phase: RuntimeErrorPhase) => {\n if (agentErrorReported) return;\n agentErrorReported = true;\n runtimeErrorReporter?.report({\n request,\n error,\n operation: \"agent.run\",\n agentId,\n threadId: debugThreadId || undefined,\n runId: debugRunId || undefined,\n phase,\n startTime,\n });\n };\n\n (async () => {\n const observable = await observableFactory();\n\n if (captureTelemetry) {\n telemetry.capture(\"oss.runtime.agent_execution_stream_started\", {});\n }\n\n if (debug?.lifecycle) {\n debugLogger!.debug(\"SSE stream opened\");\n }\n\n let eventCount = 0;\n let loggedEventCount = 0;\n\n // Provider/model/LangGraph facts, scraped off raw upstream events as\n // they pass and reported once the stream finishes. This moved here from\n // the v1 TelemetryAgentRunner, which wrapped the runner purely to\n // collect it and emitted its own duplicate copy of every stream event\n // to carry it. v2 callers had no equivalent and reported `{}`.\n // Seeded rather than empty: `llmHostClass` comes from the agent's config,\n // not from the wire, so nothing streamed will ever fill it in. Scraped\n // fields still win, since `collectExecutionInfo` writes over this.\n const executionInfo: AgentExecutionResponseInfo = { ...executionSeed };\n\n subscription = observable.subscribe({\n next: async (event) => {\n collectExecutionInfo(event, executionInfo);\n\n // Extract threadId/runId from RUN_STARTED\n if (event.type === \"RUN_STARTED\") {\n const e = event as { threadId?: string; runId?: string };\n debugThreadId = e.threadId ?? \"\";\n debugRunId = e.runId ?? \"\";\n }\n\n if (event.type === \"RUN_ERROR\") {\n const e = event as {\n message?: unknown;\n threadId?: string;\n runId?: string;\n };\n debugThreadId = e.threadId ?? debugThreadId;\n debugRunId = e.runId ?? debugRunId;\n reportAgentError(\n new Error(\n typeof e.message === \"string\"\n ? e.message\n : \"Runner reported a run error\",\n ),\n \"sse.subscription\",\n );\n }\n\n // Broadcast to debug listeners BEFORE the stream-closed gate below.\n // Intentional: debug subscribers (e.g. the VS Code Inspector panel)\n // should still receive trailing events after the SSE client for\n // this request closed its connection — they're independent\n // consumers observing the underlying runtime, not the request's\n // response stream.\n //\n // Wrapped in try/catch so a buggy debug subscriber can't propagate\n // an exception into this observer — if the throw reached the\n // `next` callback it would get routed to `error` by RxJS, closing\n // the SSE stream for an unrelated reason. Log via `logError` and\n // move on.\n if (debugEventBus) {\n try {\n debugEventBus.broadcast(event, {\n agentId: agentId ?? \"\",\n threadId: debugThreadId,\n runId: debugRunId,\n });\n } catch (broadcastError) {\n logError(broadcastError);\n }\n }\n\n if (!request.signal.aborted && !streamClosed) {\n try {\n eventCount++;\n if (debug?.events) {\n loggedEventCount++;\n if (debug.verbose) {\n debugLogger!.debug({ event }, \"Event emitted\");\n } else {\n debugLogger!.debug(\n { type: event.type, ...summarizeEvent(event) },\n \"Event emitted\",\n );\n }\n }\n await writer.write(encoder.encodeBinary(event));\n } catch (error) {\n if (error instanceof Error && error.name === \"AbortError\") {\n streamClosed = true;\n } else {\n // Non-abort write failures (backpressure disconnects,\n // transform-stream exceptions, …) were previously swallowed\n // silently — `streamClosed` stayed `false` and the next\n // event re-attempted a broken writer. Log and mark the\n // stream closed so we stop trying.\n logError(error);\n streamClosed = true;\n }\n }\n }\n },\n error: async (error) => {\n reportAgentError(error, \"sse.subscription\");\n if (captureTelemetry) {\n telemetry.capture(\"oss.runtime.agent_execution_stream_errored\", {\n ...executionInfo,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n if (debug?.lifecycle) {\n debugLogger!.debug(\n { error: error instanceof Error ? error.message : String(error) },\n \"SSE stream errored\",\n );\n }\n logError(error);\n await closeStream();\n },\n complete: async () => {\n if (captureTelemetry) {\n telemetry.capture(\n \"oss.runtime.agent_execution_stream_ended\",\n executionInfo,\n );\n }\n if (debug?.lifecycle) {\n debugLogger!.debug(\n { eventCount, loggedEventCount },\n \"SSE stream completed\",\n );\n }\n await closeStream();\n },\n });\n\n // If the client disconnected before the subscription was created,\n // unsubscribe immediately to avoid leaking the observable.\n if (request.signal.aborted) {\n subscription.unsubscribe();\n }\n })().catch(async (error) => {\n reportAgentError(error, \"sse.factory\");\n logError(error);\n await closeStream();\n });\n\n request.signal.addEventListener(\"abort\", () => {\n subscription?.unsubscribe();\n });\n\n const body =\n keepAliveSeconds > 0\n ? keepAliveSse(stream.readable, keepAliveSeconds * 1_000)\n : stream.readable;\n\n return new Response(body, {\n status: 200,\n headers: {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n },\n });\n}\n\nfunction summarizeEvent(event: BaseEvent): Record<string, unknown> {\n const e = event as any;\n const summary: Record<string, unknown> = {};\n\n if (e.messageId) summary.messageId = e.messageId;\n if (e.toolCallId) summary.toolCallId = e.toolCallId;\n if (e.toolCallName) summary.toolCallName = e.toolCallName;\n if (e.role) summary.role = e.role;\n if (e.delta != null && typeof e.delta === \"string\")\n summary.deltaLength = e.delta.length;\n if (e.snapshot && typeof e.snapshot === \"object\")\n summary.snapshotKeys = Object.keys(e.snapshot);\n if (e.delta && Array.isArray(e.delta))\n summary.operationCount = e.delta.length;\n if (e.threadId) summary.threadId = e.threadId;\n if (e.runId) summary.runId = e.runId;\n if (e.message) summary.message = e.message;\n if (e.code) summary.code = e.code;\n if (e.stepName) summary.stepName = e.stepName;\n\n return summary;\n}\n\n/**\n * Accumulate provider, model, and LangGraph facts from one upstream event.\n *\n * Mutates rather than returns so the caller keeps one record across the whole\n * stream: these arrive on different events and the last one wins. Only fields\n * the upstream actually sent are set, so an agent that reports none leaves the\n * record empty and the stream events carry `{}` as before.\n *\n * `rawEvent` is the untransformed upstream payload, present on AG-UI events\n * that wrap one. Its shape is the provider's, not ours, hence the narrowing.\n */\nfunction collectExecutionInfo(\n event: BaseEvent,\n into: AgentExecutionResponseInfo,\n): void {\n const rawEvent = (\n event as {\n rawEvent?: {\n metadata?: Record<string, unknown>;\n data?: Record<string, unknown>;\n };\n }\n ).rawEvent;\n if (!rawEvent) return;\n\n const model = (rawEvent.data as { output?: { model?: string } } | undefined)\n ?.output?.model;\n if (model) {\n into.model = model;\n // Carried forward from the v1 implementation, which set both from the\n // same field. The upstream sends no separate provider name.\n into.provider = model;\n }\n\n const metadata = rawEvent.metadata as\n | { langgraph_host?: string; langgraph_version?: string }\n | undefined;\n if (metadata?.langgraph_host) into.langGraphHost = metadata.langgraph_host;\n if (metadata?.langgraph_version) {\n into.langGraphVersion = metadata.langgraph_version;\n }\n}\n"],"mappings":"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"}
|
|
@@ -9,6 +9,7 @@ function handleSseConnect({ runtime, request, agentId, threadId, agent }) {
|
|
|
9
9
|
debugEventBus: runtime.debugEventBus,
|
|
10
10
|
agentId,
|
|
11
11
|
telemetry: runtime.telemetry,
|
|
12
|
+
keepAliveIntervalSeconds: runtime.sseKeepAliveIntervalSeconds,
|
|
12
13
|
observableFactory: () => runtime.runner.connect({
|
|
13
14
|
threadId,
|
|
14
15
|
agentId,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connect.cjs","names":["createSseEventResponse","mergeForwardableHeaders","resolveForwardHeadersPolicy"],"sources":["../../../../../src/v2/runtime/handlers/sse/connect.ts"],"sourcesContent":["import type { AbstractAgent } from \"@ag-ui/client\";\nimport type { CopilotRuntimeLike } from \"../../core/runtime\";\nimport { createSseEventResponse } from \"../shared/sse-response\";\nimport {\n mergeForwardableHeaders,\n resolveForwardHeadersPolicy,\n} from \"../header-utils\";\n\n/**\n * `headers` lives on the HTTP-backed agent configs (e.g. `HttpAgent`), not on\n * the base `AbstractAgent`. Mirror the runtime's own optional-headers shape so\n * we can read server-configured headers off the per-request clone without a\n * cast. See `agent-utils.ts`.\n */\ntype AgentWithHeaders = AbstractAgent & {\n headers?: Record<string, string>;\n};\n\ninterface HandleSseConnectParams {\n runtime: CopilotRuntimeLike;\n request: Request;\n agentId: string;\n threadId: string;\n /**\n * The per-request agent clone, carrying any server-configured `agent.headers`\n * (e.g. service-to-service auth). Used only to compute the merged header set\n * threaded into `runner.connect` below — see the note there for why that\n * merge is forward-looking plumbing rather than active outbound auth today.\n */\n agent?: AgentWithHeaders;\n}\n\nexport function handleSseConnect({\n runtime,\n request,\n agentId,\n threadId,\n agent,\n}: HandleSseConnectParams): Response {\n return createSseEventResponse({\n request,\n debugEventBus: runtime.debugEventBus,\n // Forward the real agentId so debug envelopes reflect the agent the\n // route resolved to — not the literal string \"connect\".\n agentId,\n telemetry: runtime.telemetry,\n observableFactory: () =>\n runtime.runner.connect({\n threadId,\n agentId,\n // Forward-looking plumbing: we compute the merged header set (server\n // `agent.headers` win on collision, case-insensitively; non-colliding\n // inbound headers still forward — see `mergeForwardableHeaders`, #5712)\n // and thread it into `runner.connect`. NO shipped runner consumes the\n // `headers` field of `AgentRunnerConnectRequest` today — every runner\n // (in-memory, intelligence, telemetry, sqlite) reads only `threadId`.\n // The real outbound header forwarding is the /run path, where\n // `cloneAgentForRequest` mutates `agent.headers` directly\n // (agent-utils.ts). This wiring exists so a future outbound-connecting\n // runner can pick the merged headers up without a route change; the\n // collision precedence noted here is purely about that merge, not about\n // middleware/mutation parity with /run.\n headers: mergeForwardableHeaders(\n agent?.headers,\n request,\n // Optional on `CopilotRuntimeLike` (non-breaking minor release);\n // coalesce a policy-less external implementor to the default resolved\n // policy (default-on denylist) instead of dereffing undefined.\n runtime.forwardHeadersPolicy ??\n resolveForwardHeadersPolicy(undefined),\n ),\n }),\n });\n}\n"],"mappings":";;;;;AAgCA,SAAgB,iBAAiB,EAC/B,SACA,SACA,SACA,UACA,SACmC;AACnC,QAAOA,4CAAuB;EAC5B;EACA,eAAe,QAAQ;EAGvB;EACA,WAAW,QAAQ;EACnB,yBACE,QAAQ,OAAO,QAAQ;GACrB;GACA;GAaA,SAASC,6CACP,OAAO,SACP,SAIA,QAAQ,wBACNC,iDAA4B,OAAU,CACzC;GACF,CAAC;EACL,CAAC"}
|
|
1
|
+
{"version":3,"file":"connect.cjs","names":["createSseEventResponse","mergeForwardableHeaders","resolveForwardHeadersPolicy"],"sources":["../../../../../src/v2/runtime/handlers/sse/connect.ts"],"sourcesContent":["import type { AbstractAgent } from \"@ag-ui/client\";\nimport type { CopilotRuntimeLike } from \"../../core/runtime\";\nimport { createSseEventResponse } from \"../shared/sse-response\";\nimport {\n mergeForwardableHeaders,\n resolveForwardHeadersPolicy,\n} from \"../header-utils\";\n\n/**\n * `headers` lives on the HTTP-backed agent configs (e.g. `HttpAgent`), not on\n * the base `AbstractAgent`. Mirror the runtime's own optional-headers shape so\n * we can read server-configured headers off the per-request clone without a\n * cast. See `agent-utils.ts`.\n */\ntype AgentWithHeaders = AbstractAgent & {\n headers?: Record<string, string>;\n};\n\ninterface HandleSseConnectParams {\n runtime: CopilotRuntimeLike;\n request: Request;\n agentId: string;\n threadId: string;\n /**\n * The per-request agent clone, carrying any server-configured `agent.headers`\n * (e.g. service-to-service auth). Used only to compute the merged header set\n * threaded into `runner.connect` below — see the note there for why that\n * merge is forward-looking plumbing rather than active outbound auth today.\n */\n agent?: AgentWithHeaders;\n}\n\nexport function handleSseConnect({\n runtime,\n request,\n agentId,\n threadId,\n agent,\n}: HandleSseConnectParams): Response {\n return createSseEventResponse({\n request,\n debugEventBus: runtime.debugEventBus,\n // Forward the real agentId so debug envelopes reflect the agent the\n // route resolved to — not the literal string \"connect\".\n agentId,\n telemetry: runtime.telemetry,\n keepAliveIntervalSeconds: runtime.sseKeepAliveIntervalSeconds,\n observableFactory: () =>\n runtime.runner.connect({\n threadId,\n agentId,\n // Forward-looking plumbing: we compute the merged header set (server\n // `agent.headers` win on collision, case-insensitively; non-colliding\n // inbound headers still forward — see `mergeForwardableHeaders`, #5712)\n // and thread it into `runner.connect`. NO shipped runner consumes the\n // `headers` field of `AgentRunnerConnectRequest` today — every runner\n // (in-memory, intelligence, telemetry, sqlite) reads only `threadId`.\n // The real outbound header forwarding is the /run path, where\n // `cloneAgentForRequest` mutates `agent.headers` directly\n // (agent-utils.ts). This wiring exists so a future outbound-connecting\n // runner can pick the merged headers up without a route change; the\n // collision precedence noted here is purely about that merge, not about\n // middleware/mutation parity with /run.\n headers: mergeForwardableHeaders(\n agent?.headers,\n request,\n // Optional on `CopilotRuntimeLike` (non-breaking minor release);\n // coalesce a policy-less external implementor to the default resolved\n // policy (default-on denylist) instead of dereffing undefined.\n runtime.forwardHeadersPolicy ??\n resolveForwardHeadersPolicy(undefined),\n ),\n }),\n });\n}\n"],"mappings":";;;;;AAgCA,SAAgB,iBAAiB,EAC/B,SACA,SACA,SACA,UACA,SACmC;AACnC,QAAOA,4CAAuB;EAC5B;EACA,eAAe,QAAQ;EAGvB;EACA,WAAW,QAAQ;EACnB,0BAA0B,QAAQ;EAClC,yBACE,QAAQ,OAAO,QAAQ;GACrB;GACA;GAaA,SAASC,6CACP,OAAO,SACP,SAIA,QAAQ,wBACNC,iDAA4B,OAAU,CACzC;GACF,CAAC;EACL,CAAC"}
|
|
@@ -9,6 +9,7 @@ function handleSseConnect({ runtime, request, agentId, threadId, agent }) {
|
|
|
9
9
|
debugEventBus: runtime.debugEventBus,
|
|
10
10
|
agentId,
|
|
11
11
|
telemetry: runtime.telemetry,
|
|
12
|
+
keepAliveIntervalSeconds: runtime.sseKeepAliveIntervalSeconds,
|
|
12
13
|
observableFactory: () => runtime.runner.connect({
|
|
13
14
|
threadId,
|
|
14
15
|
agentId,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connect.mjs","names":[],"sources":["../../../../../src/v2/runtime/handlers/sse/connect.ts"],"sourcesContent":["import type { AbstractAgent } from \"@ag-ui/client\";\nimport type { CopilotRuntimeLike } from \"../../core/runtime\";\nimport { createSseEventResponse } from \"../shared/sse-response\";\nimport {\n mergeForwardableHeaders,\n resolveForwardHeadersPolicy,\n} from \"../header-utils\";\n\n/**\n * `headers` lives on the HTTP-backed agent configs (e.g. `HttpAgent`), not on\n * the base `AbstractAgent`. Mirror the runtime's own optional-headers shape so\n * we can read server-configured headers off the per-request clone without a\n * cast. See `agent-utils.ts`.\n */\ntype AgentWithHeaders = AbstractAgent & {\n headers?: Record<string, string>;\n};\n\ninterface HandleSseConnectParams {\n runtime: CopilotRuntimeLike;\n request: Request;\n agentId: string;\n threadId: string;\n /**\n * The per-request agent clone, carrying any server-configured `agent.headers`\n * (e.g. service-to-service auth). Used only to compute the merged header set\n * threaded into `runner.connect` below — see the note there for why that\n * merge is forward-looking plumbing rather than active outbound auth today.\n */\n agent?: AgentWithHeaders;\n}\n\nexport function handleSseConnect({\n runtime,\n request,\n agentId,\n threadId,\n agent,\n}: HandleSseConnectParams): Response {\n return createSseEventResponse({\n request,\n debugEventBus: runtime.debugEventBus,\n // Forward the real agentId so debug envelopes reflect the agent the\n // route resolved to — not the literal string \"connect\".\n agentId,\n telemetry: runtime.telemetry,\n observableFactory: () =>\n runtime.runner.connect({\n threadId,\n agentId,\n // Forward-looking plumbing: we compute the merged header set (server\n // `agent.headers` win on collision, case-insensitively; non-colliding\n // inbound headers still forward — see `mergeForwardableHeaders`, #5712)\n // and thread it into `runner.connect`. NO shipped runner consumes the\n // `headers` field of `AgentRunnerConnectRequest` today — every runner\n // (in-memory, intelligence, telemetry, sqlite) reads only `threadId`.\n // The real outbound header forwarding is the /run path, where\n // `cloneAgentForRequest` mutates `agent.headers` directly\n // (agent-utils.ts). This wiring exists so a future outbound-connecting\n // runner can pick the merged headers up without a route change; the\n // collision precedence noted here is purely about that merge, not about\n // middleware/mutation parity with /run.\n headers: mergeForwardableHeaders(\n agent?.headers,\n request,\n // Optional on `CopilotRuntimeLike` (non-breaking minor release);\n // coalesce a policy-less external implementor to the default resolved\n // policy (default-on denylist) instead of dereffing undefined.\n runtime.forwardHeadersPolicy ??\n resolveForwardHeadersPolicy(undefined),\n ),\n }),\n });\n}\n"],"mappings":";;;;;AAgCA,SAAgB,iBAAiB,EAC/B,SACA,SACA,SACA,UACA,SACmC;AACnC,QAAO,uBAAuB;EAC5B;EACA,eAAe,QAAQ;EAGvB;EACA,WAAW,QAAQ;EACnB,yBACE,QAAQ,OAAO,QAAQ;GACrB;GACA;GAaA,SAAS,wBACP,OAAO,SACP,SAIA,QAAQ,wBACN,4BAA4B,OAAU,CACzC;GACF,CAAC;EACL,CAAC"}
|
|
1
|
+
{"version":3,"file":"connect.mjs","names":[],"sources":["../../../../../src/v2/runtime/handlers/sse/connect.ts"],"sourcesContent":["import type { AbstractAgent } from \"@ag-ui/client\";\nimport type { CopilotRuntimeLike } from \"../../core/runtime\";\nimport { createSseEventResponse } from \"../shared/sse-response\";\nimport {\n mergeForwardableHeaders,\n resolveForwardHeadersPolicy,\n} from \"../header-utils\";\n\n/**\n * `headers` lives on the HTTP-backed agent configs (e.g. `HttpAgent`), not on\n * the base `AbstractAgent`. Mirror the runtime's own optional-headers shape so\n * we can read server-configured headers off the per-request clone without a\n * cast. See `agent-utils.ts`.\n */\ntype AgentWithHeaders = AbstractAgent & {\n headers?: Record<string, string>;\n};\n\ninterface HandleSseConnectParams {\n runtime: CopilotRuntimeLike;\n request: Request;\n agentId: string;\n threadId: string;\n /**\n * The per-request agent clone, carrying any server-configured `agent.headers`\n * (e.g. service-to-service auth). Used only to compute the merged header set\n * threaded into `runner.connect` below — see the note there for why that\n * merge is forward-looking plumbing rather than active outbound auth today.\n */\n agent?: AgentWithHeaders;\n}\n\nexport function handleSseConnect({\n runtime,\n request,\n agentId,\n threadId,\n agent,\n}: HandleSseConnectParams): Response {\n return createSseEventResponse({\n request,\n debugEventBus: runtime.debugEventBus,\n // Forward the real agentId so debug envelopes reflect the agent the\n // route resolved to — not the literal string \"connect\".\n agentId,\n telemetry: runtime.telemetry,\n keepAliveIntervalSeconds: runtime.sseKeepAliveIntervalSeconds,\n observableFactory: () =>\n runtime.runner.connect({\n threadId,\n agentId,\n // Forward-looking plumbing: we compute the merged header set (server\n // `agent.headers` win on collision, case-insensitively; non-colliding\n // inbound headers still forward — see `mergeForwardableHeaders`, #5712)\n // and thread it into `runner.connect`. NO shipped runner consumes the\n // `headers` field of `AgentRunnerConnectRequest` today — every runner\n // (in-memory, intelligence, telemetry, sqlite) reads only `threadId`.\n // The real outbound header forwarding is the /run path, where\n // `cloneAgentForRequest` mutates `agent.headers` directly\n // (agent-utils.ts). This wiring exists so a future outbound-connecting\n // runner can pick the merged headers up without a route change; the\n // collision precedence noted here is purely about that merge, not about\n // middleware/mutation parity with /run.\n headers: mergeForwardableHeaders(\n agent?.headers,\n request,\n // Optional on `CopilotRuntimeLike` (non-breaking minor release);\n // coalesce a policy-less external implementor to the default resolved\n // policy (default-on denylist) instead of dereffing undefined.\n runtime.forwardHeadersPolicy ??\n resolveForwardHeadersPolicy(undefined),\n ),\n }),\n });\n}\n"],"mappings":";;;;;AAgCA,SAAgB,iBAAiB,EAC/B,SACA,SACA,SACA,UACA,SACmC;AACnC,QAAO,uBAAuB;EAC5B;EACA,eAAe,QAAQ;EAGvB;EACA,WAAW,QAAQ;EACnB,0BAA0B,QAAQ;EAClC,yBACE,QAAQ,OAAO,QAAQ;GACrB;GACA;GAaA,SAAS,wBACP,OAAO,SACP,SAIA,QAAQ,wBACN,4BAA4B,OAAU,CACzC;GACF,CAAC;EACL,CAAC"}
|
|
@@ -13,6 +13,7 @@ function handleSseRun({ runtime, request, agent, input, agentId, debug, logger,
|
|
|
13
13
|
runtimeErrorReporter: require_runtime_error_reporter.getRuntimeErrorReporter(runtime),
|
|
14
14
|
startTime,
|
|
15
15
|
telemetry: runtime.telemetry,
|
|
16
|
+
keepAliveIntervalSeconds: runtime.sseKeepAliveIntervalSeconds,
|
|
16
17
|
executionSeed: { llmHostClass: agent.modelHostClass ?? "unknown" },
|
|
17
18
|
observableFactory: () => runtime.runner.run({
|
|
18
19
|
threadId: input.threadId,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run.cjs","names":["createSseEventResponse","getRuntimeErrorReporter"],"sources":["../../../../../src/v2/runtime/handlers/sse/run.ts"],"sourcesContent":["import type { AbstractAgent, RunAgentInput } from \"@ag-ui/client\";\nimport type { ResolvedDebugConfig, ModelHostClass } from \"@copilotkit/shared\";\nimport type { CopilotRuntimeLogger } from \"../../../../v1-deprecated/lib/logger\";\nimport type { CopilotRuntimeLike } from \"../../core/runtime\";\nimport { getRuntimeErrorReporter } from \"../../core/runtime-error-reporter\";\nimport { createSseEventResponse } from \"../shared/sse-response\";\n\ninterface HandleSseRunParams {\n runtime: CopilotRuntimeLike;\n request: Request;\n agent: AbstractAgent;\n input: RunAgentInput;\n agentId: string;\n debug?: ResolvedDebugConfig;\n /** Pre-created logger instance to avoid creating a new pino logger per request. */\n logger?: CopilotRuntimeLogger;\n startTime?: number;\n}\n\nexport function handleSseRun({\n runtime,\n request,\n agent,\n input,\n agentId,\n debug,\n logger,\n startTime,\n}: HandleSseRunParams): Response {\n return createSseEventResponse({\n request,\n debugEventBus: runtime.debugEventBus,\n agentId,\n debug,\n logger,\n runtimeErrorReporter: getRuntimeErrorReporter(runtime),\n startTime,\n telemetry: runtime.telemetry,\n // Read structurally rather than via `instanceof BuiltInAgent`, which would\n // pull the agent module into this handler. Any agent can opt in by\n // exposing the same property.\n executionSeed: {\n llmHostClass:\n (agent as { modelHostClass?: ModelHostClass }).modelHostClass ??\n \"unknown\",\n },\n observableFactory: () =>\n runtime.runner.run({\n threadId: input.threadId,\n agent,\n input,\n }),\n });\n}\n"],"mappings":";;;;;AAmBA,SAAgB,aAAa,EAC3B,SACA,SACA,OACA,OACA,SACA,OACA,QACA,aAC+B;AAC/B,QAAOA,4CAAuB;EAC5B;EACA,eAAe,QAAQ;EACvB;EACA;EACA;EACA,sBAAsBC,uDAAwB,QAAQ;EACtD;EACA,WAAW,QAAQ;
|
|
1
|
+
{"version":3,"file":"run.cjs","names":["createSseEventResponse","getRuntimeErrorReporter"],"sources":["../../../../../src/v2/runtime/handlers/sse/run.ts"],"sourcesContent":["import type { AbstractAgent, RunAgentInput } from \"@ag-ui/client\";\nimport type { ResolvedDebugConfig, ModelHostClass } from \"@copilotkit/shared\";\nimport type { CopilotRuntimeLogger } from \"../../../../v1-deprecated/lib/logger\";\nimport type { CopilotRuntimeLike } from \"../../core/runtime\";\nimport { getRuntimeErrorReporter } from \"../../core/runtime-error-reporter\";\nimport { createSseEventResponse } from \"../shared/sse-response\";\n\ninterface HandleSseRunParams {\n runtime: CopilotRuntimeLike;\n request: Request;\n agent: AbstractAgent;\n input: RunAgentInput;\n agentId: string;\n debug?: ResolvedDebugConfig;\n /** Pre-created logger instance to avoid creating a new pino logger per request. */\n logger?: CopilotRuntimeLogger;\n startTime?: number;\n}\n\nexport function handleSseRun({\n runtime,\n request,\n agent,\n input,\n agentId,\n debug,\n logger,\n startTime,\n}: HandleSseRunParams): Response {\n return createSseEventResponse({\n request,\n debugEventBus: runtime.debugEventBus,\n agentId,\n debug,\n logger,\n runtimeErrorReporter: getRuntimeErrorReporter(runtime),\n startTime,\n telemetry: runtime.telemetry,\n keepAliveIntervalSeconds: runtime.sseKeepAliveIntervalSeconds,\n // Read structurally rather than via `instanceof BuiltInAgent`, which would\n // pull the agent module into this handler. Any agent can opt in by\n // exposing the same property.\n executionSeed: {\n llmHostClass:\n (agent as { modelHostClass?: ModelHostClass }).modelHostClass ??\n \"unknown\",\n },\n observableFactory: () =>\n runtime.runner.run({\n threadId: input.threadId,\n agent,\n input,\n }),\n });\n}\n"],"mappings":";;;;;AAmBA,SAAgB,aAAa,EAC3B,SACA,SACA,OACA,OACA,SACA,OACA,QACA,aAC+B;AAC/B,QAAOA,4CAAuB;EAC5B;EACA,eAAe,QAAQ;EACvB;EACA;EACA;EACA,sBAAsBC,uDAAwB,QAAQ;EACtD;EACA,WAAW,QAAQ;EACnB,0BAA0B,QAAQ;EAIlC,eAAe,EACb,cACG,MAA8C,kBAC/C,WACH;EACD,yBACE,QAAQ,OAAO,IAAI;GACjB,UAAU,MAAM;GAChB;GACA;GACD,CAAC;EACL,CAAC"}
|
|
@@ -13,6 +13,7 @@ function handleSseRun({ runtime, request, agent, input, agentId, debug, logger,
|
|
|
13
13
|
runtimeErrorReporter: getRuntimeErrorReporter(runtime),
|
|
14
14
|
startTime,
|
|
15
15
|
telemetry: runtime.telemetry,
|
|
16
|
+
keepAliveIntervalSeconds: runtime.sseKeepAliveIntervalSeconds,
|
|
16
17
|
executionSeed: { llmHostClass: agent.modelHostClass ?? "unknown" },
|
|
17
18
|
observableFactory: () => runtime.runner.run({
|
|
18
19
|
threadId: input.threadId,
|