@kuralle-agents/core 0.13.0 → 0.15.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 (160) 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 +30 -7
  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 +21 -2
  15. package/dist/flow/extraction.js +62 -7
  16. package/dist/flow/reduceTransition.d.ts +2 -2
  17. package/dist/flow/reduceTransition.js +7 -3
  18. package/dist/flow/runFlow.d.ts +11 -0
  19. package/dist/flow/runFlow.js +68 -12
  20. package/dist/foundation/AgentStateController.d.ts +1 -1
  21. package/dist/foundation/ToolExecutor.d.ts +2 -2
  22. package/dist/foundation/index.d.ts +0 -11
  23. package/dist/foundation/index.js +0 -5
  24. package/dist/index.d.ts +9 -14
  25. package/dist/index.js +5 -6
  26. package/dist/outcomes/streamPart.d.ts +2 -2
  27. package/dist/outcomes/streamPart.js +2 -3
  28. package/dist/prompts/PromptBuilder.d.ts +3 -3
  29. package/dist/prompts/PromptBuilder.js +6 -6
  30. package/dist/prompts/index.d.ts +1 -1
  31. package/dist/prompts/types.d.ts +7 -2
  32. package/dist/runtime/InMemoryRetrievalCache.d.ts +1 -1
  33. package/dist/runtime/KnowledgeProvider.d.ts +3 -2
  34. package/dist/runtime/KnowledgeProvider.js +54 -30
  35. package/dist/runtime/Runtime.d.ts +9 -1
  36. package/dist/runtime/Runtime.js +123 -30
  37. package/dist/runtime/TraceRecorder.d.ts +22 -4
  38. package/dist/runtime/TraceRecorder.js +65 -29
  39. package/dist/runtime/buildAgentToolSurface.js +1 -1
  40. package/dist/runtime/channels/TextDriver.js +12 -9
  41. package/dist/runtime/channels/executeModelTool.d.ts +6 -0
  42. package/dist/runtime/channels/executeModelTool.js +70 -10
  43. package/dist/runtime/channels/extractionTurn.js +1 -1
  44. package/dist/runtime/channels/index.d.ts +0 -3
  45. package/dist/runtime/channels/index.js +0 -6
  46. package/dist/runtime/channels/streaming/hostControlSpeak.js +5 -1
  47. package/dist/runtime/channels/streaming/speakGated.js +33 -12
  48. package/dist/runtime/citations/index.d.ts +3 -3
  49. package/dist/runtime/closeRun.js +3 -2
  50. package/dist/runtime/controlFlowSignal.d.ts +24 -0
  51. package/dist/runtime/controlFlowSignal.js +29 -0
  52. package/dist/runtime/ctx.d.ts +3 -4
  53. package/dist/runtime/ctx.js +39 -5
  54. package/dist/runtime/durable/RunStore.js +1 -1
  55. package/dist/runtime/grounding/gather.d.ts +1 -1
  56. package/dist/runtime/grounding/inMemoryKnowledge.d.ts +1 -1
  57. package/dist/runtime/grounding/knowledge.d.ts +1 -1
  58. package/dist/runtime/hostLoop.js +39 -10
  59. package/dist/runtime/index.d.ts +1 -2
  60. package/dist/runtime/index.js +1 -1
  61. package/dist/runtime/outcomeMarking.d.ts +2 -2
  62. package/dist/runtime/outcomeMarking.js +2 -1
  63. package/dist/runtime/policies/agentTurn.d.ts +1 -1
  64. package/dist/runtime/runHookSafely.d.ts +3 -0
  65. package/dist/runtime/runHookSafely.js +19 -0
  66. package/dist/scheduler/index.d.ts +2 -2
  67. package/dist/scheduler/index.js +1 -1
  68. package/dist/services/TracingService.js +2 -3
  69. package/dist/skills/collectSkills.d.ts +11 -3
  70. package/dist/skills/collectSkills.js +51 -8
  71. package/dist/skills/compositeSkillStore.d.ts +26 -0
  72. package/dist/skills/compositeSkillStore.js +54 -0
  73. package/dist/skills/defineSkill.d.ts +21 -0
  74. package/dist/skills/defineSkill.js +26 -0
  75. package/dist/skills/fsSkillStore.d.ts +3 -0
  76. package/dist/skills/fsSkillStore.js +79 -0
  77. package/dist/skills/index.d.ts +4 -0
  78. package/dist/skills/index.js +4 -0
  79. package/dist/skills/parseSkillFrontmatter.d.ts +14 -0
  80. package/dist/skills/parseSkillFrontmatter.js +183 -0
  81. package/dist/skills/wireAgentSkills.d.ts +2 -1
  82. package/dist/skills/wireAgentSkills.js +2 -2
  83. package/dist/testing/mocks.d.ts +3 -3
  84. package/dist/testing/mocks.js +7 -3
  85. package/dist/tools/controlResults.d.ts +14 -0
  86. package/dist/tools/controlResults.js +15 -0
  87. package/dist/tools/effect/ToolExecutor.d.ts +6 -0
  88. package/dist/tools/effect/ToolExecutor.js +71 -35
  89. package/dist/tools/effect/defineTool.d.ts +9 -0
  90. package/dist/tools/effect/defineTool.js +1 -0
  91. package/dist/tools/effect/errors.d.ts +2 -5
  92. package/dist/tools/effect/errors.js +2 -5
  93. package/dist/tools/fs/createFsTool.js +2 -1
  94. package/dist/tools/index.d.ts +0 -1
  95. package/dist/tools/index.js +0 -1
  96. package/dist/types/effectTool.d.ts +2 -0
  97. package/dist/types/flow.d.ts +1 -1
  98. package/dist/types/guardrails.d.ts +5 -0
  99. package/dist/types/hooks.d.ts +2 -2
  100. package/dist/types/index.d.ts +25 -16
  101. package/dist/types/index.js +8 -13
  102. package/dist/types/knowledge.d.ts +213 -0
  103. package/dist/types/processors.d.ts +0 -58
  104. package/dist/types/run-context.d.ts +3 -7
  105. package/dist/types/runtime.d.ts +3 -74
  106. package/dist/types/session.d.ts +1 -1
  107. package/dist/types/skills.d.ts +15 -1
  108. package/dist/types/stream.d.ts +133 -71
  109. package/dist/types/stream.js +34 -1
  110. package/dist/types/trace.d.ts +2 -0
  111. package/guides/AGENTS.md +3 -3
  112. package/guides/FLOWS.md +1 -1
  113. package/guides/GETTING_STARTED.md +1 -1
  114. package/guides/RUNTIME.md +20 -15
  115. package/guides/TOOLS.md +1 -1
  116. package/package.json +3 -13
  117. package/dist/foundation/AgentDefinition.d.ts +0 -17
  118. package/dist/foundation/ConversationEventLog.d.ts +0 -71
  119. package/dist/foundation/ConversationEventLog.js +0 -1
  120. package/dist/foundation/ConversationState.d.ts +0 -33
  121. package/dist/foundation/ConversationState.js +0 -1
  122. package/dist/foundation/DefaultConversationEventLog.d.ts +0 -27
  123. package/dist/foundation/DefaultConversationEventLog.js +0 -205
  124. package/dist/foundation/DefaultConversationState.d.ts +0 -36
  125. package/dist/foundation/DefaultConversationState.js +0 -103
  126. package/dist/foundation/DefaultToolExecutor.d.ts +0 -57
  127. package/dist/foundation/DefaultToolExecutor.js +0 -132
  128. package/dist/foundation/createFoundation.d.ts +0 -32
  129. package/dist/foundation/createFoundation.js +0 -33
  130. package/dist/hooks/HookRunner.d.ts +0 -37
  131. package/dist/hooks/HookRunner.js +0 -111
  132. package/dist/hooks/builtin/logging.d.ts +0 -5
  133. package/dist/hooks/builtin/logging.js +0 -98
  134. package/dist/hooks/builtin/metrics.d.ts +0 -7
  135. package/dist/hooks/builtin/metrics.js +0 -60
  136. package/dist/hooks/builtin/observability.d.ts +0 -20
  137. package/dist/hooks/builtin/observability.js +0 -572
  138. package/dist/hooks/helpers.d.ts +0 -22
  139. package/dist/hooks/helpers.js +0 -183
  140. package/dist/hooks/index.d.ts +0 -8
  141. package/dist/hooks/index.js +0 -6
  142. package/dist/realtime/RealtimeAudioClient.d.ts +0 -166
  143. package/dist/realtime/RealtimeAudioClient.js +0 -14
  144. package/dist/realtime/RealtimeSessionHandle.d.ts +0 -6
  145. package/dist/realtime/RealtimeSessionHandle.js +0 -1
  146. package/dist/realtime/index.d.ts +0 -2
  147. package/dist/realtime/index.js +0 -1
  148. package/dist/runtime/channels/VoiceDriver.d.ts +0 -31
  149. package/dist/runtime/channels/VoiceDriver.js +0 -321
  150. package/dist/runtime/channels/voiceTools.d.ts +0 -9
  151. package/dist/runtime/channels/voiceTools.js +0 -51
  152. package/dist/tools/errorHandling.d.ts +0 -35
  153. package/dist/tools/errorHandling.js +0 -145
  154. package/dist/types/voice.d.ts +0 -517
  155. package/dist/types/voice.js +0 -14
  156. package/dist/utils/isRecord.d.ts +0 -1
  157. package/dist/utils/isRecord.js +0 -3
  158. package/dist/utils/messageNormalization.d.ts +0 -2
  159. package/dist/utils/messageNormalization.js +0 -137
  160. /package/dist/{foundation/AgentDefinition.js → types/knowledge.js} +0 -0
