@promptbook/cli 0.110.0-0 → 0.110.0-10

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.
Files changed (54) hide show
  1. package/esm/index.es.js +1778 -502
  2. package/esm/index.es.js.map +1 -1
  3. package/esm/typings/src/_packages/components.index.d.ts +4 -0
  4. package/esm/typings/src/_packages/core.index.d.ts +2 -2
  5. package/esm/typings/src/_packages/openai.index.d.ts +8 -4
  6. package/esm/typings/src/_packages/types.index.d.ts +12 -4
  7. package/esm/typings/src/book-2.0/agent-source/AgentModelRequirements.d.ts +22 -21
  8. package/esm/typings/src/book-2.0/agent-source/AgentReferenceResolver.d.ts +18 -0
  9. package/esm/typings/src/book-2.0/agent-source/CreateAgentModelRequirementsOptions.d.ts +12 -0
  10. package/esm/typings/src/book-2.0/agent-source/createAgentModelRequirements.d.ts +8 -2
  11. package/esm/typings/src/book-2.0/agent-source/createAgentModelRequirementsWithCommitments.agentReferenceResolver.test.d.ts +1 -0
  12. package/esm/typings/src/book-2.0/agent-source/createAgentModelRequirementsWithCommitments.d.ts +4 -5
  13. package/esm/typings/src/book-components/Chat/AgentChip/AgentChip.d.ts +5 -1
  14. package/esm/typings/src/book-components/Chat/Chat/ChatActionsBar.d.ts +4 -2
  15. package/esm/typings/src/book-components/Chat/Chat/ChatInputArea.d.ts +1 -0
  16. package/esm/typings/src/book-components/Chat/Chat/ChatMessageItem.d.ts +4 -0
  17. package/esm/typings/src/book-components/Chat/Chat/ChatMessageList.d.ts +1 -0
  18. package/esm/typings/src/book-components/Chat/Chat/ChatProps.d.ts +15 -0
  19. package/esm/typings/src/book-components/Chat/Chat/ChatSoundToggle.d.ts +31 -0
  20. package/esm/typings/src/book-components/Chat/LlmChat/LlmChatProps.d.ts +10 -1
  21. package/esm/typings/src/book-components/Chat/SourceChip/SourceChip.d.ts +5 -1
  22. package/esm/typings/src/book-components/Chat/utils/collectTeamToolCallSummary.d.ts +69 -0
  23. package/esm/typings/src/book-components/Chat/utils/getToolCallChipletInfo.d.ts +13 -13
  24. package/esm/typings/src/book-components/Chat/utils/parseCitationsFromContent.d.ts +9 -0
  25. package/esm/typings/src/book-components/Chat/utils/toolCallParsing.d.ts +4 -0
  26. package/esm/typings/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentsDatabaseSchema.d.ts +0 -3
  27. package/esm/typings/src/commitments/_base/BaseCommitmentDefinition.d.ts +9 -0
  28. package/esm/typings/src/execution/LlmExecutionTools.d.ts +2 -1
  29. package/esm/typings/src/llm-providers/agent/Agent.d.ts +1 -1
  30. package/esm/typings/src/llm-providers/agent/AgentLlmExecutionTools.d.ts +5 -1
  31. package/esm/typings/src/llm-providers/agent/AgentLlmExecutionTools.test.d.ts +1 -0
  32. package/esm/typings/src/llm-providers/agent/AgentOptions.d.ts +10 -0
  33. package/esm/typings/src/llm-providers/agent/CreateAgentLlmExecutionToolsOptions.d.ts +13 -2
  34. package/esm/typings/src/llm-providers/agent/RemoteAgent.d.ts +2 -1
  35. package/esm/typings/src/llm-providers/openai/OpenAiAgentKitExecutionTools.d.ts +150 -0
  36. package/esm/typings/src/llm-providers/openai/OpenAiAgentKitExecutionToolsOptions.d.ts +15 -0
  37. package/esm/typings/src/llm-providers/openai/OpenAiAssistantExecutionTools.d.ts +3 -3
  38. package/esm/typings/src/llm-providers/openai/OpenAiAssistantExecutionToolsOptions.d.ts +3 -4
  39. package/esm/typings/src/llm-providers/openai/OpenAiVectorStoreHandler.d.ts +135 -0
  40. package/esm/typings/src/llm-providers/openai/utils/mapToolsToOpenAi.d.ts +1 -1
  41. package/esm/typings/src/types/LlmToolDefinition.d.ts +1 -0
  42. package/esm/typings/src/types/ModelRequirements.d.ts +9 -0
  43. package/esm/typings/src/utils/DEFAULT_THINKING_MESSAGES.d.ts +8 -0
  44. package/esm/typings/src/utils/agents/resolveAgentAvatarImageUrl.d.ts +29 -0
  45. package/esm/typings/src/utils/knowledge/inlineKnowledgeSource.d.ts +38 -0
  46. package/esm/typings/src/utils/knowledge/inlineKnowledgeSource.test.d.ts +1 -0
  47. package/esm/typings/src/utils/language/getBrowserPreferredSpeechRecognitionLanguage.d.ts +35 -0
  48. package/esm/typings/src/utils/toolCalls/getToolCallIdentity.d.ts +10 -0
  49. package/esm/typings/src/version.d.ts +1 -1
  50. package/package.json +6 -2
  51. package/umd/index.umd.js +1781 -506
  52. package/umd/index.umd.js.map +1 -1
  53. package/esm/typings/src/llm-providers/openai/OpenAiAgentExecutionTools.d.ts +0 -43
  54. package/esm/typings/src/llm-providers/openai/createOpenAiAgentExecutionTools.d.ts +0 -11
package/esm/index.es.js CHANGED
@@ -31,6 +31,7 @@ import { OpenAIClient, AzureKeyCredential } from '@azure/openai';
31
31
  import { Subject, BehaviorSubject } from 'rxjs';
32
32
  import { lookup, extension } from 'mime-types';
33
33
  import { parse, unparse } from 'papaparse';
34
+ import { Agent as Agent$1, setDefaultOpenAIClient, setDefaultOpenAIKey, fileSearchTool, tool, run } from '@openai/agents';
34
35
  import OpenAI from 'openai';
35
36
 
36
37
  // ⚠️ WARNING: This code has been generated so that any manual changes will be overwritten
@@ -47,7 +48,7 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
47
48
  * @generated
48
49
  * @see https://github.com/webgptorg/promptbook
49
50
  */
50
- const PROMPTBOOK_ENGINE_VERSION = '0.110.0-0';
51
+ const PROMPTBOOK_ENGINE_VERSION = '0.110.0-10';
51
52
  /**
52
53
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
53
54
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -15827,6 +15828,28 @@ class BaseCommitmentDefinition {
15827
15828
  return currentMessage + separator + content;
15828
15829
  });
15829
15830
  }
15831
+ /**
15832
+ * Helper method to create a new requirements object with updated prompt suffix
15833
+ */
15834
+ updatePromptSuffix(requirements, contentUpdate) {
15835
+ const newSuffix = typeof contentUpdate === 'string' ? contentUpdate : contentUpdate(requirements.promptSuffix);
15836
+ return {
15837
+ ...requirements,
15838
+ promptSuffix: newSuffix,
15839
+ };
15840
+ }
15841
+ /**
15842
+ * Helper method to append content to the prompt suffix
15843
+ * Default separator is a single newline for bullet lists.
15844
+ */
15845
+ appendToPromptSuffix(requirements, content, separator = '\n') {
15846
+ return this.updatePromptSuffix(requirements, (currentSuffix) => {
15847
+ if (!currentSuffix.trim()) {
15848
+ return content;
15849
+ }
15850
+ return `${currentSuffix}${separator}${content}`;
15851
+ });
15852
+ }
15830
15853
  /**
15831
15854
  * Helper method to add a comment section to the system message
15832
15855
  * Comments are lines starting with # that will be removed from the final system message
@@ -16004,13 +16027,9 @@ class ClosedCommitmentDefinition extends BaseCommitmentDefinition {
16004
16027
  `);
16005
16028
  }
16006
16029
  applyToAgentModelRequirements(requirements, _content) {
16007
- const updatedMetadata = {
16008
- ...requirements.metadata,
16009
- isClosed: true,
16010
- };
16011
16030
  return {
16012
16031
  ...requirements,
16013
- metadata: updatedMetadata,
16032
+ isClosed: true,
16014
16033
  };
16015
16034
  }
16016
16035
  }
@@ -16288,12 +16307,12 @@ class DictionaryCommitmentDefinition extends BaseCommitmentDefinition {
16288
16307
  return requirements;
16289
16308
  }
16290
16309
  // Get existing dictionary entries from metadata
16291
- const existingDictionary = ((_a = requirements.metadata) === null || _a === void 0 ? void 0 : _a.DICTIONARY) || '';
16310
+ const existingDictionary = ((_a = requirements._metadata) === null || _a === void 0 ? void 0 : _a.DICTIONARY) || '';
16292
16311
  // Merge the new dictionary entry with existing entries
16293
16312
  const mergedDictionary = existingDictionary ? `${existingDictionary}\n${trimmedContent}` : trimmedContent;
16294
16313
  // Store the merged dictionary in metadata for debugging and inspection
16295
16314
  const updatedMetadata = {
16296
- ...requirements.metadata,
16315
+ ...requirements._metadata,
16297
16316
  DICTIONARY: mergedDictionary,
16298
16317
  };
16299
16318
  // Create the dictionary section for the system message
@@ -16301,7 +16320,7 @@ class DictionaryCommitmentDefinition extends BaseCommitmentDefinition {
16301
16320
  const dictionarySection = `# DICTIONARY\n${mergedDictionary}`;
16302
16321
  return {
16303
16322
  ...this.appendToSystemMessage(requirements, dictionarySection),
16304
- metadata: updatedMetadata,
16323
+ _metadata: updatedMetadata,
16305
16324
  };
16306
16325
  }
16307
16326
  }
@@ -16441,10 +16460,7 @@ class FromCommitmentDefinition extends BaseCommitmentDefinition {
16441
16460
  applyToAgentModelRequirements(requirements, content) {
16442
16461
  const trimmedContent = content.trim();
16443
16462
  if (!trimmedContent) {
16444
- return {
16445
- ...requirements,
16446
- parentAgentUrl: undefined,
16447
- };
16463
+ return requirements;
16448
16464
  }
16449
16465
  if (trimmedContent.toUpperCase() === 'VOID' ||
16450
16466
  trimmedContent.toUpperCase() === 'NULL' ||
@@ -16658,6 +16674,136 @@ class ImportCommitmentDefinition extends BaseCommitmentDefinition {
16658
16674
  * Note: [💞] Ignore a discrepancy between file name and entity name
16659
16675
  */
16660
16676
 
