@kuralle-agents/core 0.3.17 → 0.3.19

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.
@@ -1 +1,2 @@
1
1
  export { filterAuditEntries } from './filterAuditEntries.js';
2
+ export { appendConversationAudit, type AuditRecordContext, type NewAuditEntry } from './record.js';
@@ -1 +1,2 @@
1
1
  export { filterAuditEntries } from './filterAuditEntries.js';
2
+ export { appendConversationAudit } from './record.js';
@@ -0,0 +1,15 @@
1
+ import type { ConversationAuditEntry, AuditEntryBase } from './types.js';
2
+ import type { Session } from '../types/session.js';
3
+ export interface AuditRecordContext {
4
+ sessionId: string;
5
+ conversationId?: string;
6
+ userId?: string;
7
+ agentId?: string;
8
+ turnIndex?: number;
9
+ }
10
+ export type NewAuditEntry = {
11
+ [K in ConversationAuditEntry['type']]: Omit<Extract<ConversationAuditEntry, {
12
+ type: K;
13
+ }>, keyof AuditEntryBase>;
14
+ }[ConversationAuditEntry['type']];
15
+ export declare function appendConversationAudit(session: Session, ctx: AuditRecordContext, entry: NewAuditEntry): void;
@@ -0,0 +1,23 @@
1
+ export function appendConversationAudit(session, ctx, entry) {
2
+ const at = new Date().toISOString();
3
+ const full = {
4
+ at,
5
+ sessionId: ctx.sessionId,
6
+ conversationId: ctx.conversationId,
7
+ userId: ctx.userId,
8
+ agentId: ctx.agentId,
9
+ turnIndex: ctx.turnIndex,
10
+ ...entry,
11
+ };
12
+ if (!session.metadata) {
13
+ session.metadata = {
14
+ createdAt: session.createdAt,
15
+ lastActiveAt: session.updatedAt,
16
+ totalTokens: 0,
17
+ totalSteps: 0,
18
+ handoffHistory: [],
19
+ };
20
+ }
21
+ session.metadata.audit ??= [];
22
+ session.metadata.audit.push(full);
23
+ }
@@ -1,3 +1,4 @@
1
+ import type { EscalationReason } from '../escalation/types.js';
1
2
  import type { Session, SourceRef, ToolCallRecord } from '../types/index.js';
