@kuralle-agents/core 0.13.0 → 0.14.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.
Files changed (158) hide show
  1. package/README.md +4 -4
  2. package/dist/ai-sdk/uiMessageStream.d.ts +2 -2
  3. package/dist/ai-sdk/uiMessageStream.js +42 -29
  4. package/dist/capabilities/LivePromptAssembler.d.ts +1 -1
  5. package/dist/capabilities/LivePromptAssembler.js +1 -1
  6. package/dist/capabilities/index.d.ts +1 -1
  7. package/dist/eval/EvalRunner.js +4 -4
  8. package/dist/eval/simulation.js +4 -3
  9. package/dist/events/TurnHandle.d.ts +3 -3
  10. package/dist/flow/collectUntilComplete.js +4 -4
  11. package/dist/flow/degrade.js +8 -4
  12. package/dist/flow/emitInteractive.d.ts +2 -2
  13. package/dist/flow/emitInteractive.js +6 -3
  14. package/dist/flow/extraction.d.ts +2 -2
  15. package/dist/flow/extraction.js +10 -4
  16. package/dist/flow/reduceTransition.d.ts +2 -2
  17. package/dist/flow/reduceTransition.js +7 -3
  18. package/dist/flow/runFlow.js +34 -12
  19. package/dist/foundation/AgentStateController.d.ts +1 -1
  20. package/dist/foundation/ToolExecutor.d.ts +2 -2
  21. package/dist/foundation/index.d.ts +0 -11
  22. package/dist/foundation/index.js +0 -5
  23. package/dist/index.d.ts +9 -14
  24. package/dist/index.js +5 -6
  25. package/dist/outcomes/streamPart.d.ts +2 -2
  26. package/dist/outcomes/streamPart.js +2 -3
  27. package/dist/prompts/PromptBuilder.d.ts +3 -3
  28. package/dist/prompts/PromptBuilder.js +6 -6
  29. package/dist/prompts/index.d.ts +1 -1
  30. package/dist/prompts/types.d.ts +7 -2
  31. package/dist/runtime/InMemoryRetrievalCache.d.ts +1 -1
  32. package/dist/runtime/KnowledgeProvider.d.ts +3 -2
  33. package/dist/runtime/KnowledgeProvider.js +54 -30
  34. package/dist/runtime/Runtime.d.ts +3 -1
  35. package/dist/runtime/Runtime.js +84 -30
  36. package/dist/runtime/TraceRecorder.d.ts +22 -4
  37. package/dist/runtime/TraceRecorder.js +65 -29
  38. package/dist/runtime/buildAgentToolSurface.js +1 -1
  39. package/dist/runtime/channels/TextDriver.js +12 -9
  40. package/dist/runtime/channels/executeModelTool.d.ts +6 -0
  41. package/dist/runtime/channels/executeModelTool.js +70 -10
  42. package/dist/runtime/channels/extractionTurn.js +1 -1
  43. package/dist/runtime/channels/index.d.ts +0 -3
  44. package/dist/runtime/channels/index.js +0 -6
  45. package/dist/runtime/channels/streaming/hostControlSpeak.js +5 -1
  46. package/dist/runtime/channels/streaming/speakGated.js +24 -12
  47. package/dist/runtime/citations/index.d.ts +3 -3
  48. package/dist/runtime/closeRun.js +3 -2
  49. package/dist/runtime/controlFlowSignal.d.ts +24 -0
  50. package/dist/runtime/controlFlowSignal.js +29 -0
  51. package/dist/runtime/ctx.d.ts +3 -4
  52. package/dist/runtime/ctx.js +39 -5
  53. package/dist/runtime/durable/RunStore.js +1 -1
  54. package/dist/runtime/grounding/gather.d.ts +1 -1
  55. package/dist/runtime/grounding/inMemoryKnowledge.d.ts +1 -1
  56. package/dist/runtime/grounding/knowledge.d.ts +1 -1
  57. package/dist/runtime/hostLoop.js +26 -6
  58. package/dist/runtime/index.d.ts +1 -2
  59. package/dist/runtime/index.js +1 -1
  60. package/dist/runtime/outcomeMarking.d.ts +2 -2
  61. package/dist/runtime/outcomeMarking.js +2 -1
  62. package/dist/runtime/policies/agentTurn.d.ts +1 -1
  63. package/dist/runtime/runHookSafely.d.ts +3 -0
  64. package/dist/runtime/runHookSafely.js +19 -0
  65. package/dist/scheduler/index.d.ts +2 -2
  66. package/dist/scheduler/index.js +1 -1
  67. package/dist/services/TracingService.js +2 -3
  68. package/dist/skills/collectSkills.d.ts +11 -3
  69. package/dist/skills/collectSkills.js +51 -8
  70. package/dist/skills/compositeSkillStore.d.ts +26 -0
  71. package/dist/skills/compositeSkillStore.js +54 -0
  72. package/dist/skills/defineSkill.d.ts +21 -0
  73. package/dist/skills/defineSkill.js +26 -0
  74. package/dist/skills/fsSkillStore.d.ts +3 -0
  75. package/dist/skills/fsSkillStore.js +79 -0
  76. package/dist/skills/index.d.ts +4 -0
  77. package/dist/skills/index.js +4 -0
  78. package/dist/skills/parseSkillFrontmatter.d.ts +14 -0
  79. package/dist/skills/parseSkillFrontmatter.js +183 -0
  80. package/dist/skills/wireAgentSkills.d.ts +2 -1
  81. package/dist/skills/wireAgentSkills.js +2 -2
  82. package/dist/testing/mocks.d.ts +3 -3
  83. package/dist/testing/mocks.js +7 -3
  84. package/dist/tools/controlResults.d.ts +14 -0
  85. package/dist/tools/controlResults.js +15 -0
  86. package/dist/tools/effect/ToolExecutor.d.ts +6 -0
  87. package/dist/tools/effect/ToolExecutor.js +71 -35
  88. package/dist/tools/effect/defineTool.d.ts +9 -0
  89. package/dist/tools/effect/defineTool.js +1 -0
  90. package/dist/tools/effect/errors.d.ts +2 -5
  91. package/dist/tools/effect/errors.js +2 -5
  92. package/dist/tools/index.d.ts +0 -1
  93. package/dist/tools/index.js +0 -1
  94. package/dist/types/effectTool.d.ts +2 -0
  95. package/dist/types/flow.d.ts +1 -1
  96. package/dist/types/guardrails.d.ts +5 -0
  97. package/dist/types/hooks.d.ts +2 -2
  98. package/dist/types/index.d.ts +25 -16
  99. package/dist/types/index.js +8 -13
  100. package/dist/types/knowledge.d.ts +213 -0
  101. package/dist/types/processors.d.ts +0 -58
  102. package/dist/types/run-context.d.ts +3 -7
  103. package/dist/types/runtime.d.ts +3 -74
  104. package/dist/types/session.d.ts +1 -1
  105. package/dist/types/skills.d.ts +15 -1
  106. package/dist/types/stream.d.ts +133 -71
  107. package/dist/types/stream.js +34 -1
  108. package/dist/types/trace.d.ts +2 -0
  109. package/guides/AGENTS.md +3 -3
  110. package/guides/FLOWS.md +1 -1
  111. package/guides/GETTING_STARTED.md +1 -1
  112. package/guides/RUNTIME.md +20 -15
  113. package/guides/TOOLS.md +1 -1
  114. package/package.json +3 -13
  115. package/dist/foundation/AgentDefinition.d.ts +0 -17
  116. package/dist/foundation/ConversationEventLog.d.ts +0 -71
  117. package/dist/foundation/ConversationEventLog.js +0 -1
  118. package/dist/foundation/ConversationState.d.ts +0 -33
  119. package/dist/foundation/ConversationState.js +0 -1
  120. package/dist/foundation/DefaultConversationEventLog.d.ts +0 -27
  121. package/dist/foundation/DefaultConversationEventLog.js +0 -205
  122. package/dist/foundation/DefaultConversationState.d.ts +0 -36
  123. package/dist/foundation/DefaultConversationState.js +0 -103
  124. package/dist/foundation/DefaultToolExecutor.d.ts +0 -57
  125. package/dist/foundation/DefaultToolExecutor.js +0 -132
  126. package/dist/foundation/createFoundation.d.ts +0 -32
  127. package/dist/foundation/createFoundation.js +0 -33
  128. package/dist/hooks/HookRunner.d.ts +0 -37
  129. package/dist/hooks/HookRunner.js +0 -111
  130. package/dist/hooks/builtin/logging.d.ts +0 -5
  131. package/dist/hooks/builtin/logging.js +0 -98
  132. package/dist/hooks/builtin/metrics.d.ts +0 -7
  133. package/dist/hooks/builtin/metrics.js +0 -60
  134. package/dist/hooks/builtin/observability.d.ts +0 -20
  135. package/dist/hooks/builtin/observability.js +0 -572
  136. package/dist/hooks/helpers.d.ts +0 -22
  137. package/dist/hooks/helpers.js +0 -183
  138. package/dist/hooks/index.d.ts +0 -8
  139. package/dist/hooks/index.js +0 -6
  140. package/dist/realtime/RealtimeAudioClient.d.ts +0 -166
  141. package/dist/realtime/RealtimeAudioClient.js +0 -14
  142. package/dist/realtime/RealtimeSessionHandle.d.ts +0 -6
  143. package/dist/realtime/RealtimeSessionHandle.js +0 -1
  144. package/dist/realtime/index.d.ts +0 -2
  145. package/dist/realtime/index.js +0 -1
  146. package/dist/runtime/channels/VoiceDriver.d.ts +0 -31
  147. package/dist/runtime/channels/VoiceDriver.js +0 -321
  148. package/dist/runtime/channels/voiceTools.d.ts +0 -9
  149. package/dist/runtime/channels/voiceTools.js +0 -51
  150. package/dist/tools/errorHandling.d.ts +0 -35
  151. package/dist/tools/errorHandling.js +0 -145
  152. package/dist/types/voice.d.ts +0 -517
  153. package/dist/types/voice.js +0 -14
  154. package/dist/utils/isRecord.d.ts +0 -1
  155. package/dist/utils/isRecord.js +0 -3
  156. package/dist/utils/messageNormalization.d.ts +0 -2
  157. package/dist/utils/messageNormalization.js +0 -137
  158. /package/dist/{foundation/AgentDefinition.js → types/knowledge.js} +0 -0