16677
+ /**
16678
+ * @@@
16679
+ *
16680
+ * @private thing of inline knowledge
16681
+ */
16682
+ const INLINE_KNOWLEDGE_BASE_NAME = 'inline-knowledge';
16683
+ /**
16684
+ * @@@
16685
+ *
16686
+ * @private thing of inline knowledge
16687
+ */
16688
+ const INLINE_KNOWLEDGE_EXTENSION = '.txt';
16689
+ /**
16690
+ * @@@
16691
+ *
16692
+ * @private thing of inline knowledge
16693
+ */
16694
+ const DATA_URL_PREFIX = 'data:';
16695
+ /**
16696
+ * @@@
16697
+ *
16698
+ * @private thing of inline knowledge
16699
+ */
16700
+ function getFirstNonEmptyLine(content) {
16701
+ const lines = content.split(/\r?\n/);
16702
+ for (const line of lines) {
16703
+ const trimmed = line.trim();
16704
+ if (trimmed) {
16705
+ return trimmed;
16706
+ }
16707
+ }
16708
+ return null;
16709
+ }
16710
+ /**
16711
+ * @@@
16712
+ *
16713
+ * @private thing of inline knowledge
16714
+ */
16715
+ function deriveBaseFilename(content) {
16716
+ const firstLine = getFirstNonEmptyLine(content);
16717
+ if (!firstLine) {
16718
+ return INLINE_KNOWLEDGE_BASE_NAME;
16719
+ }
16720
+ const normalized = normalizeToKebabCase(firstLine);
16721
+ return normalized || INLINE_KNOWLEDGE_BASE_NAME;
16722
+ }
16723
+ /**
16724
+ * Creates a data URL that represents the inline knowledge content as a text file.
16725
+ *
16726
+ * @private thing of inline knowledge
16727
+ */
16728
+ function createInlineKnowledgeSourceFile(content) {
16729
+ const trimmedContent = content.trim();
16730
+ const baseName = deriveBaseFilename(trimmedContent);
16731
+ const filename = `${baseName}${INLINE_KNOWLEDGE_EXTENSION}`;
16732
+ const mimeType = 'text/plain';
16733
+ const base64 = Buffer.from(trimmedContent, 'utf-8').toString('base64');
16734
+ const encodedFilename = encodeURIComponent(filename);
16735
+ const url = `${DATA_URL_PREFIX}${mimeType};name=${encodedFilename};charset=utf-8;base64,${base64}`;
16736
+ return {
16737
+ filename,
16738
+ mimeType,
16739
+ url,
16740
+ };
16741
+ }
16742
+ /**
16743
+ * Checks whether the provided source string is a data URL that can be decoded.
16744
+ *
16745
+ * @private thing of inline knowledge
16746
+ */
16747
+ function isDataUrlKnowledgeSource(source) {
16748
+ return typeof source === 'string' && source.startsWith(DATA_URL_PREFIX);
16749
+ }
16750
+ /**
16751
+ * Parses a data URL-based knowledge source into its raw buffer, filename, and MIME type.
16752
+ *
16753
+ * @private thing of inline knowledge
16754
+ */
16755
+ function parseDataUrlKnowledgeSource(source) {
16756
+ if (!isDataUrlKnowledgeSource(source)) {
16757
+ return null;
16758
+ }
16759
+ const commaIndex = source.indexOf(',');
16760
+ if (commaIndex === -1) {
16761
+ return null;
16762
+ }
16763
+ const header = source.slice(DATA_URL_PREFIX.length, commaIndex);
16764
+ const payload = source.slice(commaIndex + 1);
16765
+ const tokens = header.split(';');
16766
+ const mediaType = tokens[0] || 'text/plain';
16767
+ let filename = `${INLINE_KNOWLEDGE_BASE_NAME}${INLINE_KNOWLEDGE_EXTENSION}`;
16768
+ let isBase64 = false;
16769
+ for (let i = 1; i < tokens.length; i++) {
16770
+ const token = tokens[i];
16771
+ if (!token) {
16772
+ continue;
16773
+ }
16774
+ if (token.toLowerCase() === 'base64') {
16775
+ isBase64 = true;
16776
+ continue;
16777
+ }
16778
+ const [key, value] = token.split('=');
16779
+ if (key === 'name' && value !== undefined) {
16780
+ try {
16781
+ filename = decodeURIComponent(value);
16782
+ }
16783
+ catch (_a) {
16784
+ filename = value;
16785
+ }
16786
+ }
16787
+ }
16788
+ if (!isBase64) {
16789
+ return null;
16790
+ }
16791
+ try {
16792
+ const buffer = Buffer.from(payload, 'base64');
16793
+ return {
16794
+ buffer,
16795
+ filename,
16796
+ mimeType: mediaType,
16797
+ };
16798
+ }
16799
+ catch (_b) {
16800
+ return null;
16801
+ }
16802
+ }
16803
+ /**
16804
+ * Note: [💞] Ignore a discrepancy between file name and entity name
16805
+ */
16806
+
16661
16807
  /**
16662
16808
  * KNOWLEDGE commitment definition
16663
16809
  *
@@ -16756,9 +16902,13 @@ class KnowledgeCommitmentDefinition extends BaseCommitmentDefinition {
16756
16902
  return this.appendToSystemMessage(updatedRequirements, knowledgeInfo, '\n\n');
16757
16903
  }
16758
16904
  else {
16759
- // Direct text knowledge - add to system message
16760
- const knowledgeSection = `Knowledge: ${trimmedContent}`;
16761
- return this.appendToSystemMessage(requirements, knowledgeSection, '\n\n');
16905
+ const inlineSource = createInlineKnowledgeSourceFile(trimmedContent);
16906
+ const updatedRequirements = {
16907
+ ...requirements,
16908
+ knowledgeSources: [...(requirements.knowledgeSources || []), inlineSource.url],
16909
+ };
16910
+ const knowledgeInfo = `Knowledge Source Inline: ${inlineSource.filename} (derived from inline content and processed for retrieval during chat)`;
16911
+ return this.appendToSystemMessage(updatedRequirements, knowledgeInfo, '\n\n');
16762
16912
  }
16763
16913
  }
16764
16914
  }
@@ -17005,16 +17155,16 @@ class AgentMessageCommitmentDefinition extends BaseCommitmentDefinition {
17005
17155
  // and typically doesn't need to be added to the system prompt or model requirements directly.
17006
17156
  // It is extracted separately for the chat interface.
17007
17157
  var _a;
17008
- const pendingUserMessage = (_a = requirements.metadata) === null || _a === void 0 ? void 0 : _a.pendingUserMessage;
17158
+ const pendingUserMessage = (_a = requirements._metadata) === null || _a === void 0 ? void 0 : _a.pendingUserMessage;
17009
17159
  if (pendingUserMessage) {
17010
17160
  const newSample = { question: pendingUserMessage, answer: content };
17011
17161
  const newSamples = [...(requirements.samples || []), newSample];
17012
- const newMetadata = { ...requirements.metadata };
17162
+ const newMetadata = { ...requirements._metadata };
17013
17163
  delete newMetadata.pendingUserMessage;
17014
17164
  return {
17015
17165
  ...requirements,
17016
17166
  samples: newSamples,
17017
- metadata: newMetadata,
17167
+ _metadata: newMetadata,
17018
17168
  };
17019
17169
  }
17020
17170
  return requirements;
@@ -17262,8 +17412,8 @@ class UserMessageCommitmentDefinition extends BaseCommitmentDefinition {
17262
17412
  applyToAgentModelRequirements(requirements, content) {
17263
17413
  return {
17264
17414
  ...requirements,
17265
- metadata: {
17266
- ...requirements.metadata,
17415
+ _metadata: {
17416
+ ...requirements._metadata,
17267
17417
  pendingUserMessage: content,
17268
17418
  },
17269
17419
  };
@@ -18121,11 +18271,7 @@ class NoteCommitmentDefinition extends BaseCommitmentDefinition {
18121
18271
  if (trimmedContent === '') {
18122
18272
  return requirements;
18123
18273
  }
18124
- // Return requirements with updated notes but no changes to system message
18125
- return {
18126
- ...requirements,
18127
- notes: [...(requirements.notes || []), trimmedContent],
18128
- };
18274
+ return requirements;
18129
18275
  }
18130
18276
  }
18131
18277
  /**
@@ -18187,12 +18333,12 @@ class OpenCommitmentDefinition extends BaseCommitmentDefinition {
18187
18333
  // Since OPEN is default, we can just ensure isClosed is false
18188
18334
  // But to be explicit we can set it
18189
18335
  const updatedMetadata = {
18190
- ...requirements.metadata,
18336
+ ...requirements._metadata,
18191
18337
  isClosed: false,
18192
18338
  };
18193
18339
  return {
18194
18340
  ...requirements,
18195
- metadata: updatedMetadata,
18341
+ _metadata: updatedMetadata,
18196
18342
  };
18197
18343
  }
18198
18344
  }
@@ -18273,7 +18419,7 @@ class PersonaCommitmentDefinition extends BaseCommitmentDefinition {
18273
18419
  return requirements;
18274
18420
  }
18275
18421
  // Get existing persona content from metadata
18276
- const existingPersonaContent = ((_a = requirements.metadata) === null || _a === void 0 ? void 0 : _a.PERSONA) || '';
18422
+ const existingPersonaContent = ((_a = requirements._metadata) === null || _a === void 0 ? void 0 : _a.PERSONA) || '';
18277
18423
  // Merge the new content with existing persona content
18278
18424
  // When multiple PERSONA commitments exist, they are merged into one
18279
18425
  const mergedPersonaContent = existingPersonaContent
@@ -18281,12 +18427,12 @@ class PersonaCommitmentDefinition extends BaseCommitmentDefinition {
18281
18427
  : trimmedContent;
18282
18428
  // Store the merged persona content in metadata for debugging and inspection
18283
18429
  const updatedMetadata = {
18284
- ...requirements.metadata,
18430
+ ...requirements._metadata,
18285
18431
  PERSONA: mergedPersonaContent,
18286
18432
  };
18287
18433
  // Get the agent name from metadata (which should contain the first line of agent source)
18288
18434
  // If not available, extract from current system message as fallback
18289
- let agentName = (_b = requirements.metadata) === null || _b === void 0 ? void 0 : _b.agentName;
18435
+ let agentName = (_b = requirements._metadata) === null || _b === void 0 ? void 0 : _b.agentName;
18290
18436
  if (!agentName) {
18291
18437
  // Fallback: extract from current system message
18292
18438
  const currentMessage = requirements.systemMessage.trim();
@@ -18333,7 +18479,7 @@ class PersonaCommitmentDefinition extends BaseCommitmentDefinition {
18333
18479
  return {
18334
18480
  ...requirements,
18335
18481
  systemMessage: newSystemMessage,
18336
- metadata: updatedMetadata,
18482
+ _metadata: updatedMetadata,
18337
18483
  };
18338
18484
  }
18339
18485
  }
@@ -18416,7 +18562,16 @@ class RuleCommitmentDefinition extends BaseCommitmentDefinition {
18416
18562
  }
18417
18563
  // Add rule to the system message
18418
18564
  const ruleSection = `Rule: ${trimmedContent}`;
18419
- return this.appendToSystemMessage(requirements, ruleSection, '\n\n');
18565
+ const requirementsWithRule = this.appendToSystemMessage(requirements, ruleSection, '\n\n');
18566
+ const ruleLines = trimmedContent
18567
+ .split(/\r?\n/)
18568
+ .map((line) => line.trim())
18569
+ .filter(Boolean)
18570
+ .map((line) => `- ${line}`);
18571
+ if (ruleLines.length === 0) {
18572
+ return requirementsWithRule;
18573
+ }
18574
+ return this.appendToPromptSuffix(requirementsWithRule, ruleLines.join('\n'));
18420
18575
  }
18421
18576
  }
18422
18577
  /**
@@ -18922,7 +19077,7 @@ class TeamCommitmentDefinition extends BaseCommitmentDefinition {
18922
19077
  if (teammates.length === 0) {
18923
19078
  return requirements;
18924
19079
  }
18925
- const agentName = ((_a = requirements.metadata) === null || _a === void 0 ? void 0 : _a.agentName) || 'Agent';
19080
+ const agentName = ((_a = requirements._metadata) === null || _a === void 0 ? void 0 : _a.agentName) || 'Agent';
18926
19081
  const teamEntries = teammates.map((teammate) => ({
18927
19082
  toolName: createTeamToolName(teammate.url),
18928
19083
  teammate,
@@ -18962,7 +19117,7 @@ class TeamCommitmentDefinition extends BaseCommitmentDefinition {
18962
19117
  },
18963
19118
  });
18964
19119
  }
18965
- const existingTeammates = ((_b = requirements.metadata) === null || _b === void 0 ? void 0 : _b.teammates) || [];
19120
+ const existingTeammates = ((_b = requirements._metadata) === null || _b === void 0 ? void 0 : _b.teammates) || [];
18966
19121
  const updatedTeammates = [...existingTeammates];
18967
19122
  for (const entry of teamEntries) {
18968
19123
  if (updatedTeammates.some((existing) => existing.url === entry.teammate.url)) {
@@ -18991,8 +19146,8 @@ class TeamCommitmentDefinition extends BaseCommitmentDefinition {
18991
19146
  return this.appendToSystemMessage({
18992
19147
  ...requirements,
18993
19148
  tools: updatedTools,
18994
- metadata: {
18995
- ...requirements.metadata,
19149
+ _metadata: {
19150
+ ...requirements._metadata,
18996
19151
  teammates: updatedTeammates,
18997
19152
  },
18998
19153
  }, teamSystemMessage);
@@ -19092,11 +19247,16 @@ function createTeamToolFunction(entry) {
19092
19247
  const request = buildTeammateRequest(message, args.context);
19093
19248
  let response = '';
19094
19249
  let error = null;
19250
+ let toolCalls;
19095
19251
  try {
19096
19252
  const remoteAgent = await getRemoteTeammateAgent(entry.teammate.url);
19097
19253
  const prompt = buildTeammatePrompt(request);
19098
19254
  const teammateResult = await remoteAgent.callChatModel(prompt);
19099
19255
  response = teammateResult.content || '';
19256
+ toolCalls =
19257
+ 'toolCalls' in teammateResult && Array.isArray(teammateResult.toolCalls)
19258
+ ? teammateResult.toolCalls
19259
+ : undefined;
19100
19260
  }
19101
19261
  catch (err) {
19102
19262
  error = err instanceof Error ? err.message : String(err);
@@ -19106,6 +19266,7 @@ function createTeamToolFunction(entry) {
19106
19266
  teammate: teammateMetadata,
19107
19267
  request,
19108
19268
  response: teammateReply,
19269
+ toolCalls: toolCalls && toolCalls.length > 0 ? toolCalls : undefined,
19109
19270
  error,
19110
19271
  conversation: [
19111
19272
  {
@@ -19218,7 +19379,7 @@ class TemplateCommitmentDefinition extends BaseCommitmentDefinition {
19218
19379
  if (!trimmedContent) {
19219
19380
  // Store template mode flag in metadata
19220
19381
  const updatedMetadata = {
19221
- ...requirements.metadata,
19382
+ ...requirements._metadata,
19222
19383
  templateMode: true,
19223
19384
  };
19224
19385
  // Add a general instruction about using structured templates
@@ -19228,21 +19389,21 @@ class TemplateCommitmentDefinition extends BaseCommitmentDefinition {
19228
19389
  `);
19229
19390
  return {
19230
19391
  ...this.appendToSystemMessage(requirements, templateModeInstruction, '\n\n'),
19231
- metadata: updatedMetadata,
19392
+ _metadata: updatedMetadata,
19232
19393
  };
19233
19394
  }
19234
19395
  // If content is provided, add the specific template instructions
19235
19396
  const templateSection = `Response Template: ${trimmedContent}`;
19236
19397
  // Store the template in metadata for potential programmatic access
19237
- const existingTemplates = ((_a = requirements.metadata) === null || _a === void 0 ? void 0 : _a.templates) || [];
19398
+ const existingTemplates = ((_a = requirements._metadata) === null || _a === void 0 ? void 0 : _a.templates) || [];
19238
19399
  const updatedMetadata = {
19239
- ...requirements.metadata,
19400
+ ...requirements._metadata,
19240
19401
  templates: [...existingTemplates, trimmedContent],
19241
19402
  templateMode: true,
19242
19403
  };
19243
19404
  return {
19244
19405
  ...this.appendToSystemMessage(requirements, templateSection, '\n\n'),
19245
- metadata: updatedMetadata,
19406
+ _metadata: updatedMetadata,
19246
19407
  };
19247
19408
  }
19248
19409
  }
@@ -19579,8 +19740,8 @@ class UseBrowserCommitmentDefinition extends BaseCommitmentDefinition {
19579
19740
  return this.appendToSystemMessage({
19580
19741
  ...requirements,
19581
19742
  tools: updatedTools,
19582
- metadata: {
19583
- ...requirements.metadata,
19743
+ _metadata: {
19744
+ ...requirements._metadata,
19584
19745
  useBrowser: true,
19585
19746
  },
19586
19747
  }, spaceTrim$1(`
@@ -19809,8 +19970,8 @@ class UseEmailCommitmentDefinition extends BaseCommitmentDefinition {
19809
19970
  return this.appendToSystemMessage({
19810
19971
  ...requirements,
19811
19972
  tools: updatedTools,
19812
- metadata: {
19813
- ...requirements.metadata,
19973
+ _metadata: {
19974
+ ...requirements._metadata,
19814
19975
  useEmail: content || true,
19815
19976
  },
19816
19977
  }, spaceTrim$1((block) => `
@@ -19945,8 +20106,8 @@ class UseImageGeneratorCommitmentDefinition extends BaseCommitmentDefinition {
19945
20106
  return this.appendToSystemMessage({
19946
20107
  ...requirements,
19947
20108
  tools: updatedTools,
19948
- metadata: {
19949
- ...requirements.metadata,
20109
+ _metadata: {
20110
+ ...requirements._metadata,
19950
20111
  useImageGenerator: content || true,
19951
20112
  },
19952
20113
  }, spaceTrim$1(`
@@ -20237,8 +20398,8 @@ class UseSearchEngineCommitmentDefinition extends BaseCommitmentDefinition {
20237
20398
  return this.appendToSystemMessage({
20238
20399
  ...requirements,
20239
20400
  tools: updatedTools,
20240
- metadata: {
20241
- ...requirements.metadata,
20401
+ _metadata: {
20402
+ ...requirements._metadata,
20242
20403
  useSearchEngine: content || true,
20243
20404
  },
20244
20405
  }, spaceTrim$1((block) => `
@@ -20386,8 +20547,8 @@ class UseTimeCommitmentDefinition extends BaseCommitmentDefinition {
20386
20547
  return this.appendToSystemMessage({
20387
20548
  ...requirements,
20388
20549
  tools: updatedTools,
20389
- metadata: {
20390
- ...requirements.metadata,
20550
+ _metadata: {
20551
+ ...requirements._metadata,
20391
20552
  },
20392
20553
  }, spaceTrim$1((block) => `
20393
20554
  Time and date context:
@@ -24965,6 +25126,66 @@ const OPENAI_MODELS = exportJson({
24965
25126
  },
24966
25127
  /**/
24967
25128
  /**/
25129
+ {
25130
+ modelVariant: 'CHAT',
25131
+ modelTitle: 'gpt-5.2-codex',
25132
+ modelName: 'gpt-5.2-codex',
25133
+ modelDescription: 'High-capability Codex variant tuned for agentic code generation with large contexts and reasoning effort controls. Ideal for long-horizon coding workflows and multi-step reasoning.',
25134
+ pricing: {
25135
+ prompt: pricing(`$1.75 / 1M tokens`),
25136
+ output: pricing(`$14.00 / 1M tokens`),
25137
+ },
25138
+ },
25139
+ /**/
25140
+ /**/
25141
+ {
25142
+ modelVariant: 'CHAT',
25143
+ modelTitle: 'gpt-5.1-codex-max',
25144
+ modelName: 'gpt-5.1-codex-max',
25145
+ modelDescription: 'Premium GPT-5.1 Codex flavor that mirrors gpt-5.1 in capability and pricing while adding Codex tooling optimizations.',
25146
+ pricing: {
25147
+ prompt: pricing(`$1.25 / 1M tokens`),
25148
+ output: pricing(`$10.00 / 1M tokens`),
25149
+ },
25150
+ },
25151
+ /**/
25152
+ /**/
25153
+ {
25154
+ modelVariant: 'CHAT',
25155
+ modelTitle: 'gpt-5.1-codex',
25156
+ modelName: 'gpt-5.1-codex',
25157
+ modelDescription: 'Core GPT-5.1 Codex model focused on agentic coding tasks with a balanced trade-off between reasoning and cost.',
25158
+ pricing: {
25159
+ prompt: pricing(`$1.25 / 1M tokens`),
25160
+ output: pricing(`$10.00 / 1M tokens`),
25161
+ },
25162
+ },
25163
+ /**/
25164
+ /**/
25165
+ {
25166
+ modelVariant: 'CHAT',
25167
+ modelTitle: 'gpt-5.1-codex-mini',
25168
+ modelName: 'gpt-5.1-codex-mini',
25169
+ modelDescription: 'Compact, cost-effective GPT-5.1 Codex variant with a smaller context window ideal for cheap assistant iterations that still require coding awareness.',
25170
+ pricing: {
25171
+ prompt: pricing(`$0.25 / 1M tokens`),
25172
+ output: pricing(`$2.00 / 1M tokens`),
25173
+ },
25174
+ },
25175
+ /**/
25176
+ /**/
25177
+ {
25178
+ modelVariant: 'CHAT',
25179
+ modelTitle: 'gpt-5-codex',
25180
+ modelName: 'gpt-5-codex',
25181
+ modelDescription: 'Legacy GPT-5 Codex model built for agentic coding workloads with the same pricing as GPT-5 and a focus on stability.',
25182
+ pricing: {
25183
+ prompt: pricing(`$1.25 / 1M tokens`),
25184
+ output: pricing(`$10.00 / 1M tokens`),
25185
+ },
25186
+ },
25187
+ /**/
25188
+ /**/
24968
25189
  {
24969
25190
  modelVariant: 'CHAT',
24970
25191
  modelTitle: 'gpt-5-mini',
@@ -26993,6 +27214,32 @@ function isUnsupportedParameterError(error) {
26993
27214
  errorMessage.includes('does not support'));
26994
27215
  }
26995
27216
 
27217
+ /**
27218
+ * Provides access to the structured clone implementation when available.
27219
+ */
27220
+ function getStructuredCloneFunction() {
27221
+ return globalThis.structuredClone;
27222
+ }
27223
+ /**
27224
+ * Checks whether the prompt is a chat prompt that carries file attachments.
27225
+ */
27226
+ function hasChatPromptFiles(prompt) {
27227
+ return 'files' in prompt && Array.isArray(prompt.files);
27228
+ }
27229
+ /**
27230
+ * Creates a deep copy of the prompt while keeping attached files intact when structured clone is not available.
27231
+ */
27232
+ function clonePromptPreservingFiles(prompt) {
27233
+ const structuredCloneFn = getStructuredCloneFunction();
27234
+ if (typeof structuredCloneFn === 'function') {
27235
+ return structuredCloneFn(prompt);
27236
+ }
27237
+ const clonedPrompt = JSON.parse(JSON.stringify(prompt));
27238
+ if (hasChatPromptFiles(prompt)) {
27239
+ clonedPrompt.files = prompt.files;
27240
+ }
27241
+ return clonedPrompt;
27242
+ }
26996
27243
  /**
26997
27244
  * Execution Tools for calling OpenAI API or other OpenAI compatible provider
26998
27245
  *
@@ -27022,16 +27269,11 @@ class OpenAiCompatibleExecutionTools {
27022
27269
  const openAiOptions = { ...this.options };
27023
27270
  delete openAiOptions.isVerbose;
27024
27271
  delete openAiOptions.userId;
27025
- // Enhanced configuration for better ECONNRESET handling
27272
+ // Enhanced configuration with retries and timeouts.
27026
27273
  const enhancedOptions = {
27027
27274
  ...openAiOptions,
27028
27275
  timeout: API_REQUEST_TIMEOUT,
27029
27276
  maxRetries: CONNECTION_RETRIES_LIMIT,
27030
- defaultHeaders: {
27031
- Connection: 'keep-alive',
27032
- 'Keep-Alive': 'timeout=30, max=100',
27033
- ...openAiOptions.defaultHeaders,
27034
- },
27035
27277
  };
27036
27278
  this.client = new OpenAI(enhancedOptions);
27037
27279
  }
@@ -27082,7 +27324,7 @@ class OpenAiCompatibleExecutionTools {
27082
27324
  */