2
3
  export interface ValidationCapability {
3
4
  readonly name: string;
@@ -21,6 +22,13 @@ export type ValidateDecision = {
21
22
  confidence: number;
22
23
  rewrittenOutput: string;
23
24
  rationale: string;
25
+ } | {
26
+ decision: 'escalate';
27
+ confidence: number;
28
+ rationale: string;
29
+ escalateTo?: string;
30
+ escalationReason?: EscalationReason;
31
+ userFacingMessage?: string;
24
32
  } | {
25
33
  decision: 'block';
26
34
  confidence: number;
@@ -12,6 +12,7 @@ import { loadRecordedSteps } from '../runtime/durable/replay.js';
12
12
  import { SuspendError } from '../runtime/durable/RunStore.js';
13
13
  import { ToolApprovalDeniedError } from '../tools/effect/errors.js';
14
14
  import { emitInteractiveOnNodeEnter } from './emitInteractive.js';
15
+ import { appendConversationAudit } from '../audit/record.js';
15
16
  import { appendSafeAssistantMessage, degradeFlowError, findEscalateNode, } from './degrade.js';
16
17
  export class FlowOscillationError extends Error {
17
18
  constructor(from, to) {
@@ -169,6 +170,21 @@ async function dispatchNode(node, run, driver, ctx, agent, flow) {
169
170
  if (turn.control?.type === 'recover') {
170
171
  return { kind: 'end', reason: turn.control.reason ?? 'error_degraded' };
171
172
  }
173
+ if (node.confidenceGate &&
174
+ turn.confidence != null &&
175
+ turn.confidence < node.confidenceGate.min) {
176
+ appendConversationAudit(ctx.session, {
177
+ sessionId: ctx.session.id,
178
+ conversationId: ctx.session.conversationId,
179
+ userId: ctx.session.userId,
180
+ agentId: ctx.runState.activeAgentId,
181
+ }, {
182
+ type: 'escalation',
183
+ reason: 'low-confidence',
184
+ confidence: turn.confidence,
185
+ });
186
+ return normalizeTransition(node.confidenceGate.onLow);
187
+ }
172
188
  if (node.next) {
173
189
  return normalizeTransition(await node.next(turn, run.state));
174
190
  }
@@ -98,11 +98,13 @@ export class TextDriver {
98
98
  messages.push(toolResultMessage({ toolName: call.toolName, input: call.input, toolCallId: call.toolCallId }, toolResult));
99
99
  }
100
100
  }
101
- const postTurn = await applyPostTurnPolicies(ctx, draftText, toolCallsMade);
102
- const finalText = postTurn.text;
103
- out.text = finalText;
104
- if (finalText) {
105
- ctx.emit({ type: 'text-delta', text: finalText });
101
+ const postTurn = await applyPostTurnPolicies(ctx, draftText, toolCallsMade, gather.citations ?? []);
102
+ out.confidence = postTurn.confidence;
103
+ out.control = postTurn.control;
104
+ const emitText = postTurn.control ? (postTurn.blockedMessage ?? postTurn.text) : postTurn.text;
105
+ out.text = emitText;
106
+ if (emitText) {
107
+ ctx.emit({ type: 'text-delta', text: emitText });
106
108
  }
107
109
  ctx.emit({ type: 'turn-end' });
108
110
  return out;
@@ -56,10 +56,13 @@ export class VoiceDriver {
56
56
  break;
57
57
  }
58
58
  }
59
- const postTurn = await applyPostTurnPolicies(ctx, draftText, toolCallsMade);
60
- out.text = postTurn.text;
61
- if (out.text) {
62
- ctx.emit({ type: 'text-delta', text: out.text });
59
+ const postTurn = await applyPostTurnPolicies(ctx, draftText, toolCallsMade, gather.citations ?? []);
60
+ out.confidence = postTurn.confidence;
61
+ out.control = postTurn.control;
62
+ const emitText = postTurn.control ? (postTurn.blockedMessage ?? postTurn.text) : postTurn.text;
63
+ out.text = emitText;
64
+ if (emitText) {
65
+ ctx.emit({ type: 'text-delta', text: emitText });
63
66
  }
64
67
  ctx.emit({ type: 'turn-end' });
65
68
  return out;
@@ -4,4 +4,4 @@ export type { TextDriverConfig } from './TextDriver.js';
4
4
  export { VoiceDriver } from './VoiceDriver.js';
5
5
  export type { VoiceDriverConfig } from './VoiceDriver.js';
6
6
  export { resolveVoiceGeminiTools, v2ToolsToGemini } from './voiceTools.js';
7
- export { setPendingUserInput, consumePendingUserInput, peekPendingUserInput } from './inputBuffer.js';
7
+ export { setPendingUserInput, consumePendingUserInput, peekPendingUserInput, hasPendingUserInput, } from './inputBuffer.js';
@@ -5,4 +5,4 @@ export { TextDriver, buildNodePrompt } from './TextDriver.js';
5
5
  // the primary primitive; cascaded voice runs over text (see livekit-plugin).
6
6
  export { VoiceDriver } from './VoiceDriver.js';
7
7
  export { resolveVoiceGeminiTools, v2ToolsToGemini } from './voiceTools.js';
8
- export { setPendingUserInput, consumePendingUserInput, peekPendingUserInput } from './inputBuffer.js';
8
+ export { setPendingUserInput, consumePendingUserInput, peekPendingUserInput, hasPendingUserInput, } from './inputBuffer.js';
@@ -1,7 +1,9 @@
1
+ import type { SourceRef } from '../../types/voice.js';
1
2
  import type { GatherScope, RunContext } from '../../types/run-context.js';
2
3
  export type { GatherScope } from '../../types/run-context.js';
3
4
  export interface GatherResult {
4
5
  retrievalBlock?: string;
5
6
  memoryBlock?: string;
7
+ citations?: SourceRef[];
6
8
  }
7
9
  export declare function runGatherPhase(ctx: RunContext, scope?: GatherScope): Promise<GatherResult>;
@@ -1,9 +1,27 @@
1
+ function normalizeRetrieveResult(raw) {
2
+ if (raw == null) {
3
+ return {};
4
+ }
5
+ if (typeof raw === 'string') {
6
+ return { retrievalBlock: raw };
7
+ }
8
+ return { retrievalBlock: raw.block, citations: raw.citations };
9
+ }
1
10
  export async function runGatherPhase(ctx, scope) {
2
- const retrievalBlock = ctx.autoRetrieve && scope?.knowledge?.autoRetrieve !== false
3
- ? await ctx.autoRetrieve.retrieve(ctx, scope)
4
- : undefined;
11
+ let retrievalBlock;
12
+ let citations;
13
+ if (ctx.autoRetrieve && scope?.knowledge?.autoRetrieve !== false) {
14
+ const raw = await ctx.autoRetrieve.retrieve(ctx, scope);
15
+ const normalized = normalizeRetrieveResult(raw);
16
+ retrievalBlock = normalized.retrievalBlock;
17
+ citations = normalized.citations;
18
+ ctx.lastRetrievalCitations = citations;
19
+ }
20
+ else {
21
+ ctx.lastRetrievalCitations = undefined;
22
+ }
5
23
  const memoryBlock = ctx.memoryService?.preload && scope?.memory?.preload !== false
6
24
  ? await ctx.memoryService.preload(ctx, scope)
7
25
  : undefined;
8
- return { retrievalBlock, memoryBlock };
26
+ return { retrievalBlock, memoryBlock, citations };
9
27
  }
@@ -1,3 +1,4 @@
1
+ import { normalizeCitations } from '../citations/index.js';
1
2
  import { KnowledgeProvider } from '../KnowledgeProvider.js';
2
3
  function latestUserMessage(ctx) {
3
4
  for (let index = ctx.runState.messages.length - 1; index >= 0; index -= 1) {
@@ -51,7 +52,12 @@ export function buildAutoRetrieveProvider(provider, agent) {
51
52
  ...retrievalResults.map((result) => ({ text: result.text })),
52
53
  ];
53
54
  const maxChars = provider.resolveConfig(merged).maxOutputTokens * 4;
54
- return formatRetrievalBlock(combined, maxChars);
55
+ const block = formatRetrievalBlock(combined, maxChars);
56
+ const citations = normalizeCitations(retrievalResults);
57
+ if (!block && citations.length === 0) {
58
+ return undefined;
59
+ }
60
+ return { block, citations: citations.length > 0 ? citations : undefined };
55
61
  },
56
62
  };
57
63
  }
@@ -3,4 +3,5 @@ export type { RuntimeLike } from './RuntimeLike.js';
3
3
  export { SessionWorkingMemory } from './WorkingMemory.js';
4
4
  export { TextDriver, VoiceDriver } from './channels/index.js';
5
5
  export type { VoiceDriverConfig } from './channels/index.js';
6
+ export { setPendingUserInput, consumePendingUserInput, peekPendingUserInput, hasPendingUserInput, } from './channels/index.js';
6
7
  export { isAbortSignal, type InterruptionEvent, type AbortOptions, type CancellationReason, } from '../types/index.js';
@@ -1,4 +1,8 @@
1
1
  export { createRuntime, Runtime, } from './Runtime.js';
2
2
  export { SessionWorkingMemory } from './WorkingMemory.js';
3
3
  export { TextDriver, VoiceDriver } from './channels/index.js';
4
+ // Pending-input buffer helpers — required by custom ChannelDriver authors to
5
+ // implement awaitUser the same FIFO-aware way the built-in drivers do (the
6
+ // buffer is an ordered queue since 0.3.13/H3, not a single slot).
7
+ export { setPendingUserInput, consumePendingUserInput, peekPendingUserInput, hasPendingUserInput, } from './channels/index.js';
4
8
  export { isAbortSignal, } from '../types/index.js';
@@ -1,4 +1,6 @@
1
+ import type { TurnControl } from '../../types/channel.js';
1
2
  import type { ToolCallRecord } from '../../types/session.js';
3
+ import type { SourceRef } from '../../types/voice.js';
2
4
  import type { RunContext } from '../../types/run-context.js';
3
5
  export interface PreTurnResult {
4
6
  proceed: boolean;
@@ -9,6 +11,8 @@ export interface PostTurnResult {
9
11
  proceed: boolean;
10
12
  text: string;
11
13
  blockedMessage?: string;
14
+ control?: TurnControl;
15
+ confidence?: number;
12
16
  }
13
17
  export declare function applyPreTurnPolicies(ctx: RunContext): Promise<PreTurnResult>;
14
- export declare function applyPostTurnPolicies(ctx: RunContext, assistantOutput: string, toolCallsMade?: ToolCallRecord[]): Promise<PostTurnResult>;
18
+ export declare function applyPostTurnPolicies(ctx: RunContext, assistantOutput: string, toolCallsMade?: ToolCallRecord[], knowledgeCitations?: SourceRef[]): Promise<PostTurnResult>;
@@ -1,3 +1,5 @@
1
+ import { appendConversationAudit } from '../../audit/record.js';
2
+ import { SAFE_DEGRADED_MESSAGE } from '../../flow/degrade.js';
1
3
  import { runInputProcessors, runOutputProcessors } from '../../processors/ProcessorRunner.js';
2
4
  function latestUserMessage(messages) {
3
5
  for (let index = messages.length - 1; index >= 0; index -= 1) {
@@ -8,6 +10,67 @@ function latestUserMessage(messages) {
8
10
  }
9
11
  return '';
10
12
  }
13
+ function auditContext(ctx) {
14
+ return {
15
+ sessionId: ctx.session.id,
16
+ conversationId: ctx.session.conversationId,
17
+ userId: ctx.session.userId,
18
+ agentId: ctx.runState.activeAgentId,
19
+ };
20
+ }
21
+ function recordKnowledgeCitations(ctx, citations) {
22
+ for (const citation of citations) {
23
+ appendConversationAudit(ctx.session, auditContext(ctx), {
24
+ type: 'knowledge-citation',
25
+ sourceId: citation.id,
26
+ title: citation.title,
27
+ score: citation.score,
28
+ });
29
+ }
30
+ }
31
+ function recordValidationDecision(ctx, policyName, decision) {
32
+ if (decision.decision === 'block') {
33
+ appendConversationAudit(ctx.session, auditContext(ctx), {
34
+ type: 'safety-block',
35
+ moderator: policyName,
36
+ rationale: decision.rationale,
37
+ userFacingMessage: decision.userFacingMessage ?? decision.rationale,
38
+ });
39
+ appendConversationAudit(ctx.session, auditContext(ctx), {
40
+ type: 'validation',
41
+ aggregate: 'block',
42
+ confidence: decision.confidence,
43
+ rationale: decision.rationale,
44
+ moderator: policyName,
45
+ });
46
+ return;
47
+ }
48
+ if (decision.decision === 'escalate') {
49
+ appendConversationAudit(ctx.session, auditContext(ctx), {
50
+ type: 'escalation',
51
+ reason: decision.escalationReason ?? 'low-confidence',
52
+ confidence: decision.confidence,
53
+ });
54
+ appendConversationAudit(ctx.session, auditContext(ctx), {
55
+ type: 'validation',
56
+ aggregate: 'block',
57
+ confidence: decision.confidence,
58
+ rationale: decision.rationale,
59
+ moderator: policyName,
60
+ });
61
+ return;
62
+ }
63
+ appendConversationAudit(ctx.session, auditContext(ctx), {
64
+ type: 'validation',
65
+ aggregate: decision.decision === 'rewrite' ? 'rewrite' : 'continue',
66
+ confidence: decision.confidence,
67
+ rationale: decision.rationale ?? '',
68
+ moderator: policyName,
69
+ });
70
+ }
71
+ function safeBlockedText(decision) {
72
+ return decision.userFacingMessage?.trim() || decision.rationale?.trim() || SAFE_DEGRADED_MESSAGE;
73
+ }
11
74
  async function runRefinementPolicies(ctx, userMessage) {
12
75
  const policies = ctx.refinementPolicies ?? [];
13
76
  if (policies.length === 0) {
@@ -36,34 +99,56 @@ async function runRefinementPolicies(ctx, userMessage) {
36
99
  }
37
100
  return { proceed: true, userMessage: current };
38
101
  }
39
- async function runValidationPolicies(ctx, userMessage, assistantOutput, toolCallsMade) {
102
+ async function runValidationPolicies(ctx, userMessage, assistantOutput, toolCallsMade, knowledgeCitations) {
40
103
  const policies = ctx.validationPolicies ?? [];
41
104
  if (policies.length === 0) {
42
105
  return { proceed: true, text: assistantOutput };
43
106
  }
107
+ if (knowledgeCitations.length > 0) {
108
+ recordKnowledgeCitations(ctx, knowledgeCitations);
109
+ }
44
110
  const sorted = [...policies].sort((a, b) => a.name.localeCompare(b.name));
45
111
  let current = assistantOutput;
112
+ let lastConfidence;
46
113
  for (const policy of sorted) {
47
114
  const decision = await policy.validate({
48
115
  session: ctx.session,
49
116
  userMessage,
50
117
  assistantOutput: current,
51
118
  toolCallsMade,
52
- knowledgeCitations: [],
119
+ knowledgeCitations,
53
120
  abortSignal: ctx.abortSignal,
54
121
  });
122
+ recordValidationDecision(ctx, policy.name, decision);
123
+ lastConfidence = decision.confidence;
55
124
  if (decision.decision === 'block') {
125
+ const safe = safeBlockedText(decision);
56
126
  return {
57
127
  proceed: false,
58
- text: decision.userFacingMessage ?? '',
59
- blockedMessage: decision.userFacingMessage ?? decision.rationale,
128
+ text: safe,
129
+ blockedMessage: safe,
130
+ control: { type: 'recover', reason: decision.rationale },
131
+ confidence: decision.confidence,
132
+ };
133
+ }
134
+ if (decision.decision === 'escalate') {
135
+ const safe = safeBlockedText(decision);
136
+ return {
137
+ proceed: false,
138
+ text: safe,
139
+ blockedMessage: safe,
140
+ control: { type: 'escalate', reason: decision.rationale },
141
+ confidence: decision.confidence,
60
142
  };
61
143
  }
62
144
  if (decision.decision === 'rewrite') {
63
145
  current = decision.rewrittenOutput;
64
146
  }
65
147
  }
66
- return { proceed: true, text: current };
148
+ if (ctx.session.metadata) {
149
+ ctx.session.metadata.lastValidationConfidence = lastConfidence;
150
+ }
151
+ return { proceed: true, text: current, confidence: lastConfidence };
67
152
  }
68
153
  export async function applyPreTurnPolicies(ctx) {
69
154
  const userMessage = latestUserMessage(ctx.runState.messages);
@@ -92,7 +177,7 @@ export async function applyPreTurnPolicies(ctx) {
92
177
  }
93
178
  return runRefinementPolicies(ctx, latestUserMessage(ctx.runState.messages));
94
179
  }
95
- export async function applyPostTurnPolicies(ctx, assistantOutput, toolCallsMade = []) {
180
+ export async function applyPostTurnPolicies(ctx, assistantOutput, toolCallsMade = [], knowledgeCitations = []) {
96
181
  const userMessage = latestUserMessage(ctx.runState.messages);
97
182
  const processors = ctx.outputProcessors ?? [];
98
183
  let current = assistantOutput;
@@ -117,7 +202,8 @@ export async function applyPostTurnPolicies(ctx, assistantOutput, toolCallsMade
117
202
  }
118
203
  current = outcome.text;
119
204
  }
120
- return runValidationPolicies(ctx, userMessage, current, toolCallsMade);
205
+ const citations = knowledgeCitations.length > 0 ? knowledgeCitations : (ctx.lastRetrievalCitations ?? []);
206
+ return runValidationPolicies(ctx, userMessage, current, toolCallsMade, citations);
121
207
  }
122
208
  function patchLatestUserMessage(messages, next) {
123
209
  for (let index = messages.length - 1; index >= 0; index -= 1) {
@@ -5,8 +5,8 @@ export function resolveAgentPolicies(agent) {
5
5
  return {
6
6
  inputProcessors: guardrails?.input ?? [],
7
7
  outputProcessors: guardrails?.output ?? [],
8
- refinementPolicies: [],
9
- validationPolicies: [],
8
+ refinementPolicies: agent.refine ?? [],
9
+ validationPolicies: agent.validate ?? [],
10
10
  limits: agent.limits,
11
11
  enforcer: enforcementRules.length > 0 ? createToolEnforcer(enforcementRules) : undefined,
12
12
  };
@@ -4,6 +4,8 @@ import type { Flow } from './flow.js';
4
4
  import type { Route, RoutingPolicy } from './route.js';
5
5
  import type { Guardrails, Limits } from './guardrails.js';
6
6
  import type { AgentKnowledge, AgentMemory } from './grounding.js';
7
+ import type { RefinementCapability } from '../capabilities/RefinementCapability.js';
8
+ import type { ValidationCapability } from '../capabilities/ValidationCapability.js';
7
9
  import type { AnyTool } from './effectTool.js';
8
10
  export type Instructions = string | AgentPrompt | ((ctx: {
9
11
  state: Record<string, unknown>;
@@ -35,6 +37,10 @@ export interface AgentConfig {
35
37
  memory?: AgentMemory;
36
38
  guardrails?: Guardrails;
37
39
  limits?: Limits;
40
+ /** Post-turn validation policies (grounding/confidence gate). Default: none. */
41
+ validate?: ValidationCapability[];
42
+ /** Pre-turn refinement policies. Default: none. */
43
+ refine?: RefinementCapability[];
38
44
  experimental?: {
39
45
  /** Flow reply nodes: silo flow-transition control tools + deterministic evaluator (ADR 0003 H1). Default OFF. */
40
46
  outOfBandControl?: boolean;
@@ -56,6 +56,11 @@ export interface ReplyNode {
56
56
  model?: LanguageModel;
57
57
  context?: ContextStrategy;
58
58
  grounding?: NodeGrounding;
59
+ /** When set, routes to `onLow` when post-turn validation confidence is below `min`. */
60
+ confidenceGate?: {
61
+ min: number;
62
+ onLow: Transition;
63
+ };
59
64
  next?: (turn: TurnResult, state: FlowState) => Transition | Promise<Transition>;
60
65
  }
61
66
  export interface CollectNode {
@@ -9,7 +9,7 @@ import type { ValidationCapability } from '../capabilities/ValidationCapability.
9
9
  import type { Limits } from './guardrails.js';
10
10
  import type { AnyTool } from './effectTool.js';
11
11
  import type { Instructions } from './agentConfig.js';
12
- import type { AgentKnowledgeOverrides } from './voice.js';
12
+ import type { AgentKnowledgeOverrides, SourceRef } from './voice.js';
13
13
  export interface GatherScope {
14
14
  query?: string;
15
15
  knowledge?: AgentKnowledgeOverrides & {
@@ -33,8 +33,12 @@ export interface EffectToolExecutor {
33
33
  /** Resolve a registered tool definition by name (used to read flags like `needsApproval`). */
34
34
  getTool?(name: string): AnyTool | undefined;
35
35
  }
36
+ export interface AutoRetrieveResult {
37
+ block?: string;
38
+ citations?: SourceRef[];
39
+ }
36
40
  export interface AutoRetrieveProvider {
37
- retrieve(ctx: RunContext, scope?: GatherScope): Promise<string | undefined>;
41
+ retrieve(ctx: RunContext, scope?: GatherScope): Promise<AutoRetrieveResult | string | undefined>;
38
42
  }
39
43
  export interface MemoryService {
40
44
  preload?(ctx: RunContext, scope?: GatherScope): Promise<string | undefined>;
@@ -73,6 +77,8 @@ export interface RunContext {
73
77
  * context. Reset to false on every `createRunContext` (i.e. every turn).
74
78
  */
75
79
  turnInputConsumed?: boolean;
80
+ /** Citations from the latest gather-phase retrieval on this turn. */
81
+ lastRetrievalCitations?: SourceRef[];
76
82
  /** Agent base layer (ADR 0001), set when entering a flow. `baseInstructions`
77
83
  * is composed as a prefix into every node turn's system prompt (persona /
78
84
  * safety / grounding floor); `globalTools` are safe tools made model-visible
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.3.17",
9
+ "version": "0.3.19",
10
10
  "description": "A framework for structured conversational AI agents",
11
11
  "publishConfig": {
12
12
  "access": "public"
@@ -97,7 +97,7 @@
97
97
  "dotenv": "^16.4.0",
98
98
  "typescript": "^5.3.0",
99
99
  "zod": "^3.23.0",
100
- "@kuralle-agents/realtime-audio": "0.3.17"
100
+ "@kuralle-agents/realtime-audio": "0.3.19"
101
101
  },
102
102
  "dependencies": {
103
103
  "chrono-node": "^2.6.0",