promptloom 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,85 @@
1
+ /**
2
+ * promptloom — Cache boundary splitting
3
+ *
4
+ * Implements Claude Code's cache boundary mechanism:
5
+ * - A sentinel string divides the prompt into static (cacheable) and dynamic zones
6
+ * - Content before the boundary gets `cacheScope: 'global'` (cross-org cache)
7
+ * - Content after gets `cacheScope: null` (session-specific)
8
+ *
9
+ * This directly maps to Claude API's `cache_control` field on text blocks.
10
+ */
11
+
12
+ import type { CacheBlock, CacheScope } from './types.ts'
13
+
14
+ /** The sentinel string used to mark the cache boundary */
15
+ export const CACHE_BOUNDARY = '__PROMPTLOOM_CACHE_BOUNDARY__'
16
+
17
+ export interface SplitOptions {
18
+ /** Cache scope for content before the boundary (default: 'global') */
19
+ staticScope?: CacheScope
20
+ /** Cache scope for content after the boundary (default: null) */
21
+ dynamicScope?: CacheScope
22
+ /** Fallback scope when no boundary is found (default: 'org') */
23
+ fallbackScope?: CacheScope
24
+ }
25
+
26
+ /**
27
+ * Split a prompt string at the cache boundary into annotated blocks.
28
+ *
29
+ * Mirrors Claude Code's `splitSysPromptPrefix()`:
30
+ *
31
+ * - If boundary found:
32
+ * [static content → staticScope] + [dynamic content → dynamicScope]
33
+ *
34
+ * - If no boundary:
35
+ * [entire content → fallbackScope]
36
+ */
37
+ export function splitAtBoundary(
38
+ prompt: string,
39
+ options: SplitOptions = {},
40
+ ): CacheBlock[] {
41
+ const {
42
+ staticScope = 'global',
43
+ dynamicScope = null,
44
+ fallbackScope = 'org',
45
+ } = options
46
+
47
+ const boundaryIndex = prompt.indexOf(CACHE_BOUNDARY)
48
+
49
+ if (boundaryIndex === -1) {
50
+ // No boundary — single block with fallback scope
51
+ return prompt.trim()
52
+ ? [{ text: prompt, cacheScope: fallbackScope }]
53
+ : []
54
+ }
55
+
56
+ const before = prompt.slice(0, boundaryIndex).trim()
57
+ const after = prompt.slice(boundaryIndex + CACHE_BOUNDARY.length).trim()
58
+
59
+ const blocks: CacheBlock[] = []
60
+ if (before) {
61
+ blocks.push({ text: before, cacheScope: staticScope })
62
+ }
63
+ if (after) {
64
+ blocks.push({ text: after, cacheScope: dynamicScope })
65
+ }
66
+ return blocks
67
+ }
68
+
69
+ /**
70
+ * Convert cache blocks to Anthropic API text block format.
71
+ *
72
+ * Mirrors Claude Code's `buildSystemPromptBlocks()`.
73
+ */
74
+ export function toAnthropicBlocks(
75
+ blocks: CacheBlock[],
76
+ enableCaching = true,
77
+ ): Array<{ type: 'text'; text: string; cache_control?: { type: 'ephemeral' } }> {
78
+ return blocks.map((block) => ({
79
+ type: 'text' as const,
80
+ text: block.text,
81
+ ...(enableCaching && block.cacheScope !== null
82
+ ? { cache_control: { type: 'ephemeral' as const } }
83
+ : {}),
84
+ }))
85
+ }