27083
27325
  async callChatModelStream(prompt, onProgress) {
27084
27326
  // Deep clone prompt and modelRequirements to avoid mutation across calls
27085
- const clonedPrompt = JSON.parse(JSON.stringify(prompt));
27327
+ const clonedPrompt = clonePromptPreservingFiles(prompt);
27086
27328
  // Use local Set for retried parameters to ensure independence and thread safety
27087
27329
  const retriedUnsupportedParameters = new Set();
27088
27330
  return this.callChatModelWithRetry(clonedPrompt, clonedPrompt.modelRequirements, [], retriedUnsupportedParameters, onProgress);
@@ -27109,7 +27351,10 @@ class OpenAiCompatibleExecutionTools {
27109
27351
  // <- TODO: [🈁] Use `seed` here AND/OR use is `isDeterministic` for entire execution tools
27110
27352
  // <- Note: [🧆]
27111
27353
  }; // <- TODO: [💩] Guard here types better
27112
- if (format === 'JSON') {
27354
+ if (currentModelRequirements.responseFormat !== undefined) {
27355
+ modelSettings.response_format = currentModelRequirements.responseFormat;
27356
+ }
27357
+ else if (format === 'JSON') {
27113
27358
  modelSettings.response_format = {
27114
27359
  type: 'json_object',
27115
27360
  };
@@ -28322,6 +28567,7 @@ const _OpenAiAssistantMetadataRegistration = $llmToolsMetadataRegister.register(
28322
28567
  apiKey: 'sk-',
28323
28568
  assistantId: 'asst_',
28324
28569
  maxRequestsPerMinute: DEFAULT_MAX_REQUESTS_PER_MINUTE,
28570
+ isCreatingNewAssistantsAllowed: false,
28325
28571
  },
28326
28572
  };
28327
28573
  },
@@ -28416,18 +28662,6 @@ class OpenAiExecutionTools extends OpenAiCompatibleExecutionTools {
28416
28662
  get profile() {
28417
28663
  return OPENAI_PROVIDER_PROFILE;
28418
28664
  }
28419
- /*
28420
- Note: Commenting this out to avoid circular dependency
28421
- /**
28422
- * Create (sub)tools for calling OpenAI API Assistants
28423
- *
28424
- * @param assistantId Which assistant to use
28425
- * @returns Tools for calling OpenAI API Assistants with same token
28426
- * /
28427
- public createAssistantSubtools(assistantId: string_token): OpenAiAssistantExecutionTools {
28428
- return new OpenAiAssistantExecutionTools({ ...this.options, assistantId });
28429
- }
28430
- */
28431
28665
  /**
28432
28666
  * List all available models (non dynamically)
28433
28667
  *
@@ -28462,6 +28696,775 @@ class OpenAiExecutionTools extends OpenAiCompatibleExecutionTools {
28462
28696
  }
28463
28697
  }
28464
28698
 
28699
+ const DEFAULT_KNOWLEDGE_SOURCE_DOWNLOAD_TIMEOUT_MS = 30000;
28700
+ const DEFAULT_KNOWLEDGE_SOURCE_UPLOAD_TIMEOUT_MS = 900000;
28701
+ const VECTOR_STORE_PROGRESS_LOG_INTERVAL_MIN_MS = 15000;
28702
+ const VECTOR_STORE_STALL_LOG_THRESHOLD_MS = 30000;
28703
+ /**
28704
+ * Base class for OpenAI execution tools that need hosted vector stores.
28705
+ *
28706
+ * @public exported from `@promptbook/openai`
28707
+ */
28708
+ class OpenAiVectorStoreHandler extends OpenAiExecutionTools {
28709
+ /**
28710
+ * Returns the per-knowledge-source download timeout in milliseconds.
28711
+ */
28712
+ getKnowledgeSourceDownloadTimeoutMs() {
28713
+ var _a;
28714
+ return (_a = this.vectorStoreOptions.knowledgeSourceDownloadTimeoutMs) !== null && _a !== void 0 ? _a : DEFAULT_KNOWLEDGE_SOURCE_DOWNLOAD_TIMEOUT_MS;
28715
+ }
28716
+ /**
28717
+ * Returns the max concurrency for knowledge source uploads.
28718
+ */
28719
+ getKnowledgeSourceUploadMaxConcurrency() {
28720
+ var _a;
28721
+ return (_a = this.vectorStoreOptions.knowledgeSourceUploadMaxConcurrency) !== null && _a !== void 0 ? _a : 5;
28722
+ }
28723
+ /**
28724
+ * Returns the polling interval in milliseconds for vector store uploads.
28725
+ */
28726
+ getKnowledgeSourceUploadPollIntervalMs() {
28727
+ var _a;
28728
+ return (_a = this.vectorStoreOptions.knowledgeSourceUploadPollIntervalMs) !== null && _a !== void 0 ? _a : 5000;
28729
+ }
28730
+ /**
28731
+ * Returns the overall upload timeout in milliseconds for vector store uploads.
28732
+ */
28733
+ getKnowledgeSourceUploadTimeoutMs() {
28734
+ var _a;
28735
+ return (_a = this.vectorStoreOptions.knowledgeSourceUploadTimeoutMs) !== null && _a !== void 0 ? _a : DEFAULT_KNOWLEDGE_SOURCE_UPLOAD_TIMEOUT_MS;
28736
+ }
28737
+ /**
28738
+ * Returns true if we should continue even if vector store ingestion stalls.
28739
+ */
28740
+ shouldContinueOnVectorStoreStall() {
28741
+ var _a;
28742
+ return (_a = this.vectorStoreOptions.shouldContinueOnVectorStoreStall) !== null && _a !== void 0 ? _a : true;
28743
+ }
28744
+ /**
28745
+ * Returns vector-store-specific options with extended settings.
28746
+ */
28747
+ get vectorStoreOptions() {
28748
+ return this.options;
28749
+ }
28750
+ /**
28751
+ * Returns the OpenAI vector stores API surface, supporting stable and beta SDKs.
28752
+ */
28753
+ getVectorStoresApi(client) {
28754
+ var _a, _b;
28755
+ const vectorStores = (_a = client.vectorStores) !== null && _a !== void 0 ? _a : (_b = client.beta) === null || _b === void 0 ? void 0 : _b.vectorStores;
28756
+ if (!vectorStores) {
28757
+ throw new Error('OpenAI client does not support vector stores. Please ensure you are using a compatible version of the OpenAI SDK with vector store support.');
28758
+ }
28759
+ return vectorStores;
28760
+ }
28761
+ /**
28762
+ * Downloads a knowledge source URL into a File for vector store upload.
28763
+ */
28764
+ async downloadKnowledgeSourceFile(options) {
28765
+ var _a;
28766
+ const { source, timeoutMs, logLabel } = options;
28767
+ const startedAtMs = Date.now();
28768
+ const controller = new AbortController();
28769
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
28770
+ if (this.options.isVerbose) {
28771
+ console.info('[🤰]', 'Downloading knowledge source', {
28772
+ source,
28773
+ timeoutMs,
28774
+ logLabel,
28775
+ });
28776
+ }
28777
+ try {
28778
+ const response = await fetch(source, { signal: controller.signal });
28779
+ const contentType = (_a = response.headers.get('content-type')) !== null && _a !== void 0 ? _a : undefined;
28780
+ if (!response.ok) {
28781
+ console.error('[🤰]', 'Failed to download knowledge source', {
28782
+ source,
28783
+ status: response.status,
28784
+ statusText: response.statusText,
28785
+ contentType,
28786
+ elapsedMs: Date.now() - startedAtMs,
28787
+ logLabel,
28788
+ });
28789
+ return null;
28790
+ }
28791
+ const buffer = await response.arrayBuffer();
28792
+ let filename = source.split('/').pop() || 'downloaded-file';
28793
+ try {
28794
+ const url = new URL(source);
28795
+ filename = url.pathname.split('/').pop() || filename;
28796
+ }
28797
+ catch (error) {
28798
+ // Keep default filename
28799
+ }
28800
+ const file = new File([buffer], filename, contentType ? { type: contentType } : undefined);
28801
+ const elapsedMs = Date.now() - startedAtMs;
28802
+ const sizeBytes = buffer.byteLength;
28803
+ if (this.options.isVerbose) {
28804
+ console.info('[🤰]', 'Downloaded knowledge source', {
28805
+ source,
28806
+ filename,
28807
+ sizeBytes,
28808
+ contentType,
28809
+ elapsedMs,
28810
+ logLabel,
28811
+ });
28812
+ }
28813
+ return { file, sizeBytes, filename, elapsedMs };
28814
+ }
28815
+ catch (error) {
28816
+ assertsError(error);
28817
+ console.error('[🤰]', 'Error downloading knowledge source', {
28818
+ source,
28819
+ elapsedMs: Date.now() - startedAtMs,
28820
+ logLabel,
28821
+ error: serializeError(error),
28822
+ });
28823
+ return null;
28824
+ }
28825
+ finally {
28826
+ clearTimeout(timeoutId);
28827
+ }
28828
+ }
28829
+ /**
28830
+ * Logs vector store file batch diagnostics to help trace ingestion stalls or failures.
28831
+ */
28832
+ async logVectorStoreFileBatchDiagnostics(options) {
28833
+ var _a, _b, _c, _d, _e;
28834
+ const { client, vectorStoreId, batchId, uploadedFiles, logLabel, reason } = options;
28835
+ if (reason === 'stalled' && !this.options.isVerbose) {
28836
+ return;
28837
+ }
28838
+ if (!batchId.startsWith('vsfb_')) {
28839
+ console.error('[🤰]', 'Vector store file batch diagnostics skipped (invalid batch id)', {
28840
+ vectorStoreId,
28841
+ batchId,
28842
+ reason,
28843
+ logLabel,
28844
+ });
28845
+ return;
28846
+ }
28847
+ const fileIdToMetadata = new Map();
28848
+ for (const file of uploadedFiles) {
28849
+ fileIdToMetadata.set(file.fileId, file);
28850
+ }
28851
+ try {
28852
+ const vectorStores = this.getVectorStoresApi(client);
28853
+ const limit = Math.min(100, Math.max(10, uploadedFiles.length));
28854
+ const batchFilesPage = await vectorStores.fileBatches.listFiles(batchId, {
28855
+ vector_store_id: vectorStoreId,
28856
+ limit,
28857
+ });
28858
+ const batchFiles = (_a = batchFilesPage.data) !== null && _a !== void 0 ? _a : [];
28859
+ const statusCounts = {
28860
+ in_progress: 0,
28861
+ completed: 0,
28862
+ failed: 0,
28863
+ cancelled: 0,
28864
+ };
28865
+ const errorSamples = [];
28866
+ const inProgressSamples = [];
28867
+ const batchFileIds = new Set();
28868
+ for (const file of batchFiles) {
28869
+ const status = (_b = file.status) !== null && _b !== void 0 ? _b : 'unknown';
28870
+ statusCounts[status] = ((_c = statusCounts[status]) !== null && _c !== void 0 ? _c : 0) + 1;
28871
+ const vectorStoreFileId = file.id;
28872
+ const uploadedFileId = (_d = file.file_id) !== null && _d !== void 0 ? _d : file.fileId;
28873
+ const fileId = uploadedFileId !== null && uploadedFileId !== void 0 ? uploadedFileId : vectorStoreFileId;
28874
+ batchFileIds.add(fileId);
28875
+ const metadata = fileIdToMetadata.get(fileId);
28876
+ if (status === 'failed') {
28877
+ errorSamples.push({
28878
+ fileId,
28879
+ status,
28880
+ error: (_e = file.last_error) === null || _e === void 0 ? void 0 : _e.message,
28881
+ filename: metadata === null || metadata === void 0 ? void 0 : metadata.filename,
28882
+ vectorStoreFileId: uploadedFileId ? vectorStoreFileId : undefined,
28883
+ });
28884
+ }
28885
+ if (status === 'in_progress') {
28886
+ inProgressSamples.push({
28887
+ fileId,
28888
+ filename: metadata === null || metadata === void 0 ? void 0 : metadata.filename,
28889
+ vectorStoreFileId: uploadedFileId ? vectorStoreFileId : undefined,
28890
+ });
28891
+ }
28892
+ }
28893
+ const missingSamples = uploadedFiles
28894
+ .filter((file) => !batchFileIds.has(file.fileId))
28895
+ .slice(0, 5)
28896
+ .map((file) => ({
28897
+ fileId: file.fileId,
28898
+ filename: file.filename,
28899
+ sizeBytes: file.sizeBytes,
28900
+ }));
28901
+ const vectorStore = await vectorStores.retrieve(vectorStoreId);
28902
+ const logPayload = {
28903
+ vectorStoreId,
28904
+ batchId,
28905
+ reason,
28906
+ vectorStoreStatus: vectorStore.status,
28907
+ vectorStoreFileCounts: vectorStore.file_counts,
28908
+ vectorStoreUsageBytes: vectorStore.usage_bytes,
28909
+ batchFileCount: batchFiles.length,
28910
+ statusCounts,
28911
+ errorSamples: errorSamples.slice(0, 5),
28912
+ inProgressSamples,
28913
+ missingFileCount: uploadedFiles.length - batchFileIds.size,
28914
+ missingSamples,
28915
+ logLabel,
28916
+ };
28917
+ const logFunction = reason === 'stalled' ? console.info : console.error;
28918
+ logFunction('[🤰]', 'Vector store file batch diagnostics', logPayload);
28919
+ }
28920
+ catch (error) {
28921
+ assertsError(error);
28922
+ console.error('[🤰]', 'Vector store file batch diagnostics failed', {
28923
+ vectorStoreId,
28924
+ batchId,
28925
+ reason,
28926
+ logLabel,
28927
+ error: serializeError(error),
28928
+ });
28929
+ }
28930
+ }
28931
+ /**
28932
+ * Uploads knowledge source files to the vector store and polls until processing completes.
28933
+ */
28934
+ async uploadKnowledgeSourceFilesToVectorStore(options) {
28935
+ var _a, _b, _c, _d, _e, _f;
28936
+ const { client, vectorStoreId, files, totalBytes, logLabel } = options;
28937
+ const vectorStores = this.getVectorStoresApi(client);
28938
+ const uploadStartedAtMs = Date.now();
28939
+ const maxConcurrency = Math.max(1, this.getKnowledgeSourceUploadMaxConcurrency());
28940
+ const pollIntervalMs = Math.max(1000, this.getKnowledgeSourceUploadPollIntervalMs());
28941
+ const uploadTimeoutMs = Math.max(1000, this.getKnowledgeSourceUploadTimeoutMs());
28942
+ if (this.options.isVerbose) {
28943
+ console.info('[🤰]', 'Uploading knowledge source files to OpenAI', {
28944
+ vectorStoreId,
28945
+ fileCount: files.length,
28946
+ totalBytes,
28947
+ maxConcurrency,
28948
+ pollIntervalMs,
28949
+ uploadTimeoutMs,
28950
+ logLabel,
28951
+ });
28952
+ }
28953
+ const fileTypeSummary = {};
28954
+ for (const file of files) {
28955
+ const filename = (_a = file.name) !== null && _a !== void 0 ? _a : '';
28956
+ const extension = filename.includes('.')
28957
+ ? (_c = (_b = filename.split('.').pop()) === null || _b === void 0 ? void 0 : _b.toLowerCase()) !== null && _c !== void 0 ? _c : 'unknown'
28958
+ : 'unknown';
28959
+ const sizeBytes = typeof file.size === 'number' ? file.size : 0;
28960
+ const summary = (_d = fileTypeSummary[extension]) !== null && _d !== void 0 ? _d : { count: 0, totalBytes: 0 };
28961
+ summary.count += 1;
28962
+ summary.totalBytes += sizeBytes;
28963
+ fileTypeSummary[extension] = summary;
28964
+ }
28965
+ if (this.options.isVerbose) {
28966
+ console.info('[🤰]', 'Knowledge source file summary', {
28967
+ vectorStoreId,
28968
+ fileCount: files.length,
28969
+ totalBytes,
28970
+ fileTypeSummary,
28971
+ logLabel,
28972
+ });
28973
+ }
28974
+ const fileEntries = files.map((file, index) => ({ file, index }));
28975
+ const fileIterator = fileEntries.values();
28976
+ const fileIds = [];
28977
+ const uploadedFiles = [];
28978
+ const failedUploads = [];
28979
+ let uploadedCount = 0;
28980
+ const processFiles = async (iterator) => {
28981
+ var _a, _b;
28982
+ for (const { file, index } of iterator) {
28983
+ const uploadIndex = index + 1;
28984
+ const filename = file.name || `knowledge-source-${uploadIndex}`;
28985
+ const extension = filename.includes('.')
28986
+ ? (_b = (_a = filename.split('.').pop()) === null || _a === void 0 ? void 0 : _a.toLowerCase()) !== null && _b !== void 0 ? _b : 'unknown'
28987
+ : 'unknown';
28988
+ const sizeBytes = typeof file.size === 'number' ? file.size : undefined;
28989
+ const fileUploadStartedAtMs = Date.now();
28990
+ if (this.options.isVerbose) {
28991
+ console.info('[🤰]', 'Uploading knowledge source file', {
28992
+ index: uploadIndex,
28993
+ total: files.length,
28994
+ filename,
28995
+ extension,
28996
+ sizeBytes,
28997
+ logLabel,
28998
+ });
28999
+ }
29000
+ try {
29001
+ const uploaded = await client.files.create({ file, purpose: 'assistants' });
29002
+ fileIds.push(uploaded.id);
29003
+ uploadedFiles.push({ fileId: uploaded.id, filename, sizeBytes });
29004
+ uploadedCount += 1;
29005
+ if (this.options.isVerbose) {
29006
+ console.info('[🤰]', 'Uploaded knowledge source file', {
29007
+ index: uploadIndex,
29008
+ total: files.length,
29009
+ filename,
29010
+ sizeBytes,
29011
+ fileId: uploaded.id,
29012
+ elapsedMs: Date.now() - fileUploadStartedAtMs,
29013
+ logLabel,
29014
+ });
29015
+ }
29016
+ }
29017
+ catch (error) {
29018
+ assertsError(error);
29019
+ const serializedError = serializeError(error);
29020
+ failedUploads.push({ index: uploadIndex, filename, error: serializedError });
29021
+ console.error('[🤰]', 'Failed to upload knowledge source file', {
29022
+ index: uploadIndex,
29023
+ total: files.length,
29024
+ filename,
29025
+ sizeBytes,
29026
+ elapsedMs: Date.now() - fileUploadStartedAtMs,
29027
+ logLabel,
29028
+ error: serializedError,
29029
+ });
29030
+ }
29031
+ }
29032
+ };
29033
+ const workerCount = Math.min(maxConcurrency, files.length);
29034
+ const workers = Array.from({ length: workerCount }, () => processFiles(fileIterator));
29035
+ await Promise.all(workers);
29036
+ if (this.options.isVerbose) {
29037
+ console.info('[🤰]', 'Finished uploading knowledge source files', {
29038
+ vectorStoreId,
29039
+ fileCount: files.length,
29040
+ uploadedCount,
29041
+ failedCount: failedUploads.length,
29042
+ elapsedMs: Date.now() - uploadStartedAtMs,
29043
+ failedSamples: failedUploads.slice(0, 3),
29044
+ logLabel,
29045
+ });
29046
+ }
29047
+ if (fileIds.length === 0) {
29048
+ console.error('[🤰]', 'No knowledge source files were uploaded', {
29049
+ vectorStoreId,
29050
+ fileCount: files.length,
29051
+ failedCount: failedUploads.length,
29052
+ logLabel,
29053
+ });
29054
+ return null;
29055
+ }
29056
+ const batch = await vectorStores.fileBatches.create(vectorStoreId, {
29057
+ file_ids: fileIds,
29058
+ });
29059
+ const expectedBatchId = batch.id;
29060
+ const expectedBatchIdValid = expectedBatchId.startsWith('vsfb_');
29061
+ if (!expectedBatchIdValid) {
29062
+ console.error('[🤰]', 'Vector store file batch id looks invalid', {
29063
+ vectorStoreId,
29064
+ batchId: expectedBatchId,
29065
+ batchVectorStoreId: batch.vector_store_id,
29066
+ logLabel,
29067
+ });
29068
+ }
29069
+ else if (batch.vector_store_id !== vectorStoreId) {
29070
+ console.error('[🤰]', 'Vector store file batch vector store id mismatch', {
29071
+ vectorStoreId,
29072
+ batchId: expectedBatchId,
29073
+ batchVectorStoreId: batch.vector_store_id,
29074
+ logLabel,
29075
+ });
29076
+ }
29077
+ if (this.options.isVerbose) {
29078
+ console.info('[🤰]', 'Created vector store file batch', {
29079
+ vectorStoreId,
29080
+ batchId: expectedBatchId,
29081
+ fileCount: fileIds.length,
29082
+ logLabel,
29083
+ });
29084
+ }
29085
+ const pollStartedAtMs = Date.now();
29086
+ const progressLogIntervalMs = Math.max(VECTOR_STORE_PROGRESS_LOG_INTERVAL_MIN_MS, pollIntervalMs);
29087
+ const diagnosticsIntervalMs = Math.max(60000, pollIntervalMs * 5);
29088
+ // let lastStatus: string | undefined;
29089
+ let lastCountsKey = '';
29090
+ let lastProgressKey = '';
29091
+ let lastLogAtMs = 0;
29092
+ let lastProgressAtMs = pollStartedAtMs;
29093
+ let lastDiagnosticsAtMs = pollStartedAtMs;
29094
+ let latestBatch = batch;
29095
+ let loggedBatchIdMismatch = false;
29096
+ let loggedBatchIdFallback = false;
29097
+ let loggedBatchIdInvalid = false;
29098
+ let shouldPoll = true;
29099
+ while (shouldPoll) {
29100
+ const nowMs = Date.now();
29101
+ // [🤰] Note: Sometimes OpenAI returns Vector Store object instead of Batch object, or IDs get swapped.
29102
+ const rawBatchId = typeof latestBatch.id === 'string' ? latestBatch.id : '';
29103
+ const rawVectorStoreId = latestBatch.vector_store_id;
29104
+ let returnedBatchId = rawBatchId;
29105
+ let returnedBatchIdValid = typeof returnedBatchId === 'string' && returnedBatchId.startsWith('vsfb_');
29106
+ if (!returnedBatchIdValid && expectedBatchIdValid) {
29107
+ if (!loggedBatchIdFallback) {
29108
+ console.error('[🤰]', 'Vector store file batch id missing from response; falling back to expected', {
29109
+ vectorStoreId,
29110
+ expectedBatchId,
29111
+ returnedBatchId,
29112
+ rawVectorStoreId,
29113
+ logLabel,
29114
+ });
29115
+ loggedBatchIdFallback = true;
29116
+ }
29117
+ returnedBatchId = expectedBatchId;
29118
+ returnedBatchIdValid = true;
29119
+ }
29120
+ if (!returnedBatchIdValid && !loggedBatchIdInvalid) {
29121
+ console.error('[🤰]', 'Vector store file batch id is invalid; stopping polling', {
29122
+ vectorStoreId,
29123
+ expectedBatchId,
29124
+ returnedBatchId,
29125
+ rawVectorStoreId,
29126
+ logLabel,
29127
+ });
29128
+ loggedBatchIdInvalid = true;
29129
+ }
29130
+ const batchIdMismatch = expectedBatchIdValid && returnedBatchIdValid && returnedBatchId !== expectedBatchId;
29131
+ if (batchIdMismatch && !loggedBatchIdMismatch) {
29132
+ console.error('[🤰]', 'Vector store file batch id mismatch', {
29133
+ vectorStoreId,
29134
+ expectedBatchId,
29135
+ returnedBatchId,
29136
+ logLabel,
29137
+ });
29138
+ loggedBatchIdMismatch = true;
29139
+ }
29140
+ if (returnedBatchIdValid) {
29141
+ latestBatch = await vectorStores.fileBatches.retrieve(returnedBatchId, {
29142
+ vector_store_id: vectorStoreId,
29143
+ });
29144
+ }
29145
+ else {
29146
+ shouldPoll = false;
29147
+ continue;
29148
+ }
29149
+ const status = (_e = latestBatch.status) !== null && _e !== void 0 ? _e : 'unknown';
29150
+ const fileCounts = (_f = latestBatch.file_counts) !== null && _f !== void 0 ? _f : {};
29151
+ const progressKey = JSON.stringify(fileCounts);
29152
+ const statusCountsKey = `${status}-${progressKey}`;
29153
+ const isProgressing = progressKey !== lastProgressKey;
29154
+ if (isProgressing) {
29155
+ lastProgressAtMs = nowMs;
29156
+ lastProgressKey = progressKey;
29157
+ }
29158
+ if (this.options.isVerbose &&
29159
+ (statusCountsKey !== lastCountsKey || nowMs - lastLogAtMs >= progressLogIntervalMs)) {
29160
+ console.info('[🤰]', 'Vector store file batch status', {
29161
+ vectorStoreId,
29162
+ batchId: returnedBatchId,
29163
+ status,
29164
+ fileCounts,
29165
+ elapsedMs: nowMs - pollStartedAtMs,
29166
+ logLabel,
29167
+ });
29168
+ lastCountsKey = statusCountsKey;
29169
+ lastLogAtMs = nowMs;
29170
+ }
29171
+ if (status === 'in_progress' &&
29172
+ nowMs - lastProgressAtMs >= VECTOR_STORE_STALL_LOG_THRESHOLD_MS &&
29173
+ nowMs - lastDiagnosticsAtMs >= diagnosticsIntervalMs) {
29174
+ lastDiagnosticsAtMs = nowMs;
29175
+ await this.logVectorStoreFileBatchDiagnostics({
29176
+ client,
29177
+ vectorStoreId,
29178
+ batchId: returnedBatchId,
29179
+ uploadedFiles,
29180
+ logLabel,
29181
+ reason: 'stalled',
29182
+ });
29183
+ }
29184
+ if (status === 'completed') {
29185
+ if (this.options.isVerbose) {
29186
+ console.info('[🤰]', 'Vector store file batch completed', {
29187
+ vectorStoreId,
29188
+ batchId: returnedBatchId,
29189
+ fileCounts,
29190
+ elapsedMs: nowMs - pollStartedAtMs,
29191
+ logLabel,
29192
+ });
29193
+ }
29194
+ shouldPoll = false;
29195
+ continue;
29196
+ }
29197
+ if (status === 'failed') {
29198
+ console.error('[🤰]', 'Vector store file batch completed with failures', {
29199
+ vectorStoreId,
29200
+ batchId: returnedBatchId,
29201
+ fileCounts,
29202
+ elapsedMs: nowMs - pollStartedAtMs,
29203
+ logLabel,
29204
+ });
29205
+ await this.logVectorStoreFileBatchDiagnostics({
29206
+ client,
29207
+ vectorStoreId,
29208
+ batchId: returnedBatchId,
29209
+ uploadedFiles,
29210
+ logLabel,
29211
+ reason: 'failed',
29212
+ });
29213
+ shouldPoll = false;
29214
+ continue;
29215
+ }
29216
+ if (status === 'cancelled') {
29217
+ console.error('[🤰]', 'Vector store file batch did not complete', {
29218
+ vectorStoreId,
29219
+ batchId: returnedBatchId,
29220
+ status,
29221
+ fileCounts,
29222
+ elapsedMs: nowMs - pollStartedAtMs,
29223
+ logLabel,
29224
+ });
29225
+ await this.logVectorStoreFileBatchDiagnostics({
29226
+ client,
29227
+ vectorStoreId,
29228
+ batchId: returnedBatchId,
29229
+ uploadedFiles,
29230
+ logLabel,
29231
+ reason: 'failed',
29232
+ });
29233
+ shouldPoll = false;
29234
+ continue;
29235
+ }
29236
+ if (nowMs - pollStartedAtMs >= uploadTimeoutMs) {
29237
+ console.error('[🤰]', 'Timed out waiting for vector store file batch', {
29238
+ vectorStoreId,
29239
+ batchId: returnedBatchId,
29240
+ fileCounts,
29241
+ elapsedMs: nowMs - pollStartedAtMs,
29242
+ uploadTimeoutMs,
29243
+ logLabel,
29244
+ });
29245
+ await this.logVectorStoreFileBatchDiagnostics({
29246
+ client,
29247
+ vectorStoreId,
29248
+ batchId: returnedBatchId,
29249
+ uploadedFiles,
29250
+ logLabel,
29251
+ reason: 'timeout',
29252
+ });
29253
+ if (this.shouldContinueOnVectorStoreStall()) {
29254
+ console.warn('[🤰]', 'Continuing despite vector store timeout as requested', {
29255
+ vectorStoreId,
29256
+ logLabel,
29257
+ });
29258
+ shouldPoll = false;
29259
+ continue;
29260
+ }
29261
+ try {
29262
+ const cancelBatchId = batchIdMismatch && returnedBatchId.startsWith('vsfb_') ? returnedBatchId : expectedBatchId;
29263
+ if (!cancelBatchId.startsWith('vsfb_')) {
29264
+ console.error('[🤰]', 'Skipping vector store file batch cancel (invalid batch id)', {
29265
+ vectorStoreId,
29266
+ batchId: cancelBatchId,
29267
+ logLabel,
29268
+ });
29269
+ }
29270
+ else {
29271
+ await vectorStores.fileBatches.cancel(cancelBatchId, {
29272
+ vector_store_id: vectorStoreId,
29273
+ });
29274
+ }
29275
+ if (this.options.isVerbose) {
29276
+ console.info('[🤰]', 'Cancelled vector store file batch after timeout', {
29277
+ vectorStoreId,
29278
+ batchId: batchIdMismatch && returnedBatchId.startsWith('vsfb_')
29279
+ ? returnedBatchId
29280
+ : expectedBatchId,
29281
+ ...(batchIdMismatch ? { returnedBatchId } : {}),
29282
+ logLabel,
29283
+ });
29284
+ }
29285
+ }
29286
+ catch (error) {
29287
+ assertsError(error);
29288
+ console.error('[🤰]', 'Failed to cancel vector store file batch after timeout', {
29289
+ vectorStoreId,
29290
+ batchId: expectedBatchId,
29291
+ ...(batchIdMismatch ? { returnedBatchId } : {}),
29292
+ logLabel,
29293
+ error: serializeError(error),
29294
+ });
29295
+ }
29296
+ shouldPoll = false;
29297
+ continue;
29298
+ }
29299
+ await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
29300
+ }
29301
+ return latestBatch;
29302
+ }
29303
+ /**
29304
+ * Creates a vector store and uploads knowledge sources, returning its ID.
29305
+ */
29306
+ async createVectorStoreWithKnowledgeSources(options) {
29307
+ const { client, name, knowledgeSources, logLabel } = options;
29308
+ const vectorStores = this.getVectorStoresApi(client);
29309
+ const knowledgeSourcesCount = knowledgeSources.length;
29310
+ const downloadTimeoutMs = this.getKnowledgeSourceDownloadTimeoutMs();
29311
+ if (this.options.isVerbose) {
29312
+ console.info('[🤰]', 'Creating vector store with knowledge sources', {
29313
+ name,
29314
+ knowledgeSourcesCount,
29315
+ downloadTimeoutMs,
29316
+ logLabel,
29317
+ });
29318
+ }
29319
+ const vectorStore = await vectorStores.create({
29320
+ name: `${name} Knowledge Base`,
29321
+ });
29322
+ const vectorStoreId = vectorStore.id;
29323
+ if (this.options.isVerbose) {
29324
+ console.info('[🤰]', 'Vector store created', {
29325
+ vectorStoreId,
29326
+ logLabel,
29327
+ });
29328
+ }
29329
+ const fileStreams = [];
29330
+ const skippedSources = [];
29331
+ let totalBytes = 0;
29332
+ const processingStartedAtMs = Date.now();
29333
+ for (const [index, source] of knowledgeSources.entries()) {
29334
+ try {
29335
+ const isDataUrl = isDataUrlKnowledgeSource(source);
29336
+ const isHttp = source.startsWith('http://') || source.startsWith('https://');
29337
+ const sourceType = isDataUrl ? 'data_url' : isHttp ? 'url' : 'file';
29338
+ if (this.options.isVerbose) {
29339
+ console.info('[🤰]', 'Processing knowledge source', {
29340
+ index: index + 1,
29341
+ total: knowledgeSourcesCount,
29342
+ source,
29343
+ sourceType,
29344
+ logLabel,
29345
+ });
29346
+ }
29347
+ if (isDataUrl) {
29348
+ const parsed = parseDataUrlKnowledgeSource(source);
29349
+ if (!parsed) {
29350
+ skippedSources.push({ source, reason: 'invalid_data_url' });
29351
+ if (this.options.isVerbose) {
29352
+ console.info('[🤰]', 'Skipping knowledge source (invalid data URL)', {
29353
+ source,
29354
+ sourceType,
29355
+ logLabel,
29356
+ });
29357
+ }
29358
+ continue;
29359
+ }
29360
+ const dataUrlFile = new File([parsed.buffer], parsed.filename, {
29361
+ type: parsed.mimeType,
29362
+ });
29363
+ fileStreams.push(dataUrlFile);
29364
+ totalBytes += parsed.buffer.length;
29365
+ continue;
29366
+ }
29367
+ if (isHttp) {
29368
+ const downloadResult = await this.downloadKnowledgeSourceFile({
29369
+ source,
29370
+ timeoutMs: downloadTimeoutMs,
29371
+ logLabel,
29372
+ });
29373
+ if (downloadResult) {
29374
+ fileStreams.push(downloadResult.file);
29375
+ totalBytes += downloadResult.sizeBytes;
29376
+ }
29377
+ else {
29378
+ skippedSources.push({ source, reason: 'download_failed' });
29379
+ }
29380
+ }
29381
+ else {
29382
+ skippedSources.push({ source, reason: 'unsupported_source_type' });
29383
+ if (this.options.isVerbose) {
29384
+ console.info('[🤰]', 'Skipping knowledge source (unsupported type)', {
29385
+ source,
29386
+ sourceType,
29387
+ logLabel,
29388
+ });
29389
+ }
29390
+ /*
29391
+ TODO: [🤰] Resolve problem with browser environment
29392
+ // Assume it's a local file path
29393
+ // Note: This will work in Node.js environment
29394
+ // For browser environments, this would need different handling
29395
+ const fs = await import('fs');
29396
+ const fileStream = fs.createReadStream(source);
29397
+ fileStreams.push(fileStream);
29398
+ */
29399
+ }
29400
+ }
29401
+ catch (error) {
29402
+ assertsError(error);
29403
+ skippedSources.push({ source, reason: 'processing_error' });
29404
+ console.error('[🤰]', 'Error processing knowledge source', {
29405
+ source,
29406
+ logLabel,
29407
+ error: serializeError(error),
29408
+ });
29409
+ }
29410
+ }
29411
+ if (this.options.isVerbose) {
29412
+ console.info('[🤰]', 'Finished processing knowledge sources', {
29413
+ total: knowledgeSourcesCount,
29414
+ downloadedCount: fileStreams.length,
29415
+ skippedCount: skippedSources.length,
29416
+ totalBytes,
29417
+ elapsedMs: Date.now() - processingStartedAtMs,
29418
+ skippedSamples: skippedSources.slice(0, 3),
29419
+ logLabel,
29420
+ });
29421
+ }
29422
+ if (fileStreams.length > 0) {
29423
+ if (this.options.isVerbose) {
29424
+ console.info('[🤰]', 'Uploading files to vector store', {
29425
+ vectorStoreId,
29426
+ fileCount: fileStreams.length,
29427
+ totalBytes,
29428
+ maxConcurrency: this.getKnowledgeSourceUploadMaxConcurrency(),
29429
+ pollIntervalMs: this.getKnowledgeSourceUploadPollIntervalMs(),
29430
+ uploadTimeoutMs: this.getKnowledgeSourceUploadTimeoutMs(),
29431
+ logLabel,
29432
+ });
29433
+ }
29434
+ try {
29435
+ await this.uploadKnowledgeSourceFilesToVectorStore({
29436
+ client,
29437
+ vectorStoreId,
29438
+ files: fileStreams,
29439
+ totalBytes,
29440
+ logLabel,
29441
+ });
29442
+ }
29443
+ catch (error) {
29444
+ assertsError(error);
29445
+ console.error('[🤰]', 'Error uploading files to vector store', {
29446
+ vectorStoreId,
29447
+ logLabel,
29448
+ error: serializeError(error),
29449
+ });
29450
+ }
29451
+ }
29452
+ else if (this.options.isVerbose) {
29453
+ console.info('[🤰]', 'No knowledge source files to upload', {
29454
+ vectorStoreId,
29455
+ skippedCount: skippedSources.length,
29456
+ logLabel,
29457
+ });
29458
+ }
29459
+ return {
29460
+ vectorStoreId,
29461
+ uploadedFileCount: fileStreams.length,
29462
+ skippedCount: skippedSources.length,
29463
+ totalBytes,
29464
+ };
29465
+ }
29466
+ }
29467
+
28465
29468
  /**
28466
29469
  * Uploads files to OpenAI and returns their IDs
28467
29470
  *
@@ -28495,10 +29498,10 @@ async function uploadFilesToOpenAi(client, files) {
28495
29498
  * - `OpenAiAssistantExecutionTools` - which is a specific implementation of `LlmExecutionTools` for OpenAI models with assistant capabilities, recommended for usage in `Agent` or `AgentLlmExecutionTools`
28496
29499
  * - `RemoteAgent` - which is an `Agent` that connects to a Promptbook Agents Server
28497
29500
  *
29501
+ * @deprecated Use `OpenAiAgentKitExecutionTools` instead.
28498
29502
  * @public exported from `@promptbook/openai`
28499
- * @deprecated Use `OpenAiAgentExecutionTools` instead which uses the new OpenAI Responses API
28500
29503
  */
28501
- class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
29504
+ class OpenAiAssistantExecutionTools extends OpenAiVectorStoreHandler {
28502
29505
  /**
28503
29506
  * Creates OpenAI Execution Tools.
28504
29507
  *
@@ -28627,8 +29630,7 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
28627
29630
  console.info(colors.bgWhite('rawRequest (non-streaming with tools)'), JSON.stringify(rawRequest, null, 4));
28628
29631
  }
28629
29632
  // Create thread and run
28630
- const threadAndRun = await client.beta.threads.createAndRun(rawRequest);
28631
- let run = threadAndRun;
29633
+ let run = (await client.beta.threads.createAndRun(rawRequest));
28632
29634
  const completedToolCalls = [];
28633
29635
  const toolCallStartedAt = new Map();
28634
29636
  // Poll until run completes or requires action
@@ -28723,14 +29725,14 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
28723
29725
  }
28724
29726
  }
28725
29727
  // Submit tool outputs
28726
- run = await client.beta.threads.runs.submitToolOutputs(run.thread_id, run.id, {
29728
+ run = (await client.beta.threads.runs.submitToolOutputs(run.thread_id, run.id, {
28727
29729
  tool_outputs: toolOutputs,
28728
- });
29730
+ }));
28729
29731
  }
28730
29732
  else {
28731
29733
  // Wait a bit before polling again
28732
29734
  await new Promise((resolve) => setTimeout(resolve, 500));
28733
- run = await client.beta.threads.runs.retrieve(run.thread_id, run.id);
29735
+ run = (await client.beta.threads.runs.retrieve(run.thread_id, run.id));
28734
29736
  }
28735
29737
  }
28736
29738
  if (run.status !== 'completed') {
@@ -28929,6 +29931,7 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
28929
29931
  getAssistant(assistantId) {
28930
29932
  return new OpenAiAssistantExecutionTools({
28931
29933
  ...this.options,
29934
+ isCreatingNewAssistantsAllowed: this.isCreatingNewAssistantsAllowed,
28932
29935
  assistantId,
28933
29936
  });
28934
29937
  }
@@ -28954,88 +29957,13 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
28954
29957
  let vectorStoreId;
28955
29958
  // If knowledge sources are provided, create a vector store with them
28956
29959
  if (knowledgeSources && knowledgeSources.length > 0) {
28957
- if (this.options.isVerbose) {
28958
- console.info('[🤰]', 'Creating vector store with knowledge sources', {
28959
- name,
28960
- knowledgeSourcesCount,
28961
- });
28962
- }
28963
- // Create a vector store
28964
- const vectorStore = await client.beta.vectorStores.create({
28965
- name: `${name} Knowledge Base`,
29960
+ const vectorStoreResult = await this.createVectorStoreWithKnowledgeSources({
29961
+ client,
29962
+ name,
29963
+ knowledgeSources,
29964
+ logLabel: 'assistant creation',
28966
29965
  });
28967
- vectorStoreId = vectorStore.id;
28968
- if (this.options.isVerbose) {
28969
- console.info('[🤰]', 'Vector store created', {
28970
- vectorStoreId,
28971
- });
28972
- }
28973
- // Upload files from knowledge sources to the vector store
28974
- const fileStreams = [];
28975
- for (const [index, source] of knowledgeSources.entries()) {
28976
- try {
28977
- if (this.options.isVerbose) {
28978
- console.info('[🤰]', 'Processing knowledge source', {
28979
- index: index + 1,
28980
- total: knowledgeSources.length,
28981
- source,
28982
- sourceType: source.startsWith('http') || source.startsWith('https') ? 'url' : 'file',
28983
- });
28984
- }
28985
- // Check if it's a URL
28986
- if (source.startsWith('http://') || source.startsWith('https://')) {
28987
- // Download the file
28988
- const response = await fetch(source);
28989
- if (!response.ok) {
28990
- console.error(`Failed to download ${source}: ${response.statusText}`);
28991
- continue;
28992
- }
28993
- const buffer = await response.arrayBuffer();
28994
- let filename = source.split('/').pop() || 'downloaded-file';
28995
- try {
28996
- const url = new URL(source);
28997
- filename = url.pathname.split('/').pop() || filename;
28998
- }
28999
- catch (error) {
29000
- // Keep default filename
29001
- }
29002
- const blob = new Blob([buffer]);
29003
- const file = new File([blob], filename);
29004
- fileStreams.push(file);
29005
- }
29006
- else {
29007
- /*
29008
- TODO: [🐱‍🚀] Resolve problem with browser environment
29009
- // Assume it's a local file path
29010
- // Note: This will work in Node.js environment
29011
- // For browser environments, this would need different handling
29012
- const fs = await import('fs');
29013
- const fileStream = fs.createReadStream(source);
29014
- fileStreams.push(fileStream);
29015
- */
29016
- }
29017
- }
29018
- catch (error) {
29019
- console.error(`Error processing knowledge source ${source}:`, error);
29020
- }
29021
- }
29022
- // Batch upload files to the vector store
29023
- if (fileStreams.length > 0) {
29024
- try {
29025
- await client.beta.vectorStores.fileBatches.uploadAndPoll(vectorStoreId, {
29026
- files: fileStreams,
29027
- });
29028
- if (this.options.isVerbose) {
29029
- console.info('[🤰]', 'Uploaded files to vector store', {
29030
- vectorStoreId,
29031
- fileCount: fileStreams.length,
29032
- });
29033
- }
29034
- }
29035
- catch (error) {
29036
- console.error('Error uploading files to vector store:', error);
29037
- }
29038
- }
29966
+ vectorStoreId = vectorStoreResult.vectorStoreId;
29039
29967
  }
