@kuralle-agents/core 0.7.1 → 0.7.2

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/dist/index.d.ts CHANGED
@@ -55,6 +55,8 @@ export { resolveAgentWorkspace, type ResolvedAgentWorkspace, } from './runtime/r
55
55
  export { DEFAULT_CONTEXT_BUDGET, VOICE_CONTEXT_BUDGET, computeMessageHistoryBudget, truncateToTokenBudget, formatMemoryWithBudget, estimateTokenCount, ContextBudget, } from './runtime/ContextBudget.js';
56
56
  export type { ContextBudgetConfig } from './runtime/ContextBudget.js';
57
57
  export { TokenAccumulator } from './runtime/TokenAccumulator.js';
58
+ export { applyPromptCache, applyAnthropicCacheControl, buildOpenAIResponsesProviderOptions, isAnthropicLanguageModel, isOpenAIResponsesModel, } from './runtime/promptCache.js';
59
+ export type { AnthropicCacheTtl, OpenAIResponsesCompactOptions } from './runtime/promptCache.js';
58
60
  export { handoffFilters, removeToolHistory, keepRecentMessages, removeKeys, composeFilters, } from './runtime/handoffFilters.js';
59
61
  export type { HandoffInputFilter, HandoffInputData, HandoffInputResult, } from './runtime/handoffFilters.js';
60
62
  export { DefaultToolExecutor, DefaultConversationState, DefaultConversationEventLog, DefaultAgentStateController, createFoundation, } from './foundation/index.js';
