@kognitivedev/vercel-ai-provider 0.2.22 → 0.2.28

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/src/index.ts CHANGED
@@ -4,7 +4,21 @@ import {
4
4
  generateText as aiGenerateText,
5
5
  type LanguageModel,
6
6
  } from "ai";
7
- import { randomUUID } from "crypto";
7
+ import { MemoryClient } from "@kognitivedev/memory";
8
+ import { createTopicMemoryTool, TOPIC_MEMORY_TOOL_ID } from "@kognitivedev/tools";
9
+ import {
10
+ normalizeAutomaticThreadTitleConfig,
11
+ buildRemoteLogPayload,
12
+ buildRemoteRunPayload,
13
+ buildRemoteTraceFinishPayload,
14
+ buildRemoteTracePreviews,
15
+ buildRemoteTraceStartPayload,
16
+ createRemoteExecutionContext,
17
+ isSessionScopedExecution,
18
+ isModerationError,
19
+ normalizeRemoteUsage,
20
+ type AutomaticThreadTitleConfig,
21
+ } from "@kognitivedev/shared";
8
22
  import { createPromptHubClient } from "@kognitivedev/prompthub";
9
23
  export { renderTemplate, type TemplateVariables } from "./template";
10
24
  import { renderTemplate } from "./template";
@@ -108,12 +122,18 @@ export interface PromptConfig {
108
122
  tag?: string;
109
123
  }
110
124
 