@@ -1,205 +0,0 @@
1
- import crypto from 'node:crypto';
2
- import { isRecord } from '../utils/isRecord.js';
3
- /** Working memory key for the runtime event log array. */
4
- const EVENT_LOG_KEY = 'runtimeEventLog';
5
- /** Working memory key for accumulated assistant text between flushes. */
6
- const ASSISTANT_TEXT_KEY = '__ariaAssistantText';
7
- /** Working memory key for turn tracking. */
8
- const SESSION_TURN_KEY = '__ariaSessionTurn';
9
- /** Max event entries before oldest are evicted. */
10
- const MAX_ENTRIES = 2000;
11
- /** Stream part types that trigger a checkpoint save. */
12
- const CHECKPOINT_TYPES = new Set([
13
- 'tool-result',
14
- 'tool-error',
15
- 'flow-transition',
16
- ]);
17
- /**
18
- * Default conversation event log extracted from SessionEventManager + Runtime checkpoint logic.
19
- *
20
- * Handles:
21
- * - Recording stream parts into session working memory
22
- * - Text-delta accumulation and flush on terminal events
23
- * - Value truncation at depth 5 for safe serialization
24
- * - Checkpoint determination and persistence
25
- */
26
- export class DefaultConversationEventLog {
27
- sessionStore;
28
- constructor(config) {
29
- this.sessionStore = config.sessionStore;
30
- }
31
- record(context, part) {
32
- // Fast path: text-delta only accumulates text — no UUID, no base object
33
- if (part.type === 'text-delta') {
34
- const prevRaw = context.session.workingMemory[ASSISTANT_TEXT_KEY];
35
- const prev = typeof prevRaw === 'string' ? prevRaw : '';
36
- context.session.workingMemory[ASSISTANT_TEXT_KEY] = prev + part.delta;
37
- return;
38
- }
39
- const base = {
40
- id: crypto.randomUUID(),
41
- sessionId: context.session.id,
42
- agentId: context.agentId,
43
- turn: this.getSessionTurn(context.session),
44
- timestamp: new Date().toISOString(),
45
- };
46
- switch (part.type) {
47
- case 'input':
48
- this.appendEvent(context, { ...base, type: 'user', text: part.text, userId: part.userId });
49
- return;
50
- case 'tool-call':
51
- this.appendEvent(context, {
52
- ...base,
53
- type: 'tool_call',
54
- toolCallId: part.toolCallId,
55
- toolName: part.toolName,
56
- args: this.toEventLogValue(part.args),
57
- });
58
- return;
59
- case 'tool-result':
60
- this.appendEvent(context, {
61
- ...base,
62
- type: 'tool_result',
63
- toolCallId: part.toolCallId,
64
- toolName: part.toolName,
65
- result: this.toEventLogValue(part.result),
66
- });
67
- return;
68
- case 'tool-error':
69
- this.appendEvent(context, {
70
- ...base,
71
- type: 'tool_error',
72
- toolCallId: part.toolCallId,
73
- toolName: part.toolName,
74
- error: part.error,
75
- });
76
- return;
77
- case 'flow-transition':
78
- this.appendEvent(context, {
79
- ...base,
80
- type: 'transition',
81
- kind: 'flow',
82
- from: part.from,
83
- to: part.to,
84
- });
85
- return;
86
- case 'handoff':
87
- this.appendEvent(context, {
88
- ...base,
89
- type: 'transition',
90
- kind: 'handoff',
91
- from: part.from,
92
- to: part.to,
93
- reason: part.reason,
94
- });
95
- return;
96
- case 'turn-end':
97
- this.flushAssistantText(context, 'turn-end');
98
- return;
99
- case 'done':
100
- this.flushAssistantText(context, 'done');
101
- return;
102
- case 'error':
103
- this.flushAssistantText(context, 'error');
104
- return;
105
- case 'text-clear':
106
- delete context.session.workingMemory[ASSISTANT_TEXT_KEY];
107
- return;
108
- default:
109
- return;
110
- }
111
- }
112
- async checkpoint(session) {
113
- const now = new Date();
114
- session.updatedAt = now;
115
- if (session.metadata) {
116
- session.metadata.lastActiveAt = now;
117
- }
118
- // Strip non-serializable pending extraction promises before save —
119
- // they can't survive JSON.stringify (Redis/Postgres) or structuredClone (cache).
120
- const pending = session.__pendingExtractions;
121
- delete session.__pendingExtractions;
122
- await this.sessionStore.save(session);
123
- // Restore so in-process turn tracking continues
124
- if (pending?.length) {
125
- session.__pendingExtractions = pending;
126
- }
127
- }
128
- shouldCheckpoint(part) {
129
- return CHECKPOINT_TYPES.has(part.type);
130
- }
131
- cleanup(session) {
132
- delete session.workingMemory[ASSISTANT_TEXT_KEY];
133
- }
134
- // --- Private helpers (extracted from SessionEventManager) ---
135
- getSessionTurn(session) {
136
- const value = session.workingMemory[SESSION_TURN_KEY];
137
- return typeof value === 'number' && Number.isFinite(value) ? value : 0;
138
- }
139
- appendEvent(context, entry) {
140
- const current = context.session.workingMemory[EVENT_LOG_KEY];
141
- let events;
142
- if (Array.isArray(current)) {
143
- events = current;
144
- }
145
- else {
146
- events = [];
147
- context.session.workingMemory[EVENT_LOG_KEY] = events;
148
- }
149
- events.push(entry);
150
- if (events.length > MAX_ENTRIES) {
151
- events.splice(0, events.length - MAX_ENTRIES);
152
- }
153
- // Touch session
154
- const now = new Date();
155
- context.session.updatedAt = now;
156
- if (context.session.metadata) {
157
- context.session.metadata.lastActiveAt = now;
158
- }
159
- }
160
- flushAssistantText(context, trigger) {
161
- const textRaw = context.session.workingMemory[ASSISTANT_TEXT_KEY];
162
- const text = (typeof textRaw === 'string' ? textRaw : '').trim();
163
- if (text.length > 0) {
164
- this.appendEvent(context, {
165
- id: crypto.randomUUID(),
166
- sessionId: context.session.id,
167
- agentId: context.agentId,
168
- turn: this.getSessionTurn(context.session),
169
- type: 'assistant_final',
170
- trigger,
171
- text,
172
- timestamp: new Date().toISOString(),
173
- });
174
- }
175
- delete context.session.workingMemory[ASSISTANT_TEXT_KEY];
176
- }
177
- toEventLogValue(value, depth = 0) {
178
- if (value === null || value === undefined) {
179
- return value;
180
- }
181
- if (depth >= 5) {
182
- return '[truncated]';
183
- }
184
- if (value instanceof Date) {
185
- return value.toISOString();
186
- }
187
- if (value instanceof Error) {
188
- return { name: value.name, message: value.message };
189
- }
190
- if (Array.isArray(value)) {
191
- return value.slice(0, 50).map(item => this.toEventLogValue(item, depth + 1));
192
- }
193
- if (typeof value === 'function') {
194
- return '[function]';
195
- }
196
- if (isRecord(value)) {
197
- const out = {};
198
- for (const [k, v] of Object.entries(value).slice(0, 50)) {
199
- out[k] = this.toEventLogValue(v, depth + 1);
200
- }
201
- return out;
202
- }
203
- return value;
204
- }
205
- }
@@ -1,36 +0,0 @@
1
- import type { ModelMessage } from 'ai';
2
- import type { ChannelId, Session, WorkingMemory } from '../types/index.js';
3
- import type { SessionStore } from '../session/SessionStore.js';
4
- import type { ConversationState } from './ConversationState.js';
5
- export interface DefaultConversationStateConfig {
6
- sessionStore: SessionStore;
7
- defaultAgentId: string;
8
- }
9
- /**
10
- * Default conversation state implementation extracted from Runtime.
11
- *
12
- * Handles:
13
- * - Session load/create/save/delete via SessionStore
14
- * - Message appending with normalization
15
- * - Turn counting
16
- * - Working memory access
17
- */
18
- export declare class DefaultConversationState implements ConversationState {
19
- private sessionStore;
20
- private defaultAgentId;
21
- constructor(config: DefaultConversationStateConfig);
22
- load(sessionId: string, userId?: string): Promise<Session>;
23
- save(session: Session): Promise<void>;
24
- delete(sessionId: string): Promise<void>;
25
- workingMemory(session: Session): WorkingMemory;
26
- appendUserMessage(session: Session, text: string): void;
27
- appendAssistantMessage(session: Session, text: string): void;
28
- appendMessage(session: Session, message: ModelMessage): void;
29
- getSessionTurn(session: Session): number;
30
- bumpSessionTurn(session: Session): number;
31
- touchSession(session: Session): void;
32
- createSession(id: string, defaultAgentId: string, userId?: string, opts?: {
33
- channelId?: ChannelId;
34
- conversationId?: string;
35
- }): Session;
36
- }
@@ -1,103 +0,0 @@
1
- import { SessionWorkingMemory } from '../runtime/WorkingMemory.js';
2
- import { normalizeModelMessage } from '../utils/messageNormalization.js';
3
- /** Internal working memory key for turn tracking. */
4
- const SESSION_TURN_KEY = '__ariaSessionTurn';
5
- /**
6
- * Default conversation state implementation extracted from Runtime.
7
- *
8
- * Handles:
9
- * - Session load/create/save/delete via SessionStore
10
- * - Message appending with normalization
11
- * - Turn counting
12
- * - Working memory access
13
- */
14
- export class DefaultConversationState {
15
- sessionStore;
16
- defaultAgentId;
17
- constructor(config) {
18
- this.sessionStore = config.sessionStore;
19
- this.defaultAgentId = config.defaultAgentId;
20
- }
21
- async load(sessionId, userId) {
22
- const existing = await this.sessionStore.get(sessionId);
23
- if (existing) {
24
- return existing;
25
- }
26
- return this.createSession(sessionId, this.defaultAgentId, userId);
27
- }
28
- async save(session) {
29
- this.touchSession(session);
30
- await this.sessionStore.save(session);
31
- }
32
- async delete(sessionId) {
33
- await this.sessionStore.delete(sessionId);
34
- }
35
- workingMemory(session) {
36
- return new SessionWorkingMemory(session);
37
- }
38
- appendUserMessage(session, text) {
39
- const message = {
40
- role: 'user',
41
- content: [{ type: 'text', text }],
42
- };
43
- this.appendMessage(session, message);
44
- }
45
- appendAssistantMessage(session, text) {
46
- const message = {
47
- role: 'assistant',
48
- content: [{ type: 'text', text }],
49
- };
50
- this.appendMessage(session, message);
51
- }
52
- appendMessage(session, message) {
53
- const normalized = normalizeModelMessage(message);
54
- if (normalized) {
55
- session.messages.push(normalized);
56
- this.touchSession(session);
57
- }
58
- }
59
- getSessionTurn(session) {
60
- const value = session.workingMemory[SESSION_TURN_KEY];
61
- return typeof value === 'number' && Number.isFinite(value) ? value : 0;
62
- }
63
- bumpSessionTurn(session) {
64
- const next = this.getSessionTurn(session) + 1;
65
- session.workingMemory[SESSION_TURN_KEY] = next;
66
- this.touchSession(session);
67
- return next;
68
- }
69
- touchSession(session) {
70
- const now = new Date();
71
- session.updatedAt = now;
72
- if (session.metadata) {
73
- session.metadata.lastActiveAt = now;
74
- }
75
- }
76
- createSession(id, defaultAgentId, userId, opts) {
77
- const now = new Date();
78
- const channelId = opts?.channelId ?? 'web';
79
- const conversationId = opts?.conversationId ?? id;
80
- return {
81
- id,
82
- conversationId,
83
- channelId,
84
- userId,
85
- messages: [],
86
- createdAt: now,
87
- updatedAt: now,
88
- workingMemory: {},
89
- currentAgent: defaultAgentId,
90
- activeAgentId: defaultAgentId,
91
- state: {},
92
- metadata: {
93
- createdAt: now,
94
- lastActiveAt: now,
95
- totalTokens: 0,
96
- totalSteps: 0,
97
- handoffHistory: [],
98
- },
99
- agentStates: {},
100
- handoffHistory: [],
101
- };
102
- }
103
- }
@@ -1,57 +0,0 @@
1
- import type { Session, ToolCallRecord } from '../types/index.js';
2
- import type { ToolExecutor, ExecutableTool } from './ToolExecutor.js';
3
- import type { ToolEnforcer } from '../guards/ToolEnforcer.js';
4
- import type { HookRunner } from '../hooks/HookRunner.js';
5
- import type { MemoryService } from '../memory/MemoryService.js';
6
- export interface DefaultToolExecutorConfig {
7
- enforcer: ToolEnforcer;
8
- hookRunner: HookRunner;
9
- memoryService?: MemoryService;
10
- /** Default timeout in milliseconds for tool execution. Defaults to 30000 (30s). */
11
- defaultToolTimeoutMs?: number;
12
- }
13
- /**
14
- * Error thrown when a tool execution exceeds the configured timeout.
15
- */
16
- export declare class ToolTimeoutError extends Error {
17
- readonly toolName: string;
18
- readonly timeoutMs: number;
19
- constructor(toolName: string, timeoutMs: number);
20
- }
21
- /**
22
- * Default tool executor extracted from Runtime.wrapToolsWithEnforcement().
23
- *
24
- * Handles:
25
- * - Enforcement checks via ToolEnforcer
26
- * - Idempotency key generation
27
- * - Context enrichment (experimental_context)
28
- * - Error propagation via HookRunner
29
- */
30
- export declare class DefaultToolExecutor implements ToolExecutor {
31
- private enforcer;
32
- private hookRunner;
33
- private memoryService?;
34
- private defaultToolTimeoutMs?;
35
- constructor(config: DefaultToolExecutorConfig);
36
- execute(args: {
37
- session: Session;
38
- userId?: string;
39
- agentId: string;
40
- toolName: string;
41
- tool: ExecutableTool;
42
- input: unknown;
43
- toolCallId?: string;
44
- abortSignal?: AbortSignal;
45
- step?: number;
46
- turn?: number;
47
- toolCallHistory?: ToolCallRecord[];
48
- }): Promise<unknown>;
49
- buildIdempotencyKey(args: {
50
- sessionId: string;
51
- agentId: string;
52
- step: number;
53
- toolName: string;
54
- toolCallId: string;
55
- }): string;
56
- private withToolExecutionMetadata;
57
- }
@@ -1,132 +0,0 @@
1
- import crypto from 'node:crypto';
2
- import { isRecord } from '../utils/isRecord.js';
3
- /**
4
- * Error thrown when a tool execution exceeds the configured timeout.
5
- */
6
- export class ToolTimeoutError extends Error {
7
- toolName;
8
- timeoutMs;
9
- constructor(toolName, timeoutMs) {
10
- super(`Tool "${toolName}" timeout after ${timeoutMs}ms`);
11
- this.name = 'ToolTimeoutError';
12
- this.toolName = toolName;
13
- this.timeoutMs = timeoutMs;
14
- }
15
- }
16
- /**
17
- * Default tool executor extracted from Runtime.wrapToolsWithEnforcement().
18
- *
19
- * Handles:
20
- * - Enforcement checks via ToolEnforcer
21
- * - Idempotency key generation
22
- * - Context enrichment (experimental_context)
23
- * - Error propagation via HookRunner
24
- */
25
- export class DefaultToolExecutor {
26
- enforcer;
27
- hookRunner;
28
- memoryService;
29
- defaultToolTimeoutMs;
30
- constructor(config) {
31
- this.enforcer = config.enforcer;
32
- this.hookRunner = config.hookRunner;
33
- this.memoryService = config.memoryService;
34
- this.defaultToolTimeoutMs = config.defaultToolTimeoutMs;
35
- }
36
- async execute(args) {
37
- const { session, agentId, toolName, tool, input, step = 0, turn = 0, toolCallHistory = [], } = args;
38
- const toolCallId = args.toolCallId ?? crypto.randomUUID();
39
- const idempotencyKey = this.buildIdempotencyKey({
40
- sessionId: session.id,
41
- agentId,
42
- step,
43
- toolName,
44
- toolCallId,
45
- });
46
- const callRecord = {
47
- toolCallId,
48
- toolName,
49
- args: input,
50
- idempotencyKey,
51
- success: true,
52
- timestamp: Date.now(),
53
- };
54
- // Enforcement check
55
- const enforcement = await this.enforcer.check(callRecord, {
56
- previousCalls: toolCallHistory,
57
- currentStep: step,
58
- sessionState: session.state ?? {},
59
- });
60
- if (!enforcement.allowed) {
61
- const reason = enforcement.reason ?? 'Tool call blocked by enforcement';
62
- callRecord.success = false;
63
- callRecord.error = new Error(reason);
64
- toolCallHistory.push(callRecord);
65
- const runContext = {
66
- session,
67
- agentId,
68
- stepCount: step,
69
- totalTokens: 0,
70
- handoffStack: [],
71
- startTime: Date.now(),
72
- consecutiveErrors: 0,
73
- toolCallHistory,
74
- };
75
- await this.hookRunner.onToolError(runContext, callRecord, callRecord.error);
76
- throw callRecord.error;
77
- }
78
- // Build enriched options for the tool's execute function
79
- if (!('execute' in tool) || typeof tool.execute !== 'function') {
80
- throw new Error(`Tool "${toolName}" does not have an execute function`);
81
- }
82
- const enrichedOptions = this.withToolExecutionMetadata(undefined, { session, agentId, step, turn, toolName, toolCallId, idempotencyKey });
83
- try {
84
- const timeoutMs = tool.timeout ??
85
- this.defaultToolTimeoutMs ??
86
- 30_000;
87
- const exec = tool.execute.bind(tool);
88
- const result = await Promise.race([
89
- exec(input, enrichedOptions),
90
- new Promise((_, reject) => {
91
- const timer = setTimeout(() => reject(new ToolTimeoutError(toolName, timeoutMs)), timeoutMs);
92
- if (typeof timer === 'object' && timer !== null && 'unref' in timer) {
93
- timer.unref();
94
- }
95
- }),
96
- ]);
97
- return result;
98
- }
99
- catch (error) {
100
- console.error(`[ToolExecutor] Tool execution failed for ${toolName}:`, error);
101
- throw error;
102
- }
103
- }
104
- buildIdempotencyKey(args) {
105
- return `${args.sessionId}:${args.agentId}:${args.step}:${args.toolName}:${args.toolCallId}`;
106
- }
107
- withToolExecutionMetadata(options, ctx) {
108
- const baseOptions = options ?? {
109
- toolCallId: ctx.toolCallId,
110
- messages: [],
111
- };
112
- const existingContext = isRecord(baseOptions.experimental_context)
113
- ? baseOptions.experimental_context
114
- : {};
115
- return {
116
- ...baseOptions,
117
- toolCallId: ctx.toolCallId,
118
- experimental_context: {
119
- ...existingContext,
120
- session: ctx.session,
121
- sessionId: ctx.session.id,
122
- agentId: ctx.agentId,
123
- step: ctx.step,
124
- turn: ctx.turn,
125
- toolName: ctx.toolName,
126
- toolCallId: ctx.toolCallId,
127
- idempotencyKey: ctx.idempotencyKey,
128
- memoryService: this.memoryService,
129
- },
130
- };
131
- }
132
- }
@@ -1,32 +0,0 @@
1
- import type { SessionStore } from '../session/SessionStore.js';
2
- import type { MemoryService } from '../memory/MemoryService.js';
3
- import type { EnforcementRule, HarnessHooks } from '../types/index.js';
4
- import type { ToolExecutor } from './ToolExecutor.js';
5
- import type { ConversationState } from './ConversationState.js';
6
- import type { ConversationEventLog } from './ConversationEventLog.js';
7
- import type { AgentStateController } from './AgentStateController.js';
8
- /**
9
- * Configuration for creating a foundation service bundle.
10
- */
11
- export interface FoundationConfig {
12
- sessionStore?: SessionStore;
13
- defaultAgentId?: string;
14
- enforcementRules?: EnforcementRule[];
15
- hooks?: HarnessHooks;
16
- memoryService?: MemoryService;
17
- }
18
- /**
19
- * Bundle of all foundation services. Both Runtime and VoiceEngine
20
- * compose this to share operational logic.
21
- */
22
- export interface Foundation {
23
- toolExecutor: ToolExecutor;
24
- conversationState: ConversationState;
25
- eventLog: ConversationEventLog;
26
- agentState: AgentStateController;
27
- }
28
- /**
29
- * Create a foundation service bundle with default implementations.
30
- * Services can be individually overridden if needed.
31
- */
32
- export declare function createFoundation(config?: FoundationConfig): Foundation;
@@ -1,33 +0,0 @@
1
- import { DefaultToolExecutor } from './DefaultToolExecutor.js';
2
- import { DefaultConversationState } from './DefaultConversationState.js';
3
- import { DefaultConversationEventLog } from './DefaultConversationEventLog.js';
4
- import { DefaultAgentStateController } from './DefaultAgentStateController.js';
5
- import { ToolEnforcer } from '../guards/ToolEnforcer.js';
6
- import { defaultEnforcementRules } from '../guards/rules.js';
7
- import { HookRunner } from '../hooks/HookRunner.js';
8
- import { MemoryStore } from '../session/stores/MemoryStore.js';
9
- /**
10
- * Create a foundation service bundle with default implementations.
11
- * Services can be individually overridden if needed.
12
- */
13
- export function createFoundation(config = {}) {
14
- const sessionStore = config.sessionStore ?? new MemoryStore();
15
- const defaultAgentId = config.defaultAgentId ?? 'default';
16
- const enforcer = new ToolEnforcer(config.enforcementRules ?? defaultEnforcementRules);
17
- const hookRunner = new HookRunner(config.hooks);
18
- return {
19
- toolExecutor: new DefaultToolExecutor({
20
- enforcer,
21
- hookRunner,
22
- memoryService: config.memoryService,
23
- }),
24
- conversationState: new DefaultConversationState({
25
- sessionStore,
26
- defaultAgentId,
27
- }),
28
- eventLog: new DefaultConversationEventLog({
29
- sessionStore,
30
- }),
31
- agentState: new DefaultAgentStateController(),
32
- };
33
- }
@@ -1,37 +0,0 @@
1
- import type { HarnessHooks, RunContext, StepResult, ToolCallRecord, HarnessStreamPart, Session, TurnUsage, ConversationOutcomeRecord, TurnEndHookResult, TurnSummary } from '../types/index.js';
2
- import type { SessionEndMetadata } from '../types/telemetry.js';
3
- import type { ModelMessage } from 'ai';
4
- type HookErrorHandler = (hookName: string, error: Error) => void;
5
- export declare class HookRunner {
6
- private hooks;
7
- private errorHandler;
8
- constructor(hooks?: HarnessHooks, errorHandler?: HookErrorHandler);
9
- /** Check if a specific hook is configured. */
10
- has(hookName: string): boolean;
11
- run<K extends keyof HarnessHooks>(hookName: K, ...args: NonNullable<HarnessHooks[K]> extends (...args: infer P) => unknown ? P : never): Promise<void>;
12
- onStart(context: RunContext): Promise<void>;
13
- onEnd(context: RunContext, result: {
14
- success: boolean;
15
- error?: Error;
16
- }): Promise<void>;
17
- onStepStart(context: RunContext, step: number): Promise<void>;
18
- onStepEnd(context: RunContext, step: number, result: StepResult): Promise<void>;
19
- onTokensUpdate(context: RunContext, turn: TurnUsage): Promise<void>;
20
- onToolCall(context: RunContext, call: ToolCallRecord): Promise<void>;
21
- onToolResult(context: RunContext, call: ToolCallRecord): Promise<void>;
22
- onToolError(context: RunContext, call: ToolCallRecord, error: Error): Promise<void>;
23
- onTurnEnd(context: RunContext, summary: TurnSummary): Promise<TurnEndHookResult | void>;
24
- onAgentStart(context: RunContext, agentId: string): Promise<void>;
25
- onAgentEnd(context: RunContext, agentId: string): Promise<void>;
26
- onHandoff(context: RunContext, from: string, to: string, reason: string): Promise<void>;
27
- onError(context: RunContext, error: Error): Promise<void>;
28
- onMessage(context: RunContext, message: ModelMessage): Promise<void>;
29
- onStreamPart(context: RunContext, part: HarnessStreamPart): Promise<void>;
30
- onSessionEnd(session: Session, metadata: SessionEndMetadata): Promise<void>;
31
- onConversationEnd(session: Session, outcome: ConversationOutcomeRecord): Promise<void>;
32
- merge(additionalHooks: HarnessHooks): void;
33
- setHooks(hooks: HarnessHooks): void;
34
- getHooks(): HarnessHooks;
35
- }
36
- export declare function createHookRunner(hooks?: HarnessHooks, errorHandler?: HookErrorHandler): HookRunner;
37
- export {};