29040
29968
  // Create assistant with vector store attached
29041
29969
  const assistantConfig = {
@@ -29102,91 +30030,14 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
29102
30030
  const client = await this.getClient();
29103
30031
  let vectorStoreId;
29104
30032
  // If knowledge sources are provided, create a vector store with them
29105
- // TODO: [🧠] Reuse vector store creation logic from createNewAssistant
29106
30033
  if (knowledgeSources && knowledgeSources.length > 0) {
29107
- if (this.options.isVerbose) {
29108
- console.info('[🤰]', 'Creating vector store for assistant update', {
29109
- assistantId,
29110
- name,
29111
- knowledgeSourcesCount,
29112
- });
29113
- }
29114
- // Create a vector store
29115
- const vectorStore = await client.beta.vectorStores.create({
29116
- name: `${name} Knowledge Base`,
30034
+ const vectorStoreResult = await this.createVectorStoreWithKnowledgeSources({
30035
+ client,
30036
+ name: name !== null && name !== void 0 ? name : assistantId,
30037
+ knowledgeSources,
30038
+ logLabel: 'assistant update',
29117
30039
  });
29118
- vectorStoreId = vectorStore.id;
29119
- if (this.options.isVerbose) {
29120
- console.info('[🤰]', 'Vector store created for assistant update', {
29121
- vectorStoreId,
29122
- });
29123
- }
29124
- // Upload files from knowledge sources to the vector store
29125
- const fileStreams = [];
29126
- for (const [index, source] of knowledgeSources.entries()) {
29127
- try {
29128
- if (this.options.isVerbose) {
29129
- console.info('[🤰]', 'Processing knowledge source for update', {
29130
- index: index + 1,
29131
- total: knowledgeSources.length,
29132
- source,
29133
- sourceType: source.startsWith('http') || source.startsWith('https') ? 'url' : 'file',
29134
- });
29135
- }
29136
- // Check if it's a URL
29137
- if (source.startsWith('http://') || source.startsWith('https://')) {
29138
- // Download the file
29139
- const response = await fetch(source);
29140
- if (!response.ok) {
29141
- console.error(`Failed to download ${source}: ${response.statusText}`);
29142
- continue;
29143
- }
29144
- const buffer = await response.arrayBuffer();
29145
- let filename = source.split('/').pop() || 'downloaded-file';
29146
- try {
29147
- const url = new URL(source);
29148
- filename = url.pathname.split('/').pop() || filename;
29149
- }
29150
- catch (error) {
29151
- // Keep default filename
29152
- }
29153
- const blob = new Blob([buffer]);
29154
- const file = new File([blob], filename);
29155
- fileStreams.push(file);
29156
- }
29157
- else {
29158
- /*
29159
- TODO: [🐱‍🚀] Resolve problem with browser environment
29160
- // Assume it's a local file path
29161
- // Note: This will work in Node.js environment
29162
- // For browser environments, this would need different handling
29163
- const fs = await import('fs');
29164
- const fileStream = fs.createReadStream(source);
29165
- fileStreams.push(fileStream);
29166
- */
29167
- }
29168
- }
29169
- catch (error) {
29170
- console.error(`Error processing knowledge source ${source}:`, error);
29171
- }
29172
- }
29173
- // Batch upload files to the vector store
29174
- if (fileStreams.length > 0) {
29175
- try {
29176
- await client.beta.vectorStores.fileBatches.uploadAndPoll(vectorStoreId, {
29177
- files: fileStreams,
29178
- });
29179
- if (this.options.isVerbose) {
29180
- console.info('[🤰]', 'Uploaded files to vector store for update', {
29181
- vectorStoreId,
29182
- fileCount: fileStreams.length,
29183
- });
29184
- }
29185
- }
29186
- catch (error) {
29187
- console.error('Error uploading files to vector store:', error);
29188
- }
29189
- }
30040
+ vectorStoreId = vectorStoreResult.vectorStoreId;
29190
30041
  }
29191
30042
  const assistantUpdate = {
29192
30043
  name,
@@ -29229,7 +30080,7 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
29229
30080
  * Discriminant for type guards
29230
30081
  */
29231
30082
  get discriminant() {
29232
- return DISCRIMINANT;
30083
+ return DISCRIMINANT$1;
29233
30084
  }
29234
30085
  /**
29235
30086
  * Type guard to check if given `LlmExecutionTools` are instanceof `OpenAiAssistantExecutionTools`
@@ -29237,7 +30088,7 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
29237
30088
  * Note: This is useful when you can possibly have multiple versions of `@promptbook/openai` installed
29238
30089
  */
29239
30090
  static isOpenAiAssistantExecutionTools(llmExecutionTools) {
29240
- return llmExecutionTools.discriminant === DISCRIMINANT;
30091
+ return llmExecutionTools.discriminant === DISCRIMINANT$1;
29241
30092
  }
29242
30093
  }
29243
30094
  /**
@@ -29245,7 +30096,7 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
29245
30096
  *
29246
30097
  * @private const of `OpenAiAssistantExecutionTools`
29247
30098
  */
29248
- const DISCRIMINANT = 'OPEN_AI_ASSISTANT_V1';
30099
+ const DISCRIMINANT$1 = 'OPEN_AI_ASSISTANT_V1';
29249
30100
  /**
29250
30101
  * TODO: !!!!! [✨🥚] Knowledge should work both with and without scrapers
29251
30102
  * TODO: [🙎] In `OpenAiAssistantExecutionTools` Allow to create abstract assistants with `isCreatingNewAssistantsAllowed`
@@ -30398,11 +31249,14 @@ const _FormattedBookInMarkdownTranspilerRegistration = $bookTranspilersRegister.
30398
31249
  function createEmptyAgentModelRequirements() {
30399
31250
  return {
30400
31251
  systemMessage: '',
31252
+ promptSuffix: '',
30401
31253
  // modelName: 'gpt-5',
30402
31254
  modelName: 'gemini-2.5-flash-lite',
30403
31255
  temperature: 0.7,
30404
31256
  topP: 0.9,
30405
31257
  topK: 50,
31258
+ parentAgentUrl: null,
31259
+ isClosed: false,
30406
31260
  };
30407
31261
  }
30408
31262
  /**
@@ -30792,14 +31646,26 @@ function removeCommentsFromSystemMessage(systemMessage) {
30792
31646
  }
30793
31647
 
30794
31648
  /**
30795
- * Creates agent model requirements using the new commitment system
31649
+ * Creates agent model requirements using the new commitment system.
31650
+ *
30796
31651
  * This function uses a reduce-like pattern where each commitment applies its changes
30797
- * to build the final requirements starting from a basic empty model
31652
+ * to build the final requirements starting from a basic empty model.
30798
31653
  *
30799
- * @public exported from `@promptbook/core`
31654
+ * @param agentSource - Agent source book to parse.
31655
+ * @param modelName - Optional override for the agent model name.
31656
+ * @param options - Additional options such as the agent reference resolver.
31657
+ *
31658
+ * @private @@@
31659
+ */
31660
+ const COMMITMENTS_WITH_AGENT_REFERENCES = new Set(['FROM', 'IMPORT', 'IMPORTS', 'TEAM']);
31661
+ /**
31662
+ * @@@
31663
+ *
31664
+ * @private @@@
30800
31665
  */
30801
- async function createAgentModelRequirementsWithCommitments(agentSource, modelName) {
31666
+ async function createAgentModelRequirementsWithCommitments(agentSource, modelName, options) {
30802
31667
  var _a;
31668
+ const agentReferenceResolver = options === null || options === void 0 ? void 0 : options.agentReferenceResolver;
30803
31669
  // Parse the agent source to extract commitments
30804
31670
  const parseResult = parseAgentSourceWithCommitments(agentSource);
30805
31671
  // Apply DELETE filtering: remove prior commitments tagged by parameters targeted by DELETE/CANCEL/DISCARD/REMOVE
@@ -30836,8 +31702,8 @@ async function createAgentModelRequirementsWithCommitments(agentSource, modelNam
30836
31702
  // Store the agent name in metadata so commitments can access it
30837
31703
  requirements = {
30838
31704
  ...requirements,
30839
- metadata: {
30840
- ...requirements.metadata,
31705
+ _metadata: {
31706
+ ...requirements._metadata,
30841
31707
  agentName: parseResult.agentName,
30842
31708
  },
30843
31709
  };
@@ -30851,6 +31717,11 @@ async function createAgentModelRequirementsWithCommitments(agentSource, modelNam
30851
31717
  // Apply each commitment in order using reduce-like pattern
30852
31718
  for (let i = 0; i < filteredCommitments.length; i++) {
30853
31719
  const commitment = filteredCommitments[i];
31720
+ const isReferenceCommitment = Boolean(agentReferenceResolver && COMMITMENTS_WITH_AGENT_REFERENCES.has(commitment.type));
31721
+ let commitmentContent = commitment.content;
31722
+ if (isReferenceCommitment && agentReferenceResolver) {
31723
+ commitmentContent = await agentReferenceResolver.resolveCommitmentContent(commitment.type, commitment.content);
31724
+ }
30854
31725
  // CLOSED commitment should work only if its the last commitment in the book
30855
31726
  if (commitment.type === 'CLOSED' && i !== filteredCommitments.length - 1) {
30856
31727
  continue;
@@ -30858,7 +31729,7 @@ async function createAgentModelRequirementsWithCommitments(agentSource, modelNam
30858
31729
  const definition = getCommitmentDefinition(commitment.type);
30859
31730
  if (definition) {
30860
31731
  try {
30861
- requirements = definition.applyToAgentModelRequirements(requirements, commitment.content);
31732
+ requirements = definition.applyToAgentModelRequirements(requirements, commitmentContent);
30862
31733
  }
30863
31734
  catch (error) {
30864
31735
  console.warn(`Failed to apply commitment ${commitment.type}:`, error);
@@ -31317,23 +32188,28 @@ function normalizeSeparator(content) {
31317
32188
  */
31318
32189
 
31319
32190
  /**
31320
- * Creates model requirements for an agent based on its source
32191
+ * Creates model requirements for an agent based on its source.
31321
32192
  *
31322
32193
  * There are 2 similar functions:
31323
32194
  * - `parseAgentSource` which is a lightweight parser for agent source, it parses basic information and its purpose is to be quick and synchronous. The commitments there are hardcoded.
31324
32195
  * - `createAgentModelRequirements` which is an asynchronous function that creates model requirements it applies each commitment one by one and works asynchronous.
31325
32196
  *
32197
+ * @param agentSource - Book describing the agent.
32198
+ * @param modelName - Optional override for the agent's model.
32199
+ * @param availableModels - Models that could fulfill the agent.
32200
+ * @param llmTools - Execution tools used when selecting a best model.
32201
+ * @param options - Optional hooks such as the agent reference resolver.
31326
32202
  * @public exported from `@promptbook/core`
31327
32203
  */
31328
- async function createAgentModelRequirements(agentSource, modelName, availableModels, llmTools) {
32204
+ async function createAgentModelRequirements(agentSource, modelName, availableModels, llmTools, options) {
31329
32205
  // If availableModels are provided and no specific modelName is given,
31330
32206
  // use preparePersona to select the best model
31331
32207
  if (availableModels && !modelName && llmTools) {
31332
32208
  const selectedModelName = await selectBestModelUsingPersona(agentSource, llmTools);
31333
- return createAgentModelRequirementsWithCommitments(agentSource, selectedModelName);
32209
+ return createAgentModelRequirementsWithCommitments(agentSource, selectedModelName, options);
31334
32210
  }
31335
32211
  // Use the new commitment-based system with provided or default model
31336
- return createAgentModelRequirementsWithCommitments(agentSource, modelName);
32212
+ return createAgentModelRequirementsWithCommitments(agentSource, modelName, options);
31337
32213
  }
31338
32214
  /**
31339
32215
  * Selects the best model using the preparePersona function
@@ -31831,6 +32707,40 @@ function isAssistantPreparationToolCall(toolCall) {
31831
32707
  return toolCall.name === ASSISTANT_PREPARATION_TOOL_CALL_NAME;
31832
32708
  }
31833
32709
 
32710
+ /**
32711
+ * Builds a stable identity string for tool calls across partial updates.
32712
+ *
32713
+ * @param toolCall - Tool call entry to identify.
32714
+ * @returns Stable identity string for deduplication.
32715
+ *
32716
+ * @private function of <Chat/>
32717
+ */
32718
+ function getToolCallIdentity(toolCall) {
32719
+ const rawToolCall = toolCall.rawToolCall;
32720
+ const rawId = (rawToolCall === null || rawToolCall === void 0 ? void 0 : rawToolCall.id) || (rawToolCall === null || rawToolCall === void 0 ? void 0 : rawToolCall.callId) || (rawToolCall === null || rawToolCall === void 0 ? void 0 : rawToolCall.call_id);
32721
+ if (rawId) {
32722
+ return `id:${rawId}`;
32723
+ }
32724
+ if (toolCall.createdAt) {
32725
+ return `time:${toolCall.createdAt}:${toolCall.name}`;
32726
+ }
32727
+ const argsKey = (() => {
32728
+ if (typeof toolCall.arguments === 'string') {
32729
+ return toolCall.arguments;
32730
+ }
32731
+ if (!toolCall.arguments) {
32732
+ return '';
32733
+ }
32734
+ try {
32735
+ return JSON.stringify(toolCall.arguments);
32736
+ }
32737
+ catch (_a) {
32738
+ return '';
32739
+ }
32740
+ })();
32741
+ return `fallback:${toolCall.name}:${argsKey}`;
32742
+ }
32743
+
31834
32744
  /*! *****************************************************************************
31835
32745
  Copyright (c) Microsoft Corporation.
31836
32746
 
@@ -32052,206 +32962,490 @@ function promptbookifyAiText(text) {
32052
32962
  * TODO: [🧠][✌️] Make some Promptbook-native token system
32053
32963
  */
32054
32964
 
32965
+ const DEFAULT_AGENT_KIT_MODEL_NAME = 'gpt-5.2';
32966
+ const DEFAULT_JSON_SCHEMA_NAME = 'StructuredOutput';
32967
+ /*
32968
+ TODO: Use or remove
32969
+ const EMPTY_JSON_SCHEMA: JsonSchemaDefinition['schema'] = {
32970
+ type: 'object',
32971
+ properties: {},
32972
+ required: [],
32973
+ additionalProperties: true,
32974
+ };
32975
+ */
32976
+ function buildJsonSchemaDefinition(jsonSchema) {
32977
+ var _a, _b, _c;
32978
+ const schema = (_a = jsonSchema === null || jsonSchema === void 0 ? void 0 : jsonSchema.schema) !== null && _a !== void 0 ? _a : {};
32979
+ return {
32980
+ type: 'json_schema',
32981
+ name: (_b = jsonSchema === null || jsonSchema === void 0 ? void 0 : jsonSchema.name) !== null && _b !== void 0 ? _b : DEFAULT_JSON_SCHEMA_NAME,
32982
+ strict: Boolean(jsonSchema === null || jsonSchema === void 0 ? void 0 : jsonSchema.strict),
32983
+ schema: {
32984
+ type: 'object',
32985
+ properties: ((_c = schema.properties) !== null && _c !== void 0 ? _c : {}),
32986
+ required: Array.isArray(schema.required) ? schema.required : [],
32987
+ additionalProperties: schema.additionalProperties === undefined ? true : Boolean(schema.additionalProperties),
32988
+ description: schema.description,
32989
+ },
32990
+ };
32991
+ }
32992
+ /**
32993
+ * Maps OpenAI `response_format` payloads to AgentKit output types so the runner can forward
32994
+ * structured-output preferences to OpenAI while still reusing the same AgentKit agent instance.
32995
+ *
32996
+ * @param responseFormat - The OpenAI `response_format` payload from the user request.
32997
+ * @returns An Agent output type compatible with the requested schema or `undefined` when no impact is required.
32998
+ * @private utility of Open AI
32999
+ */
33000
+ function mapResponseFormatToAgentOutputType(responseFormat) {
33001
+ if (!responseFormat) {
33002
+ return undefined;
33003
+ }
33004
+ if (typeof responseFormat === 'string') {
33005
+ if (responseFormat === 'text') {
33006
+ return 'text';
33007
+ }
33008
+ if (responseFormat === 'json_schema' || responseFormat === 'json_object') {
33009
+ return buildJsonSchemaDefinition();
33010
+ }
33011
+ return 'text';
33012
+ }
33013
+ switch (responseFormat.type) {
33014
+ case 'text':
33015
+ return 'text';
33016
+ case 'json_schema':
33017
+ return buildJsonSchemaDefinition(responseFormat.json_schema);
33018
+ case 'json_object':
33019
+ return buildJsonSchemaDefinition();
33020
+ default:
33021
+ return undefined;
33022
+ }
33023
+ }
32055
33024
  /**
32056
- * Execution Tools for calling OpenAI API using the Responses API (Agents)
33025
+ * Execution tools for OpenAI AgentKit (Agents SDK).
32057
33026
  *
32058
33027
  * @public exported from `@promptbook/openai`
32059
33028
  */
32060
- class OpenAiAgentExecutionTools extends OpenAiExecutionTools {
33029
+ class OpenAiAgentKitExecutionTools extends OpenAiVectorStoreHandler {
33030
+ /**
33031
+ * Creates OpenAI AgentKit execution tools.
33032
+ */
32061
33033
  constructor(options) {
33034
+ var _a;
33035
+ if (options.isProxied) {
33036
+ throw new NotYetImplementedError(`Proxy mode is not yet implemented for OpenAI AgentKit`);
33037
+ }
32062
33038
  super(options);
32063
- this.vectorStoreId = options.vectorStoreId;
33039
+ this.preparedAgentKitAgent = null;
33040
+ this.agentKitModelName = (_a = options.agentKitModelName) !== null && _a !== void 0 ? _a : DEFAULT_AGENT_KIT_MODEL_NAME;
32064
33041
  }
32065
33042
  get title() {
32066
- return 'OpenAI Agent';
33043
+ return 'OpenAI AgentKit';
32067
33044
  }
32068
33045
  get description() {
32069
- return 'Use OpenAI Responses API (Agentic)';
33046
+ return 'Use OpenAI AgentKit for agent-style chat with tools and knowledge';
32070
33047
  }
32071
33048
  /**
32072
- * Calls OpenAI API to use a chat model with streaming.
33049
+ * Calls OpenAI AgentKit with a chat prompt (non-streaming).
33050
+ */
33051
+ async callChatModel(prompt) {
33052
+ return this.callChatModelStream(prompt, () => { });
33053
+ }
33054
+ /**
33055
+ * Calls OpenAI AgentKit with a chat prompt (streaming).
32073
33056
  */
32074
33057
  async callChatModelStream(prompt, onProgress) {
32075
- if (this.options.isVerbose) {
32076
- console.info('💬 OpenAI Agent callChatModel call', { prompt });
32077
- }
32078
33058
  const { content, parameters, modelRequirements } = prompt;
32079
- const client = await this.getClient();
32080
33059
  if (modelRequirements.modelVariant !== 'CHAT') {
32081
33060
  throw new PipelineExecutionError('Use callChatModel only for CHAT variant');
32082
33061
  }
33062
+ for (const key of ['maxTokens', 'modelName', 'seed', 'temperature']) {
33063
+ if (modelRequirements[key] !== undefined) {
33064
+ throw new NotYetImplementedError(`In \`OpenAiAgentKitExecutionTools\` you cannot specify \`${key}\``);
33065
+ }
33066
+ }
32083
33067
  const rawPromptContent = templateParameters(content, {
32084
33068
  ...parameters,
32085
- modelName: 'agent',
33069
+ modelName: this.agentKitModelName,
32086
33070
  });
32087
- // Build input items
32088
- const input = []; // TODO: Type properly when OpenAI types are updated
32089
- // Add previous messages from thread (if any)
32090
- if ('thread' in prompt && Array.isArray(prompt.thread)) {
32091
- const previousMessages = prompt.thread.map((msg) => ({
32092
- role: msg.sender === 'assistant' ? 'assistant' : 'user',
32093
- content: msg.content,
32094
- }));
32095
- input.push(...previousMessages);
32096
- }
32097
- // Add current user message
32098
- input.push({
32099
- role: 'user',
32100
- content: rawPromptContent,
33071
+ const responseFormatOutputType = mapResponseFormatToAgentOutputType(modelRequirements.responseFormat);
33072
+ const preparedAgentKitAgent = await this.prepareAgentKitAgent({
33073
+ name: (prompt.title || 'Agent'),
33074
+ instructions: modelRequirements.systemMessage || '',
33075
+ knowledgeSources: modelRequirements.knowledgeSources,
33076
+ tools: 'tools' in prompt && Array.isArray(prompt.tools) ? prompt.tools : modelRequirements.tools,
32101
33077
  });
32102
- // Prepare tools
32103
- const tools = modelRequirements.tools ? mapToolsToOpenAi(modelRequirements.tools) : undefined;
32104
- // Add file_search if vector store is present
32105
- const agentTools = tools ? [...tools] : [];
32106
- let toolResources = undefined;
32107
- if (this.vectorStoreId) {
32108
- agentTools.push({ type: 'file_search' });
32109
- toolResources = {
32110
- file_search: {
32111
- vector_store_ids: [this.vectorStoreId],
32112
- },
32113
- };
33078
+ return this.callChatModelStreamWithPreparedAgent({
33079
+ openAiAgentKitAgent: preparedAgentKitAgent.agent,
33080
+ prompt,
33081
+ rawPromptContent,
33082
+ onProgress,
33083
+ responseFormatOutputType,
33084
+ });
33085
+ }
33086
+ /**
33087
+ * Returns a prepared AgentKit agent when the server wants to manage caching externally.
33088
+ */
33089
+ getPreparedAgentKitAgent() {
33090
+ return this.preparedAgentKitAgent;
33091
+ }
33092
+ /**
33093
+ * Stores a prepared AgentKit agent for later reuse by external cache managers.
33094
+ */
33095
+ setPreparedAgentKitAgent(preparedAgent) {
33096
+ this.preparedAgentKitAgent = preparedAgent;
33097
+ }
33098
+ /**
33099
+ * Creates a new tools instance bound to a prepared AgentKit agent.
33100
+ */
33101
+ getPreparedAgentTools(preparedAgent) {
33102
+ const tools = new OpenAiAgentKitExecutionTools(this.agentKitOptions);
33103
+ tools.setPreparedAgentKitAgent(preparedAgent);
33104
+ return tools;
33105
+ }
33106
+ /**
33107
+ * Prepares an AgentKit agent with optional knowledge sources and tool definitions.
33108
+ */
33109
+ async prepareAgentKitAgent(options) {
33110
+ var _a, _b;
33111
+ const { name, instructions, knowledgeSources, tools, vectorStoreId: cachedVectorStoreId, storeAsPrepared, } = options;
33112
+ await this.ensureAgentKitDefaults();
33113
+ if (this.options.isVerbose) {
33114
+ console.info('[🤰]', 'Preparing OpenAI AgentKit agent', {
33115
+ name,
33116
+ instructionsLength: instructions.length,
33117
+ knowledgeSourcesCount: (_a = knowledgeSources === null || knowledgeSources === void 0 ? void 0 : knowledgeSources.length) !== null && _a !== void 0 ? _a : 0,
33118
+ toolsCount: (_b = tools === null || tools === void 0 ? void 0 : tools.length) !== null && _b !== void 0 ? _b : 0,
33119
+ });
32114
33120
  }
32115
- // Add file_search also if knowledgeSources are present in the prompt (passed via AgentLlmExecutionTools)
32116
- if (modelRequirements.knowledgeSources &&
32117
- modelRequirements.knowledgeSources.length > 0 &&
32118
- !this.vectorStoreId) {
32119
- // Note: Vector store should have been created by AgentLlmExecutionTools and passed via options.
32120
- // If we are here, it means we have knowledge sources but no vector store ID.
32121
- // We can't easily create one here without persisting it.
32122
- console.warn('Knowledge sources provided but no vector store ID. Creating temporary vector store is not implemented in callChatModelStream.');
33121
+ let vectorStoreId = cachedVectorStoreId;
33122
+ if (!vectorStoreId && knowledgeSources && knowledgeSources.length > 0) {
33123
+ const vectorStoreResult = await this.createVectorStoreWithKnowledgeSources({
33124
+ client: await this.getClient(),
33125
+ name,
33126
+ knowledgeSources,
33127
+ logLabel: 'agentkit preparation',
33128
+ });
33129
+ vectorStoreId = vectorStoreResult.vectorStoreId;
32123
33130
  }
32124
- const start = $getCurrentDate();
32125
- // Construct the request
32126
- const rawRequest = {
32127
- // TODO: Type properly as OpenAI.Responses.CreateResponseParams
32128
- model: modelRequirements.modelName || 'gpt-4o',
32129
- input,
32130
- instructions: modelRequirements.systemMessage,
32131
- tools: agentTools.length > 0 ? agentTools : undefined,
32132
- tool_resources: toolResources,
32133
- store: false, // Stateless by default as we pass full history
33131
+ else if (vectorStoreId && this.options.isVerbose) {
33132
+ console.info('[🤰]', 'Using cached vector store for AgentKit agent', {
33133
+ name,
33134
+ vectorStoreId,
33135
+ });
33136
+ }
33137
+ const agentKitTools = this.buildAgentKitTools({ tools, vectorStoreId });
33138
+ const openAiAgentKitAgent = new Agent$1({
33139
+ name,
33140
+ model: this.agentKitModelName,
33141
+ instructions: instructions || 'You are a helpful assistant.',
33142
+ tools: agentKitTools,
33143
+ });
33144
+ const preparedAgent = {
33145
+ agent: openAiAgentKitAgent,
33146
+ vectorStoreId,
32134
33147
  };
32135
- if (this.options.isVerbose) {
32136
- console.info(colors.bgWhite('rawRequest (Responses API)'), JSON.stringify(rawRequest, null, 4));
33148
+ if (storeAsPrepared) {
33149
+ this.setPreparedAgentKitAgent(preparedAgent);
32137
33150
  }
32138
- // Call Responses API
32139
- // Note: Using any cast because types might not be updated yet
32140
- const response = await client.responses.create(rawRequest);
32141
33151
  if (this.options.isVerbose) {
32142
- console.info(colors.bgWhite('rawResponse'), JSON.stringify(response, null, 4));
33152
+ console.info('[🤰]', 'OpenAI AgentKit agent ready', {
33153
+ name,
33154
+ model: this.agentKitModelName,
33155
+ toolCount: agentKitTools.length,
33156
+ hasVectorStore: Boolean(vectorStoreId),
33157
+ });
32143
33158
  }
32144
- const complete = $getCurrentDate();
32145
- let resultContent = '';
32146
- const toolCalls = [];
32147
- // Parse output items
32148
- if (response.output) {
32149
- for (const item of response.output) {
32150
- if (item.type === 'message' && item.role === 'assistant') {
32151
- for (const contentPart of item.content) {
32152
- if (contentPart.type === 'output_text') {
32153
- // "output_text" based on migration guide, or "text"? Guide says "output_text" in example.
32154
- resultContent += contentPart.text;
33159
+ return preparedAgent;
33160
+ }
33161
+ /**
33162
+ * Ensures the AgentKit SDK is wired to the OpenAI client and API key.
33163
+ */
33164
+ async ensureAgentKitDefaults() {
33165
+ const client = await this.getClient();
33166
+ setDefaultOpenAIClient(client);
33167
+ const apiKey = this.agentKitOptions.apiKey;
33168
+ if (apiKey && typeof apiKey === 'string') {
33169
+ setDefaultOpenAIKey(apiKey);
33170
+ }
33171
+ }
33172
+ /**
33173
+ * Builds the tool list for AgentKit, including hosted file search when applicable.
33174
+ */
33175
+ buildAgentKitTools(options) {
33176
+ var _a;
33177
+ const { tools, vectorStoreId } = options;
33178
+ const agentKitTools = [];
33179
+ if (vectorStoreId) {
33180
+ agentKitTools.push(fileSearchTool(vectorStoreId));
33181
+ }
33182
+ if (tools && tools.length > 0) {
33183
+ const scriptTools = this.resolveScriptTools();
33184
+ for (const toolDefinition of tools) {
33185
+ agentKitTools.push(tool({
33186
+ name: toolDefinition.name,
33187
+ description: toolDefinition.description,
33188
+ parameters: toolDefinition.parameters
33189
+ ? {
33190
+ ...toolDefinition.parameters,
33191
+ additionalProperties: false,
33192
+ required: (_a = toolDefinition.parameters.required) !== null && _a !== void 0 ? _a : [],
33193
+ }
33194
+ : undefined,
33195
+ strict: false,
33196
+ execute: async (input, runContext, details) => {
33197
+ var _a, _b, _c;
33198
+ const scriptTool = scriptTools[0];
33199
+ const functionName = toolDefinition.name;
33200
+ const calledAt = $getCurrentDate();
33201
+ const callId = (_a = details === null || details === void 0 ? void 0 : details.toolCall) === null || _a === void 0 ? void 0 : _a.callId;
33202
+ const functionArgs = input !== null && input !== void 0 ? input : {};
33203
+ if (this.options.isVerbose) {
33204
+ console.info('[🤰]', 'Executing AgentKit tool', {
33205
+ functionName,
33206
+ callId,
33207
+ calledAt,
33208
+ });
33209
+ }
33210
+ try {
33211
+ return await scriptTool.execute({
33212
+ scriptLanguage: 'javascript',
33213
+ script: `
33214
+ const args = ${JSON.stringify(functionArgs)};
33215
+ return await ${functionName}(args);
33216
+ `,
33217
+ parameters: (_c = (_b = runContext === null || runContext === void 0 ? void 0 : runContext.context) === null || _b === void 0 ? void 0 : _b.parameters) !== null && _c !== void 0 ? _c : {},
33218
+ });
32155
33219
  }
32156
- else if (contentPart.type === 'text') {
32157
- resultContent += contentPart.text.value || contentPart.text;
33220
+ catch (error) {
33221
+ assertsError(error);
33222
+ const serializedError = serializeError(error);
33223
+ const errorMessage = spaceTrim$2((block) => `
33224
+
33225
+ The invoked tool \`${functionName}\` failed with error:
33226
+
33227
+ \`\`\`json
33228
+ ${block(JSON.stringify(serializedError, null, 4))}
33229
+ \`\`\`
33230
+
33231
+ `);
33232
+ console.error('[🤰]', 'AgentKit tool execution failed', {
33233
+ functionName,
33234
+ callId,
33235
+ error: serializedError,
33236
+ });
33237
+ return errorMessage;
32158
33238
  }
33239
+ },
33240
+ }));
33241
+ }
33242
+ }
33243
+ return agentKitTools;
33244
+ }
33245
+ /**
33246
+ * Resolves the configured script tools for tool execution.
33247
+ */
33248
+ resolveScriptTools() {
33249
+ const executionTools = this.options.executionTools;
33250
+ if (!executionTools || !executionTools.script) {
33251
+ throw new PipelineExecutionError(`Model requested tools but no executionTools.script were provided in OpenAiAgentKitExecutionTools options`);
33252
+ }
33253
+ return Array.isArray(executionTools.script) ? executionTools.script : [executionTools.script];
33254
+ }
33255
+ /**
33256
+ * Runs a prepared AgentKit agent and streams results back to the caller.
33257
+ */
33258
+ async callChatModelStreamWithPreparedAgent(options) {
33259
+ var _a, _b, _c, _d;
33260
+ const { openAiAgentKitAgent, prompt, onProgress } = options;
33261
+ const rawPromptContent = (_a = options.rawPromptContent) !== null && _a !== void 0 ? _a : templateParameters(prompt.content, {
33262
+ ...prompt.parameters,
33263
+ modelName: this.agentKitModelName,
33264
+ });
33265
+ const agentForRun = options.responseFormatOutputType !== undefined
33266
+ ? openAiAgentKitAgent.clone({
33267
+ outputType: options.responseFormatOutputType,
33268
+ })
33269
+ : openAiAgentKitAgent;
33270
+ const start = $getCurrentDate();
33271
+ let latestContent = '';
33272
+ const toolCalls = [];
33273
+ const toolCallIndexById = new Map();
33274
+ const inputItems = await this.buildAgentKitInputItems(prompt, rawPromptContent);
33275
+ const rawRequest = {
33276
+ agentName: agentForRun.name,
33277
+ input: inputItems,
33278
+ };
33279
+ const streamResult = await run(agentForRun, inputItems, {
33280
+ stream: true,
33281
+ context: { parameters: prompt.parameters },
33282
+ });
33283
+ for await (const event of streamResult) {
33284
+ if (event.type === 'raw_model_stream_event' && ((_b = event.data) === null || _b === void 0 ? void 0 : _b.type) === 'output_text_delta') {
33285
+ latestContent += event.data.delta;
33286
+ onProgress({
33287
+ content: latestContent,
33288
+ modelName: this.agentKitModelName,
33289
+ timing: { start, complete: $getCurrentDate() },
33290
+ usage: UNCERTAIN_USAGE,
33291
+ rawPromptContent: rawPromptContent,
33292
+ rawRequest: null,
33293
+ rawResponse: {},
33294
+ });
33295
+ continue;
33296
+ }
33297
+ if (event.type === 'run_item_stream_event') {
33298
+ const rawItem = (_c = event.item) === null || _c === void 0 ? void 0 : _c.rawItem;
33299
+ if (event.name === 'tool_called' && (rawItem === null || rawItem === void 0 ? void 0 : rawItem.type) === 'function_call') {
33300
+ const toolCall = {
33301
+ name: rawItem.name,
33302
+ arguments: rawItem.arguments,
33303
+ rawToolCall: rawItem,
33304
+ createdAt: $getCurrentDate(),
33305
+ };
33306
+ toolCallIndexById.set(rawItem.callId, toolCalls.length);
33307
+ toolCalls.push(toolCall);
33308
+ onProgress({
33309
+ content: latestContent,
33310
+ modelName: this.agentKitModelName,
33311
+ timing: { start, complete: $getCurrentDate() },
33312
+ usage: UNCERTAIN_USAGE,
33313
+ rawPromptContent: rawPromptContent,
33314
+ rawRequest: null,
33315
+ rawResponse: {},
33316
+ toolCalls: [toolCall],
33317
+ });
33318
+ }
33319
+ if (event.name === 'tool_output' && (rawItem === null || rawItem === void 0 ? void 0 : rawItem.type) === 'function_call_result') {
33320
+ const index = toolCallIndexById.get(rawItem.callId);
33321
+ const result = this.formatAgentKitToolOutput(rawItem.output);
33322
+ if (index !== undefined) {
33323
+ const existingToolCall = toolCalls[index];
33324
+ const completedToolCall = {
33325
+ ...existingToolCall,
33326
+ result,
33327
+ rawToolCall: rawItem,
33328
+ };
33329
+ toolCalls[index] = completedToolCall;
33330
+ onProgress({
33331
+ content: latestContent,
33332
+ modelName: this.agentKitModelName,
33333
+ timing: { start, complete: $getCurrentDate() },
33334
+ usage: UNCERTAIN_USAGE,
33335
+ rawPromptContent: rawPromptContent,
33336
+ rawRequest: null,
33337
+ rawResponse: {},
33338
+ toolCalls: [completedToolCall],
33339
+ });
32159
33340
  }
32160
33341
  }
32161
- else if (item.type === 'function_call') ;
32162
33342
  }
32163
33343
  }
32164
- // Use output_text helper if available (mentioned in guide)
32165
- if (response.output_text) {
32166
- resultContent = response.output_text;
32167
- }
32168
- // TODO: Handle tool calls properly (Requires clearer docs or experimentation)
32169
- onProgress({
32170
- content: resultContent,
32171
- modelName: response.model || 'agent',
33344
+ await streamResult.completed;
33345
+ const complete = $getCurrentDate();
33346
+ const finalContent = ((_d = streamResult.finalOutput) !== null && _d !== void 0 ? _d : latestContent);
33347
+ const finalResult = {
33348
+ content: finalContent,
33349
+ modelName: this.agentKitModelName,
32172
33350
  timing: { start, complete },
32173
33351
  usage: UNCERTAIN_USAGE,
32174
- rawPromptContent,
33352
+ rawPromptContent: rawPromptContent,
32175
33353
  rawRequest,
32176
- rawResponse: response,
32177
- });
32178
- return exportJson({
32179
- name: 'promptResult',
32180
- message: `Result of \`OpenAiAgentExecutionTools.callChatModelStream\``,
32181
- order: [],
32182
- value: {
32183
- content: resultContent,
32184
- modelName: response.model || 'agent',
32185
- timing: { start, complete },
32186
- usage: UNCERTAIN_USAGE,
32187
- rawPromptContent,
32188
- rawRequest,
32189
- rawResponse: response,
32190
- toolCalls: toolCalls.length > 0 ? toolCalls : undefined,
32191
- },
32192
- });
33354
+ rawResponse: { runResult: streamResult },
33355
+ toolCalls: toolCalls.length > 0 ? toolCalls : undefined,
33356
+ };
33357
+ onProgress(finalResult);
33358
+ return finalResult;
32193
33359
  }
32194
33360
  /**
32195
- * Creates a vector store from knowledge sources
33361
+ * Builds AgentKit input items from the prompt and optional thread.
32196
33362
  */
32197
- static async createVectorStore(client, name, knowledgeSources) {
32198
- // Create a vector store
32199
- const vectorStore = await client.beta.vectorStores.create({
32200
- name: `${name} Knowledge Base`,
32201
- });
32202
- const vectorStoreId = vectorStore.id;
32203
- // Upload files from knowledge sources to the vector store
32204
- const fileStreams = [];
32205
- for (const source of knowledgeSources) {
32206
- try {
32207
- // Check if it's a URL
32208
- if (source.startsWith('http://') || source.startsWith('https://')) {
32209
- // Download the file
32210
- const response = await fetch(source);
32211
- if (!response.ok) {
32212
- console.error(`Failed to download ${source}: ${response.statusText}`);
32213
- continue;
32214
- }
32215
- const buffer = await response.arrayBuffer();
32216
- const filename = source.split('/').pop() || 'downloaded-file';
32217
- const blob = new Blob([buffer]);
32218
- const file = new File([blob], filename);
32219
- fileStreams.push(file);
33363
+ async buildAgentKitInputItems(prompt, rawPromptContent) {
33364
+ var _a;
33365
+ const inputItems = [];
33366
+ if ('thread' in prompt && Array.isArray(prompt.thread)) {
33367
+ for (const message of prompt.thread) {
33368
+ const sender = message.sender;
33369
+ const content = (_a = message.content) !== null && _a !== void 0 ? _a : '';
33370
+ if (sender === 'assistant' || sender === 'agent') {
33371
+ inputItems.push({
33372
+ role: 'assistant',
33373
+ status: 'completed',
33374
+ content: [{ type: 'output_text', text: content }],
33375
+ });
32220
33376
  }
32221
33377
  else {
32222
- // Local files not supported in browser env easily, same as before
33378
+ inputItems.push({
33379
+ role: 'user',
33380
+ content,
33381
+ });
32223
33382
  }
32224
33383
  }
32225
- catch (error) {
32226
- console.error(`Error processing knowledge source ${source}:`, error);
32227
- }
32228
33384
  }
32229
- // Batch upload files to the vector store
32230
- if (fileStreams.length > 0) {
32231
- try {
32232
- await client.beta.vectorStores.fileBatches.uploadAndPoll(vectorStoreId, {
32233
- files: fileStreams,
32234
- });
32235
- }
32236
- catch (error) {
32237
- console.error('Error uploading files to vector store:', error);
33385
+ const userContent = await this.buildAgentKitUserContent(prompt, rawPromptContent);
33386
+ inputItems.push({
33387
+ role: 'user',
33388
+ content: userContent,
33389
+ });
33390
+ return inputItems;
33391
+ }
33392
+ /**
33393
+ * Builds the user message content for AgentKit runs, including file inputs when provided.
33394
+ */
33395
+ async buildAgentKitUserContent(prompt, rawPromptContent) {
33396
+ if ('files' in prompt && Array.isArray(prompt.files) && prompt.files.length > 0) {
33397
+ const fileItems = await Promise.all(prompt.files.map(async (file) => {
33398
+ const arrayBuffer = await file.arrayBuffer();
33399
+ const base64 = Buffer.from(arrayBuffer).toString('base64');
33400
+ return {
33401
+ type: 'input_image',
33402
+ image: `data:${file.type};base64,${base64}`,
33403
+ };
33404
+ }));
33405
+ return [{ type: 'input_text', text: rawPromptContent }, ...fileItems];
33406
+ }
33407
+ return rawPromptContent;
33408
+ }
33409
+ /**
33410
+ * Normalizes AgentKit tool outputs into a string for Promptbook tool call results.
33411
+ */
33412
+ formatAgentKitToolOutput(output) {
33413
+ if (typeof output === 'string') {
33414
+ return output;
33415
+ }
33416
+ if (output && typeof output === 'object') {
33417
+ const textOutput = output;
33418
+ if (textOutput.type === 'text' && typeof textOutput.text === 'string') {
33419
+ return textOutput.text;
32238
33420
  }
32239
33421
  }
32240
- return vectorStoreId;
33422
+ return JSON.stringify(output !== null && output !== void 0 ? output : null);
32241
33423
  }
32242
33424
  /**
32243
- * Discriminant for type guards
33425
+ * Returns AgentKit-specific options.
33426
+ */
33427
+ get agentKitOptions() {
33428
+ return this.options;
33429
+ }
33430
+ /**
33431
+ * Discriminant for type guards.
32244
33432
  */
32245
33433
  get discriminant() {
32246
- return 'OPEN_AI_AGENT';
33434
+ return DISCRIMINANT;
32247
33435
  }
32248
33436
  /**
32249
- * Type guard to check if given `LlmExecutionTools` are instanceof `OpenAiAgentExecutionTools`
33437
+ * Type guard to check if given `LlmExecutionTools` are instanceof `OpenAiAgentKitExecutionTools`.
32250
33438
  */
32251
- static isOpenAiAgentExecutionTools(llmExecutionTools) {
32252
- return llmExecutionTools.discriminant === 'OPEN_AI_AGENT';
33439
+ static isOpenAiAgentKitExecutionTools(llmExecutionTools) {
33440
+ return llmExecutionTools.discriminant === DISCRIMINANT;
32253
33441
  }
32254
33442
  }
33443
+ /**
33444
+ * Discriminant for type guards.
33445
+ *
33446
+ * @private const of `OpenAiAgentKitExecutionTools`
33447
+ */
33448
+ const DISCRIMINANT = 'OPEN_AI_AGENT_KIT_V1';
32255
33449
 
32256
33450
  /**
32257
33451
  * Emits a progress update to signal assistant preparation before long setup work.
@@ -32288,8 +33482,8 @@ function emitAssistantPreparationProgress(options) {
32288
33482
  * - `Agent` - which represents an AI Agent with its source, memories, actions, etc. Agent is a higher-level abstraction which is internally using:
32289
33483
  * - `LlmExecutionTools` - which wraps one or more LLM models and provides an interface to execute them
32290
33484
  * - `AgentLlmExecutionTools` - which is a specific implementation of `LlmExecutionTools` that wraps another LlmExecutionTools and applies agent-specific system prompts and requirements
32291
- * - `OpenAiAgentExecutionTools` - which is a specific implementation of `LlmExecutionTools` for OpenAI models with agent capabilities (using Responses API), recommended for usage in `Agent` or `AgentLlmExecutionTools`
32292
33485
  * - `OpenAiAssistantExecutionTools` - (Deprecated) which is a specific implementation of `LlmExecutionTools` for OpenAI models with assistant capabilities
33486
+ * - `OpenAiAgentKitExecutionTools` - which is a specific implementation of `LlmExecutionTools` backed by OpenAI AgentKit
32293
33487
  * - `RemoteAgent` - which is an `Agent` that connects to a Promptbook Agents Server
32294
33488
  *
32295
33489
  * @public exported from `@promptbook/core`
@@ -32424,97 +33618,129 @@ class AgentLlmExecutionTools {
32424
33618
  * Calls the chat model with agent-specific system prompt and requirements with streaming
32425
33619
  */
32426
33620
  async callChatModelStream(prompt, onProgress) {
33621
+ var _a, _b;
32427
33622
  // Ensure we're working with a chat prompt
32428
33623
  if (prompt.modelRequirements.modelVariant !== 'CHAT') {
32429
33624
  throw new Error('AgentLlmExecutionTools only supports chat prompts');
32430
33625
  }
32431
33626
  const modelRequirements = await this.getModelRequirements();
33627
+ const { _metadata, promptSuffix, ...sanitizedRequirements } = modelRequirements;
32432
33628
  const chatPrompt = prompt;
32433
33629
  let underlyingLlmResult;
32434
- // Create modified chat prompt with agent system message
33630
+ const chatPromptContentWithSuffix = promptSuffix
33631
+ ? `${chatPrompt.content}\n\n${promptSuffix}`
33632
+ : chatPrompt.content;
32435
33633
  const promptWithAgentModelRequirements = {
32436
33634
  ...chatPrompt,
33635
+ content: chatPromptContentWithSuffix,
32437
33636
  modelRequirements: {
32438
33637
  ...chatPrompt.modelRequirements,
32439
- ...modelRequirements,
33638
+ ...sanitizedRequirements,
32440
33639
  // Spread tools to convert readonly array to mutable
32441
- tools: modelRequirements.tools ? [...modelRequirements.tools] : chatPrompt.modelRequirements.tools,
33640
+ tools: sanitizedRequirements.tools
33641
+ ? [...sanitizedRequirements.tools]
33642
+ : chatPrompt.modelRequirements.tools,
32442
33643
  // Spread knowledgeSources to convert readonly array to mutable
32443
- knowledgeSources: modelRequirements.knowledgeSources
32444
- ? [...modelRequirements.knowledgeSources]
33644
+ knowledgeSources: sanitizedRequirements.knowledgeSources
33645
+ ? [...sanitizedRequirements.knowledgeSources]
32445
33646
  : undefined,
32446
33647
  // Prepend agent system message to existing system message
32447
- systemMessage: modelRequirements.systemMessage +
33648
+ systemMessage: sanitizedRequirements.systemMessage +
32448
33649
  (chatPrompt.modelRequirements.systemMessage
32449
33650
  ? `\n\n${chatPrompt.modelRequirements.systemMessage}`
32450
33651
  : ''),
32451
33652
  }, // Cast to avoid readonly mismatch from spread
32452
33653
  };
32453
33654
  console.log('!!!! promptWithAgentModelRequirements:', promptWithAgentModelRequirements);
32454
- if (OpenAiAgentExecutionTools.isOpenAiAgentExecutionTools(this.options.llmTools)) {
32455
- const requirementsHash = SHA256(JSON.stringify(modelRequirements)).toString();
32456
- const cached = AgentLlmExecutionTools.vectorStoreCache.get(this.title);
32457
- let agentTools;
32458
- if (cached && cached.requirementsHash === requirementsHash) {
33655
+ if (OpenAiAgentKitExecutionTools.isOpenAiAgentKitExecutionTools(this.options.llmTools)) {
33656
+ const requirementsHash = SHA256(JSON.stringify(sanitizedRequirements)).toString();
33657
+ const vectorStoreHash = SHA256(JSON.stringify((_a = sanitizedRequirements.knowledgeSources) !== null && _a !== void 0 ? _a : [])).toString();
33658
+ const cachedVectorStore = AgentLlmExecutionTools.vectorStoreCache.get(this.title);
33659
+ const cachedAgentKit = AgentLlmExecutionTools.agentKitAgentCache.get(this.title);
33660
+ let preparedAgentKit = this.options.assistantPreparationMode === 'external'
33661
+ ? this.options.llmTools.getPreparedAgentKitAgent()
33662
+ : null;
33663
+ const vectorStoreId = (preparedAgentKit === null || preparedAgentKit === void 0 ? void 0 : preparedAgentKit.vectorStoreId) ||
33664
+ (cachedVectorStore && cachedVectorStore.requirementsHash === vectorStoreHash
33665
+ ? cachedVectorStore.vectorStoreId
33666
+ : undefined);
33667
+ if (!preparedAgentKit && cachedAgentKit && cachedAgentKit.requirementsHash === requirementsHash) {
32459
33668
  if (this.options.isVerbose) {
32460
- console.log(`1️⃣ Using cached OpenAI Agent Vector Store for agent ${this.title}...`);
33669
+ console.info('[🤰]', 'Using cached OpenAI AgentKit agent', {
33670
+ agent: this.title,
33671
+ });
32461
33672
  }
32462
- // Create new instance with cached vectorStoreId
32463
- // We need to access options from the original tool.
32464
- // We assume isOpenAiAgentExecutionTools implies it has options we can clone.
32465
- // But protected options are not accessible.
32466
- // We can cast to access options if they were public, or use a method to clone.
32467
- // OpenAiAgentExecutionTools doesn't have a clone method.
32468
- // However, we can just assume the passed tool *might* not have the vector store yet, or we are replacing it.
32469
- // Actually, if the passed tool IS OpenAiAgentExecutionTools, we should use it as a base.
32470
- // TODO: [🧠] This is a bit hacky, accessing protected options or recreating tools.
32471
- // Ideally OpenAiAgentExecutionTools should have a method `withVectorStoreId`.
32472
- agentTools = new OpenAiAgentExecutionTools({
32473
- ...this.options.llmTools.options,
32474
- vectorStoreId: cached.vectorStoreId,
32475
- });
33673
+ preparedAgentKit = {
33674
+ agent: cachedAgentKit.agent,
33675
+ vectorStoreId: cachedAgentKit.vectorStoreId,
33676
+ };
32476
33677
  }
32477
- else {
33678
+ if (!preparedAgentKit) {
32478
33679
  if (this.options.isVerbose) {
32479
- console.log(`1️⃣ Creating/Updating OpenAI Agent Vector Store for agent ${this.title}...`);
32480
- }
32481
- let vectorStoreId;
32482
- if (modelRequirements.knowledgeSources && modelRequirements.knowledgeSources.length > 0) {
32483
- const client = await this.options.llmTools.getClient();
32484
- vectorStoreId = await OpenAiAgentExecutionTools.createVectorStore(client, this.title, modelRequirements.knowledgeSources);
33680
+ console.info('[🤰]', 'Preparing OpenAI AgentKit agent', {
33681
+ agent: this.title,
33682
+ });
32485
33683
  }
32486
- if (vectorStoreId) {
32487
- AgentLlmExecutionTools.vectorStoreCache.set(this.title, {
32488
- vectorStoreId,
32489
- requirementsHash,
33684
+ if (!vectorStoreId && ((_b = sanitizedRequirements.knowledgeSources) === null || _b === void 0 ? void 0 : _b.length)) {
33685
+ emitAssistantPreparationProgress({
33686
+ onProgress,
33687
+ prompt,
33688
+ modelName: this.modelName,
33689
+ phase: 'Creating knowledge base',
32490
33690
  });
32491
33691
  }
32492
- agentTools = new OpenAiAgentExecutionTools({
32493
- ...this.options.llmTools.options,
33692
+ emitAssistantPreparationProgress({
33693
+ onProgress,
33694
+ prompt,
33695
+ modelName: this.modelName,
33696
+ phase: 'Preparing AgentKit agent',
33697
+ });
33698
+ preparedAgentKit = await this.options.llmTools.prepareAgentKitAgent({
33699
+ name: this.title,
33700
+ instructions: sanitizedRequirements.systemMessage || '',
33701
+ knowledgeSources: sanitizedRequirements.knowledgeSources,
33702
+ tools: sanitizedRequirements.tools ? [...sanitizedRequirements.tools] : undefined,
32494
33703
  vectorStoreId,
32495
33704
  });
32496
33705
  }
32497
- // Create modified chat prompt with agent system message specific to OpenAI Agent
32498
- // Note: Unlike Assistants API, Responses API expects instructions (system message) to be passed in the call.
32499
- // So we use promptWithAgentModelRequirements which has the system message prepended.
32500
- // But we need to make sure we pass knowledgeSources in modelRequirements so OpenAiAgentExecutionTools can fallback to warning if vectorStoreId is missing (though we just handled it).
32501
- const promptForAgent = {
32502
- ...promptWithAgentModelRequirements,
32503
- modelRequirements: {
32504
- ...promptWithAgentModelRequirements.modelRequirements,
32505
- knowledgeSources: modelRequirements.knowledgeSources
32506
- ? [...modelRequirements.knowledgeSources]
32507
- : undefined, // Pass knowledge sources explicitly
32508
- },
32509
- };
32510
- underlyingLlmResult = await agentTools.callChatModelStream(promptForAgent, onProgress);
33706
+ if (preparedAgentKit.vectorStoreId) {
33707
+ AgentLlmExecutionTools.vectorStoreCache.set(this.title, {
33708
+ vectorStoreId: preparedAgentKit.vectorStoreId,
33709
+ requirementsHash: vectorStoreHash,
33710
+ });
33711
+ }
33712
+ AgentLlmExecutionTools.agentKitAgentCache.set(this.title, {
33713
+ agent: preparedAgentKit.agent,
33714
+ requirementsHash,
33715
+ vectorStoreId: preparedAgentKit.vectorStoreId,
33716
+ });
33717
+ const responseFormatOutputType = mapResponseFormatToAgentOutputType(promptWithAgentModelRequirements.modelRequirements.responseFormat);
33718
+ underlyingLlmResult = await this.options.llmTools.callChatModelStreamWithPreparedAgent({
33719
+ openAiAgentKitAgent: preparedAgentKit.agent,
33720
+ prompt: promptWithAgentModelRequirements,
33721
+ onProgress,
33722
+ responseFormatOutputType,
33723
+ });
32511
33724
  }
32512
33725
  else if (OpenAiAssistantExecutionTools.isOpenAiAssistantExecutionTools(this.options.llmTools)) {
32513
33726
  // ... deprecated path ...
32514
- const requirementsHash = SHA256(JSON.stringify(modelRequirements)).toString();
33727
+ const requirementsHash = SHA256(JSON.stringify(sanitizedRequirements)).toString();
32515
33728
  const cached = AgentLlmExecutionTools.assistantCache.get(this.title);
32516
33729
  let assistant;
32517
- if (cached) {
33730
+ if (this.options.assistantPreparationMode === 'external') {
33731
+ assistant = this.options.llmTools;
33732
+ if (this.options.isVerbose) {
33733
+ console.info('[🤰]', 'Using externally managed OpenAI Assistant', {
33734
+ agent: this.title,
33735
+ assistantId: assistant.assistantId,
33736
+ });
33737
+ }
33738
+ AgentLlmExecutionTools.assistantCache.set(this.title, {
33739
+ assistantId: assistant.assistantId,
33740
+ requirementsHash,
33741
+ });
33742
+ }
33743
+ else if (cached) {
32518
33744
  if (cached.requirementsHash === requirementsHash) {
32519
33745
  if (this.options.isVerbose) {
32520
33746
  console.info('[🤰]', 'Using cached OpenAI Assistant', {
@@ -32540,9 +33766,9 @@ class AgentLlmExecutionTools {
32540
33766
  assistant = await this.options.llmTools.updateAssistant({
32541
33767
  assistantId: cached.assistantId,
32542
33768
  name: this.title,
32543
- instructions: modelRequirements.systemMessage,
32544
- knowledgeSources: modelRequirements.knowledgeSources,
32545
- tools: modelRequirements.tools ? [...modelRequirements.tools] : undefined,
33769
+ instructions: sanitizedRequirements.systemMessage,
33770
+ knowledgeSources: sanitizedRequirements.knowledgeSources,
33771
+ tools: sanitizedRequirements.tools ? [...sanitizedRequirements.tools] : undefined,
32546
33772
  });
32547
33773
  AgentLlmExecutionTools.assistantCache.set(this.title, {
32548
33774
  assistantId: assistant.assistantId,
@@ -32565,9 +33791,9 @@ class AgentLlmExecutionTools {
32565
33791
  });
32566
33792
  assistant = await this.options.llmTools.createNewAssistant({
32567
33793
  name: this.title,
32568
- instructions: modelRequirements.systemMessage,
32569
- knowledgeSources: modelRequirements.knowledgeSources,
32570
- tools: modelRequirements.tools ? [...modelRequirements.tools] : undefined,
33794
+ instructions: sanitizedRequirements.systemMessage,
33795
+ knowledgeSources: sanitizedRequirements.knowledgeSources,
33796
+ tools: sanitizedRequirements.tools ? [...sanitizedRequirements.tools] : undefined,
32571
33797
  /*
32572
33798
  !!!
32573
33799
  metadata: {
@@ -32609,18 +33835,28 @@ class AgentLlmExecutionTools {
32609
33835
  }
32610
33836
  }
32611
33837
  let content = underlyingLlmResult.content;
32612
- // Note: Cleanup the AI artifacts from the content
32613
- content = humanizeAiText(content);
32614
- // Note: Make sure the content is Promptbook-like
32615
- content = promptbookifyAiText(content);
33838
+ if (typeof content === 'string') {
33839
+ // Note: Cleanup the AI artifacts from the content
33840
+ content = humanizeAiText(content);
33841
+ // Note: Make sure the content is Promptbook-like
33842
+ content = promptbookifyAiText(content);
33843
+ }
33844
+ else {
33845
+ // TODO: Maybe deep `humanizeAiText` + `promptbookifyAiText` inside of the object
33846
+ content = JSON.stringify(content);
33847
+ }
32616
33848
  const agentResult = {
32617
33849
  ...underlyingLlmResult,
32618
- content,
33850
+ content: content,
32619
33851
  modelName: this.modelName,
32620
33852
  };
32621
33853
  return agentResult;
32622
33854
  }
32623
33855
  }
33856
+ /**
33857
+ * Cached AgentKit agents to avoid rebuilding identical instances.
33858
+ */
33859
+ AgentLlmExecutionTools.agentKitAgentCache = new Map();
32624
33860
  /**
32625
33861
  * Cache of OpenAI assistants to avoid creating duplicates
32626
33862
  */
@@ -32701,8 +33937,8 @@ function buildTeacherSummary(commitments, used) {
32701
33937
  * - `Agent` - which represents an AI Agent with its source, memories, actions, etc. Agent is a higher-level abstraction which is internally using:
32702
33938
  * - `LlmExecutionTools` - which wraps one or more LLM models and provides an interface to execute them
32703
33939
  * - `AgentLlmExecutionTools` - which is a specific implementation of `LlmExecutionTools` that wraps another LlmExecutionTools and applies agent-specific system prompts and requirements
32704
- * - `OpenAiAgentExecutionTools` - which is a specific implementation of `LlmExecutionTools` for OpenAI models with agent capabilities (using Responses API), recommended for usage in `Agent` or `AgentLlmExecutionTools`
32705
33940
  * - `OpenAiAssistantExecutionTools` - (Deprecated) which is a specific implementation of `LlmExecutionTools` for OpenAI models with assistant capabilities
33941
+ * - `OpenAiAgentKitExecutionTools` - which is a specific implementation of `LlmExecutionTools` backed by OpenAI AgentKit
32706
33942
  * - `RemoteAgent` - which is an `Agent` that connects to a Promptbook Agents Server
32707
33943
  *
32708
33944
  * @public exported from `@promptbook/core`
@@ -32733,6 +33969,7 @@ class Agent extends AgentLlmExecutionTools {
32733
33969
  super({
32734
33970
  isVerbose: options.isVerbose,
32735
33971
  llmTools: getSingleLlmExecutionTools(options.executionTools.llm),
33972
+ assistantPreparationMode: options.assistantPreparationMode,
32736
33973
  agentSource: agentSource.value, // <- TODO: [🐱‍🚀] Allow to pass BehaviorSubject<string_book> OR refresh llmExecutionTools.callChat on agentSource change
32737
33974
  });
32738
33975
  _Agent_instances.add(this);
@@ -32799,7 +34036,6 @@ class Agent extends AgentLlmExecutionTools {
32799
34036
  * Note: This method also implements the learning mechanism
32800
34037
  */
32801
34038
  async callChatModelStream(prompt, onProgress) {
32802
- var _a;
32803
34039
  // [1] Check if the user is asking the same thing as in the samples
32804
34040
  const modelRequirements = await this.getModelRequirements();
32805
34041
  if (modelRequirements.samples) {
@@ -32847,7 +34083,7 @@ class Agent extends AgentLlmExecutionTools {
32847
34083
  if (result.rawResponse && 'sample' in result.rawResponse) {
32848
34084
  return result;
32849
34085
  }
32850
- if ((_a = modelRequirements.metadata) === null || _a === void 0 ? void 0 : _a.isClosed) {
34086
+ if (modelRequirements.isClosed) {
32851
34087
  return result;
32852
34088
  }
32853
34089
  // Note: [0] Notify start of self-learning
@@ -33008,6 +34244,63 @@ async function _Agent_selfLearnTeacher(prompt, result) {
33008
34244
  * TODO: [🧠][😰]Agent is not working with the parameters, should it be?
33009
34245
  */
33010
34246
 
34247
+ /**
34248
+ * Resolve a remote META IMAGE value into an absolute URL when possible.
34249
+ */
34250
+ function resolveRemoteImageUrl(imageUrl, agentUrl) {
34251
+ if (!imageUrl) {
34252
+ return undefined;
34253
+ }
34254
+ if (imageUrl.startsWith('http://') ||
34255
+ imageUrl.startsWith('https://') ||
34256
+ imageUrl.startsWith('data:') ||
34257
+ imageUrl.startsWith('blob:')) {
34258
+ return imageUrl;
34259
+ }
34260
+ try {
34261
+ return new URL(imageUrl, agentUrl).href;
34262
+ }
34263
+ catch (_a) {
34264
+ return imageUrl;
34265
+ }
34266
+ }
34267
+ /**
34268
+ * Format a META commitment line when the value is provided.
34269
+ */
34270
+ function formatMetaLine(label, value) {
34271
+ if (!value) {
34272
+ return null;
34273
+ }
34274
+ return `META ${label} ${value}`;
34275
+ }
34276
+ /**
34277
+ * Build a minimal agent source snapshot for remote agents.
34278
+ */
34279
+ function buildRemoteAgentSource(profile, meta) {
34280
+ const metaLines = [
34281
+ formatMetaLine('FULLNAME', meta === null || meta === void 0 ? void 0 : meta.fullname),
34282
+ formatMetaLine('IMAGE', meta === null || meta === void 0 ? void 0 : meta.image),
34283
+ formatMetaLine('DESCRIPTION', meta === null || meta === void 0 ? void 0 : meta.description),
34284
+ formatMetaLine('COLOR', meta === null || meta === void 0 ? void 0 : meta.color),
34285
+ formatMetaLine('FONT', meta === null || meta === void 0 ? void 0 : meta.font),
34286
+ formatMetaLine('LINK', meta === null || meta === void 0 ? void 0 : meta.link),
34287
+ ]
34288
+ .filter((line) => Boolean(line))
34289
+ .join('\n');
34290
+ const personaBlock = profile.personaDescription
34291
+ ? spaceTrim$2((block) => `
34292
+ PERSONA
34293
+ ${block(profile.personaDescription || '')}
34294
+ `)
34295
+ : '';
34296
+ return book `
34297
+ ${profile.agentName}
34298
+
34299
+ ${metaLines}
34300
+
34301
+ ${personaBlock}
34302
+ `;
34303
+ }
33011
34304
  /**
33012
34305
  * Represents one AI Agent
33013
34306
  *
@@ -33015,13 +34308,15 @@ async function _Agent_selfLearnTeacher(prompt, result) {
33015
34308
  * - `Agent` - which represents an AI Agent with its source, memories, actions, etc. Agent is a higher-level abstraction which is internally using:
33016
34309
  * - `LlmExecutionTools` - which wraps one or more LLM models and provides an interface to execute them
33017
34310
  * - `AgentLlmExecutionTools` - which is a specific implementation of `LlmExecutionTools` that wraps another LlmExecutionTools and applies agent-specific system prompts and requirements
33018
- * - `OpenAiAssistantExecutionTools` - which is a specific implementation of `LlmExecutionTools` for OpenAI models with assistant capabilities, recommended for usage in `Agent` or `AgentLlmExecutionTools`
34311
+ * - `OpenAiAssistantExecutionTools` - (Deprecated) which is a specific implementation of `LlmExecutionTools` for OpenAI models with assistant capabilities
34312
+ * - `OpenAiAgentKitExecutionTools` - which is a specific implementation of `LlmExecutionTools` backed by OpenAI AgentKit
33019
34313
  * - `RemoteAgent` - which is an `Agent` that connects to a Promptbook Agents Server
33020
34314
  *
33021
34315
  * @public exported from `@promptbook/core`
33022
34316
  */
33023
34317
  class RemoteAgent extends Agent {
33024
34318
  static async connect(options) {
34319
+ var _a, _b, _c;
33025
34320
  const agentProfileUrl = `${options.agentUrl}/api/profile`;
33026
34321
  const profileResponse = await fetch(agentProfileUrl);
33027
34322
  // <- TODO: [🐱‍🚀] What about closed-source agents?
@@ -33041,14 +34336,14 @@ class RemoteAgent extends Agent {
33041
34336
 
33042
34337
  `));
33043
34338
  }
33044
- const profile = await profileResponse.json();
34339
+ const profile = (await profileResponse.json());
34340
+ const resolvedMeta = {
34341
+ ...(profile.meta || {}),
34342
+ image: resolveRemoteImageUrl((_a = profile.meta) === null || _a === void 0 ? void 0 : _a.image, options.agentUrl),
34343
+ };
33045
34344
  // Note: We are creating dummy agent source because we don't have the source from the remote agent
33046
34345
  // But we populate the metadata from the profile
33047
- const agentSource = new BehaviorSubject(book `
33048
- ${profile.agentName}
33049
-
33050
- ${profile.personaDescription}
33051
- `);
34346
+ const agentSource = new BehaviorSubject(buildRemoteAgentSource(profile, resolvedMeta));
33052
34347
  // <- TODO: [🐱‍🚀] createBookFromProfile
33053
34348
  // <- TODO: [🐱‍🚀] Support updating and self-updating
33054
34349
  const remoteAgent = new RemoteAgent({
@@ -33071,10 +34366,10 @@ class RemoteAgent extends Agent {
33071
34366
  });
33072
34367
  remoteAgent._remoteAgentName = profile.agentName;
33073
34368
  remoteAgent._remoteAgentHash = profile.agentHash;
33074
- remoteAgent.personaDescription = profile.personaDescription;
33075
- remoteAgent.initialMessage = profile.initialMessage;
33076
- remoteAgent.links = profile.links;
33077
- remoteAgent.meta = profile.meta;
34369
+ remoteAgent.personaDescription = (_b = profile.personaDescription) !== null && _b !== void 0 ? _b : null;
34370
+ remoteAgent.initialMessage = (_c = profile.initialMessage) !== null && _c !== void 0 ? _c : null;
34371
+ remoteAgent.links = profile.links || [];
34372
+ remoteAgent.meta = resolvedMeta;
33078
34373
  remoteAgent.capabilities = profile.capabilities || [];
33079
34374
  remoteAgent.samples = profile.samples || [];
33080
34375
  remoteAgent.toolTitles = profile.toolTitles || {};
@@ -33178,26 +34473,7 @@ class RemoteAgent extends Agent {
33178
34473
  };
33179
34474
  };
33180
34475
  const getToolCallKey = (toolCall) => {
33181
- var _a;
33182
- const rawId = (_a = toolCall.rawToolCall) === null || _a === void 0 ? void 0 : _a.id;
33183
- if (rawId) {
33184
- return `id:${rawId}`;
33185
- }
33186
- const argsKey = (() => {
33187
- if (typeof toolCall.arguments === 'string') {
33188
- return toolCall.arguments;
33189
- }
33190
- if (!toolCall.arguments) {
33191
- return '';
33192
- }
33193
- try {
33194
- return JSON.stringify(toolCall.arguments);
33195
- }
33196
- catch (_a) {
33197
- return '';
33198
- }
33199
- })();
33200
- return `${toolCall.name}:${toolCall.createdAt || ''}:${argsKey}`;
34476
+ return getToolCallIdentity(toolCall);
33201
34477
  };
33202
34478
  const mergeToolCall = (existing, incoming) => {
33203
34479
  const incomingResult = incoming.result;