@@ -1,321 +0,0 @@
1
- import { runSilentExtraction } from './extractionTurn.js';
2
- import { resolveStructuredDecide } from '../../flow/choiceMatch.js';
3
- import { buildNodePrompt, resolveInstructions, composeSystem } from '../../flow/nodeBuilders.js';
4
- import { executeModelToolCall } from './executeModelTool.js';
5
- import { applyPreTurnPolicies, applyPostTurnPolicies } from '../policies/agentTurn.js';
6
- import { resolveMaxSteps } from '../policies/limits.js';
7
- import { speakGated } from './streaming/speakGated.js';
8
- import { resolveStreamMode } from './streaming/mode.js';
9
- import { createDeferredTokenSource, } from './streaming/deferredTokenSource.js';
10
- import { appendGatherBlocks, resolveNodeGatherScope, runGatherPhase } from '../grounding/index.js';
11
- import { resolveVoiceGeminiTools } from './voiceTools.js';
12
- function emitNativeRealtimePostHocGate(ctx, client, gate) {
13
- if (gate.escalated) {
14
- ctx.emit({
15
- type: 'safety-blocked',
16
- moderator: gate.moderator,
17
- rationale: gate.rationale,
18
- userFacingMessage: gate.safeText,
19
- });
20
- }
21
- else {
22
- ctx.emit({
23
- type: 'pipeline-validation-block',
24
- rationale: gate.rationale,
25
- userFacingMessage: gate.safeText,
26
- });
27
- ctx.emit({
28
- type: 'safety-blocked',
29
- moderator: gate.moderator,
30
- rationale: gate.rationale,
31
- userFacingMessage: gate.safeText,
32
- });
33
- }
34
- client.requestResponse?.(gate.safeText);
35
- }
36
- export class VoiceDriver {
37
- outputCapability = 'native-realtime';
38
- client;
39
- toolDefs;
40
- maxSteps;
41
- heardCharCount = 0;
42
- pendingBargeInInput = null;
43
- constructor(config) {
44
- this.client = config.client;
45
- this.toolDefs = config.toolDefs ?? {};
46
- this.maxSteps = config.maxSteps ?? 5;
47
- }
48
- async runAgentTurn(node, ctx) {
49
- const replyNode = node.node;
50
- if (replyNode.kind !== 'reply') {
51
- throw new Error(`VoiceDriver.runAgentTurn expects a reply node, got ${replyNode.kind}`);
52
- }
53
- const preTurn = await applyPreTurnPolicies(ctx);
54
- if (!preTurn.proceed) {
55
- const blocked = preTurn.blockedMessage ?? 'Input blocked by guardrails';
56
- ctx.emit({
57
- type: 'safety-blocked',
58
- moderator: preTurn.blockedBy ?? 'input-guardrails',
59
- rationale: preTurn.blockedReason ?? 'input blocked',
60
- userFacingMessage: blocked,
61
- });
62
- const id = crypto.randomUUID();
63
- ctx.emit({ type: 'text-start', id });
64
- ctx.emit({ type: 'text-delta', id, delta: blocked });
65
- ctx.emit({ type: 'text-end', id });
66
- ctx.emit({ type: 'turn-end' });
67
- return { text: blocked, toolResults: [] };
68
- }
69
- const scope = resolveNodeGatherScope(replyNode, ctx.runState.state, ctx.runState.messages);
70
- const gather = await runGatherPhase(ctx, scope);
71
- const out = { text: '', toolResults: [] };
72
- const nodeSystem = node.prompt || buildNodePrompt(replyNode, ctx.runState.state);
73
- const baseSystem = composeSystem(ctx.baseInstructions, nodeSystem, ctx.runState.state, ctx.skillPrompt, ctx.workingMemoryPrompt);
74
- const system = appendGatherBlocks(baseSystem, [gather.retrievalBlock, gather.memoryBlock]);
75
- const geminiTools = resolveVoiceGeminiTools(node, { ...this.toolDefs, ...(ctx.globalTools ?? {}), ...(ctx.workingMemoryTools ?? {}) }, { siloFlowControl: ctx.outOfBandControl && !node.freeConversation });
76
- const toolCallsMade = [];
77
- const maxSteps = resolveMaxSteps(ctx.limits, this.maxSteps);
78
- const mode = resolveStreamMode(ctx, node);
79
- const turnId = crypto.randomUUID();
80
- const transcript = createDeferredTokenSource();
81
- let draftText = '';
82
- this.heardCharCount = 0;
83
- let postHocGate = null;
84
- const speakPromise = speakGated({
85
- ctx,
86
- mode,
87
- turnId,
88
- source: transcript.source,
89
- runGate: async (text, _final) => {
90
- const r = await applyPostTurnPolicies(ctx, text, toolCallsMade, gather.citations ?? []);
91
- if (!r.proceed) {
92
- const safeText = r.blockedMessage ?? r.text;
93
- postHocGate = {
94
- safeText,
95
- rationale: r.control?.reason ?? 'blocked',
96
- moderator: 'post-turn-gate',
97
- escalated: r.control?.type === 'escalate',
98
- };
99
- }
100
- return {
101
- blocked: !r.proceed,
102
- text: r.proceed ? r.text : (r.blockedMessage ?? r.text),
103
- reason: r.control?.reason,
104
- control: r.control,
105
- confidence: r.confidence,
106
- };
107
- },
108
- });
109
- await this.reconfigure({ systemInstruction: system, tools: geminiTools });
110
- for (let step = 0; step < maxSteps; step += 1) {
111
- const { outcome, assistantText } = await this.collectProviderTurn(ctx, out, step === 0, toolCallsMade, node.localTools, transcript);
112
- draftText += assistantText;
113
- if (outcome === 'interrupted') {
114
- transcript.close('interrupted');
115
- try {
116
- await speakPromise;
117
- }
118
- catch {
119
- /* speakGated cancels in-flight stream on interrupt */
120
- }
121
- out.interrupted = true;
122
- out.truncateAt = this.heardCharCount;
123
- out.text = truncateToHeard(draftText, this.heardCharCount);
124
- const id = crypto.randomUUID();
125
- ctx.emit({ type: 'text-start', id });
126
- ctx.emit({ type: 'text-delta', id, delta: out.text });
127
- ctx.emit({ type: 'text-end', id });
128
- ctx.emit({ type: 'turn-end' });
129
- return out;
130
- }
131
- if (out.toolResults.length === 0 || outcome === 'complete') {
132
- break;
133
- }
134
- }
135
- transcript.close('complete');
136
- const spoken = await speakPromise;
137
- out.text = spoken.text;
138
- // Preserve host-control raised during provider tool execution (enter_flow /
139
- // transfer_to_agent); the post-hoc gate must not clobber it (else native
140
- // realtime silently drops valid routing). Same fix as TextDriver.
141
- out.control = spoken.control ?? out.control;
142
- out.confidence = spoken.confidence;
143
- if (postHocGate) {
144
- // Advisory post-hoc gate: provider audio may already have played; correction does not un-speak it.
145
- emitNativeRealtimePostHocGate(ctx, this.client, postHocGate);
146
- out.gateScope = 'advisory';
147
- }
148
- ctx.emit({ type: 'turn-end' });
149
- return out;
150
- }
151
- async runStructured(node, ctx) {
152
- const system = composeSystem(ctx.baseInstructions, resolveInstructions(node.instructions, ctx.runState.state), ctx.runState.state, ctx.skillPrompt, ctx.workingMemoryPrompt);
153
- return resolveStructuredDecide(node, ctx, system);
154
- }
155
- // Non-speaking collect extraction: uses the shared text-model extraction path
156
- // rather than the realtime audio provider, so the agent never SPEAKS during
157
- // field collection (which is where ungrounded narration leaked). Identical
158
- // behavior to TextDriver — voice and text emit the same structural events; the
159
- // user-facing question is the deterministic CollectNode.ask, synthesized after.
160
- runExtraction(node, ctx) {
161
- return runSilentExtraction(node, ctx, ctx.controlModel, resolveMaxSteps(ctx.limits, this.maxSteps));
162
- }
163
- async awaitUser(ctx) {
164
- if (this.pendingBargeInInput != null) {
165
- const input = this.pendingBargeInInput;
166
- this.pendingBargeInInput = null;
167
- return { type: 'message', input };
168
- }
169
- const input = await this.waitForUserTurn(ctx);
170
- return { type: 'message', input };
171
- }
172
- async reconfigure(config) {
173
- await this.client.updateConfig(config);
174
- }
175
- getHeardCharCount() {
176
- return this.heardCharCount;
177
- }
178
- async collectProviderTurn(ctx, out, triggerResponse, toolCallsMade, localTools, transcript) {
179
- return new Promise((resolve, reject) => {
180
- let assistantText = '';
181
- let settled = false;
182
- let sawInterrupt = false;
183
- const finish = (outcome) => {
184
- if (settled)
185
- return;
186
- settled = true;
187
- cleanup();
188
- resolve({ outcome, assistantText });
189
- };
190
- const fail = (error) => {
191
- if (settled)
192
- return;
193
- settled = true;
194
- cleanup();
195
- transcript.close('error');
196
- reject(error);
197
- };
198
- const onTranscript = (text, role) => {
199
- if (role === 'assistant') {
200
- assistantText += text;
201
- this.heardCharCount += text.length;
202
- transcript.push(text);
203
- }
204
- if (role === 'user' && sawInterrupt) {
205
- this.pendingBargeInInput = text;
206
- }
207
- };
208
- const onToolCall = (id, name, args) => {
209
- void (async () => {
210
- ctx.emit({ type: 'tool-call', toolName: name, args, toolCallId: id });
211
- const { result: toolResult, control, failed } = await executeModelToolCall(ctx, { toolName: name, input: args, toolCallId: id }, {
212
- ...ctx.globalTools,
213
- ...(ctx.workingMemoryTools ?? {}),
214
- ...localTools,
215
- });
216
- out.toolResults.push({ name, args, result: toolResult, toolCallId: id });
217
- toolCallsMade.push({
218
- toolCallId: id,
219
- toolName: name,
220
- args,
221
- result: toolResult,
222
- success: !failed,
223
- timestamp: Date.now(),
224
- });
225
- out.control ??= control;
226
- ctx.emit({ type: 'tool-result', toolName: name, result: toolResult, toolCallId: id });
227
- this.client.sendToolResponse([{ id, name, output: toolResult }]);
228
- })();
229
- };
230
- const onTurnComplete = () => {
231
- finish(sawInterrupt ? 'interrupted' : 'complete');
232
- };
233
- const onInterrupted = () => {
234
- sawInterrupt = true;
235
- out.truncateAt = this.heardCharCount;
236
- };
237
- const onError = (error) => {
238
- ctx.emit({ type: 'error', error });
239
- fail(new Error(error));
240
- };
241
- const onAbort = () => {
242
- sawInterrupt = true;
243
- out.truncateAt = this.heardCharCount;
244
- finish('interrupted');
245
- };
246
- const cleanup = () => {
247
- this.client.off('transcript', onTranscript);
248
- this.client.off('tool-call', onToolCall);
249
- this.client.off('turn-complete', onTurnComplete);
250
- this.client.off('interrupted', onInterrupted);
251
- this.client.off('error', onError);
252
- ctx.bargeIn?.removeEventListener('abort', onAbort);
253
- ctx.abortSignal?.removeEventListener('abort', onAbort);
254
- };
255
- this.client.on('transcript', onTranscript);
256
- this.client.on('tool-call', onToolCall);
257
- this.client.on('turn-complete', onTurnComplete);
258
- this.client.on('interrupted', onInterrupted);
259
- this.client.on('error', onError);
260
- ctx.bargeIn?.addEventListener('abort', onAbort, { once: true });
261
- ctx.abortSignal?.addEventListener('abort', onAbort, { once: true });
262
- if (triggerResponse && this.client.requestResponse) {
263
- this.client.requestResponse();
264
- }
265
- });
266
- }
267
- waitForUserTurn(_ctx) {
268
- return new Promise((resolve, reject) => {
269
- let userText = '';
270
- let settled = false;
271
- const finish = (text) => {
272
- if (settled)
273
- return;
274
- settled = true;
275
- cleanup();
276
- resolve(text);
277
- };
278
- const fail = (error) => {
279
- if (settled)
280
- return;
281
- settled = true;
282
- cleanup();
283
- reject(error);
284
- };
285
- const onTranscript = (text, role) => {
286
- if (role === 'user') {
287
- userText += text;
288
- }
289
- };
290
- const onTurnComplete = () => {
291
- finish(userText);
292
- };
293
- const onError = (error) => {
294
- fail(new Error(error));
295
- };
296
- const cleanup = () => {
297
- this.client.off('transcript', onTranscript);
298
- this.client.off('turn-complete', onTurnComplete);
299
- this.client.off('error', onError);
300
- };
301
- this.client.on('transcript', onTranscript);
302
- this.client.on('turn-complete', onTurnComplete);
303
- this.client.on('error', onError);
304
- });
305
- }
306
- }
307
- function truncateToHeard(text, heardChars) {
308
- if (heardChars <= 0) {
309
- return '';
310
- }
311
- if (heardChars >= text.length) {
312
- return text;
313
- }
314
- const truncated = text.slice(0, heardChars);
315
- const lastSpace = truncated.lastIndexOf(' ');
316
- if (lastSpace > heardChars * 0.6) {
317
- return truncated.slice(0, lastSpace);
318
- }
319
- return truncated;
320
- }
321
- export { buildNodePrompt };
@@ -1,9 +0,0 @@
1
- import type { GeminiFunctionDeclaration } from '../../capabilities/adapters/gemini.js';
2
- import type { ResolvedNode } from '../../types/channel.js';
3
- import type { AnyTool } from '../../types/effectTool.js';
4
- import { buildToolSet } from '../../tools/effect/defineTool.js';
5
- export declare function v2ToolsToGemini(tools: Record<string, AnyTool>): GeminiFunctionDeclaration[];
6
- export declare function resolveVoiceGeminiTools(resolved: ResolvedNode, toolDefs: Record<string, AnyTool>, options?: {
7
- siloFlowControl?: boolean;
8
- }): GeminiFunctionDeclaration[];
9
- export { buildToolSet };
@@ -1,51 +0,0 @@
1
- import { z } from 'zod';
2
- import { toGeminiDeclarations } from '../../capabilities/adapters/gemini.js';
3
- import { buildToolSet } from '../../tools/effect/defineTool.js';
4
- import { isFlowTransitionControlTool } from '../../flow/flowControlTools.js';
5
- function toolToDeclaration(name, tool) {
6
- return {
7
- name: tool.name || name,
8
- description: tool.description,
9
- parameters: tool.input ?? z.object({}),
10
- execute: async () => ({}),
11
- };
12
- }
13
- export function v2ToolsToGemini(tools) {
14
- const declarations = Object.entries(tools).map(([name, tool]) => toolToDeclaration(name, tool));
15
- return toGeminiDeclarations(declarations);
16
- }
17
- export function resolveVoiceGeminiTools(resolved, toolDefs, options) {
18
- const siloFlowControl = options?.siloFlowControl === true;
19
- const merged = { ...toolDefs, ...(resolved.localTools ?? {}) };
20
- const fromNode = toolSetToEffectTools(resolved.tools);
21
- for (const [name, tool] of Object.entries(fromNode)) {
22
- if (siloFlowControl && isFlowTransitionControlTool(name)) {
23
- continue;
24
- }
25
- if (!merged[name]) {
26
- merged[name] = tool;
27
- }
28
- }
29
- if (siloFlowControl) {
30
- for (const name of Object.keys(merged)) {
31
- if (isFlowTransitionControlTool(name)) {
32
- delete merged[name];
33
- }
34
- }
35
- }
36
- return v2ToolsToGemini(merged);
37
- }
38
- function toolSetToEffectTools(tools) {
39
- const out = {};
40
- for (const [name, aiTool] of Object.entries(tools)) {
41
- const spec = aiTool;
42
- out[name] = {
43
- name,
44
- description: spec.description ?? name,
45
- input: spec.inputSchema,
46
- execute: async () => ({}),
47
- };
48
- }
49
- return out;
50
- }
51
- export { buildToolSet };
@@ -1,35 +0,0 @@
1
- export interface ToolErrorConfig<T = unknown> {
2
- maxRetries?: number;
3
- retryDelayMs?: number;
4
- fallbackValue?: T;
5
- onError?: (error: Error, attempt: number) => Promise<void>;
6
- shouldRetry?: (error: Error) => boolean;
7
- }
8
- export interface ToolResult<T = unknown> {
9
- success: boolean;
10
- data?: T;
11
- error?: Error;
12
- attempts: number;
13
- totalTimeMs: number;
14
- }
15
- export declare function withErrorHandling<T, Args extends unknown[], R = Awaited<T>>(fn: (...args: Args) => T, config?: ToolErrorConfig<R>): (...args: Args) => Promise<ToolResult<R>>;
16
- export declare function executeWithRetry<T>(fn: () => Promise<T>, config?: {
17
- maxRetries?: number;
18
- delayMs?: number;
19
- backoffMultiplier?: number;
20
- maxDelayMs?: number;
21
- onRetry?: (error: Error, attempt: number) => void;
22
- }): Promise<T>;
23
- export declare function createCircuitBreaker<T, Args extends unknown[]>(fn: (...args: Args) => Promise<T>, config?: {
24
- failureThreshold?: number;
25
- resetTimeoutMs?: number;
26
- }): (...args: Args) => Promise<T>;
27
- export declare function isPermanentError(error: Error): boolean;
28
- export declare class CircuitOpenError extends Error {
29
- constructor(message: string);
30
- }
31
- export declare function isCircuitOpenError(error: unknown): error is CircuitOpenError;
32
- export declare class ToolTimeoutError extends Error {
33
- constructor(message?: string);
34
- }
35
- export declare function withTimeout<T>(fn: () => Promise<T>, timeoutMs: number, error?: Error): Promise<T>;
@@ -1,145 +0,0 @@
1
- export function withErrorHandling(fn, config = {}) {
2
- const { maxRetries = 3, retryDelayMs = 1000, fallbackValue, onError, shouldRetry = (e) => !isPermanentError(e), } = config;
3
- return async (...args) => {
4
- const startTime = Date.now();
5
- let lastError;
6
- let attempts = 0;
7
- while (attempts <= maxRetries) {
8
- attempts++;
9
- try {
10
- const result = await fn(...args);
11
- return {
12
- success: true,
13
- data: result,
14
- attempts,
15
- totalTimeMs: Date.now() - startTime,
16
- };
17
- }
18
- catch (error) {
19
- lastError = error;
20
- if (onError) {
21
- await onError(lastError, attempts);
22
- }
23
- if (attempts <= maxRetries && shouldRetry(lastError)) {
24
- await sleep(retryDelayMs * attempts);
25
- }
26
- else {
27
- break;
28
- }
29
- }
30
- }
31
- return {
32
- success: false,
33
- error: lastError,
34
- attempts,
35
- totalTimeMs: Date.now() - startTime,
36
- data: fallbackValue,
37
- };
38
- };
39
- }
40
- export async function executeWithRetry(fn, config = {}) {
41
- const { maxRetries = 3, delayMs = 1000, backoffMultiplier = 2, maxDelayMs = 10000, onRetry, } = config;
42
- let lastError;
43
- let currentDelay = delayMs;
44
- for (let attempt = 1; attempt <= maxRetries + 1; attempt++) {
45
- try {
46
- return await fn();
47
- }
48
- catch (error) {
49
- lastError = error;
50
- if (isPermanentError(lastError) || attempt > maxRetries) {
51
- throw lastError;
52
- }
53
- if (onRetry) {
54
- onRetry(lastError, attempt);
55
- }
56
- await sleep(currentDelay);
57
- currentDelay = Math.min(currentDelay * backoffMultiplier, maxDelayMs);
58
- }
59
- }
60
- throw lastError;
61
- }
62
- export function createCircuitBreaker(fn, config = {}) {
63
- const { failureThreshold = 5, resetTimeoutMs = 30000, } = config;
64
- let failures = 0;
65
- let lastFailureTime;
66
- let state = 'CLOSED';
67
- return async (...args) => {
68
- const now = Date.now();
69
- if (state === 'OPEN') {
70
- if (lastFailureTime && now - lastFailureTime >= resetTimeoutMs) {
71
- state = 'HALF_OPEN';
72
- }
73
- else {
74
- throw new CircuitOpenError('Circuit breaker is open');
75
- }
76
- }
77
- try {
78
- const result = await fn(...args);
79
- if (state === 'HALF_OPEN') {
80
- state = 'CLOSED';
81
- failures = 0;
82
- }
83
- else {
84
- failures = 0; // Success in CLOSED state resets failure count
85
- }
86
- return result;
87
- }
88
- catch (error) {
89
- failures++;
90
- lastFailureTime = now;
91
- if (state === 'HALF_OPEN' || failures >= failureThreshold) {
92
- state = 'OPEN';
93
- }
94
- throw error;
95
- }
96
- };
97
- }
98
- export function isPermanentError(error) {
99
- const permanentErrors = [
100
- 'not found',
101
- 'invalid',
102
- 'unauthorized',
103
- 'forbidden',
104
- 'not implemented',
105
- 'deprecated',
106
- 'already exists',
107
- 'permission denied',
108
- ];
109
- const message = error.message.toLowerCase();
110
- return permanentErrors.some((e) => message.includes(e));
111
- }
112
- export class CircuitOpenError extends Error {
113
- constructor(message) {
114
- super(message);
115
- this.name = 'CircuitOpenError';
116
- }
117
- }
118
- export function isCircuitOpenError(error) {
119
- return error instanceof CircuitOpenError;
120
- }
121
- export class ToolTimeoutError extends Error {
122
- constructor(message = 'Tool execution timed out') {
123
- super(message);
124
- this.name = 'ToolTimeoutError';
125
- }
126
- }
127
- export async function withTimeout(fn, timeoutMs, error) {
128
- let timer;
129
- const timeoutPromise = new Promise((_, reject) => {
130
- timer = setTimeout(() => {
131
- reject(error ?? new ToolTimeoutError());
132
- }, timeoutMs);
133
- });
134
- try {
135
- return await Promise.race([fn(), timeoutPromise]);
136
- }
137
- finally {
138
- if (timer) {
139
- clearTimeout(timer);
140
- }
141
- }
142
- }
143
- function sleep(ms) {
144
- return new Promise((resolve) => setTimeout(resolve, ms));
145
- }