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.
- package/LICENSE +21 -0
- package/README.md +388 -0
- package/README.zh-CN.md +388 -0
- package/bin/cli.ts +275 -0
- package/dist/index.cjs +522 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +611 -0
- package/dist/index.d.ts +611 -0
- package/dist/index.js +476 -0
- package/dist/index.js.map +1 -0
- package/package.json +58 -0
- package/src/boundary.ts +85 -0
- package/src/compiler.test.ts +625 -0
- package/src/compiler.ts +315 -0
- package/src/index.ts +98 -0
- package/src/providers.ts +160 -0
- package/src/section.ts +103 -0
- package/src/tokens.ts +170 -0
- package/src/tool.ts +128 -0
- package/src/types.ts +214 -0
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,611 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* promptloom — Core type definitions
|
|
3
|
+
*
|
|
4
|
+
* The type system for a prompt compiler:
|
|
5
|
+
* - Sections with conditional inclusion
|
|
6
|
+
* - Multi-zone cache scoping
|
|
7
|
+
* - Tools with deferred loading
|
|
8
|
+
* - Token budgeting
|
|
9
|
+
* - Multi-provider output
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Context passed to compile() for conditional section evaluation.
|
|
13
|
+
*
|
|
14
|
+
* In Claude Code, sections are gated on feature flags, model capabilities,
|
|
15
|
+
* and user type. CompileContext is the generic equivalent.
|
|
16
|
+
*/
|
|
17
|
+
interface CompileContext {
|
|
18
|
+
/** Current model name (e.g., 'claude-opus-4-6') */
|
|
19
|
+
model?: string;
|
|
20
|
+
/** API provider (e.g., 'anthropic', 'bedrock', 'vertex', 'openai') */
|
|
21
|
+
provider?: string;
|
|
22
|
+
/** Allow arbitrary user-defined context */
|
|
23
|
+
[key: string]: unknown;
|
|
24
|
+
}
|
|
25
|
+
/** A compute function that returns a section's content, or null to skip */
|
|
26
|
+
type ComputeFn = () => string | null | Promise<string | null>;
|
|
27
|
+
/** Predicate for conditional section inclusion */
|
|
28
|
+
type WhenPredicate = (ctx: CompileContext) => boolean;
|
|
29
|
+
/** Cache scope for prompt blocks, matching Anthropic API semantics */
|
|
30
|
+
type CacheScope = 'global' | 'org' | null;
|
|
31
|
+
/**
|
|
32
|
+
* Options for section creation.
|
|
33
|
+
*/
|
|
34
|
+
interface SectionOptions {
|
|
35
|
+
/**
|
|
36
|
+
* Conditional predicate. Section is only included when this returns true.
|
|
37
|
+
*
|
|
38
|
+
* In Claude Code, this maps to `feature('FLAG')` and `process.env.USER_TYPE` checks
|
|
39
|
+
* that gate sections like TOKEN_BUDGET, KAIROS, VERIFICATION_AGENT.
|
|
40
|
+
*/
|
|
41
|
+
when?: WhenPredicate;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* A prompt section — the atomic unit of prompt assembly.
|
|
45
|
+
*
|
|
46
|
+
* Sections can be:
|
|
47
|
+
* - Static: content is computed once and cached for the session
|
|
48
|
+
* - Dynamic (cacheBreak: true): recomputed every time compile() is called
|
|
49
|
+
* - Conditional: only included when `when(context)` returns true
|
|
50
|
+
*/
|
|
51
|
+
interface Section {
|
|
52
|
+
/** Unique identifier for this section */
|
|
53
|
+
name: string;
|
|
54
|
+
/** Function that computes the section content */
|
|
55
|
+
compute: ComputeFn;
|
|
56
|
+
/** If true, this section is recomputed every compile() call */
|
|
57
|
+
cacheBreak: boolean;
|
|
58
|
+
/** If provided, section is only included when predicate returns true */
|
|
59
|
+
when?: WhenPredicate;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* A zone marker in the entry list.
|
|
63
|
+
*
|
|
64
|
+
* Zones create cache block boundaries. All sections between two zone markers
|
|
65
|
+
* are compiled into a single CacheBlock with the zone's scope.
|
|
66
|
+
*
|
|
67
|
+
* In Claude Code, this is implemented via `SYSTEM_PROMPT_DYNAMIC_BOUNDARY`
|
|
68
|
+
* (a single boundary producing 2 zones). promptloom generalizes to N zones.
|
|
69
|
+
*/
|
|
70
|
+
interface ZoneMarker {
|
|
71
|
+
readonly __type: 'zone';
|
|
72
|
+
scope: CacheScope;
|
|
73
|
+
}
|
|
74
|
+
/** An entry in the compiler's internal list */
|
|
75
|
+
type Entry = Section | ZoneMarker;
|
|
76
|
+
/**
|
|
77
|
+
* A compiled prompt block with cache scope annotation.
|
|
78
|
+
*
|
|
79
|
+
* When sent to the Anthropic API, blocks with a non-null cacheScope
|
|
80
|
+
* get a `cache_control` field, enabling prompt caching.
|
|
81
|
+
*/
|
|
82
|
+
interface CacheBlock {
|
|
83
|
+
/** The text content of this block */
|
|
84
|
+
text: string;
|
|
85
|
+
/** Cache scope: 'global' (cross-org), 'org' (org-level), or null (no cache) */
|
|
86
|
+
cacheScope: CacheScope;
|
|
87
|
+
}
|
|
88
|
+
/** JSON Schema for tool input parameters */
|
|
89
|
+
type JsonSchema = Record<string, unknown>;
|
|
90
|
+
/**
|
|
91
|
+
* A tool definition with an embedded prompt.
|
|
92
|
+
*
|
|
93
|
+
* In Claude Code, each tool carries its own "user manual" for the LLM.
|
|
94
|
+
* The prompt is injected into the tool's description field in the API request.
|
|
95
|
+
*/
|
|
96
|
+
interface ToolDef {
|
|
97
|
+
/** Tool name (e.g., 'bash', 'read_file') */
|
|
98
|
+
name: string;
|
|
99
|
+
/**
|
|
100
|
+
* Tool prompt — the LLM-facing description.
|
|
101
|
+
* Can be a static string or an async function for dynamic prompts.
|
|
102
|
+
*/
|
|
103
|
+
prompt: string | (() => string | Promise<string>);
|
|
104
|
+
/** JSON Schema defining the tool's input parameters */
|
|
105
|
+
inputSchema: JsonSchema;
|
|
106
|
+
/** If true, safe to call concurrently with other tools */
|
|
107
|
+
concurrencySafe?: boolean;
|
|
108
|
+
/** If true, this tool only reads and never writes */
|
|
109
|
+
readOnly?: boolean;
|
|
110
|
+
/**
|
|
111
|
+
* If true, the tool schema is deferred (not loaded until model requests it).
|
|
112
|
+
*
|
|
113
|
+
* In Claude Code, deferred tools get `defer_loading: true` in their schema
|
|
114
|
+
* and are discovered via ToolSearchTool on demand. This reduces system prompt
|
|
115
|
+
* token cost when you have many tools.
|
|
116
|
+
*/
|
|
117
|
+
deferred?: boolean;
|
|
118
|
+
/**
|
|
119
|
+
* Explicit ordering for cache stability.
|
|
120
|
+
* Lower numbers come first. Tools without order use insertion order.
|
|
121
|
+
*
|
|
122
|
+
* In Claude Code, tool order is kept stable to maximize prompt cache hits —
|
|
123
|
+
* reordering tools changes the serialized bytes, breaking the cache.
|
|
124
|
+
*/
|
|
125
|
+
order?: number;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* A compiled tool schema ready for the API.
|
|
129
|
+
*/
|
|
130
|
+
interface CompiledTool {
|
|
131
|
+
name: string;
|
|
132
|
+
description: string;
|
|
133
|
+
input_schema: JsonSchema;
|
|
134
|
+
cache_control?: {
|
|
135
|
+
type: 'ephemeral';
|
|
136
|
+
scope?: CacheScope;
|
|
137
|
+
};
|
|
138
|
+
/** When true, the model must explicitly request this tool via tool search */
|
|
139
|
+
defer_loading?: true;
|
|
140
|
+
}
|
|
141
|
+
interface TokenEstimate {
|
|
142
|
+
/** Total estimated tokens for system prompt */
|
|
143
|
+
systemPrompt: number;
|
|
144
|
+
/** Total estimated tokens for inline (non-deferred) tool schemas */
|
|
145
|
+
tools: number;
|
|
146
|
+
/** Total estimated tokens for deferred tool schemas */
|
|
147
|
+
deferredTools: number;
|
|
148
|
+
/** Combined total (systemPrompt + tools, deferred excluded) */
|
|
149
|
+
total: number;
|
|
150
|
+
}
|
|
151
|
+
interface TokenBudgetConfig {
|
|
152
|
+
/** Total token budget for the conversation turn */
|
|
153
|
+
budget: number;
|
|
154
|
+
/** Stop at this fraction of budget (default: 0.9) */
|
|
155
|
+
completionThreshold?: number;
|
|
156
|
+
/** Minimum delta to avoid diminishing returns detection (default: 500) */
|
|
157
|
+
diminishingThreshold?: number;
|
|
158
|
+
}
|
|
159
|
+
interface CompilerOptions {
|
|
160
|
+
/** Default cache scope for the initial zone (default: 'org') */
|
|
161
|
+
defaultCacheScope?: CacheScope;
|
|
162
|
+
/** Cache scope used by boundary() for the dynamic zone (default: null) */
|
|
163
|
+
dynamicCacheScope?: CacheScope;
|
|
164
|
+
/** Bytes per token for rough estimation (default: 4) */
|
|
165
|
+
bytesPerToken?: number;
|
|
166
|
+
/**
|
|
167
|
+
* When true, the initial zone scope is upgraded to 'global'.
|
|
168
|
+
* Only effective for 1P Anthropic API usage.
|
|
169
|
+
*/
|
|
170
|
+
enableGlobalCache?: boolean;
|
|
171
|
+
}
|
|
172
|
+
interface CompileResult {
|
|
173
|
+
/** Prompt blocks with cache annotations, one per zone */
|
|
174
|
+
blocks: CacheBlock[];
|
|
175
|
+
/** Inline tool schemas (non-deferred) with resolved prompts */
|
|
176
|
+
tools: CompiledTool[];
|
|
177
|
+
/** Deferred tool schemas (loaded on demand) */
|
|
178
|
+
deferredTools: CompiledTool[];
|
|
179
|
+
/** Token estimates */
|
|
180
|
+
tokens: TokenEstimate;
|
|
181
|
+
/** The full system prompt as a single string (blocks joined) */
|
|
182
|
+
text: string;
|
|
183
|
+
}
|
|
184
|
+
type ProviderFormat = 'anthropic' | 'openai' | 'bedrock';
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* promptloom — The Prompt Compiler
|
|
188
|
+
*
|
|
189
|
+
* Implements Claude Code's prompt assembly pattern, generalized:
|
|
190
|
+
*
|
|
191
|
+
* 1. Multi-zone cache scoping (N blocks, not just 2)
|
|
192
|
+
* 2. Conditional section inclusion (when predicates)
|
|
193
|
+
* 3. Static/dynamic section caching
|
|
194
|
+
* 4. Tool schemas with embedded prompts and deferred loading
|
|
195
|
+
* 5. Stable tool ordering for cache hits
|
|
196
|
+
* 6. Token estimation and budget tracking
|
|
197
|
+
*
|
|
198
|
+
* Usage:
|
|
199
|
+
*
|
|
200
|
+
* const pc = new PromptCompiler()
|
|
201
|
+
*
|
|
202
|
+
* // Zone 1: no-cache header
|
|
203
|
+
* pc.zone(null)
|
|
204
|
+
* pc.static('attribution', 'x-model: claude')
|
|
205
|
+
*
|
|
206
|
+
* // Zone 2: globally cacheable
|
|
207
|
+
* pc.zone('global')
|
|
208
|
+
* pc.static('identity', 'You are a coding assistant.')
|
|
209
|
+
* pc.static('rules', () => loadRules())
|
|
210
|
+
*
|
|
211
|
+
* // Zone 3: session-specific
|
|
212
|
+
* pc.zone(null)
|
|
213
|
+
* pc.dynamic('git', async () => gitStatus())
|
|
214
|
+
* pc.static('opus_only', 'Use extended thinking.', {
|
|
215
|
+
* when: (ctx) => ctx.model?.includes('opus'),
|
|
216
|
+
* })
|
|
217
|
+
*
|
|
218
|
+
* // Tools
|
|
219
|
+
* pc.tool({ name: 'bash', prompt: '...', inputSchema: {...} })
|
|
220
|
+
* pc.tool({ name: 'rare_tool', prompt: '...', inputSchema: {...}, deferred: true })
|
|
221
|
+
*
|
|
222
|
+
* const result = await pc.compile({ model: 'claude-opus-4-6' })
|
|
223
|
+
*/
|
|
224
|
+
|
|
225
|
+
declare class PromptCompiler {
|
|
226
|
+
private entries;
|
|
227
|
+
private tools;
|
|
228
|
+
private sectionCache;
|
|
229
|
+
private toolCache;
|
|
230
|
+
private options;
|
|
231
|
+
constructor(options?: CompilerOptions);
|
|
232
|
+
/**
|
|
233
|
+
* Start a new cache zone. All sections after this marker are compiled
|
|
234
|
+
* into a single CacheBlock with the specified scope.
|
|
235
|
+
*
|
|
236
|
+
* Generalizes Claude Code's `SYSTEM_PROMPT_DYNAMIC_BOUNDARY` to support
|
|
237
|
+
* N zones instead of just 2.
|
|
238
|
+
*
|
|
239
|
+
* @example
|
|
240
|
+
* pc.zone(null) // no-cache zone (attribution, headers)
|
|
241
|
+
* pc.zone('global') // globally cacheable (identity, rules)
|
|
242
|
+
* pc.zone('org') // org-level cacheable
|
|
243
|
+
* pc.zone(null) // session-specific (dynamic context)
|
|
244
|
+
*/
|
|
245
|
+
zone(scope: CacheScope): this;
|
|
246
|
+
/**
|
|
247
|
+
* Insert a cache boundary. Shorthand for starting a new zone with
|
|
248
|
+
* `dynamicCacheScope` (default: null).
|
|
249
|
+
*
|
|
250
|
+
* Equivalent to Claude Code's `SYSTEM_PROMPT_DYNAMIC_BOUNDARY`.
|
|
251
|
+
* Only effective when `enableGlobalCache` is true (otherwise a no-op
|
|
252
|
+
* since there's no scope difference to split on).
|
|
253
|
+
*
|
|
254
|
+
* For explicit multi-zone control, use `zone()` instead.
|
|
255
|
+
*/
|
|
256
|
+
boundary(): this;
|
|
257
|
+
/**
|
|
258
|
+
* Add a static section. Computed once and cached for the session.
|
|
259
|
+
*
|
|
260
|
+
* Equivalent to Claude Code's `systemPromptSection()`.
|
|
261
|
+
*
|
|
262
|
+
* @param options.when - Conditional predicate. Section is skipped when false.
|
|
263
|
+
*/
|
|
264
|
+
static(name: string, content: string | ComputeFn, options?: SectionOptions): this;
|
|
265
|
+
/**
|
|
266
|
+
* Add a dynamic section. Recomputed every compile() call.
|
|
267
|
+
*
|
|
268
|
+
* Equivalent to Claude Code's `DANGEROUS_uncachedSystemPromptSection()`.
|
|
269
|
+
*
|
|
270
|
+
* @param options.when - Conditional predicate. Section is skipped when false.
|
|
271
|
+
*/
|
|
272
|
+
dynamic(name: string, compute: ComputeFn, options?: SectionOptions): this;
|
|
273
|
+
/**
|
|
274
|
+
* Register a tool with an embedded prompt.
|
|
275
|
+
*
|
|
276
|
+
* Tools with `deferred: true` are compiled separately and excluded
|
|
277
|
+
* from the main tool list. They can be discovered on demand.
|
|
278
|
+
*
|
|
279
|
+
* Tools with `order` fields are sorted for stable serialization
|
|
280
|
+
* (cache hit optimization).
|
|
281
|
+
*/
|
|
282
|
+
tool(def: ToolDef): this;
|
|
283
|
+
/**
|
|
284
|
+
* Compile the prompt: resolve sections per zone, compile tools,
|
|
285
|
+
* separate deferred tools, and estimate tokens.
|
|
286
|
+
*
|
|
287
|
+
* @param context - Optional context for conditional section evaluation.
|
|
288
|
+
* Sections with `when` predicates are evaluated against this.
|
|
289
|
+
*/
|
|
290
|
+
compile(context?: CompileContext): Promise<CompileResult>;
|
|
291
|
+
/** Clear all caches. Call on `/clear` or `/compact`. */
|
|
292
|
+
clearCache(): void;
|
|
293
|
+
/** Clear only section cache (tools stay cached) */
|
|
294
|
+
clearSectionCache(): void;
|
|
295
|
+
/** Clear only tool cache (forces prompt re-resolution) */
|
|
296
|
+
clearToolCache(): void;
|
|
297
|
+
/** Get the number of registered sections */
|
|
298
|
+
get sectionCount(): number;
|
|
299
|
+
/** Get the number of registered tools (inline + deferred) */
|
|
300
|
+
get toolCount(): number;
|
|
301
|
+
/** List registered section names with their types */
|
|
302
|
+
listSections(): Array<{
|
|
303
|
+
name: string;
|
|
304
|
+
type: 'static' | 'dynamic' | 'zone';
|
|
305
|
+
}>;
|
|
306
|
+
/** List registered tool names */
|
|
307
|
+
listTools(): string[];
|
|
308
|
+
/**
|
|
309
|
+
* Group entries into zones.
|
|
310
|
+
*
|
|
311
|
+
* Entries before any zone marker belong to the "initial zone" whose
|
|
312
|
+
* scope is determined by `enableGlobalCache` and `defaultCacheScope`.
|
|
313
|
+
*/
|
|
314
|
+
private groupIntoZones;
|
|
315
|
+
private estimateToolTokens;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* promptloom — Section management
|
|
320
|
+
*
|
|
321
|
+
* Implements the two-tier section system from Claude Code:
|
|
322
|
+
* - `section()`: cached within session, computed once
|
|
323
|
+
* - `dynamicSection()`: recomputed every compile() call (cacheBreak: true)
|
|
324
|
+
*
|
|
325
|
+
* Extended with conditional inclusion via `when` predicates,
|
|
326
|
+
* mirroring Claude Code's `feature()` and `process.env.USER_TYPE` gates.
|
|
327
|
+
*/
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Create a static section. Content is computed once and cached.
|
|
331
|
+
*
|
|
332
|
+
* Use for: identity prompts, rules, style guides — anything that
|
|
333
|
+
* doesn't change between turns.
|
|
334
|
+
*/
|
|
335
|
+
declare function section(name: string, compute: ComputeFn, options?: SectionOptions): Section;
|
|
336
|
+
/**
|
|
337
|
+
* Create a dynamic section. Content is recomputed every compile() call.
|
|
338
|
+
*
|
|
339
|
+
* Claude Code calls this `DANGEROUS_uncachedSystemPromptSection` —
|
|
340
|
+
* the naming reflects that dynamic sections break prompt cache stability.
|
|
341
|
+
*
|
|
342
|
+
* Use for: MCP server instructions, real-time status, anything that
|
|
343
|
+
* changes between turns.
|
|
344
|
+
*/
|
|
345
|
+
declare function dynamicSection(name: string, compute: ComputeFn, options?: SectionOptions): Section;
|
|
346
|
+
/**
|
|
347
|
+
* In-memory section cache.
|
|
348
|
+
*
|
|
349
|
+
* Mirrors `STATE.systemPromptSectionCache` from Claude Code.
|
|
350
|
+
* Static sections are computed once per session and cached here.
|
|
351
|
+
* Dynamic sections (cacheBreak: true) bypass the cache entirely.
|
|
352
|
+
*/
|
|
353
|
+
declare class SectionCache {
|
|
354
|
+
private cache;
|
|
355
|
+
get(name: string): string | null | undefined;
|
|
356
|
+
has(name: string): boolean;
|
|
357
|
+
set(name: string, value: string | null): void;
|
|
358
|
+
clear(): void;
|
|
359
|
+
get size(): number;
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Resolve an array of sections, using cache for static ones.
|
|
363
|
+
*
|
|
364
|
+
* Mirrors `resolveSystemPromptSections()` from Claude Code:
|
|
365
|
+
* - Sections with a `when` predicate are filtered by compile context
|
|
366
|
+
* - Static sections: check cache first, compute if missing
|
|
367
|
+
* - Dynamic sections: always recompute
|
|
368
|
+
* - All sections resolved in parallel via Promise.all
|
|
369
|
+
*/
|
|
370
|
+
declare function resolveSections(sections: Section[], cache: SectionCache, context?: CompileContext): Promise<(string | null)[]>;
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* promptloom — Cache boundary splitting
|
|
374
|
+
*
|
|
375
|
+
* Implements Claude Code's cache boundary mechanism:
|
|
376
|
+
* - A sentinel string divides the prompt into static (cacheable) and dynamic zones
|
|
377
|
+
* - Content before the boundary gets `cacheScope: 'global'` (cross-org cache)
|
|
378
|
+
* - Content after gets `cacheScope: null` (session-specific)
|
|
379
|
+
*
|
|
380
|
+
* This directly maps to Claude API's `cache_control` field on text blocks.
|
|
381
|
+
*/
|
|
382
|
+
|
|
383
|
+
/** The sentinel string used to mark the cache boundary */
|
|
384
|
+
declare const CACHE_BOUNDARY = "__PROMPTLOOM_CACHE_BOUNDARY__";
|
|
385
|
+
interface SplitOptions {
|
|
386
|
+
/** Cache scope for content before the boundary (default: 'global') */
|
|
387
|
+
staticScope?: CacheScope;
|
|
388
|
+
/** Cache scope for content after the boundary (default: null) */
|
|
389
|
+
dynamicScope?: CacheScope;
|
|
390
|
+
/** Fallback scope when no boundary is found (default: 'org') */
|
|
391
|
+
fallbackScope?: CacheScope;
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* Split a prompt string at the cache boundary into annotated blocks.
|
|
395
|
+
*
|
|
396
|
+
* Mirrors Claude Code's `splitSysPromptPrefix()`:
|
|
397
|
+
*
|
|
398
|
+
* - If boundary found:
|
|
399
|
+
* [static content → staticScope] + [dynamic content → dynamicScope]
|
|
400
|
+
*
|
|
401
|
+
* - If no boundary:
|
|
402
|
+
* [entire content → fallbackScope]
|
|
403
|
+
*/
|
|
404
|
+
declare function splitAtBoundary(prompt: string, options?: SplitOptions): CacheBlock[];
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* promptloom — Multi-provider output formatting
|
|
408
|
+
*
|
|
409
|
+
* Claude Code supports multiple API providers:
|
|
410
|
+
* - Anthropic (1P): cache_control with scope on text blocks
|
|
411
|
+
* - AWS Bedrock: cache_control without scope
|
|
412
|
+
* - Google Vertex: cache_control without scope
|
|
413
|
+
* - OpenAI: single string system prompt, no caching API
|
|
414
|
+
*
|
|
415
|
+
* This module formats CompileResult for each provider.
|
|
416
|
+
*/
|
|
417
|
+
|
|
418
|
+
interface AnthropicCacheControl {
|
|
419
|
+
type: 'ephemeral';
|
|
420
|
+
ttl?: '5m' | '1h';
|
|
421
|
+
}
|
|
422
|
+
interface AnthropicTextBlock {
|
|
423
|
+
type: 'text';
|
|
424
|
+
text: string;
|
|
425
|
+
cache_control?: AnthropicCacheControl;
|
|
426
|
+
}
|
|
427
|
+
interface AnthropicTool {
|
|
428
|
+
name: string;
|
|
429
|
+
description: string;
|
|
430
|
+
input_schema: Record<string, unknown>;
|
|
431
|
+
cache_control?: AnthropicCacheControl;
|
|
432
|
+
defer_loading?: true;
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* Format compile result for Anthropic Messages API.
|
|
436
|
+
*
|
|
437
|
+
* Mirrors Claude Code's `buildSystemPromptBlocks()`.
|
|
438
|
+
* Blocks with a non-null cacheScope get `cache_control: { type: 'ephemeral' }`.
|
|
439
|
+
*/
|
|
440
|
+
declare function toAnthropic(result: CompileResult): {
|
|
441
|
+
system: AnthropicTextBlock[];
|
|
442
|
+
tools: AnthropicTool[];
|
|
443
|
+
};
|
|
444
|
+
interface OpenAITool {
|
|
445
|
+
type: 'function';
|
|
446
|
+
function: {
|
|
447
|
+
name: string;
|
|
448
|
+
description: string;
|
|
449
|
+
parameters: Record<string, unknown>;
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
/**
|
|
453
|
+
* Format compile result for OpenAI Chat Completions API.
|
|
454
|
+
*
|
|
455
|
+
* OpenAI uses a single string for system prompt (no block-level caching).
|
|
456
|
+
* Tools are wrapped in the `{ type: 'function', function: {...} }` format.
|
|
457
|
+
*/
|
|
458
|
+
declare function toOpenAI(result: CompileResult): {
|
|
459
|
+
system: string;
|
|
460
|
+
tools: OpenAITool[];
|
|
461
|
+
};
|
|
462
|
+
type BedrockSystemBlock = {
|
|
463
|
+
text: string;
|
|
464
|
+
} | {
|
|
465
|
+
cachePoint: {
|
|
466
|
+
type: 'default';
|
|
467
|
+
};
|
|
468
|
+
};
|
|
469
|
+
interface BedrockTool {
|
|
470
|
+
toolSpec: {
|
|
471
|
+
name: string;
|
|
472
|
+
description: string;
|
|
473
|
+
inputSchema: {
|
|
474
|
+
jsonSchema: Record<string, unknown>;
|
|
475
|
+
};
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
/**
|
|
479
|
+
* Format compile result for AWS Bedrock Converse API.
|
|
480
|
+
*
|
|
481
|
+
* Bedrock uses `cachePoint: { type: 'default' }` instead of Anthropic's
|
|
482
|
+
* `cache_control: { type: 'ephemeral' }`. Tools use `toolSpec` wrapper.
|
|
483
|
+
*/
|
|
484
|
+
declare function toBedrock(result: CompileResult): {
|
|
485
|
+
system: BedrockSystemBlock[];
|
|
486
|
+
toolConfig: {
|
|
487
|
+
tools: BedrockTool[];
|
|
488
|
+
};
|
|
489
|
+
};
|
|
490
|
+
/**
|
|
491
|
+
* Convert cache blocks to Anthropic API text block format.
|
|
492
|
+
*
|
|
493
|
+
* @deprecated Use `toAnthropic(result).system` instead for full formatting.
|
|
494
|
+
* Kept for backward compatibility.
|
|
495
|
+
*/
|
|
496
|
+
declare function toAnthropicBlocks(blocks: CacheBlock[], enableCaching?: boolean): AnthropicTextBlock[];
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* promptloom — Tool prompt management
|
|
500
|
+
*
|
|
501
|
+
* In Claude Code, every tool has its own `prompt.ts` — a "user manual"
|
|
502
|
+
* written for the LLM. Tool prompts are:
|
|
503
|
+
* - Computed once per session and cached (avoids mid-session drift)
|
|
504
|
+
* - Injected into the tool's `description` field in the API request
|
|
505
|
+
* - Can be static strings or async functions
|
|
506
|
+
* - Optionally deferred (loaded on demand via ToolSearchTool)
|
|
507
|
+
*
|
|
508
|
+
* This module provides the tool registry and compilation logic.
|
|
509
|
+
*/
|
|
510
|
+
|
|
511
|
+
/**
|
|
512
|
+
* Cache for resolved tool prompts.
|
|
513
|
+
*
|
|
514
|
+
* Mirrors Claude Code's `getToolSchemaCache()`:
|
|
515
|
+
* "Prevents mid-session GrowthBook flips from churning serialized tools array.
|
|
516
|
+
* One generation per session unless cache is cleared."
|
|
517
|
+
*/
|
|
518
|
+
declare class ToolCache {
|
|
519
|
+
private cache;
|
|
520
|
+
get(key: string): CompiledTool | undefined;
|
|
521
|
+
set(key: string, tool: CompiledTool): void;
|
|
522
|
+
has(key: string): boolean;
|
|
523
|
+
clear(): void;
|
|
524
|
+
}
|
|
525
|
+
/**
|
|
526
|
+
* Compile a single tool definition into API-ready format.
|
|
527
|
+
*
|
|
528
|
+
* Uses session-level cache: first call computes, subsequent calls return cached.
|
|
529
|
+
* Deferred tools get `defer_loading: true` in their schema.
|
|
530
|
+
*/
|
|
531
|
+
declare function compileTool(def: ToolDef, cache: ToolCache, cacheScope?: CacheScope): Promise<CompiledTool>;
|
|
532
|
+
/**
|
|
533
|
+
* Compile all tool definitions, resolving prompts in parallel.
|
|
534
|
+
*
|
|
535
|
+
* Tools are sorted by explicit `order` field first, then by insertion order.
|
|
536
|
+
* This ensures stable serialization for prompt cache hits.
|
|
537
|
+
*/
|
|
538
|
+
declare function compileTools(defs: ToolDef[], cache: ToolCache, cacheScope?: CacheScope): Promise<CompiledTool[]>;
|
|
539
|
+
/**
|
|
540
|
+
* Create a tool definition with safe defaults.
|
|
541
|
+
*/
|
|
542
|
+
declare function defineTool(def: ToolDef): ToolDef;
|
|
543
|
+
|
|
544
|
+
/**
|
|
545
|
+
* promptloom — Token estimation and budget utilities
|
|
546
|
+
*
|
|
547
|
+
* Mirrors Claude Code's token counting strategy:
|
|
548
|
+
* - Rough estimation based on byte length (fast, no API call)
|
|
549
|
+
* - File-type-aware estimation (JSON is denser → 2 bytes/token)
|
|
550
|
+
* - Budget tracking with diminishing returns detection
|
|
551
|
+
* - Natural language budget parsing (+500k, spend 2M tokens)
|
|
552
|
+
*/
|
|
553
|
+
|
|
554
|
+
/**
|
|
555
|
+
* Estimate token count from a string using byte-length heuristic.
|
|
556
|
+
*
|
|
557
|
+
* Claude Code uses 4 bytes/token as default, 2 for dense formats like JSON.
|
|
558
|
+
* This is intentionally approximate — accurate counting requires an API call.
|
|
559
|
+
*/
|
|
560
|
+
declare function estimateTokens(content: string, bytesPerToken?: number): number;
|
|
561
|
+
/**
|
|
562
|
+
* Estimate tokens with file-type awareness.
|
|
563
|
+
* JSON/XML are denser (more tokens per byte) than natural language.
|
|
564
|
+
*/
|
|
565
|
+
declare function estimateTokensForFileType(content: string, extension: string): number;
|
|
566
|
+
interface BudgetTracker {
|
|
567
|
+
continuationCount: number;
|
|
568
|
+
lastDeltaTokens: number;
|
|
569
|
+
lastGlobalTurnTokens: number;
|
|
570
|
+
startedAt: number;
|
|
571
|
+
}
|
|
572
|
+
type BudgetDecision = {
|
|
573
|
+
action: 'continue';
|
|
574
|
+
nudgeMessage: string;
|
|
575
|
+
pct: number;
|
|
576
|
+
} | {
|
|
577
|
+
action: 'stop';
|
|
578
|
+
reason: 'budget_reached' | 'diminishing_returns' | 'no_budget';
|
|
579
|
+
pct: number;
|
|
580
|
+
};
|
|
581
|
+
declare function createBudgetTracker(): BudgetTracker;
|
|
582
|
+
/**
|
|
583
|
+
* Check token budget and decide whether to continue.
|
|
584
|
+
*
|
|
585
|
+
* Mirrors Claude Code's `checkTokenBudget()`:
|
|
586
|
+
* - Continue if below completion threshold (default 90%)
|
|
587
|
+
* - Detect diminishing returns (3+ continuations with tiny deltas)
|
|
588
|
+
* - Return nudge messages to keep the model working
|
|
589
|
+
*/
|
|
590
|
+
declare function checkBudget(tracker: BudgetTracker, currentTokens: number, config: TokenBudgetConfig): BudgetDecision;
|
|
591
|
+
/**
|
|
592
|
+
* Parse a token budget from natural language.
|
|
593
|
+
*
|
|
594
|
+
* Mirrors Claude Code's `parseTokenBudget()` from `src/utils/tokenBudget.ts`.
|
|
595
|
+
*
|
|
596
|
+
* Supported syntaxes:
|
|
597
|
+
* - Shorthand at start: "+500k", "+2M", "+1.5b"
|
|
598
|
+
* - Shorthand at end: "do this task. +500k."
|
|
599
|
+
* - Verbose: "use 500k tokens", "spend 2M tokens"
|
|
600
|
+
*
|
|
601
|
+
* @returns The parsed budget in tokens, or null if no budget found.
|
|
602
|
+
*
|
|
603
|
+
* @example
|
|
604
|
+
* parseTokenBudget('+500k') // 500_000
|
|
605
|
+
* parseTokenBudget('spend 2M tokens') // 2_000_000
|
|
606
|
+
* parseTokenBudget('+1.5b') // 1_500_000_000
|
|
607
|
+
* parseTokenBudget('hello world') // null
|
|
608
|
+
*/
|
|
609
|
+
declare function parseTokenBudget(text: string): number | null;
|
|
610
|
+
|
|
611
|
+
export { type AnthropicCacheControl, type AnthropicTextBlock, type AnthropicTool, type BedrockSystemBlock, type BedrockTool, type BudgetDecision, type BudgetTracker, CACHE_BOUNDARY, type CacheBlock, type CacheScope, type CompileContext, type CompileResult, type CompiledTool, type CompilerOptions, type ComputeFn, type Entry, type JsonSchema, type OpenAITool, PromptCompiler, type ProviderFormat, type Section, SectionCache, type SectionOptions, type SplitOptions, type TokenBudgetConfig, type TokenEstimate, ToolCache, type ToolDef, type WhenPredicate, type ZoneMarker, checkBudget, compileTool, compileTools, createBudgetTracker, defineTool, dynamicSection, estimateTokens, estimateTokensForFileType, parseTokenBudget, resolveSections, section, splitAtBoundary, toAnthropic, toAnthropicBlocks, toBedrock, toOpenAI };
|