conversationalist 0.0.7 → 0.0.8

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 (97) hide show
  1. package/README.md +150 -90
  2. package/dist/adapters/anthropic/index.d.ts +11 -6
  3. package/dist/adapters/anthropic/index.d.ts.map +1 -1
  4. package/dist/adapters/anthropic/index.js +16 -3
  5. package/dist/adapters/anthropic/index.js.map +5 -4
  6. package/dist/adapters/gemini/index.d.ts +2 -2
  7. package/dist/adapters/gemini/index.d.ts.map +1 -1
  8. package/dist/adapters/gemini/index.js +52 -9
  9. package/dist/adapters/gemini/index.js.map +5 -4
  10. package/dist/adapters/openai/index.d.ts +32 -5
  11. package/dist/adapters/openai/index.d.ts.map +1 -1
  12. package/dist/adapters/openai/index.js +30 -8
  13. package/dist/adapters/openai/index.js.map +5 -4
  14. package/dist/context.d.ts.map +1 -1
  15. package/dist/conversation/append.d.ts +4 -4
  16. package/dist/conversation/append.d.ts.map +1 -1
  17. package/dist/conversation/create.d.ts +2 -3
  18. package/dist/conversation/create.d.ts.map +1 -1
  19. package/dist/conversation/index.d.ts +2 -2
  20. package/dist/conversation/index.d.ts.map +1 -1
  21. package/dist/conversation/modify.d.ts.map +1 -1
  22. package/dist/conversation/query.d.ts +9 -5
  23. package/dist/conversation/query.d.ts.map +1 -1
  24. package/dist/conversation/serialization.d.ts +6 -28
  25. package/dist/conversation/serialization.d.ts.map +1 -1
  26. package/dist/conversation/system-messages.d.ts +3 -3
  27. package/dist/conversation/system-messages.d.ts.map +1 -1
  28. package/dist/conversation/transform.d.ts.map +1 -1
  29. package/dist/conversation.d.ts +74 -18
  30. package/dist/conversation.d.ts.map +1 -1
  31. package/dist/export/index.d.ts +7 -0
  32. package/dist/export/index.d.ts.map +1 -0
  33. package/dist/export/index.js +3762 -0
  34. package/dist/export/index.js.map +62 -0
  35. package/dist/history.d.ts +102 -24
  36. package/dist/history.d.ts.map +1 -1
  37. package/dist/index.d.ts +7 -8
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +392 -4075
  40. package/dist/index.js.map +15 -60
  41. package/dist/markdown/index.d.ts +15 -0
  42. package/dist/markdown/index.d.ts.map +1 -0
  43. package/dist/markdown/index.js +4969 -0
  44. package/dist/markdown/index.js.map +69 -0
  45. package/dist/message.d.ts +1 -1
  46. package/dist/message.d.ts.map +1 -1
  47. package/dist/multi-modal.d.ts +3 -0
  48. package/dist/multi-modal.d.ts.map +1 -1
  49. package/dist/plugins/index.d.ts +1 -1
  50. package/dist/plugins/index.d.ts.map +1 -1
  51. package/dist/plugins/index.js +59 -0
  52. package/dist/plugins/index.js.map +10 -0
  53. package/dist/plugins/pii-redaction.d.ts +10 -1
  54. package/dist/plugins/pii-redaction.d.ts.map +1 -1
  55. package/dist/redaction/index.d.ts +2 -0
  56. package/dist/redaction/index.d.ts.map +1 -0
  57. package/dist/redaction/index.js +59 -0
  58. package/dist/redaction/index.js.map +10 -0
  59. package/dist/schemas/index.d.ts +2 -0
  60. package/dist/schemas/index.d.ts.map +1 -0
  61. package/dist/schemas/index.js +114 -0
  62. package/dist/schemas/index.js.map +10 -0
  63. package/dist/schemas.d.ts +324 -15
  64. package/dist/schemas.d.ts.map +1 -1
  65. package/dist/sort/index.d.ts +2 -0
  66. package/dist/sort/index.d.ts.map +1 -0
  67. package/dist/sort/index.js +32 -0
  68. package/dist/sort/index.js.map +10 -0
  69. package/dist/streaming.d.ts +3 -3
  70. package/dist/streaming.d.ts.map +1 -1
  71. package/dist/types.d.ts +72 -50
  72. package/dist/types.d.ts.map +1 -1
  73. package/dist/utilities/index.d.ts +1 -3
  74. package/dist/utilities/index.d.ts.map +1 -1
  75. package/dist/utilities/line-endings.d.ts +5 -0
  76. package/dist/utilities/line-endings.d.ts.map +1 -0
  77. package/dist/utilities/markdown.d.ts +1 -1
  78. package/dist/utilities/markdown.d.ts.map +1 -1
  79. package/dist/utilities/message-store.d.ts +6 -0
  80. package/dist/utilities/message-store.d.ts.map +1 -0
  81. package/dist/utilities/message.d.ts +9 -3
  82. package/dist/utilities/message.d.ts.map +1 -1
  83. package/dist/utilities/tool-calls.d.ts +4 -4
  84. package/dist/utilities/tool-calls.d.ts.map +1 -1
  85. package/dist/utilities/tool-results.d.ts +10 -0
  86. package/dist/utilities/tool-results.d.ts.map +1 -0
  87. package/dist/utilities/transient.d.ts +2 -2
  88. package/dist/utilities/transient.d.ts.map +1 -1
  89. package/dist/utilities.d.ts +3 -5
  90. package/dist/utilities.d.ts.map +1 -1
  91. package/dist/versioning/index.d.ts +3 -0
  92. package/dist/versioning/index.d.ts.map +1 -0
  93. package/dist/versioning/index.js +58 -0
  94. package/dist/versioning/index.js.map +11 -0
  95. package/dist/with-conversation.d.ts +8 -8
  96. package/dist/with-conversation.d.ts.map +1 -1
  97. package/package.json +26 -1
package/README.md CHANGED
@@ -13,7 +13,7 @@ In a modern AI application, a conversation is more than just a list of strings.
13
13
 
14
14
  - **Tool Use**: Pairing function calls with their results and ensuring they stay in sync.
15
15
  - **Hidden Logic**: Internal "thought" messages or snapshots that should be saved but never sent to the provider.
16
- - **Metadata**: Tracking tags, custom IDs, and tokens across different steps.
16
+ - **Metadata**: Tracking custom IDs and tokens across different steps.
17
17
  - **Streaming**: Gracefully handling partial messages in a UI without messy state transitions.
18
18
 
19
19
  Conversationalist handles these complexities through a robust, type-safe API that treats your conversation as the "Single Source of Truth."