111
- export type CLStreamTextOptions = Omit<Parameters<typeof aiStreamText>[0], 'system' | 'prompt'> & {
112
- prompt: PromptConfig;
125
+ export interface CognitiveToolOptions {
126
+ autoTopicMemoryTool?: boolean;
127
+ }
128
+
129
+ export type CLStreamTextOptions = Omit<Parameters<typeof aiStreamText>[0], 'prompt'> & {
130
+ prompt?: PromptConfig;
131
+ kognitive?: CognitiveToolOptions;
113
132
  };
114
133
 
115
- export type CLGenerateTextOptions = Omit<Parameters<typeof aiGenerateText>[0], 'system' | 'prompt'> & {
116
- prompt: PromptConfig;
134
+ export type CLGenerateTextOptions = Omit<Parameters<typeof aiGenerateText>[0], 'prompt'> & {
135
+ prompt?: PromptConfig;
136
+ kognitive?: CognitiveToolOptions;
117
137
  };
118
138
 
119
139
  export interface LogConversationPayload {
@@ -140,6 +160,8 @@ export interface LogConversationPayload {
140
160
  durationMs?: number;
141
161
  tools?: Array<{ name: string; description?: string; parameters?: Record<string, unknown> }>;
142
162
  agentRunId?: string;
163
+ turnId?: string;
164
+ turnIndex?: number;
143
165
  metadata?: Record<string, unknown>;
144
166
  spans?: Array<{
145
167
  spanKey: string;
@@ -153,6 +175,11 @@ export interface LogConversationPayload {
153
175
  errorMessage?: string;
154
176
  metadata?: Record<string, unknown>;
155
177
  }>;
178
+ automaticTitle?: AutomaticThreadTitleConfig;
179
+ }
180
+
181
+ interface LogConversationResult {
182
+ generatedTitle: string | null;
156
183
  }
157
184
 
158
185
  export type CognitiveLayer = CLModelWrapper & {
@@ -162,7 +189,7 @@ export type CognitiveLayer = CLModelWrapper & {
162
189
  slug: string,
163
190
  userId?: string | { userId?: string; tag?: string }
164
191
  ) => Promise<CachedPrompt>;
165
- logConversation: (payload: LogConversationPayload) => Promise<void>;
192
+ logConversation: (payload: LogConversationPayload) => Promise<LogConversationResult | null>;
166
193
  triggerProcessing: (userId: string, sessionId: string) => void;
167
194
  clearSessionCache: (sessionKey?: string) => void;
168
195
  };
@@ -181,24 +208,6 @@ export interface CachedPrompt {
181
208
  variant?: "control" | "variant";
182
209
  }
183
210
 
184
- function getContentText(content: any): string {
185
- if (typeof content === "string") {
186
- if (content.includes("data:image/") && content.includes("base64,")) return "[Image]";
187
- return content;
188
- }
189
- if (!Array.isArray(content)) return "";
190
-
191
- return content.map((part) => {
192
- if (!part || typeof part !== "object") return "";
193
- if (typeof part.text === "string") return part.text;
194
- if (part.type === "tool-call" && typeof part.toolName === "string") return `Called ${part.toolName}`;
195
- if (part.type === "tool-result") return "Received tool result";
196
- if (part.type === "image" || part.type === "image_url") return "[Image]";
197
- if (part.type === "file") return "[File]";
198
- return "";
199
- }).filter(Boolean).join(" ");
200
- }
201
-
202
211
  /**
203
212
  * Unwraps V2/V3 ToolResultOutput discriminated union to a displayable value.
204
213
  * Stream ToolResult uses plain `result` (passthrough), while prompt ToolResultPart
@@ -223,16 +232,6 @@ function extractOutputValue(raw: unknown): unknown {
223
232
  }
224
233
  }
225
234
 
226
- function buildTracePreviews(messages: any[]): { requestPreview: string; responsePreview: string } {
227
- const request = [...messages].reverse().find((message) => message?.role === "user");
228
- const response = [...messages].reverse().find((message) => message?.role === "assistant");
229
-
230
- return {
231
- requestPreview: request ? getContentText(request.content).slice(0, 220) : "No request captured",
232
- responsePreview: response ? getContentText(response.content).slice(0, 240) : "No response captured",
233
- };
234
- }
235
-
236
235
  function buildTraceSpansFromMessages(messages: any[]): Array<{
237
236
  spanKey: string;
238
237
  parentSpanKey?: string;
@@ -328,6 +327,174 @@ const SESSION_KEY = Symbol.for("cl:session");
328
327
  // Session key → prompt metadata (populated by cl.streamText/cl.generateText, read by middleware)
329
328
  const sessionPromptMetadata = new Map<string, { promptSlug: string; promptVersion: number; promptId: string; tag?: string; abTestId?: string; variant?: "control" | "variant" }>();
330
329
 
330
+ function toAISDKTopicMemoryTool(
331
+ userId: string,
332
+ baseUrl: string,
333
+ apiKey: string,
334
+ ): Record<string, unknown> {
335
+ const tool = createTopicMemoryTool({ apiKey, baseUrl });
336
+
337
+ return {
338
+ description: tool.description,
339
+ inputSchema: tool.inputSchema,
340
+ execute: async (input: unknown) => {
341
+ const result = await tool.execute(input as any, {
342
+ abortSignal: new AbortController().signal,
343
+ resourceId: { userId },
344
+ metadata: undefined,
345
+ emit: () => {},
346
+ });
347
+
348
+ return tool.toModelOutput ? tool.toModelOutput(result) : result;
349
+ },
350
+ };
351
+ }
352
+
353
+ function withAutoInjectedTools(
354
+ params: Record<string, unknown>,
355
+ session: { userId: string; projectId?: string; sessionId?: string } | undefined,
356
+ baseUrl: string,
357
+ apiKey: string,
358
+ options?: CognitiveToolOptions,
359
+ ): Record<string, unknown> {
360
+ if (!options?.autoTopicMemoryTool || !isValidId(session?.userId)) {
361
+ return params;
362
+ }
363
+
364
+ const existingTools = params.tools;
365
+ if (existingTools && (typeof existingTools !== "object" || Array.isArray(existingTools))) {
366
+ return params;
367
+ }
368
+
369
+ const toolSet = { ...((existingTools as Record<string, unknown> | undefined) ?? {}) };
370
+ if (TOPIC_MEMORY_TOOL_ID in toolSet) {
371
+ return params;
372
+ }
373
+
374
+ toolSet[TOPIC_MEMORY_TOOL_ID] = toAISDKTopicMemoryTool(session.userId, baseUrl, apiKey);
375
+ return {
376
+ ...params,
377
+ tools: toolSet,
378
+ };
379
+ }
380
+
381
+ export function toMessageEventStream(
382
+ result: { fullStream: AsyncIterable<any> },
383
+ ): ReadableStream<{ event: string; data: unknown }> {
384
+ return new ReadableStream({
385
+ async start(controller) {
386
+ try {
387
+ for await (const chunk of result.fullStream) {
388
+ if (chunk.type === "text-delta" && chunk.delta) {
389
+ controller.enqueue({
390
+ event: "messages",
391
+ data: { token: chunk.delta },
392
+ });
393
+ continue;
394
+ }
395
+
396
+ if (chunk.type === "tool-call") {
397
+ controller.enqueue({
398
+ event: "messages",
399
+ data: {
400
+ type: "tool-call",
401
+ id: chunk.toolCallId,
402
+ name: chunk.toolName,
403
+ input: chunk.input,
404
+ },
405
+ });
406
+ continue;
407
+ }
408
+
409
+ if (chunk.type === "tool-result") {
410
+ controller.enqueue({
411
+ event: "messages",
412
+ data: {
413
+ type: "tool-result",
414
+ id: chunk.toolCallId,
415
+ name: chunk.toolName,
416
+ result: chunk.result,
417
+ },
418
+ });
419
+ continue;
420
+ }
421
+
422
+ if (chunk.type === "error") {
423
+ controller.enqueue({
424
+ event: "debug",
425
+ data: {
426
+ type: "error",
427
+ message: chunk.error instanceof Error ? chunk.error.message : String(chunk.error),
428
+ },
429
+ });
430
+ }
431
+ }
432
+ } finally {
433
+ controller.close();
434
+ }
435
+ },
436
+ });
437
+ }
438
+
439
+ export function toGeneratedMessageEventStream(
440
+ result: {
441
+ text?: string;
442
+ toolCalls?: Array<{ toolCallId?: string; toolName?: string; input?: unknown }>;
443
+ toolResults?: Array<{ toolCallId?: string; toolName?: string; output?: unknown; result?: unknown; isError?: boolean }>;
444
+ steps?: Array<{
445
+ toolCalls?: Array<{ toolCallId?: string; toolName?: string; input?: unknown }>;
446
+ toolResults?: Array<{ toolCallId?: string; toolName?: string; output?: unknown; result?: unknown; isError?: boolean }>;
447
+ }>;
448
+ },
449
+ ): ReadableStream<{ event: string; data: unknown }> {
450
+ return new ReadableStream({
451
+ start(controller) {
452
+ const steps = result.steps?.length
453
+ ? result.steps
454
+ : [{
455
+ toolCalls: result.toolCalls,
456
+ toolResults: result.toolResults,
457
+ }];
458
+
459
+ for (const step of steps) {
460
+ for (const toolCall of step.toolCalls ?? []) {
461
+ controller.enqueue({
462
+ event: "messages",
463
+ data: {
464
+ type: "tool-call",
465
+ id: toolCall.toolCallId,
466
+ name: toolCall.toolName ?? "tool",
467
+ input: toolCall.input,
468
+ },
469
+ });
470
+ }
471
+
472
+ for (const toolResult of step.toolResults ?? []) {
473
+ controller.enqueue({
474
+ event: "messages",
475
+ data: {
476
+ type: "tool-result",
477
+ id: toolResult.toolCallId,
478
+ name: toolResult.toolName ?? "tool",
479
+ result: toolResult.output ?? toolResult.result,
480
+ ...(toolResult.isError ? { isError: true } : {}),
481
+ },
482
+ });
483
+ }
484
+ }
485
+
486
+ if (result.text) {
487
+ controller.enqueue({
488
+ event: "messages",
489
+ data: { token: result.text },
490
+ });
491
+ }
492
+
493
+ controller.close();
494
+ },
495
+ });
496
+ }
497
+
331
498
  /**
332
499
  * Check if any system message already contains a <MemoryContext> block.
333
500
  */
@@ -357,6 +524,11 @@ export function createCognitiveLayer(config: {
357
524
  // Default to 'info' log level
358
525
  const logLevel = clConfig.logLevel || 'info';
359
526
  const logger = createLogger(logLevel);
527
+ const memoryClient = new MemoryClient({
528
+ apiKey: clConfig.apiKey,
529
+ baseUrl,
530
+ logger: logger as any,
531
+ });
360
532
 
361
533
  const authHeaders = {
362
534
  "Content-Type": "application/json",
@@ -368,6 +540,23 @@ export function createCognitiveLayer(config: {
368
540
  apiKey: clConfig.apiKey,
369
541
  logger,
370
542
  });
543
+ const sessionTurnIndexes = new Map<string, number>();
544
+
545
+ const reserveTurnIndex = (
546
+ agentName: string,
547
+ userId: string | undefined,
548
+ sessionId: string | undefined,
549
+ requestedRunScope?: string,
550
+ ): number | undefined => {
551
+ if (!isValidId(userId) || !isValidId(sessionId) || !isSessionScopedExecution(requestedRunScope, sessionId ? "session" : "invocation")) {
552
+ return undefined;
553
+ }
554
+
555
+ const key = `${agentName}:${getSessionKey(userId, sessionId)}`;
556
+ const nextIndex = sessionTurnIndexes.get(key) ?? 0;
557
+ sessionTurnIndexes.set(key, nextIndex + 1);
558
+ return nextIndex;
559
+ };
371
560
 
372
561
  const resolvePrompt = async (slug: string, userIdOrOptions?: string | { userId?: string; tag?: string }): Promise<CachedPrompt> => {
373
562
  const userId = typeof userIdOrOptions === "string"
@@ -409,7 +598,7 @@ export function createCognitiveLayer(config: {
409
598
 
410
599
  const logConversation = async (payload: LogConversationPayload) => {
411
600
  try {
412
- await fetch(`${baseUrl}/api/cognitive/log`, {
601
+ const response = await fetch(`${baseUrl}/api/cognitive/log`, {
413
602
  method: "POST",
414
603
  headers: authHeaders,
415
604
  body: JSON.stringify({
@@ -418,18 +607,74 @@ export function createCognitiveLayer(config: {
418
607
  timestamp: new Date().toISOString(),
419
608
  }),
420
609
  });
610
+ if (!response.ok) {
611
+ logger.warn("Log failed", { status: response.status });
612
+ return null;
613
+ }
614
+ const data = await response.json().catch(() => null) as { generatedTitle?: unknown } | null;
615
+ return {
616
+ generatedTitle: typeof data?.generatedTitle === "string" ? data.generatedTitle : null,
617
+ };
421
618
  } catch (e) {
422
619
  logger.error("Log failed", e);
620
+ return null;
423
621
  }
424
622
  };
425
623
 
426
- const triggerProcessing = (userId: string, sessionId: string) => {
427
- const run = () => {
428
- fetch(`${baseUrl}/api/cognitive/process`, {
624
+ const postAgentRun = async (payload: Record<string, unknown>) => {
625
+ try {
626
+ const response = await fetch(`${baseUrl}/api/cognitive/agent-run`, {
627
+ method: "POST",
628
+ headers: authHeaders,
629
+ body: JSON.stringify(payload),
630
+ });
631
+ if (!response.ok) {
632
+ logger.warn("Agent run request failed", { status: response.status });
633
+ return null;
634
+ }
635
+ return await response.json().catch(() => null);
636
+ } catch (e) {
637
+ logger.error("Agent run request failed", e);
638
+ return null;
639
+ }
640
+ };
641
+
642
+ const postTraceEvents = async (payload: Record<string, unknown>) => {
643
+ try {
644
+ const response = await fetch(`${baseUrl}/api/cognitive/trace-events`, {
429
645
  method: "POST",
430
646
  headers: authHeaders,
431
- body: JSON.stringify({ userId, sessionId }),
432
- }).catch(e => logger.error("Process trigger failed", e));
647
+ body: JSON.stringify(payload),
648
+ });
649
+ if (!response.ok) {
650
+ logger.warn("Trace events request failed", { status: response.status });
651
+ return null;
652
+ }
653
+ return await response.json().catch(() => null);
654
+ } catch (e) {
655
+ logger.error("Trace events request failed", e);
656
+ return null;
657
+ }
658
+ };
659
+
660
+ const appendLiveTraceEvents = async (payload: {
661
+ traceDbId?: string;
662
+ traceId: string;
663
+ sessionDbId?: string;
664
+ events: Array<{ eventType: string; spanKey?: string; status?: string; payload?: Record<string, unknown> }>;
665
+ }) => {
666
+ await postTraceEvents({
667
+ traceDbId: payload.traceDbId,
668
+ traceId: payload.traceId,
669
+ sessionDbId: payload.sessionDbId,
670
+ events: payload.events,
671
+ });
672
+ };
673
+
674
+ const triggerProcessing = (userId: string, sessionId: string) => {
675
+ const run = () => {
676
+ memoryClient.triggerProcessing(userId, sessionId)
677
+ .catch((e) => logger.error("Process trigger failed", e));
433
678
  };
434
679
 
435
680
  if (processDelay > 0) {
@@ -462,6 +707,33 @@ export function createCognitiveLayer(config: {
462
707
  return { nextParams, messages: updated, mode: "prepend-system" };
463
708
  };
464
709
 
710
+ const getKognitiveProviderConfig = (params: any): Record<string, unknown> | undefined => {
711
+ const providerOptionsKognitive = params?.providerOptions?.kognitive;
712
+ if (providerOptionsKognitive && typeof providerOptionsKognitive === "object") {
713
+ return providerOptionsKognitive as Record<string, unknown>;
714
+ }
715
+
716
+ const providerMetadataKognitive = params?.providerMetadata?.kognitive;
717
+ if (providerMetadataKognitive && typeof providerMetadataKognitive === "object") {
718
+ return providerMetadataKognitive as Record<string, unknown>;
719
+ }
720
+
721
+ return undefined;
722
+ };
723
+
724
+ const resolveRequestedRunScope = (params: any): string | undefined => {
725
+ const direct = typeof params?.runScope === "string" ? params.runScope : undefined;
726
+ const kognitiveConfig = getKognitiveProviderConfig(params);
727
+ const providerMetadataScope = typeof kognitiveConfig?.runScope === "string"
728
+ ? kognitiveConfig.runScope
729
+ : undefined;
730
+ return direct ?? providerMetadataScope;
731
+ };
732
+
733
+ const resolveAutomaticTitleConfig = (params: any): AutomaticThreadTitleConfig | undefined => {
734
+ return normalizeAutomaticThreadTitleConfig(getKognitiveProviderConfig(params)?.automaticTitle);
735
+ };
736
+
465
737
  const buildMiddleware = (userId: string | undefined, projectId: string | undefined, sessionId: string | undefined, modelId: string) => ({
466
738
  specificationVersion: 'v3' as const,
467
739
  async transformParams({ params }: { params: any }) {
@@ -484,68 +756,38 @@ export function createCognitiveLayer(config: {
484
756
  // 3) Fetch snapshot only if not cached
485
757
  if (systemPromptToAdd === undefined) {
486
758
  try {
487
- const url = `${baseUrl}/api/cognitive/snapshot?userId=${userId}`;
488
759
  logger.debug("Fetching snapshot from backend", {
489
760
  userId,
490
761
  sessionId,
491
- url,
762
+ url: `${baseUrl}/api/cognitive/snapshot?userId=${userId}`,
492
763
  baseUrl,
493
764
  apiKeyHint: maskSecret(clConfig.apiKey),
494
765
  });
495
- const res = await fetch(url, {
496
- headers: { "Authorization": `Bearer ${clConfig.apiKey}` },
497
- });
498
- logger.debug("Snapshot response received", {
766
+ const snapshot = await memoryClient.getSnapshot(userId);
767
+ const systemBlock = snapshot?.systemBlock || "";
768
+ const userContextBlock = snapshot?.userContextBlock || "";
769
+ const topicIndexBlock = snapshot?.topicIndexBlock || "";
770
+ const topicContextBlock = snapshot?.topicContextBlock || "";
771
+ systemPromptToAdd = snapshot ? memoryClient.buildMemoryBlock(snapshot) : "";
772
+
773
+ sessionSnapshots.set(sessionKey, systemPromptToAdd);
774
+
775
+ logger.info("Snapshot fetched and cached", {
499
776
  userId,
500
777
  sessionId,
501
- status: res.status,
502
- ok: res.ok,
503
- contentType: res.headers.get("content-type"),
778
+ sessionKey,
779
+ systemLen: systemBlock.length,
780
+ userLen: userContextBlock.length,
781
+ topicIndexLen: topicIndexBlock.length,
782
+ topicContextLen: topicContextBlock.length,
783
+ });
784
+ logger.debug("Full snapshot data", {
785
+ systemBlock,
786
+ userContextBlock,
787
+ topicIndexBlock,
788
+ topicContextBlock,
789
+ rawData: snapshot,
504
790
  });
505
- if (res.ok) {
506
- const data = await res.json();
507
- const systemBlock = data.systemBlock || "";
508
- const userContextBlock = data.userContextBlock || "";
509
- systemPromptToAdd =
510
- systemBlock !== "" || userContextBlock !== ""
511
- ? `
512
- <MemoryContext>
513
- Use the following memory to stay consistent. Prefer UserContext facts for answers; AgentHeuristics guide style, safety, and priorities.
514
- ${systemBlock || "None"}
515
- ${userContextBlock || "None"}
516
- </MemoryContext>
517
- `.trim()
518
- : "";
519
-
520
- // Cache the snapshot for this session
521
- sessionSnapshots.set(sessionKey, systemPromptToAdd);
522
-
523
- logger.info("Snapshot fetched and cached", {
524
- userId,
525
- sessionId,
526
- sessionKey,
527
- systemLen: systemBlock.length,
528
- userLen: userContextBlock.length,
529
- });
530
- // At debug level, log the full snapshot data
531
- logger.debug("Full snapshot data", {
532
- systemBlock,
533
- userContextBlock,
534
- rawData: data,
535
- });
536
- } else {
537
- const body = await res.text();
538
- logger.warn("Snapshot fetch failed", { status: res.status });
539
- logger.debug("Snapshot response body preview", {
540
- userId,
541
- projectId,
542
- sessionId,
543
- status: res.status,
544
- bodyPreview: previewText(body),
545
- });
546
- systemPromptToAdd = "";
547
- sessionSnapshots.set(sessionKey, systemPromptToAdd);
548
- }
549
791
  } catch (e) {
550
792
  logger.warn("Failed to fetch snapshot", e);
551
793
  systemPromptToAdd = "";
@@ -576,10 +818,86 @@ ${userContextBlock || "None"}
576
818
 
577
819
  async wrapGenerate({ doGenerate, params }: { doGenerate: any; params: any }) {
578
820
  const startedAt = new Date();
821
+ const requestedRunScope = resolveRequestedRunScope(params);
822
+ const remoteAgentName = clConfig.appId ?? modelId;
823
+ const turnIndex = reserveTurnIndex(remoteAgentName, userId, sessionId, requestedRunScope);
824
+ const remoteExecution = createRemoteExecutionContext({
825
+ agentName: remoteAgentName,
826
+ sessionId,
827
+ requestedRunScope,
828
+ runId: getKognitiveProviderConfig(params)?.agentRunId as string | undefined,
829
+ turnIndex,
830
+ });
831
+ const traceId = remoteExecution.traceId;
832
+ let liveTraceDbId: string | undefined;
833
+ if (isValidId(userId) && isValidId(sessionId)) {
834
+ const messagesInput = (params as any).prompt || (params as any).messages || [];
835
+ const { requestPreview } = buildRemoteTracePreviews(messagesInput);
836
+ await postAgentRun(buildRemoteRunPayload({
837
+ execution: remoteExecution,
838
+ userId,
839
+ sessionId,
840
+ modelId,
841
+ status: "running",
842
+ inputPreview: requestPreview,
843
+ startedAt: startedAt.toISOString(),
844
+ metadata: {
845
+ appId: clConfig.appId,
846
+ },
847
+ sessionMetadata: {
848
+ kind: "chat",
849
+ agentName: remoteExecution.agentName,
850
+ },
851
+ }));
852
+ }
853
+ if (isValidId(userId) && isValidId(sessionId) && isValidId(projectId)) {
854
+ const messagesInput = (params as any).prompt || (params as any).messages || [];
855
+ const { requestPreview } = buildRemoteTracePreviews(messagesInput);
856
+ const traceResponse = await postTraceEvents(buildRemoteTraceStartPayload({
857
+ execution: remoteExecution,
858
+ userId,
859
+ projectId,
860
+ sessionId,
861
+ requestPreview,
862
+ modelId,
863
+ metadata: {
864
+ appId: clConfig.appId,
865
+ },
866
+ startedAt: startedAt.toISOString(),
867
+ }));
868
+ liveTraceDbId = typeof traceResponse?.traceDbId === "string" ? traceResponse.traceDbId : undefined;
869
+ }
579
870
  let result;
580
871
  try {
581
872
  result = await doGenerate();
582
873
  } catch (err) {
874
+ const errorMessage = err instanceof Error ? err.message : String(err);
875
+ if (liveTraceDbId) {
876
+ await postTraceEvents(buildRemoteTraceFinishPayload({
877
+ execution: remoteExecution,
878
+ traceDbId: liveTraceDbId,
879
+ state: "error",
880
+ responsePreview: errorMessage.slice(0, 240),
881
+ durationMs: Date.now() - startedAt.getTime(),
882
+ errorMessage,
883
+ }));
884
+ }
885
+ if (isValidId(userId) && isValidId(sessionId)) {
886
+ await postAgentRun(buildRemoteRunPayload({
887
+ execution: remoteExecution,
888
+ userId,
889
+ sessionId,
890
+ modelId,
891
+ status: "failed",
892
+ errorMessage,
893
+ durationMs: Date.now() - startedAt.getTime(),
894
+ startedAt: startedAt.toISOString(),
895
+ completedAt: new Date().toISOString(),
896
+ metadata: {
897
+ appId: clConfig.appId,
898
+ },
899
+ }));
900
+ }
583
901
  logger.error("doGenerate failed", err);
584
902
  logger.error("doGenerate params.prompt", JSON.stringify((params as any).prompt?.map((m: any) => ({ role: m.role, contentType: typeof m.content, contentLength: Array.isArray(m.content) ? m.content.length : undefined })), null, 2));
585
903
  throw err;
@@ -589,7 +907,7 @@ ${userContextBlock || "None"}
589
907
  const endedAt = new Date();
590
908
  const sessionKey = getSessionKey(userId, sessionId);
591
909
  const promptMeta = sessionPromptMetadata.get(sessionKey);
592
- const agentRunId = (params as any)?.providerMetadata?.kognitive?.agentRunId as string | undefined;
910
+ const automaticTitle = resolveAutomaticTitleConfig(params);
593
911
 
594
912
  const messagesInput = (params as any).prompt || (params as any).messages || [];
595
913
 
@@ -619,11 +937,39 @@ ${userContextBlock || "None"}
619
937
  ? [{ role: "assistant", content: assistantParts }]
620
938
  : [];
621
939
  const finalMessages = [...messagesInput, ...assistantMessage];
622
- const { requestPreview, responsePreview } = buildTracePreviews(finalMessages);
940
+ const { requestPreview, responsePreview } = buildRemoteTracePreviews(finalMessages);
623
941
  const spans = buildTraceSpansFromMessages(finalMessages);
624
942
  const toolDefs = extractToolDefinitions(params);
943
+ const usage = normalizeRemoteUsage(result.usage);
944
+
945
+ await postAgentRun(buildRemoteRunPayload({
946
+ execution: remoteExecution,
947
+ userId,
948
+ sessionId,
949
+ modelId,
950
+ status: "completed",
951
+ inputPreview: requestPreview,
952
+ outputPreview: responsePreview,
953
+ inputTokens: usage.inputTokens,
954
+ outputTokens: usage.outputTokens,
955
+ cachedInputTokens: usage.cachedInputTokens,
956
+ durationMs: endedAt.getTime() - startedAt.getTime(),
957
+ startedAt: startedAt.toISOString(),
958
+ completedAt: endedAt.toISOString(),
959
+ metadata: {
960
+ appId: clConfig.appId,
961
+ ...(promptMeta?.tag && { promptTag: promptMeta.tag }),
962
+ ...(promptMeta?.abTestId && { abTestId: promptMeta.abTestId }),
963
+ ...(promptMeta?.variant && { variant: promptMeta.variant }),
964
+ },
965
+ sessionMetadata: {
966
+ kind: "chat",
967
+ agentName: remoteExecution.agentName,
968
+ },
969
+ }));
625
970
 
626
- logConversation({
971
+ const logResult = await logConversation(buildRemoteLogPayload({
972
+ execution: remoteExecution,
627
973
  userId,
628
974
  sessionId,
629
975
  messages: finalMessages,
@@ -638,8 +984,6 @@ ${userContextBlock || "None"}
638
984
  variant: promptMeta.variant,
639
985
  }),
640
986
  ...(toolDefs && { tools: toolDefs }),
641
- ...(agentRunId && { agentRunId }),
642
- traceId: randomUUID(),
643
987
  requestPreview,
644
988
  responsePreview,
645
989
  state: "completed",
@@ -648,24 +992,111 @@ ${userContextBlock || "None"}
648
992
  durationMs: endedAt.getTime() - startedAt.getTime(),
649
993
  metadata: {
650
994
  appId: clConfig.appId,
651
- ...(promptMeta?.tag && { promptTag: promptMeta.tag }),
652
- ...(promptMeta?.abTestId && { abTestId: promptMeta.abTestId }),
653
- ...(promptMeta?.variant && { variant: promptMeta.variant }),
995
+ ...(promptMeta?.tag && { promptTag: promptMeta.tag }),
996
+ ...(promptMeta?.abTestId && { abTestId: promptMeta.abTestId }),
997
+ ...(promptMeta?.variant && { variant: promptMeta.variant }),
654
998
  },
655
999
  spans,
656
- }).then(() => triggerProcessing(userId, sessionId));
1000
+ automaticTitle,
1001
+ }) as unknown as LogConversationPayload);
1002
+ if (logResult) {
1003
+ triggerProcessing(userId, sessionId);
1004
+ }
1005
+
1006
+ await postTraceEvents(buildRemoteTraceFinishPayload({
1007
+ execution: remoteExecution,
1008
+ traceDbId: liveTraceDbId,
1009
+ state: "completed",
1010
+ responsePreview,
1011
+ durationMs: endedAt.getTime() - startedAt.getTime(),
1012
+ usage: result.usage,
1013
+ }));
657
1014
  }
658
1015
 
659
1016
  return result;
660
1017
  },
661
1018
  async wrapStream({ doStream, params }: { doStream: any; params: any }) {
662
1019
  const startedAt = new Date();
663
- const traceId = randomUUID();
1020
+ const requestedRunScope = resolveRequestedRunScope(params);
1021
+ const remoteAgentName = clConfig.appId ?? modelId;
1022
+ const turnIndex = reserveTurnIndex(remoteAgentName, userId, sessionId, requestedRunScope);
1023
+ const remoteExecution = createRemoteExecutionContext({
1024
+ agentName: remoteAgentName,
1025
+ sessionId,
1026
+ requestedRunScope,
1027
+ runId: getKognitiveProviderConfig(params)?.agentRunId as string | undefined,
1028
+ turnIndex,
1029
+ });
1030
+ const traceId = remoteExecution.traceId;
1031
+ let liveTraceDbId: string | undefined;
1032
+ if (isValidId(userId) && isValidId(sessionId)) {
1033
+ const messagesInput = (params as any).prompt || (params as any).messages || [];
1034
+ const { requestPreview } = buildRemoteTracePreviews(messagesInput);
1035
+ await postAgentRun(buildRemoteRunPayload({
1036
+ execution: remoteExecution,
1037
+ userId,
1038
+ sessionId,
1039
+ modelId,
1040
+ status: "running",
1041
+ inputPreview: requestPreview,
1042
+ startedAt: startedAt.toISOString(),
1043
+ metadata: {
1044
+ appId: clConfig.appId,
1045
+ },
1046
+ sessionMetadata: {
1047
+ kind: "chat",
1048
+ agentName: remoteExecution.agentName,
1049
+ },
1050
+ }));
1051
+ }
1052
+ if (isValidId(userId) && isValidId(sessionId) && isValidId(projectId)) {
1053
+ const messagesInput = (params as any).prompt || (params as any).messages || [];
1054
+ const { requestPreview } = buildRemoteTracePreviews(messagesInput);
1055
+ const traceResponse = await postTraceEvents(buildRemoteTraceStartPayload({
1056
+ execution: remoteExecution,
1057
+ userId,
1058
+ projectId,
1059
+ sessionId,
1060
+ requestPreview,
1061
+ modelId,
1062
+ metadata: {
1063
+ appId: clConfig.appId,
1064
+ },
1065
+ startedAt: startedAt.toISOString(),
1066
+ }));
1067
+ liveTraceDbId = typeof traceResponse?.traceDbId === "string" ? traceResponse.traceDbId : undefined;
1068
+ }
664
1069
  let result;
665
1070
  try {
666
1071
  logger.debug("Starting doStream with params", JSON.stringify(params, null, 2));
667
1072
  result = await doStream();
668
1073
  } catch (err) {
1074
+ if (liveTraceDbId) {
1075
+ await postTraceEvents(buildRemoteTraceFinishPayload({
1076
+ execution: remoteExecution,
1077
+ traceDbId: liveTraceDbId,
1078
+ state: "error",
1079
+ responsePreview: err instanceof Error ? err.message.slice(0, 240) : "Stream failed",
1080
+ durationMs: Date.now() - startedAt.getTime(),
1081
+ errorMessage: err instanceof Error ? err.message : String(err),
1082
+ }));
1083
+ }
1084
+ if (isValidId(userId) && isValidId(sessionId)) {
1085
+ await postAgentRun(buildRemoteRunPayload({
1086
+ execution: remoteExecution,
1087
+ userId,
1088
+ sessionId,
1089
+ modelId,
1090
+ status: "failed",
1091
+ errorMessage: err instanceof Error ? err.message : String(err),
1092
+ durationMs: Date.now() - startedAt.getTime(),
1093
+ startedAt: startedAt.toISOString(),
1094
+ completedAt: new Date().toISOString(),
1095
+ metadata: {
1096
+ appId: clConfig.appId,
1097
+ },
1098
+ }));
1099
+ }
669
1100
  console.log((err as TypeError).cause)
670
1101
  console.log((err as TypeError).stack)
671
1102
  logger.error("doStream failed", err);
@@ -676,7 +1107,7 @@ ${userContextBlock || "None"}
676
1107
  if (isValidId(userId) && isValidId(sessionId)) {
677
1108
  const sessionKey = getSessionKey(userId, sessionId);
678
1109
  const promptMeta = sessionPromptMetadata.get(sessionKey);
679
- const agentRunId = (params as any)?.providerMetadata?.kognitive?.agentRunId as string | undefined;
1110
+ const automaticTitle = resolveAutomaticTitleConfig(params);
680
1111
 
681
1112
  const messagesInput = (params as any).prompt || (params as any).messages || [];
682
1113
  const resultMessages = (result as any)?.response?.messages;
@@ -686,7 +1117,10 @@ ${userContextBlock || "None"}
686
1117
 
687
1118
  let streamUsage: Record<string, unknown> | undefined;
688
1119
  let accumulatedText = '';
1120
+ let pendingProgressDelta = '';
1121
+ let lastProgressAt = Date.now();
689
1122
  const toolCallInputs = new Map<string, { toolName: string; chunks: string[] }>();
1123
+ const startedToolCalls = new Set<string>();
690
1124
  const completedToolCalls: any[] = [];
691
1125
  const completedToolResults: any[] = [];
692
1126
 
@@ -695,6 +1129,23 @@ ${userContextBlock || "None"}
695
1129
  transform(chunk, controller) {
696
1130
  if (chunk.type === 'text-delta') {
697
1131
  accumulatedText += chunk.delta;
1132
+ pendingProgressDelta += chunk.delta;
1133
+ if (liveTraceDbId && pendingProgressDelta && (pendingProgressDelta.length >= 80 || Date.now() - lastProgressAt >= 250)) {
1134
+ void appendLiveTraceEvents({
1135
+ traceDbId: liveTraceDbId,
1136
+ traceId,
1137
+ events: [{
1138
+ eventType: "assistant.progress",
1139
+ status: "active",
1140
+ payload: {
1141
+ text: pendingProgressDelta,
1142
+ preview: previewText(accumulatedText),
1143
+ },
1144
+ }],
1145
+ });
1146
+ pendingProgressDelta = '';
1147
+ lastProgressAt = Date.now();
1148
+ }
698
1149
  }
699
1150
  if (chunk.type === 'finish' && chunk.usage) {
700
1151
  streamUsage = chunk.usage;
@@ -702,6 +1153,23 @@ ${userContextBlock || "None"}
702
1153
  // Capture tool-call stream chunks (V2/V3 shared types)
703
1154
  if (chunk.type === 'tool-input-start') {
704
1155
  toolCallInputs.set(chunk.id, { toolName: chunk.toolName, chunks: [] });
1156
+ if (liveTraceDbId && !startedToolCalls.has(chunk.id)) {
1157
+ startedToolCalls.add(chunk.id);
1158
+ void appendLiveTraceEvents({
1159
+ traceDbId: liveTraceDbId,
1160
+ traceId,
1161
+ events: [{
1162
+ eventType: "tool.started",
1163
+ spanKey: chunk.id,
1164
+ status: "active",
1165
+ payload: {
1166
+ toolCallId: chunk.id,
1167
+ toolName: chunk.toolName,
1168
+ inputPreview: "",
1169
+ },
1170
+ }],
1171
+ });
1172
+ }
705
1173
  }
706
1174
  if (chunk.type === 'tool-input-delta') {
707
1175
  const entry = toolCallInputs.get(chunk.id);
@@ -714,6 +1182,29 @@ ${userContextBlock || "None"}
714
1182
  toolName: chunk.toolName,
715
1183
  input: chunk.input,
716
1184
  });
1185
+ if (!toolCallInputs.has(chunk.toolCallId)) {
1186
+ toolCallInputs.set(chunk.toolCallId, {
1187
+ toolName: chunk.toolName,
1188
+ chunks: typeof chunk.input === "string" ? [chunk.input] : [],
1189
+ });
1190
+ }
1191
+ if (liveTraceDbId && !startedToolCalls.has(chunk.toolCallId)) {
1192
+ startedToolCalls.add(chunk.toolCallId);
1193
+ void appendLiveTraceEvents({
1194
+ traceDbId: liveTraceDbId,
1195
+ traceId,
1196
+ events: [{
1197
+ eventType: "tool.started",
1198
+ spanKey: chunk.toolCallId,
1199
+ status: "active",
1200
+ payload: {
1201
+ toolCallId: chunk.toolCallId,
1202
+ toolName: chunk.toolName,
1203
+ inputPreview: typeof chunk.input === "string" ? previewText(chunk.input) : "",
1204
+ },
1205
+ }],
1206
+ });
1207
+ }
717
1208
  }
718
1209
  if (chunk.type === 'tool-result') {
719
1210
  completedToolResults.push({
@@ -722,11 +1213,41 @@ ${userContextBlock || "None"}
722
1213
  toolName: chunk.toolName,
723
1214
  result: chunk.result,
724
1215
  });
1216
+ if (liveTraceDbId) {
1217
+ void appendLiveTraceEvents({
1218
+ traceDbId: liveTraceDbId,
1219
+ traceId,
1220
+ events: [{
1221
+ eventType: "tool.completed",
1222
+ spanKey: chunk.toolCallId,
1223
+ status: "completed",
1224
+ payload: {
1225
+ toolCallId: chunk.toolCallId,
1226
+ toolName: chunk.toolName,
1227
+ outputPreview: previewText(JSON.stringify(chunk.result)),
1228
+ },
1229
+ }],
1230
+ });
1231
+ }
725
1232
  }
726
1233
  controller.enqueue(chunk);
727
1234
  },
728
1235
  async flush() {
729
1236
  const endedAt = new Date();
1237
+ if (liveTraceDbId && pendingProgressDelta) {
1238
+ await appendLiveTraceEvents({
1239
+ traceDbId: liveTraceDbId,
1240
+ traceId,
1241
+ events: [{
1242
+ eventType: "assistant.progress",
1243
+ status: "active",
1244
+ payload: {
1245
+ text: pendingProgressDelta,
1246
+ preview: previewText(accumulatedText),
1247
+ },
1248
+ }],
1249
+ });
1250
+ }
730
1251
 
731
1252
  // Finalize any tool calls from incremental input chunks
732
1253
  for (const [id, entry] of toolCallInputs) {
@@ -753,14 +1274,40 @@ ${userContextBlock || "None"}
753
1274
  allMessages.push({ role: "tool", content: completedToolResults });
754
1275
  }
755
1276
 
756
- const { requestPreview, responsePreview } = buildTracePreviews(allMessages);
1277
+ const { requestPreview, responsePreview } = buildRemoteTracePreviews(allMessages);
757
1278
  const spans = buildTraceSpansFromMessages(allMessages);
758
1279
  const toolDefs = extractToolDefinitions(params);
1280
+ const usage = normalizeRemoteUsage(streamUsage);
759
1281
 
760
- // Fire-and-forget: do not await so the stream closes immediately,
761
- // allowing the AI SDK's multi-step continuation logic to proceed.
762
- logConversation({
763
- userId,
1282
+ await postAgentRun(buildRemoteRunPayload({
1283
+ execution: remoteExecution,
1284
+ userId,
1285
+ sessionId,
1286
+ modelId,
1287
+ status: "completed",
1288
+ inputPreview: requestPreview,
1289
+ outputPreview: responsePreview,
1290
+ inputTokens: usage.inputTokens,
1291
+ outputTokens: usage.outputTokens,
1292
+ cachedInputTokens: usage.cachedInputTokens,
1293
+ durationMs: endedAt.getTime() - startedAt.getTime(),
1294
+ startedAt: startedAt.toISOString(),
1295
+ completedAt: endedAt.toISOString(),
1296
+ metadata: {
1297
+ appId: clConfig.appId,
1298
+ ...(promptMeta?.tag && { promptTag: promptMeta.tag }),
1299
+ ...(promptMeta?.abTestId && { abTestId: promptMeta.abTestId }),
1300
+ ...(promptMeta?.variant && { variant: promptMeta.variant }),
1301
+ },
1302
+ sessionMetadata: {
1303
+ kind: "chat",
1304
+ agentName: remoteExecution.agentName,
1305
+ },
1306
+ }));
1307
+
1308
+ const logResult = await logConversation(buildRemoteLogPayload({
1309
+ execution: remoteExecution,
1310
+ userId,
764
1311
  sessionId,
765
1312
  messages: allMessages,
766
1313
  modelId,
@@ -774,8 +1321,6 @@ ${userContextBlock || "None"}
774
1321
  variant: promptMeta.variant,
775
1322
  }),
776
1323
  ...(toolDefs && { tools: toolDefs }),
777
- ...(agentRunId && { agentRunId }),
778
- traceId,
779
1324
  requestPreview,
780
1325
  responsePreview,
781
1326
  state: "completed",
@@ -789,8 +1334,36 @@ ${userContextBlock || "None"}
789
1334
  ...(promptMeta?.variant && { variant: promptMeta.variant }),
790
1335
  },
791
1336
  spans,
792
- }).then(() => triggerProcessing(userId, sessionId))
793
- .catch((e) => logger.error("Stream log failed", e));
1337
+ automaticTitle,
1338
+ }) as unknown as LogConversationPayload);
1339
+ if (logResult) {
1340
+ triggerProcessing(userId, sessionId);
1341
+ }
1342
+ if (logResult?.generatedTitle) {
1343
+ const currentProviderMetadata = result.providerMetadata && typeof result.providerMetadata === "object"
1344
+ ? result.providerMetadata as Record<string, unknown>
1345
+ : {};
1346
+ const currentKognitiveMetadata = currentProviderMetadata.kognitive
1347
+ && typeof currentProviderMetadata.kognitive === "object"
1348
+ ? currentProviderMetadata.kognitive as Record<string, unknown>
1349
+ : {};
1350
+ result.providerMetadata = {
1351
+ ...currentProviderMetadata,
1352
+ kognitive: {
1353
+ ...currentKognitiveMetadata,
1354
+ generatedTitle: logResult.generatedTitle,
1355
+ },
1356
+ };
1357
+ }
1358
+
1359
+ await postTraceEvents(buildRemoteTraceFinishPayload({
1360
+ execution: remoteExecution,
1361
+ traceDbId: liveTraceDbId,
1362
+ state: "completed",
1363
+ responsePreview,
1364
+ durationMs: endedAt.getTime() - startedAt.getTime(),
1365
+ usage: streamUsage,
1366
+ }));
794
1367
  }
795
1368
  });
796
1369
 
@@ -865,25 +1438,30 @@ ${userContextBlock || "None"}
865
1438
  };
866
1439
 
867
1440
  const clStreamText = async (options: CLStreamTextOptions) => {
868
- const { prompt: promptConfig, ...rest } = options;
869
-
870
- const session = (options.model as any)[SESSION_KEY] as { userId: string; projectId?: string; sessionId?: string } | undefined;
1441
+ const { prompt: promptConfig, kognitive, ...rest } = options;
1442
+ const session = (options.model as any)[SESSION_KEY] as { userId: string; projectId?: string; sessionId?: string } | undefined;
871
1443
 
872
1444
  // Resolve and interpolate prompt (graceful fallback on failure)
873
1445
  let resolved: CachedPrompt | null = null;
874
- try {
875
- resolved = await resolvePrompt(promptConfig.slug, {
876
- userId: session?.userId,
877
- tag: promptConfig.tag,
878
- });
879
- } catch (err) {
880
- logger.warn(`Failed to resolve prompt "${promptConfig.slug}", streaming without system prompt.`, err);
1446
+ if (promptConfig?.slug) {
1447
+ try {
1448
+ resolved = await resolvePrompt(promptConfig.slug, {
1449
+ userId: session?.userId,
1450
+ tag: promptConfig.tag,
1451
+ });
1452
+ } catch (err) {
1453
+ if (isModerationError(err)) {
1454
+ throw err;
1455
+ }
1456
+ logger.warn(`Failed to resolve prompt "${promptConfig.slug}", streaming without system prompt.`, err);
1457
+ }
881
1458
  }
882
1459
 
883
- let system: string | undefined;
1460
+ let system = typeof rest.system === "string" ? rest.system : undefined;
884
1461
  if (resolved) {
885
- system = promptConfig.variables
886
- ? renderTemplate(resolved.content, promptConfig.variables)
1462
+ const resolvedPromptConfig = promptConfig!;
1463
+ system = resolvedPromptConfig.variables
1464
+ ? renderTemplate(resolved.content, resolvedPromptConfig.variables)
887
1465
  : resolved.content;
888
1466
 
889
1467
  // Store prompt metadata for the session (read by middleware during logging)
@@ -900,40 +1478,52 @@ ${userContextBlock || "None"}
900
1478
  }
901
1479
 
902
1480
  logger.info("cl.streamText called", {
903
- slug: promptConfig.slug,
1481
+ slug: resolvedPromptConfig.slug,
904
1482
  version: resolved.version,
905
1483
  systemLength: system.length,
906
1484
  });
907
- } else {
1485
+ } else if (promptConfig?.slug) {
908
1486
  logger.info("cl.streamText called without resolved prompt", {
909
1487
  slug: promptConfig.slug,
910
1488
  });
911
1489
  }
912
1490
 
913
1491
  const model = resolveModel(options.model, resolved?.gatewaySlug);
914
- return aiStreamText({ ...rest, model, ...(system && { system }) } as any);
1492
+ const nextParams = withAutoInjectedTools(
1493
+ { ...rest, model, ...(system && { system }) },
1494
+ session,
1495
+ baseUrl,
1496
+ clConfig.apiKey,
1497
+ kognitive,
1498
+ );
1499
+ return aiStreamText(nextParams as any);
915
1500
  };
916
1501
 
917
1502
  const clGenerateText = async (options: CLGenerateTextOptions) => {
918
- const { prompt: promptConfig, ...rest } = options;
919
-
920
- const session = (options.model as any)[SESSION_KEY] as { userId: string; projectId?: string; sessionId?: string } | undefined;
1503
+ const { prompt: promptConfig, kognitive, ...rest } = options;
1504
+ const session = (options.model as any)[SESSION_KEY] as { userId: string; projectId?: string; sessionId?: string } | undefined;
921
1505
 
922
1506
  // Resolve and interpolate prompt (graceful fallback on failure)
923
1507
  let resolved: CachedPrompt | null = null;
924
- try {
925
- resolved = await resolvePrompt(promptConfig.slug, {
926
- userId: session?.userId,
927
- tag: promptConfig.tag,
928
- });
929
- } catch (err) {
930
- logger.warn(`Failed to resolve prompt "${promptConfig.slug}", generating without system prompt.`, err);
1508
+ if (promptConfig?.slug) {
1509
+ try {
1510
+ resolved = await resolvePrompt(promptConfig.slug, {
1511
+ userId: session?.userId,
1512
+ tag: promptConfig.tag,
1513
+ });
1514
+ } catch (err) {
1515
+ if (isModerationError(err)) {
1516
+ throw err;
1517
+ }
1518
+ logger.warn(`Failed to resolve prompt "${promptConfig.slug}", generating without system prompt.`, err);
1519
+ }
931
1520
  }
932
1521
 
933
- let system: string | undefined;
1522
+ let system = typeof rest.system === "string" ? rest.system : undefined;
934
1523
  if (resolved) {
935
- system = promptConfig.variables
936
- ? renderTemplate(resolved.content, promptConfig.variables)
1524
+ const resolvedPromptConfig = promptConfig!;
1525
+ system = resolvedPromptConfig.variables
1526
+ ? renderTemplate(resolved.content, resolvedPromptConfig.variables)
937
1527
  : resolved.content;
938
1528
 
939
1529
  // Store prompt metadata for the session (read by middleware during logging)
@@ -950,18 +1540,25 @@ ${userContextBlock || "None"}
950
1540
  }
951
1541
 
952
1542
  logger.info("cl.generateText called", {
953
- slug: promptConfig.slug,
1543
+ slug: resolvedPromptConfig.slug,
954
1544
  version: resolved.version,
955
1545
  systemLength: system.length,
956
1546
  });
957
- } else {
1547
+ } else if (promptConfig?.slug) {
958
1548
  logger.info("cl.generateText called without resolved prompt", {
959
1549
  slug: promptConfig.slug,
960
1550
  });
961
1551
  }
962
1552
 
963
1553
  const model = resolveModel(options.model, resolved?.gatewaySlug);
964
- return aiGenerateText({ ...rest, model, ...(system && { system }) } as any);
1554
+ const nextParams = withAutoInjectedTools(
1555
+ { ...rest, model, ...(system && { system }) },
1556
+ session,
1557
+ baseUrl,
1558
+ clConfig.apiKey,
1559
+ kognitive,
1560
+ );
1561
+ return aiGenerateText(nextParams as any);
965
1562
  };
966
1563
 
967
1564
  // Return the model wrapper function with streamText/generateText attached
@@ -975,9 +1572,13 @@ ${userContextBlock || "None"}
975
1572
  if (sessionKey) {
976
1573
  sessionSnapshots.delete(sessionKey);
977
1574
  sessionPromptMetadata.delete(sessionKey);
1575
+ for (const key of sessionTurnIndexes.keys()) {
1576
+ if (key.endsWith(`:${sessionKey}`)) sessionTurnIndexes.delete(key);
1577
+ }
978
1578
  } else {
979
1579
  sessionSnapshots.clear();
980
1580
  sessionPromptMetadata.clear();
1581
+ sessionTurnIndexes.clear();
981
1582
  }
982
1583
  },
983
1584
  });