package/dist/index.js CHANGED
@@ -34,6 +34,7 @@ export { wireWorkingMemory, loadWorkingMemoryBlocks, formatWorkingMemorySection,
34
34
  export { resolveAgentWorkspace, } from './runtime/resolveAgentWorkspace.js';
35
35
  export { DEFAULT_CONTEXT_BUDGET, VOICE_CONTEXT_BUDGET, computeMessageHistoryBudget, truncateToTokenBudget, formatMemoryWithBudget, estimateTokenCount, ContextBudget, } from './runtime/ContextBudget.js';
36
36
  export { TokenAccumulator } from './runtime/TokenAccumulator.js';
37
+ export { applyPromptCache, applyAnthropicCacheControl, buildOpenAIResponsesProviderOptions, isAnthropicLanguageModel, isOpenAIResponsesModel, } from './runtime/promptCache.js';
37
38
  export { handoffFilters, removeToolHistory, keepRecentMessages, removeKeys, composeFilters, } from './runtime/handoffFilters.js';
38
39
  export { DefaultToolExecutor, DefaultConversationState, DefaultConversationEventLog, DefaultAgentStateController, createFoundation, } from './foundation/index.js';
39
40
  export { CapabilityHost, ExtractionCapability, GuardrailCapability, AutoRetrieveCapability, PassThroughRefinement, PassThroughValidation, toGeminiDeclarations, toAISDKTools, } from './capabilities/index.js';
@@ -9,6 +9,7 @@ import { resolveMaxSteps } from '../policies/limits.js';
9
9
  import { speakWithHostControl } from './streaming/hostControlSpeak.js';
10
10
  import { resolveStreamMode } from './streaming/mode.js';
11
11
  import { appendGatherBlocks, resolveNodeGatherScope, runGatherPhase } from '../grounding/index.js';
12
+ import { applyPromptCache } from '../promptCache.js';
12
13
  import { isFlowTransitionControlTool } from '../../flow/flowControlTools.js';
13
14
  import { resolveStructuredDecide } from '../../flow/choiceMatch.js';
14
15
  export class TextDriver {
@@ -50,12 +51,14 @@ export class TextDriver {
50
51
  const source = {
51
52
  async *[Symbol.asyncIterator]() {
52
53
  for (let step = 0; step < maxSteps; step += 1) {
54
+ const cached = applyPromptCache(model, ctx.session.id, messages);
53
55
  const result = streamText({
54
56
  model,
55
57
  system,
56
- messages,
58
+ messages: cached.messages,
57
59
  tools: aiTools,
58
60
  abortSignal: ctx.abortSignal,
61
+ ...(cached.providerOptions ? { providerOptions: cached.providerOptions } : {}),
59
62
  });
60
63
  for await (const part of result.fullStream) {
61
64
  if (part.type === 'text-delta') {
@@ -2,6 +2,7 @@ import { streamText } from 'ai';
2
2
  import { executeModelToolCall, toolResultMessage } from './executeModelTool.js';
3
3
  import { buildToolSet } from '../../tools/effect/index.js';
4
4
  import { buildNodePrompt, composeSystem } from '../../flow/nodeBuilders.js';
5
+ import { applyPromptCache } from '../promptCache.js';
5
6
  /**
6
7
  * Shared, NON-SPEAKING field extraction for `collect` nodes, used by every
7
8
  * ChannelDriver so text and voice behave identically. It runs the model with the
@@ -20,13 +21,15 @@ export async function runSilentExtraction(node, ctx, model, maxSteps) {
20
21
  const aiTools = resolveExtractionTools(node);
21
22
  const out = { text: '', toolResults: [] };
22
23
  for (let step = 0; step < maxSteps; step += 1) {
24
+ const cached = applyPromptCache(model, ctx.session.id, messages);
23
25
  const result = streamText({
24
26
  model,
25
27
  system,
26
- messages,
28
+ messages: cached.messages,
27
29
  tools: aiTools,
28
30
  temperature: 0,
29
31
  abortSignal: ctx.abortSignal,
32
+ ...(cached.providerOptions ? { providerOptions: cached.providerOptions } : {}),
30
33
  });
31
34
  for await (const part of result.fullStream) {
32
35
  // Intentionally NOT handling 'text-delta' — extraction never speaks.
@@ -23,7 +23,7 @@
23
23
  * but extends caching across short user idle periods + cross-session
24
24
  * within an hour.
25
25
  */
26
- import type { ModelMessage } from 'ai';
26
+ import type { JSONValue, ModelMessage } from 'ai';
27
27
  export type AnthropicCacheTtl = '5m' | '1h';
28
28
  /**
29
29
  * Returns a SHALLOW-COPIED message list with cache_control markers
@@ -90,3 +90,19 @@ export interface OpenAIResponsesCompactOptions {
90
90
  * branch but ignored otherwise.
91
91
  */
92
92
  export declare function buildOpenAIResponsesProviderOptions(opts: OpenAIResponsesCompactOptions, sessionId: string): Record<string, unknown> | null;
93
+ /**
94
+ * Single wiring point for provider prompt caching, called by the driver
95
+ * `streamText` sites. Gated by the conservative detectors so non-matching
96
+ * providers are untouched:
97
+ * - Anthropic → applies `cache_control` breakpoints (caches the system+tools
98
+ * prefix + recent history; ~75% input-cost + TTFT off multi-turn turns).
99
+ * - OpenAI Responses → sets `promptCacheKey` (= sessionId, pins same-session
100
+ * turns to one cache slot) + `truncation: 'auto'` overflow safety net.
101
+ * Returns the (possibly transformed) messages + an optional `providerOptions`
102
+ * bag ready to spread into `streamText`. Default-on: both are no-ops on
103
+ * providers that ignore the fields.
104
+ */
105
+ export declare function applyPromptCache(model: unknown, sessionId: string, messages: ModelMessage[]): {
106
+ messages: ModelMessage[];
107
+ providerOptions?: Record<string, Record<string, JSONValue>>;
108
+ };
@@ -110,6 +110,30 @@ export function buildOpenAIResponsesProviderOptions(opts, sessionId) {
110
110
  }
111
111
  return Object.keys(out).length > 0 ? out : null;
112
112
  }
113
+ /**
114
+ * Single wiring point for provider prompt caching, called by the driver
115
+ * `streamText` sites. Gated by the conservative detectors so non-matching
116
+ * providers are untouched:
117
+ * - Anthropic → applies `cache_control` breakpoints (caches the system+tools
118
+ * prefix + recent history; ~75% input-cost + TTFT off multi-turn turns).
119
+ * - OpenAI Responses → sets `promptCacheKey` (= sessionId, pins same-session
120
+ * turns to one cache slot) + `truncation: 'auto'` overflow safety net.
121
+ * Returns the (possibly transformed) messages + an optional `providerOptions`
122
+ * bag ready to spread into `streamText`. Default-on: both are no-ops on
123
+ * providers that ignore the fields.
124
+ */
125
+ export function applyPromptCache(model, sessionId, messages) {
126
+ const outMessages = isAnthropicLanguageModel(model)
127
+ ? applyAnthropicCacheControl(messages)
128
+ : messages;
129
+ if (isOpenAIResponsesModel(model)) {
130
+ const openai = buildOpenAIResponsesProviderOptions({ useSessionAsPromptCacheKey: true, truncationFallback: 'auto' }, sessionId);
131
+ if (openai) {
132
+ return { messages: outMessages, providerOptions: { openai: openai } };
133
+ }
134
+ }
135
+ return { messages: outMessages };
136
+ }
113
137
  function withProviderOptions(msg, cacheControl) {
114
138
  const existing = msg.providerOptions ?? {};
115
139
  const existingAnthropic = existing.anthropic ?? {};
package/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "url": "git+https://github.com/kuralle/kuralle-agents.git",
7
7
  "directory": "packages/kuralle-core"
8
8
  },
9
- "version": "0.7.1",
9
+ "version": "0.7.2",
10
10
  "description": "A framework for structured conversational AI agents",
11
11
  "publishConfig": {
12
12
  "access": "public"
@@ -103,7 +103,7 @@
103
103
  "typescript": "^5.3.0",
104
104
  "vitest": "^3.2.4",
105
105
  "zod": "^4.0.0",
106
- "@kuralle-agents/realtime-audio": "0.7.1"
106
+ "@kuralle-agents/realtime-audio": "0.7.2"
107
107
  },
108
108
  "dependencies": {
109
109
  "chrono-node": "^2.6.0"