@@ -25,7 +25,7 @@ Managing LLM conversations manually often leads to "provider lock-in" or fragile
25
25
  - **Decoupling Logic from Providers**: Write your business logic once using Conversationalist's message model, and use adapters to talk to OpenAI, Anthropic, or Gemini.
26
26
  - **Built-in Context Management**: Automatically handle context window limits by truncating history while preserving critical system instructions or recent messages.
27
27
  - **Type Safety Out-of-the-Box**: Built with Zod and TypeScript, ensuring that your conversation data is valid at runtime and compile-time.
28
- - **Unified Serialization**: One standard format (`ConversationJSON`) for your database, your frontend, and your backend.
28
+ - **Unified Serialization**: One standard format (`Conversation`) for your database, your frontend, and your backend.
29
29
 
30
30
  ## The Immutable Advantage
31
31
 
@@ -65,7 +65,6 @@ import {
65
65
  appendAssistantMessage,
66
66
  appendUserMessage,
67
67
  createConversation,
68
- serializeConversation,
69
68
  } from 'conversationalist';
70
69
  import { toOpenAIMessages } from 'conversationalist/openai';
71
70
 
@@ -82,17 +81,14 @@ conversation = appendAssistantMessage(conversation, 'Let me check that for you.'
82
81
  // 3. Adapt for a provider
83
82
  const openAIMessages = toOpenAIMessages(conversation);
84
83
  // [{ role: 'user', content: 'Where is my order?' }, ...]
85
-
86
- // 4. Save to your database
87
- const data = serializeConversation(conversation);
88
- // db.save(data.id, JSON.stringify(data));
89
84
  ```
90
85
 
91
86
  ## Core Concepts
92
87
 
93
88
  ### Conversations
94
89
 
95
- A conversation is an immutable record with metadata, tags, timestamps, and ordered messages.
90
+ A conversation is an immutable record with metadata, timestamps, a `messages` record keyed
91
+ by message ID, and an `ids` array that preserves order.
96
92
 
97
93
  ```ts
98
94
  import { createConversation } from 'conversationalist';
@@ -101,14 +97,20 @@ const conversation = createConversation({
101
97
  title: 'My Chat',
102
98
  status: 'active',
103
99
  metadata: { customerId: 'cus_123' },
104
- tags: ['support', 'vip'],
105
100
  });
106
101
  ```
107
102
 
103
+ Conversations track message order via `conversation.ids`. Every mutation keeps `ids` in sync
104
+ with `messages`. Use `getMessages(conversation)` for ordered arrays, or
105
+ `getMessageIds()` if you just need the IDs.
106
+
108
107
  ### Messages
109
108
 
110
109
  Messages have roles and can contain text or multi-modal content. Optional fields include
111
- `metadata`, `hidden`, `tokenUsage`, `toolCall`, `toolResult`, and `goalCompleted`.
110
+ `metadata`, `hidden`, `tokenUsage`, `toolCall`, and `toolResult`. Assistant messages can also
111
+ include `goalCompleted` (see `AssistantMessage`).
112
+ Use `isAssistantMessage` to narrow when you need `goalCompleted`.
113
+ Metadata and tool payloads are typed as `JSONValue` so conversations remain JSON-serializable.
112
114
 
113
115
  **Roles**: `user`, `assistant`, `system`, `developer`, `tool-use`, `tool-result`, `snapshot`.
114
116
  The `snapshot` role is for internal state and is skipped by adapters.
@@ -220,19 +222,15 @@ boundTruncate(4000); // Uses tiktokenEstimator automatically
220
222
 
221
223
  ### Markdown Conversion
222
224
 
223
- Convert conversations to human-readable Markdown format, or parse Markdown back into a conversation object.
225
+ Convert conversations to human-readable Markdown format, or parse Markdown back into a conversation object. These helpers live in `conversationalist/markdown`.
224
226
 
225
227
  #### Basic Usage (Clean Markdown)
226
228
 
227
229
  By default, `toMarkdown` produces clean, readable Markdown without metadata:
228
230
 
229
231
  ```ts
230
- import {
231
- toMarkdown,
232
- fromMarkdown,
233
- createConversation,
234
- appendMessages,
235
- } from 'conversationalist';
232
+ import { appendMessages, createConversation } from 'conversationalist';
233
+ import { fromMarkdown, toMarkdown } from 'conversationalist/markdown';
236
234
 
237
235
  let conversation = createConversation({ id: 'conv-1' });
238
236
  conversation = appendMessages(
@@ -272,7 +270,6 @@ const markdown = toMarkdown(conversation, { includeMetadata: true });
272
270
  // id: conv-1
273
271
  // status: active
274
272
  // metadata: {}
275
- // tags: []
276
273
  // createdAt: '2024-01-15T10:00:00.000Z'
277
274
  // updatedAt: '2024-01-15T10:01:00.000Z'
278
275
  // messages:
@@ -298,7 +295,7 @@ const markdown = toMarkdown(conversation, { includeMetadata: true });
298
295
  // Parse back with all metadata preserved
299
296
  const restored = fromMarkdown(markdown);
300
297
  // restored.id === 'conv-1'
301
- // restored.messages[0].id === 'msg-1'
298
+ // restored.ids[0] === 'msg-1'
302
299
  ```
303
300
 
304
301
  #### Multi-Modal Content
@@ -326,18 +323,15 @@ const md = toMarkdown(conversation);
326
323
 
327
324
  ### PII Redaction Plugin
328
325
 
329
- The library includes a built-in `piiRedactionPlugin` that can automatically redact emails, phone numbers, and common API key patterns.
326
+ The library includes a built-in `redactPii` plugin that can automatically redact emails, phone numbers, and common API key patterns.
330
327
 
331
328
  ```ts
332
- import {
333
- appendUserMessage,
334
- createConversation,
335
- piiRedactionPlugin,
336
- } from 'conversationalist';
329
+ import { appendUserMessage, createConversation, getMessages } from 'conversationalist';
330
+ import { redactPii } from 'conversationalist/redaction';
337
331
 
338
332
  // 1. Enable by adding to your environment
339
333
  const env = {
340
- plugins: [piiRedactionPlugin],
334
+ plugins: [redactPii],
341
335
  };
342
336
 
343
337
  // 2. Use the environment when appending messages
@@ -349,7 +343,7 @@ conversation = appendUserMessage(
349
343
  env,
350
344
  );
351
345
 
352
- console.log(conversation.messages[0].content);
346
+ console.log(getMessages(conversation)[0]?.content);
353
347
  // "Contact me at [EMAIL_REDACTED]"
354
348
  ```
355
349
 
@@ -357,7 +351,7 @@ When using `ConversationHistory`, you only need to provide the plugin once durin
357
351
 
358
352
  ```ts
359
353
  const history = new ConversationHistory(createConversation(), {
360
- plugins: [piiRedactionPlugin],
354
+ plugins: [redactPii],
361
355
  });
362
356
 
363
357
  const appendUser = history.bind(appendUserMessage);
@@ -374,6 +368,7 @@ import { toAnthropicMessages } from 'conversationalist/anthropic';
374
368
  import { toGeminiMessages } from 'conversationalist/gemini';
375
369
  ```
376
370
 
371
+ - Adapter outputs are SDK-compatible (OpenAI `ChatCompletionMessageParam[]`, Anthropic `MessageParam[]`, Gemini `Content[]`).
377
372
  - **OpenAI**: Supports `toOpenAIMessages` and `toOpenAIMessagesGrouped` (which groups consecutive tool calls).
378
373
  - **Anthropic**: Maps system messages and tool blocks to Anthropic's specific format.
379
374
  - **Gemini**: Handles Gemini's unique content/part structure.
@@ -533,6 +528,8 @@ history.truncateToTokenLimit(4000);
533
528
  const messages = history.getMessages();
534
529
  const stats = history.getStatistics();
535
530
  const tokens = history.estimateTokens();
531
+ const ids = history.ids;
532
+ const firstMessage = history.get(ids[0]!);
536
533
  ```
537
534
 
538
535
  ### Event Subscription
@@ -587,10 +584,10 @@ history.undo();
587
584
  history.appendUserMessage('Path B');
588
585
 
589
586
  console.log(history.branchCount); // 2
590
- console.log(history.current.messages[0].content); // "Path B"
587
+ console.log(history.getMessages()[0]?.content); // "Path B"
591
588
 
592
589
  history.switchToBranch(0);
593
- console.log(history.current.messages[0].content); // "Path A"
590
+ console.log(history.getMessages()[0]?.content); // "Path A"
594
591
  ```
595
592
 
596
593
  ### Serialization
@@ -598,15 +595,15 @@ console.log(history.current.messages[0].content); // "Path A"
598
595
  You can serialize the entire history tree (including all branches) to JSON and reconstruct it later.
599
596
 
600
597
  ```ts
601
- // 1. Save to JSON
602
- const json = history.toJSON();
603
- // localStorage.setItem('chat_history', JSON.stringify(json));
598
+ // 1. Capture a snapshot
599
+ const snapshot = history.snapshot();
600
+ // localStorage.setItem('chat_history', JSON.stringify(snapshot));
604
601
 
605
- // 2. Restore from JSON
606
- const restored = ConversationHistory.from(json);
602
+ // 2. Restore from a snapshot
603
+ const restored = ConversationHistory.from(snapshot);
607
604
 
608
605
  // You can also provide a new environment (e.g. with fresh token counters)
609
- const restoredWithEnv = ConversationHistory.from(json, {
606
+ const restoredWithEnv = ConversationHistory.from(snapshot, {
610
607
  estimateTokens: myNewEstimator,
611
608
  });
612
609
  ```
@@ -615,44 +612,25 @@ const restoredWithEnv = ConversationHistory.from(json, {
615
612
 
616
613
  ### Schema Versioning
617
614
 
618
- Conversations include a `schemaVersion` field for forward compatibility. When loading older data, use `migrateConversationJSON` to upgrade it to the current schema:
615
+ Conversations include a `schemaVersion` field for forward compatibility. When loading older data, use `migrateConversation` to upgrade it to the current schema:
619
616
 
620
617
  ```ts
618
+ import { deserializeConversation } from 'conversationalist';
621
619
  import {
622
- migrateConversationJSON,
623
- deserializeConversation,
620
+ migrateConversation,
624
621
  CURRENT_SCHEMA_VERSION,
625
- } from 'conversationalist';
622
+ } from 'conversationalist/versioning';
626
623
 
627
624
  // Old data without schemaVersion
628
625
  const legacyData = JSON.parse(oldStorage);
629
- const migrated = migrateConversationJSON(legacyData);
626
+ const migrated = migrateConversation(legacyData);
630
627
  // migrated.schemaVersion === CURRENT_SCHEMA_VERSION
631
628
 
632
629
  const conversation = deserializeConversation(migrated);
633
630
  ```
634
631
 
635
- ### Serialization Options
636
-
637
- `serializeConversation` accepts options for controlling the output:
638
-
639
- ```ts
640
- import { serializeConversation } from 'conversationalist';
641
-
642
- const json = serializeConversation(conversation, {
643
- // Sort keys and messages for stable, diff-friendly output
644
- deterministic: true,
645
-
646
- // Remove metadata keys starting with '_' (transient UI state)
647
- stripTransient: true,
648
-
649
- // Replace tool arguments with '[REDACTED]'
650
- redactToolArguments: true,
651
-
652
- // Replace tool result content with '[REDACTED]'
653
- redactToolResults: true,
654
- });
655
- ```
632
+ Conversations are already JSON-serializable; persist them directly and apply utilities
633
+ like `stripTransientMetadata` or `redactMessageAtPosition` when you need to sanitize data.
656
634
 
657
635
  ### Transient Metadata Convention
658
636
 
@@ -677,12 +655,12 @@ stripTransientFromRecord({ _loading: true, source: 'web' });
677
655
  const cleaned = stripTransientMetadata(conversation);
678
656
  ```
679
657
 
680
- ### Deterministic Output
658
+ ### Sort Utilities
681
659
 
682
- For reproducible snapshots or tests, use the deterministic utilities:
660
+ For reproducible snapshots or tests, use the sort utilities:
683
661
 
684
662
  ```ts
685
- import { sortObjectKeys, sortMessagesByPosition } from 'conversationalist';
663
+ import { sortObjectKeys, sortMessagesByPosition } from 'conversationalist/sort';
686
664
 
687
665
  // Sort object keys alphabetically (recursive)
688
666
  const sorted = sortObjectKeys({ z: 1, a: 2, nested: { b: 3, a: 4 } });
@@ -702,7 +680,7 @@ import {
702
680
  LABEL_TO_ROLE,
703
681
  getRoleLabel,
704
682
  getRoleFromLabel,
705
- } from 'conversationalist';
683
+ } from 'conversationalist/markdown';
706
684
 
707
685
  // Get display label for a role
708
686
  getRoleLabel('tool-use'); // 'Tool Use'
@@ -722,8 +700,13 @@ LABEL_TO_ROLE['System']; // 'system'
722
700
  You can also convert a conversation to Markdown format for human-readable storage or export, and restore it later.
723
701
 
724
702
  ```ts
703
+ import { ConversationHistory } from 'conversationalist';
704
+ import { historyFromMarkdown, historyToMarkdown } from 'conversationalist/markdown';
705
+
706
+ const history = new ConversationHistory();
707
+
725
708
  // Export to clean, readable Markdown
726
- const markdown = history.toMarkdown();
709
+ const markdown = historyToMarkdown(history);
727
710
  // ### User
728
711
  //
729
712
  // Hello!
@@ -733,10 +716,30 @@ const markdown = history.toMarkdown();
733
716
  // Hi there!
734
717
 
735
718
  // Export with full metadata (lossless round-trip)
736
- const markdownWithMetadata = history.toMarkdown({ includeMetadata: true });
719
+ const markdownWithMetadata = historyToMarkdown(history, { includeMetadata: true });
720
+
721
+ // Export with additional controls (redaction, transient stripping, hidden handling)
722
+ const markdownSafe = historyToMarkdown(history, {
723
+ includeMetadata: true,
724
+ stripTransient: true,
725
+ redactToolArguments: true,
726
+ redactToolResults: true,
727
+ includeHidden: false,
728
+ });
737
729
 
738
730
  // Restore from Markdown
739
- const restored = ConversationHistory.fromMarkdown(markdownWithMetadata);
731
+ const restored = historyFromMarkdown(markdownWithMetadata);
732
+ ```
733
+
734
+ ### Export Helpers
735
+
736
+ For markdown export workflows, use the built-in helpers:
737
+
738
+ ```ts
739
+ import { exportMarkdown, normalizeLineEndings } from 'conversationalist/export';
740
+
741
+ const normalizedMarkdown = exportMarkdown(conversation, { includeMetadata: true });
742
+ const normalized = normalizeLineEndings('line1\r\nline2');
740
743
  ```
741
744
 
742
745
  ## Integration
@@ -747,7 +750,7 @@ Because **Conversationalist** is immutable, it works perfectly with React's `use
747
750
 
748
751
  ```tsx
749
752
  import { useState } from 'react';
750
- import { createConversation, appendUserMessage } from 'conversationalist';
753
+ import { appendUserMessage, createConversation, getMessages } from 'conversationalist';
751
754
 
752
755
  export function ChatApp() {
753
756
  const [conversation, setConversation] = useState(() => createConversation());
@@ -759,7 +762,7 @@ export function ChatApp() {
759
762
 
760
763
  return (
761
764
  <div>
762
- {conversation.messages.map((m) => (
765
+ {getMessages(conversation).map((m) => (
763
766
  <div key={m.id}>{String(m.content)}</div>
764
767
  ))}
765
768
  <button onClick={() => handleSend('Hello!')}>Send</button>
@@ -774,7 +777,7 @@ For more complex applications, you can wrap the logic into a custom hook. This e
774
777
 
775
778
  ```tsx
776
779
  import { useState, useCallback, useEffect } from 'react';
777
- import { createConversation, ConversationHistory } from 'conversationalist';
780
+ import { ConversationHistory, createConversation, getMessages } from 'conversationalist';
778
781
 
779
782
  export function useChat(initialTitle?: string) {
780
783
  // 1. Initialize history (this could also come from context or props)
@@ -818,7 +821,7 @@ export function useChat(initialTitle?: string) {
818
821
 
819
822
  return {
820
823
  conversation,
821
- messages: conversation.messages,
824
+ messages: getMessages(conversation),
822
825
  loading,
823
826
  sendMessage,
824
827
  undo: () => history.undo(),
@@ -862,7 +865,11 @@ In Svelte 5, you can manage conversation state using the `$state` rune. Since **
862
865
 
863
866
  ```svelte
864
867
  <script lang="ts">
865
- import { createConversation, appendUserMessage } from 'conversationalist';
868
+ import {
869
+ appendUserMessage,
870
+ createConversation,
871
+ getMessages,
872
+ } from 'conversationalist';
866
873
 
867
874
  let conversation = $state(createConversation());
868
875
 
@@ -872,7 +879,7 @@ In Svelte 5, you can manage conversation state using the `$state` rune. Since **
872
879
  </script>
873
880
 
874
881
  <div>
875
- {#each conversation.messages as m (m.id)}
882
+ {#each getMessages(conversation) as m (m.id)}
876
883
  <div>{String(m.content)}</div>
877
884
  {/each}
878
885
  <button onclick={() => handleSend('Hello!')}>Send</button>
@@ -885,14 +892,14 @@ Svelte 5's runes pair perfectly with **Conversationalist**. You can use the `Con
885
892
 
886
893
  ```svelte
887
894
  <script lang="ts">
888
- import { ConversationHistory } from 'conversationalist';
895
+ import { ConversationHistory, getMessages } from 'conversationalist';
889
896
 
890
897
  // history implements the Svelte store contract
891
898
  const history = new ConversationHistory();
892
899
  </script>
893
900
 
894
901
  <div>
895
- {#each $history.messages as m (m.id)}
902
+ {#each getMessages($history) as m (m.id)}
896
903
  <div>{String(m.content)}</div>
897
904
  {/each}
898
905
  <button onclick={() => history.appendUserMessage('Hello!')}>
@@ -905,19 +912,72 @@ Svelte 5's runes pair perfectly with **Conversationalist**. You can use the `Con
905
912
 
906
913
  ## API Overview
907
914
 
908
- | Category | Key Functions |
909
- | :---------------- | :------------------------------------------------------------------------------------------------------- |
910
- | **Creation** | `createConversation`, `serializeConversation`, `deserializeConversation`, `migrateConversationJSON` |
911
- | **Appending** | `appendUserMessage`, `appendAssistantMessage`, `appendSystemMessage`, `appendMessages` |
912
- | **Streaming** | `appendStreamingMessage`, `updateStreamingMessage`, `finalizeStreamingMessage`, `cancelStreamingMessage` |
913
- | **Modification** | `redactMessageAtPosition`, `replaceSystemMessage`, `collapseSystemMessages` |
914
- | **Context** | `truncateToTokenLimit`, `getRecentMessages`, `estimateConversationTokens` |
915
- | **Querying** | `getConversationMessages`, `getMessageByIdentifier`, `computeConversationStatistics` |
916
- | **Conversion** | `toMarkdown`, `fromMarkdown`, `toChatMessages`, `pairToolCallsWithResults` |
917
- | **Role Labels** | `ROLE_LABELS`, `LABEL_TO_ROLE`, `getRoleLabel`, `getRoleFromLabel` |
918
- | **Transient** | `isTransientKey`, `stripTransientFromRecord`, `stripTransientMetadata` |
919
- | **Deterministic** | `sortObjectKeys`, `sortMessagesByPosition` |
920
- | **History** | `ConversationHistory`, `bindToConversationHistory` |
915
+ | Category | Key Functions |
916
+ | :--------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
917
+ | **Creation** | `createConversation`, `deserializeConversation` |
918
+ | **Appending** | `appendUserMessage`, `appendAssistantMessage`, `appendSystemMessage`, `appendMessages` |
919
+ | **Streaming** | `appendStreamingMessage`, `updateStreamingMessage`, `finalizeStreamingMessage`, `cancelStreamingMessage` |
920
+ | **Modification** | `redactMessageAtPosition`, `replaceSystemMessage`, `collapseSystemMessages` |
921
+ | **Context** | `truncateToTokenLimit`, `getRecentMessages`, `estimateConversationTokens` |
922
+ | **Querying** | `getMessages`, `getMessageIds`, `getMessageById`, `getStatistics` |
923
+ | **Conversion** | `toChatMessages`, `pairToolCallsWithResults` |
924
+ | **Markdown** | `toMarkdown`, `fromMarkdown`, `historyToMarkdown`, `historyFromMarkdown` (from `conversationalist/markdown`) |
925
+ | **Export** | `exportMarkdown`, `normalizeLineEndings` (from `conversationalist/export`) |
926
+ | **Schemas** | `conversationSchema`, `messageJSONSchema`, `messageInputSchema`, `messageRoleSchema`, `multiModalContentSchema`, `jsonValueSchema`, `toolCallSchema`, `toolResultSchema`, `tokenUsageSchema` (from `conversationalist/schemas`) |
927
+ | **Role Labels** | `ROLE_LABELS`, `LABEL_TO_ROLE`, `getRoleLabel`, `getRoleFromLabel` (from `conversationalist/markdown`) |
928
+ | **Transient** | `isTransientKey`, `stripTransientFromRecord`, `stripTransientMetadata` |
929
+ | **Redaction** | `redactPii`, `createPIIRedactionPlugin`, `createPIIRedaction`, `DEFAULT_PII_RULES` (from `conversationalist/redaction`) |
930
+ | **Versioning** | `migrateConversation`, `CURRENT_SCHEMA_VERSION` (from `conversationalist/versioning`) |
931
+ | **Sort** | `sortObjectKeys`, `sortMessagesByPosition` (from `conversationalist/sort`) |
932
+ | **History** | `ConversationHistory` |
933
+
934
+ ## Standard Schema Compliance
935
+
936
+ All exported Zod schemas implement the [Standard Schema](https://standardschema.dev/) specification via Zod's built-in support. This means they can be used with any Standard Schema-compatible tool without library-specific adapters.
937
+
938
+ ### Exported Schemas
939
+
940
+ | Schema | Purpose |
941
+ | :------------------------ | :---------------------------------- |
942
+ | `conversationSchema` | Complete conversation with metadata |
943
+ | `jsonValueSchema` | JSON-serializable values |
944
+ | `messageJSONSchema` | Serialized message format |
945
+ | `messageInputSchema` | Input for creating messages |
946
+ | `messageRoleSchema` | Valid message roles enum |
947
+ | `multiModalContentSchema` | Text or image content |
948
+ | `toolCallSchema` | Tool function calls |
949
+ | `toolResultSchema` | Tool execution results |
950
+ | `tokenUsageSchema` | Token usage statistics |
951
+
952
+ ### Usage with Standard Schema Consumers
953
+
954
+ ```ts
955
+ import { conversationSchema } from 'conversationalist/schemas';
956
+
957
+ // Access the Standard Schema interface
958
+ const standardSchema = conversationSchema['~standard'];
959
+
960
+ // Use with any Standard Schema consumer
961
+ const result = standardSchema.validate(unknownData);
962
+ if (result.issues) {
963
+ console.error('Validation failed:', result.issues);
964
+ } else {
965
+ console.log('Valid conversation:', result.value);
966
+ }
967
+ ```
968
+
969
+ ### Type Inference
970
+
971
+ Standard Schema preserves type information:
972
+
973
+ ```ts
974
+ import type { StandardSchemaV1 } from '@standard-schema/spec';
975
+ import { conversationSchema } from 'conversationalist/schemas';
976
+
977
+ // Type is inferred correctly
978
+ type ConversationInput = StandardSchemaV1.InferInput<typeof conversationSchema>;
979
+ type ConversationOutput = StandardSchemaV1.InferOutput<typeof conversationSchema>;
980
+ ```
921
981
 
922
982
  ## Deterministic Environments (Testing)
923
983
 
@@ -9,14 +9,19 @@ export interface AnthropicTextBlock {
9
9
  /**
10
10
  * Anthropic image content block.
11
11
  */
12
+ export interface AnthropicBase64ImageSource {
13
+ type: 'base64';
14
+ media_type: string;
15
+ data: string;
16
+ }
17
+ export interface AnthropicUrlImageSource {
18
+ type: 'url';
19
+ url: string;
20
+ }
21
+ export type AnthropicImageSource = AnthropicBase64ImageSource | AnthropicUrlImageSource;
12
22
  export interface AnthropicImageBlock {
13
23
  type: 'image';
14
- source: {
15
- type: 'base64' | 'url';
16
- media_type?: string;
17
- data?: string;
18
- url?: string;
19
- };
24
+ source: AnthropicImageSource;
20
25
  }
21
26
  /**
22
27
  * Anthropic tool use content block.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/adapters/anthropic/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAiC,MAAM,aAAa,CAAC;AAE/E;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE;QACN,IAAI,EAAE,QAAQ,GAAG,KAAK,CAAC;QACvB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,GAAG,CAAC,EAAE,MAAM,CAAC;KACd,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,UAAU,CAAC;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,aAAa,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAC7B,kBAAkB,GAClB,mBAAmB,GACnB,qBAAqB,GACrB,wBAAwB,CAAC;AAE7B;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAC3B,OAAO,EAAE,MAAM,GAAG,qBAAqB,EAAE,CAAC;CAC3C;AAED;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,gBAAgB,EAAE,CAAC;CAC9B;AA+GD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,mBAAmB,CAAC,YAAY,EAAE,YAAY,GAAG,qBAAqB,CAiFrF"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/adapters/anthropic/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAiC,MAAM,aAAa,CAAC;AAG/E;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACzC,IAAI,EAAE,QAAQ,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,KAAK,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,MAAM,oBAAoB,GAAG,0BAA0B,GAAG,uBAAuB,CAAC;AAExF,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,oBAAoB,CAAC;CAC9B;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,UAAU,CAAC;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,aAAa,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAC7B,kBAAkB,GAClB,mBAAmB,GACnB,qBAAqB,GACrB,wBAAwB,CAAC;AAE7B;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAC3B,OAAO,EAAE,MAAM,GAAG,qBAAqB,EAAE,CAAC;CAC3C;AAED;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,gBAAgB,EAAE,CAAC;CAC9B;AA+GD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,mBAAmB,CAAC,YAAY,EAAE,YAAY,GAAG,qBAAqB,CAkFrF"}
@@ -1,3 +1,15 @@
1
+ // src/utilities/message-store.ts
2
+ function getOrderedMessages(conversation) {
3
+ const ordered = [];
4
+ for (const id of conversation.ids) {
5
+ const message = conversation.messages[id];
6
+ if (message) {
7
+ ordered.push(message);
8
+ }
9
+ }
10
+ return ordered;
11
+ }
12
+
1
13
  // src/adapters/anthropic/index.ts
2
14
  function toAnthropicContent(content) {
3
15
  if (typeof content === "string") {
@@ -75,7 +87,8 @@ function extractSystemContent(messages) {
75
87
  `);
76
88
  }
77
89
  function toAnthropicMessages(conversation) {
78
- const system = extractSystemContent(conversation.messages);
90
+ const ordered = getOrderedMessages(conversation);
91
+ const system = extractSystemContent(ordered);
79
92
  const messages = [];
80
93
  let currentRole = null;
81
94
  let currentBlocks = [];
@@ -89,7 +102,7 @@ function toAnthropicMessages(conversation) {
89
102
  }
90
103
  currentRole = null;
91
104
  };
92
- for (const message of conversation.messages) {
105
+ for (const message of ordered) {
93
106
  if (message.hidden)
94
107
  continue;
95
108
  if (message.role === "system" || message.role === "developer") {
@@ -144,4 +157,4 @@ export {
144
157
  toAnthropicMessages
145
158
  };
146
159
 
147
- //# debugId=46DDFFB8D921BA7364756E2164756E21
160
+ //# debugId=77199835081EDC2864756E2164756E21
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../../../src/adapters/anthropic/index.ts"],
3
+ "sources": ["../../../src/utilities/message-store.ts", "../../../src/adapters/anthropic/index.ts"],
4
4
  "sourcesContent": [
5
- "import type { MultiModalContent } from '@lasercat/homogenaize';\n\nimport type { Conversation, Message, ToolCall, ToolResult } from '../../types';\n\n/**\n * Anthropic text content block.\n */\nexport interface AnthropicTextBlock {\n type: 'text';\n text: string;\n}\n\n/**\n * Anthropic image content block.\n */\nexport interface AnthropicImageBlock {\n type: 'image';\n source: {\n type: 'base64' | 'url';\n media_type?: string;\n data?: string;\n url?: string;\n };\n}\n\n/**\n * Anthropic tool use content block.\n */\nexport interface AnthropicToolUseBlock {\n type: 'tool_use';\n id: string;\n name: string;\n input: unknown;\n}\n\n/**\n * Anthropic tool result content block.\n */\nexport interface AnthropicToolResultBlock {\n type: 'tool_result';\n tool_use_id: string;\n content: string;\n is_error?: boolean;\n}\n\n/**\n * Anthropic content block union type.\n */\nexport type AnthropicContentBlock =\n | AnthropicTextBlock\n | AnthropicImageBlock\n | AnthropicToolUseBlock\n | AnthropicToolResultBlock;\n\n/**\n * Anthropic message format for the Messages API.\n */\nexport interface AnthropicMessage {\n role: 'user' | 'assistant';\n content: string | AnthropicContentBlock[];\n}\n\n/**\n * Result of converting a conversation to Anthropic format.\n * System messages are extracted separately since Anthropic uses a top-level system parameter.\n */\nexport interface AnthropicConversation {\n system?: string;\n messages: AnthropicMessage[];\n}\n\n/**\n * Converts internal multi-modal content to Anthropic content blocks.\n */\nfunction toAnthropicContent(\n content: string | ReadonlyArray<MultiModalContent>,\n): string | AnthropicContentBlock[] {\n if (typeof content === 'string') {\n return content;\n }\n\n const blocks: AnthropicContentBlock[] = [];\n for (const part of content) {\n if (part.type === 'text') {\n blocks.push({ type: 'text', text: part.text ?? '' });\n } else if (part.type === 'image') {\n // Anthropic supports both URL and base64\n const url = part.url ?? '';\n if (url.startsWith('data:')) {\n // Base64 data URL\n const matches = url.match(/^data:([^;]+);base64,(.+)$/);\n if (matches && matches[1] && matches[2]) {\n blocks.push({\n type: 'image',\n source: {\n type: 'base64',\n media_type: matches[1],\n data: matches[2],\n },\n });\n }\n } else {\n // Regular URL\n blocks.push({\n type: 'image',\n source: {\n type: 'url',\n url,\n },\n });\n }\n }\n }\n\n return blocks.length === 1 && blocks[0]?.type === 'text' ? blocks[0].text : blocks;\n}\n\n/**\n * Converts an internal ToolCall to Anthropic tool_use block.\n */\nfunction toToolUseBlock(toolCall: ToolCall): AnthropicToolUseBlock {\n return {\n type: 'tool_use',\n id: toolCall.id,\n name: toolCall.name,\n input:\n typeof toolCall.arguments === 'string'\n ? JSON.parse(toolCall.arguments)\n : toolCall.arguments,\n };\n}\n\n/**\n * Converts an internal ToolResult to Anthropic tool_result block.\n */\nfunction toToolResultBlock(toolResult: ToolResult): AnthropicToolResultBlock {\n const result: AnthropicToolResultBlock = {\n type: 'tool_result',\n tool_use_id: toolResult.callId,\n content:\n typeof toolResult.content === 'string'\n ? toolResult.content\n : JSON.stringify(toolResult.content),\n };\n\n if (toolResult.outcome === 'error') {\n result.is_error = true;\n }\n\n return result;\n}\n\n/**\n * Collects system message content from a conversation.\n */\nfunction extractSystemContent(messages: ReadonlyArray<Message>): string | undefined {\n const systemMessages = messages.filter(\n (m) => (m.role === 'system' || m.role === 'developer') && !m.hidden,\n );\n\n if (systemMessages.length === 0) {\n return undefined;\n }\n\n const parts: string[] = [];\n for (const msg of systemMessages) {\n if (typeof msg.content === 'string') {\n parts.push(msg.content);\n } else {\n for (const part of msg.content) {\n if (part.type === 'text') {\n parts.push(part.text ?? '');\n }\n }\n }\n }\n\n return parts.join('\\n\\n');\n}\n\n/**\n * Converts a conversation to Anthropic Messages API format.\n * System messages are extracted to the top-level `system` field.\n * Tool calls become tool_use blocks, tool results become tool_result blocks.\n *\n * @example\n * ```ts\n * import { toAnthropicMessages } from 'conversationalist/anthropic';\n *\n * const { system, messages } = toAnthropicMessages(conversation);\n * const response = await anthropic.messages.create({\n * model: 'claude-3-opus-20240229',\n * system,\n * messages,\n * });\n * ```\n */\nexport function toAnthropicMessages(conversation: Conversation): AnthropicConversation {\n const system = extractSystemContent(conversation.messages);\n const messages: AnthropicMessage[] = [];\n\n // Track pending content blocks to merge consecutive same-role messages\n let currentRole: 'user' | 'assistant' | null = null;\n let currentBlocks: AnthropicContentBlock[] = [];\n\n const flushCurrent = () => {\n if (currentRole && currentBlocks.length > 0) {\n messages.push({\n role: currentRole,\n content:\n currentBlocks.length === 1 && currentBlocks[0]?.type === 'text'\n ? currentBlocks[0].text\n : currentBlocks,\n });\n currentBlocks = [];\n }\n currentRole = null;\n };\n\n for (const message of conversation.messages) {\n if (message.hidden) continue;\n\n // Skip system messages (already extracted)\n if (message.role === 'system' || message.role === 'developer') {\n continue;\n }\n\n // Skip snapshots\n if (message.role === 'snapshot') {\n continue;\n }\n\n let targetRole: 'user' | 'assistant';\n let blocks: AnthropicContentBlock[] = [];\n\n if (message.role === 'user') {\n targetRole = 'user';\n const content = toAnthropicContent(message.content);\n if (typeof content === 'string') {\n blocks = [{ type: 'text', text: content }];\n } else {\n blocks = content;\n }\n } else if (message.role === 'assistant') {\n targetRole = 'assistant';\n const content = toAnthropicContent(message.content);\n if (typeof content === 'string') {\n blocks = [{ type: 'text', text: content }];\n } else {\n blocks = content;\n }\n } else if (message.role === 'tool-use' && message.toolCall) {\n targetRole = 'assistant';\n blocks = [toToolUseBlock(message.toolCall)];\n } else if (message.role === 'tool-result' && message.toolResult) {\n targetRole = 'user';\n blocks = [toToolResultBlock(message.toolResult)];\n } else {\n continue;\n }\n\n // Merge with current or start new\n if (currentRole === targetRole) {\n currentBlocks.push(...blocks);\n } else {\n flushCurrent();\n currentRole = targetRole;\n currentBlocks = blocks;\n }\n }\n\n flushCurrent();\n\n const result: AnthropicConversation = { messages };\n if (system !== undefined) {\n result.system = system;\n }\n return result;\n}\n"
5
+ "import type { Conversation, Message } from '../types';\n\nexport function getOrderedMessages(conversation: Conversation): Message[] {\n const ordered: Message[] = [];\n for (const id of conversation.ids) {\n const message = conversation.messages[id];\n if (message) {\n ordered.push(message);\n }\n }\n return ordered;\n}\n\nexport function toIdRecord<T extends { id: string }>(\n items: readonly T[],\n): Record<string, T> {\n const record: Record<string, T> = {};\n for (const item of items) {\n record[item.id] = item;\n }\n return record;\n}\n",
6
+ "import type { MultiModalContent } from '@lasercat/homogenaize';\n\nimport type { Conversation, Message, ToolCall, ToolResult } from '../../types';\nimport { getOrderedMessages } from '../../utilities/message-store';\n\n/**\n * Anthropic text content block.\n */\nexport interface AnthropicTextBlock {\n type: 'text';\n text: string;\n}\n\n/**\n * Anthropic image content block.\n */\nexport interface AnthropicBase64ImageSource {\n type: 'base64';\n media_type: string;\n data: string;\n}\n\nexport interface AnthropicUrlImageSource {\n type: 'url';\n url: string;\n}\n\nexport type AnthropicImageSource = AnthropicBase64ImageSource | AnthropicUrlImageSource;\n\nexport interface AnthropicImageBlock {\n type: 'image';\n source: AnthropicImageSource;\n}\n\n/**\n * Anthropic tool use content block.\n */\nexport interface AnthropicToolUseBlock {\n type: 'tool_use';\n id: string;\n name: string;\n input: unknown;\n}\n\n/**\n * Anthropic tool result content block.\n */\nexport interface AnthropicToolResultBlock {\n type: 'tool_result';\n tool_use_id: string;\n content: string;\n is_error?: boolean;\n}\n\n/**\n * Anthropic content block union type.\n */\nexport type AnthropicContentBlock =\n | AnthropicTextBlock\n | AnthropicImageBlock\n | AnthropicToolUseBlock\n | AnthropicToolResultBlock;\n\n/**\n * Anthropic message format for the Messages API.\n */\nexport interface AnthropicMessage {\n role: 'user' | 'assistant';\n content: string | AnthropicContentBlock[];\n}\n\n/**\n * Result of converting a conversation to Anthropic format.\n * System messages are extracted separately since Anthropic uses a top-level system parameter.\n */\nexport interface AnthropicConversation {\n system?: string;\n messages: AnthropicMessage[];\n}\n\n/**\n * Converts internal multi-modal content to Anthropic content blocks.\n */\nfunction toAnthropicContent(\n content: string | ReadonlyArray<MultiModalContent>,\n): string | AnthropicContentBlock[] {\n if (typeof content === 'string') {\n return content;\n }\n\n const blocks: AnthropicContentBlock[] = [];\n for (const part of content) {\n if (part.type === 'text') {\n blocks.push({ type: 'text', text: part.text ?? '' });\n } else if (part.type === 'image') {\n // Anthropic supports both URL and base64\n const url = part.url ?? '';\n if (url.startsWith('data:')) {\n // Base64 data URL\n const matches = url.match(/^data:([^;]+);base64,(.+)$/);\n if (matches && matches[1] && matches[2]) {\n blocks.push({\n type: 'image',\n source: {\n type: 'base64',\n media_type: matches[1],\n data: matches[2],\n },\n });\n }\n } else {\n // Regular URL\n blocks.push({\n type: 'image',\n source: {\n type: 'url',\n url,\n },\n });\n }\n }\n }\n\n return blocks.length === 1 && blocks[0]?.type === 'text' ? blocks[0].text : blocks;\n}\n\n/**\n * Converts an internal ToolCall to Anthropic tool_use block.\n */\nfunction toToolUseBlock(toolCall: ToolCall): AnthropicToolUseBlock {\n return {\n type: 'tool_use',\n id: toolCall.id,\n name: toolCall.name,\n input:\n typeof toolCall.arguments === 'string'\n ? JSON.parse(toolCall.arguments)\n : toolCall.arguments,\n };\n}\n\n/**\n * Converts an internal ToolResult to Anthropic tool_result block.\n */\nfunction toToolResultBlock(toolResult: ToolResult): AnthropicToolResultBlock {\n const result: AnthropicToolResultBlock = {\n type: 'tool_result',\n tool_use_id: toolResult.callId,\n content:\n typeof toolResult.content === 'string'\n ? toolResult.content\n : JSON.stringify(toolResult.content),\n };\n\n if (toolResult.outcome === 'error') {\n result.is_error = true;\n }\n\n return result;\n}\n\n/**\n * Collects system message content from a conversation.\n */\nfunction extractSystemContent(messages: ReadonlyArray<Message>): string | undefined {\n const systemMessages = messages.filter(\n (m) => (m.role === 'system' || m.role === 'developer') && !m.hidden,\n );\n\n if (systemMessages.length === 0) {\n return undefined;\n }\n\n const parts: string[] = [];\n for (const msg of systemMessages) {\n if (typeof msg.content === 'string') {\n parts.push(msg.content);\n } else {\n for (const part of msg.content) {\n if (part.type === 'text') {\n parts.push(part.text ?? '');\n }\n }\n }\n }\n\n return parts.join('\\n\\n');\n}\n\n/**\n * Converts a conversation to Anthropic Messages API format.\n * System messages are extracted to the top-level `system` field.\n * Tool calls become tool_use blocks, tool results become tool_result blocks.\n *\n * @example\n * ```ts\n * import { toAnthropicMessages } from 'conversationalist/anthropic';\n *\n * const { system, messages } = toAnthropicMessages(conversation);\n * const response = await anthropic.messages.create({\n * model: 'claude-3-opus-20240229',\n * system,\n * messages,\n * });\n * ```\n */\nexport function toAnthropicMessages(conversation: Conversation): AnthropicConversation {\n const ordered = getOrderedMessages(conversation);\n const system = extractSystemContent(ordered);\n const messages: AnthropicMessage[] = [];\n\n // Track pending content blocks to merge consecutive same-role messages\n let currentRole: 'user' | 'assistant' | null = null;\n let currentBlocks: AnthropicContentBlock[] = [];\n\n const flushCurrent = () => {\n if (currentRole && currentBlocks.length > 0) {\n messages.push({\n role: currentRole,\n content:\n currentBlocks.length === 1 && currentBlocks[0]?.type === 'text'\n ? currentBlocks[0].text\n : currentBlocks,\n });\n currentBlocks = [];\n }\n currentRole = null;\n };\n\n for (const message of ordered) {\n if (message.hidden) continue;\n\n // Skip system messages (already extracted)\n if (message.role === 'system' || message.role === 'developer') {\n continue;\n }\n\n // Skip snapshots\n if (message.role === 'snapshot') {\n continue;\n }\n\n let targetRole: 'user' | 'assistant';\n let blocks: AnthropicContentBlock[] = [];\n\n if (message.role === 'user') {\n targetRole = 'user';\n const content = toAnthropicContent(message.content);\n if (typeof content === 'string') {\n blocks = [{ type: 'text', text: content }];\n } else {\n blocks = content;\n }\n } else if (message.role === 'assistant') {\n targetRole = 'assistant';\n const content = toAnthropicContent(message.content);\n if (typeof content === 'string') {\n blocks = [{ type: 'text', text: content }];\n } else {\n blocks = content;\n }\n } else if (message.role === 'tool-use' && message.toolCall) {\n targetRole = 'assistant';\n blocks = [toToolUseBlock(message.toolCall)];\n } else if (message.role === 'tool-result' && message.toolResult) {\n targetRole = 'user';\n blocks = [toToolResultBlock(message.toolResult)];\n } else {\n continue;\n }\n\n // Merge with current or start new\n if (currentRole === targetRole) {\n currentBlocks.push(...blocks);\n } else {\n flushCurrent();\n currentRole = targetRole;\n currentBlocks = blocks;\n }\n }\n\n flushCurrent();\n\n const result: AnthropicConversation = { messages };\n if (system !== undefined) {\n result.system = system;\n }\n return result;\n}\n"
6
7
  ],
7
- "mappings": "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",
8
- "debugId": "46DDFFB8D921BA7364756E2164756E21",
8
+ "mappings": "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",
9
+ "debugId": "77199835081EDC2864756E2164756E21",
9
10
  "names": []
10
11
  }
@@ -19,7 +19,7 @@ export interface GeminiInlineDataPart {
19
19
  */
20
20
  export interface GeminiFileDataPart {
21
21
  fileData: {
22
- mimeType?: string;
22
+ mimeType: string;
23
23
  fileUri: string;
24
24
  };
25
25
  }
@@ -38,7 +38,7 @@ export interface GeminiFunctionCallPart {
38
38
  export interface GeminiFunctionResponsePart {
39
39
  functionResponse: {
40
40
  name: string;
41
- response: unknown;
41
+ response: Record<string, unknown>;
42
42
  };
43
43
  }
44
44
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/adapters/gemini/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAiC,MAAM,aAAa,CAAC;AAE/E;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE;QACV,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE;QACR,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,YAAY,EAAE;QACZ,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KAC/B,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACzC,gBAAgB,EAAE;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,OAAO,CAAC;KACnB,CAAC;CACH;AAED;;GAEG;AACH,MAAM,MAAM,UAAU,GAClB,cAAc,GACd,oBAAoB,GACpB,kBAAkB,GAClB,sBAAsB,GACtB,0BAA0B,CAAC;AAE/B;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IACvB,KAAK,EAAE,UAAU,EAAE,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,iBAAiB,CAAC,EAAE,aAAa,CAAC;IAClC,QAAQ,EAAE,aAAa,EAAE,CAAC;CAC3B;AA+GD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,YAAY,GAAG,kBAAkB,CAkF/E"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/adapters/gemini/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAiC,MAAM,aAAa,CAAC;AAG/E;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE;QACV,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE;QACR,QAAQ,EAAE,MAAM,CAAC;QACjB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,YAAY,EAAE;QACZ,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KAC/B,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACzC,gBAAgB,EAAE;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACnC,CAAC;CACH;AAED;;GAEG;AACH,MAAM,MAAM,UAAU,GAClB,cAAc,GACd,oBAAoB,GACpB,kBAAkB,GAClB,sBAAsB,GACtB,0BAA0B,CAAC;AAE/B;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IACvB,KAAK,EAAE,UAAU,EAAE,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,iBAAiB,CAAC,EAAE,aAAa,CAAC;IAClC,QAAQ,EAAE,aAAa,EAAE,CAAC;CAC3B;AAkJD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,YAAY,GAAG,kBAAkB,CAmF/E"}