@onereach/step-voice 7.0.26-agenttimeout.0 → 7.0.26-agenttimeout.10
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.
|
@@ -4,13 +4,70 @@ const tslib_1 = require("tslib");
|
|
|
4
4
|
const voice_1 = tslib_1.__importDefault(require("./voice"));
|
|
5
5
|
class VoiceAgentFunctionResult extends voice_1.default {
|
|
6
6
|
async runStep() {
|
|
7
|
-
const
|
|
7
|
+
const { result, stopAgent, functionId: inputFunctionId } = this.data;
|
|
8
|
+
this.log.info("VoiceAgentFunctionResult: start", {
|
|
9
|
+
threadId: this.thread.id,
|
|
10
|
+
inputFunctionId,
|
|
11
|
+
stateKeys: Object.keys(this.state || {}),
|
|
12
|
+
stateDirect: this.state?.direct,
|
|
13
|
+
stateResultFunctionId: this.state?.result?.functionId,
|
|
14
|
+
stateResultCallId: this.state?.result?.callId,
|
|
15
|
+
});
|
|
16
|
+
// Attempt to get functionId from multiple sources to handle parallel function calls.
|
|
17
|
+
// Session storage is race-prone when multiple function_call threads run concurrently.
|
|
18
|
+
const threadStateFunctionId = this.state?.result?.functionId;
|
|
19
|
+
const resolvedFunctionId = inputFunctionId || threadStateFunctionId;
|
|
20
|
+
this.log.info("VoiceAgentFunctionResult: resolvedFunctionId", {
|
|
21
|
+
resolvedFunctionId,
|
|
22
|
+
source: inputFunctionId ? 'data.functionId' : threadStateFunctionId ? 'state.result.functionId' : 'none',
|
|
23
|
+
});
|
|
24
|
+
let ctx = null;
|
|
25
|
+
let ctxSource = 'none';
|
|
26
|
+
// Priority 1: functionId-keyed context (race-safe, unique per function call)
|
|
27
|
+
if (resolvedFunctionId) {
|
|
28
|
+
ctx = await this.thread.get(`__voiceAgentContext_fn_${resolvedFunctionId}`);
|
|
29
|
+
if (ctx) {
|
|
30
|
+
ctxSource = `__voiceAgentContext_fn_${resolvedFunctionId}`;
|
|
31
|
+
this.log.info("VoiceAgentFunctionResult: ctx from fn-keyed store", { ctxSource, functionId: ctx.functionId });
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
this.log.info("VoiceAgentFunctionResult: fn-keyed store miss", { key: `__voiceAgentContext_fn_${resolvedFunctionId}` });
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
// Priority 2: Thread-specific context
|
|
38
|
+
if (!ctx) {
|
|
39
|
+
ctx = await this.thread.get(`__voiceAgentContext_${this.thread.id}`);
|
|
40
|
+
if (ctx) {
|
|
41
|
+
ctxSource = `__voiceAgentContext_${this.thread.id}`;
|
|
42
|
+
this.log.info("VoiceAgentFunctionResult: ctx from thread-specific store", { ctxSource, functionId: ctx.functionId });
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
this.log.info("VoiceAgentFunctionResult: thread-specific store miss", { key: `__voiceAgentContext_${this.thread.id}` });
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
// Priority 3: Use state.result directly if it has callId (from exitToThread → exitStep chain)
|
|
49
|
+
if (!ctx && this.state?.result?.callId) {
|
|
50
|
+
ctx = this.state.result;
|
|
51
|
+
ctxSource = 'state.result';
|
|
52
|
+
this.log.info("VoiceAgentFunctionResult: ctx from state.result", { functionId: ctx.functionId, callId: ctx.callId });
|
|
53
|
+
}
|
|
54
|
+
// Priority 4: Shared session context (backward compat, NOT race-safe for parallel calls)
|
|
55
|
+
if (!ctx) {
|
|
56
|
+
ctx = await this.thread.get('__voiceAgentContext') ?? {};
|
|
57
|
+
ctxSource = '__voiceAgentContext (shared session)';
|
|
58
|
+
this.log.info("VoiceAgentFunctionResult: ctx from shared session", { functionId: ctx?.functionId, callId: ctx?.callId });
|
|
59
|
+
}
|
|
8
60
|
const { callId, callCallback, name: functionName, functionId } = ctx;
|
|
9
|
-
|
|
61
|
+
this.log.info("VoiceAgentFunctionResult: resolved context", {
|
|
62
|
+
ctxSource,
|
|
63
|
+
callId,
|
|
64
|
+
functionId,
|
|
65
|
+
functionName,
|
|
66
|
+
hasCallback: !!callCallback,
|
|
67
|
+
});
|
|
10
68
|
if (!callId) {
|
|
11
69
|
throw new Error('Voice Agent Function Result: missing call context (must be placed on a Voice Agent function exit)');
|
|
12
70
|
}
|
|
13
|
-
this.log.info("Voice Agent Function Result Params", ctx);
|
|
14
71
|
const params = {
|
|
15
72
|
functionName: functionName,
|
|
16
73
|
functionId: functionId,
|
|
@@ -27,7 +84,7 @@ class VoiceAgentFunctionResult extends voice_1.default {
|
|
|
27
84
|
commands: [{ name: 'agentFunctionResult', params }]
|
|
28
85
|
}
|
|
29
86
|
};
|
|
30
|
-
this.log.info("
|
|
87
|
+
this.log.info("VoiceAgentFunctionResult: sending", { functionId, functionName, callId });
|
|
31
88
|
await this.thread.eventManager.emit(event, {
|
|
32
89
|
target: callCallback,
|
|
33
90
|
invocationType: 'async',
|
package/dst/Voice Agent.js
CHANGED
|
@@ -111,7 +111,7 @@ class VoiceAgent extends voice_1.default {
|
|
|
111
111
|
this.throwError(new Error(`Voice Agent: unknown mode "${mode}"`));
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
|
-
if (this.isStepFlagEnabled(tool_feedback?.enabled)) {
|
|
114
|
+
if (this.isStepFlagEnabled(tool_feedback?.enabled) && tools.length > 0) {
|
|
115
115
|
const validatedFeedback = this.validateToolFeedback(tool_feedback);
|
|
116
116
|
if (validatedFeedback) {
|
|
117
117
|
config.tool_feedback = validatedFeedback;
|
|
@@ -142,48 +142,50 @@ class VoiceAgent extends voice_1.default {
|
|
|
142
142
|
}
|
|
143
143
|
validateToolFeedback(config) {
|
|
144
144
|
if (config == null || typeof config !== 'object')
|
|
145
|
-
return
|
|
145
|
+
return null;
|
|
146
146
|
const cfg = config;
|
|
147
147
|
// If enabled is not boolean true, silently return undefined (disabled case)
|
|
148
148
|
if (typeof cfg.enabled !== 'boolean' || cfg.enabled !== true)
|
|
149
|
-
return
|
|
149
|
+
return null;
|
|
150
150
|
// Validate pre_tool_speech
|
|
151
151
|
const validPreToolSpeech = ['auto', 'static', 'off'];
|
|
152
152
|
if (!validPreToolSpeech.includes(cfg.pre_tool_speech)) {
|
|
153
153
|
this.log.warn('Voice Agent: invalid tool_feedback.pre_tool_speech — feedback config excluded');
|
|
154
|
-
return
|
|
154
|
+
return null;
|
|
155
155
|
}
|
|
156
156
|
// Validate tool_call_sound
|
|
157
157
|
const validToolCallSound = ['typing', 'none'];
|
|
158
158
|
if (!validToolCallSound.includes(cfg.tool_call_sound)) {
|
|
159
159
|
this.log.warn('Voice Agent: invalid tool_feedback.tool_call_sound — feedback config excluded');
|
|
160
|
-
return
|
|
160
|
+
return null;
|
|
161
161
|
}
|
|
162
162
|
// Validate tool_call_sound_behavior
|
|
163
163
|
const validToolCallSoundBehavior = ['always', 'with_pre_speech'];
|
|
164
164
|
if (!validToolCallSoundBehavior.includes(cfg.tool_call_sound_behavior)) {
|
|
165
165
|
this.log.warn('Voice Agent: invalid tool_feedback.tool_call_sound_behavior — feedback config excluded');
|
|
166
|
-
return
|
|
166
|
+
return null;
|
|
167
167
|
}
|
|
168
168
|
// Validate delay_seconds
|
|
169
|
-
const delay = cfg.delay_seconds;
|
|
170
|
-
if (
|
|
169
|
+
const delay = Number(cfg.delay_seconds);
|
|
170
|
+
if (delay <= 0 || delay > 30) {
|
|
171
171
|
this.log.warn('Voice Agent: invalid tool_feedback.delay_seconds — feedback config excluded');
|
|
172
|
-
return
|
|
172
|
+
return null;
|
|
173
173
|
}
|
|
174
|
-
// Validate pre_tool_speech_phrases
|
|
174
|
+
// Validate pre_tool_speech_phrases (only required when mode is 'static')
|
|
175
175
|
const phrases = cfg.pre_tool_speech_phrases;
|
|
176
|
-
if (
|
|
177
|
-
phrases
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
176
|
+
if (cfg.pre_tool_speech === 'static') {
|
|
177
|
+
if (!Array.isArray(phrases) ||
|
|
178
|
+
phrases.length < 1 ||
|
|
179
|
+
phrases.length > 20 ||
|
|
180
|
+
!phrases.every((p) => typeof p === 'string' && p.trim().length > 0)) {
|
|
181
|
+
this.log.warn('Voice Agent: invalid tool_feedback.pre_tool_speech_phrases — feedback config excluded');
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
182
184
|
}
|
|
183
185
|
return {
|
|
184
186
|
enabled: true,
|
|
185
187
|
pre_tool_speech: cfg.pre_tool_speech,
|
|
186
|
-
pre_tool_speech_phrases: phrases,
|
|
188
|
+
pre_tool_speech_phrases: (Array.isArray(phrases) ? phrases : []),
|
|
187
189
|
tool_call_sound: cfg.tool_call_sound,
|
|
188
190
|
tool_call_sound_behavior: cfg.tool_call_sound_behavior,
|
|
189
191
|
delay_seconds: delay
|
|
@@ -216,7 +218,7 @@ class VoiceAgent extends voice_1.default {
|
|
|
216
218
|
const result = {};
|
|
217
219
|
if ('delay_seconds' in raw && raw.delay_seconds != null) {
|
|
218
220
|
const delay = Number(raw.delay_seconds);
|
|
219
|
-
if (
|
|
221
|
+
if (delay <= 0 || delay > 30) {
|
|
220
222
|
this.log.warn(`Voice Agent: invalid feedback.delay_seconds for tool "${toolName}" — per-tool feedback excluded`);
|
|
221
223
|
return null;
|
|
222
224
|
}
|
|
@@ -252,8 +254,8 @@ class VoiceAgent extends voice_1.default {
|
|
|
252
254
|
if (config == null || typeof config !== 'object' || Array.isArray(config))
|
|
253
255
|
return null;
|
|
254
256
|
const cfg = config;
|
|
255
|
-
const timeoutSeconds = cfg.timeout_seconds;
|
|
256
|
-
if (
|
|
257
|
+
const timeoutSeconds = Number(cfg.timeout_seconds);
|
|
258
|
+
if (timeoutSeconds <= 0 || timeoutSeconds > 30) {
|
|
257
259
|
this.log.warn('Voice Agent: invalid soft_timeout.timeout_seconds — soft timeout config excluded');
|
|
258
260
|
return null;
|
|
259
261
|
}
|
|
@@ -294,8 +296,22 @@ class VoiceAgent extends voice_1.default {
|
|
|
294
296
|
return options;
|
|
295
297
|
}
|
|
296
298
|
async exitToThread() {
|
|
297
|
-
|
|
298
|
-
this.
|
|
299
|
+
const result = this.state.result;
|
|
300
|
+
this.log.info("VoiceAgent.exitToThread: writing context", {
|
|
301
|
+
threadId: this.thread.id,
|
|
302
|
+
functionId: result?.functionId,
|
|
303
|
+
callId: result?.callId,
|
|
304
|
+
name: result?.name,
|
|
305
|
+
});
|
|
306
|
+
await this.thread.set(`__voiceAgentContext_${this.thread.id}`, result);
|
|
307
|
+
await this.thread.set('__voiceAgentContext', result);
|
|
308
|
+
if (result?.functionId) {
|
|
309
|
+
await this.thread.set(`__voiceAgentContext_fn_${result.functionId}`, result);
|
|
310
|
+
this.log.info("VoiceAgent.exitToThread: wrote fn-keyed context", {
|
|
311
|
+
key: `__voiceAgentContext_fn_${result.functionId}`,
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
this.thread.exitStep(this.state.exitStep, result);
|
|
299
315
|
}
|
|
300
316
|
}
|
|
301
317
|
exports.default = VoiceAgent;
|
package/dst/voice.js
CHANGED
|
@@ -18,8 +18,7 @@ class VoiceStep extends step_1.default {
|
|
|
18
18
|
if (this.cache != null) {
|
|
19
19
|
if (this.thread.id !== this.dataThreadId)
|
|
20
20
|
this.thread.refThread(this.dataThreadId);
|
|
21
|
-
|
|
22
|
-
await this.handleHeartbeat(this.cache);
|
|
21
|
+
await this.handleHeartbeat(this.cache);
|
|
23
22
|
}
|
|
24
23
|
}
|
|
25
24
|
exitStep(exitId, data, byThread = false) {
|