mirror of
https://github.com/jambonz/jambonz-feature-server.git
synced 2026-10-04 02:04:12 +00:00
fix: end the call when dial finishes with no actionHook, or a failed one
- a websocket application was parked in _awaitCommandsOrHangup after a dial it was never asked about: no actionHook at all, or one whose request failed. An http application already ends the call here simply by running out of verbs; a websocket one sat in dead air until timeLimit - TaskDial._endSessionUnlessHandedOff -> CallSession.expectNoFurtherVerbs suppresses the await for that one task iteration, so the loop falls out and the call tears down normally - verbs still queued are unaffected: the flag is read behind the tasks.length === 0 test, so the next verb always runs - skipped for a nested dial, and when the application already took the call elsewhere (KillReason.Replaced / ReferComplete) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
db18b558ce
commit
20210563f9
@@ -1348,6 +1348,7 @@ class CallSession extends Emitter {
|
||||
}
|
||||
|
||||
while (this.tasks.length && !this.callGone) {
|
||||
this._noFurtherVerbsExpected = false; // scoped to the task about to run
|
||||
const taskNum = ++this.taskIdx;
|
||||
const stackNum = this.stackIdx;
|
||||
const task = this.tasks.shift();
|
||||
@@ -1403,7 +1404,8 @@ class CallSession extends Emitter {
|
||||
this.appIsUsingWebsockets &&
|
||||
!this.requestor.closedGracefully &&
|
||||
!this.callGone &&
|
||||
!this.isConfirmCallSession
|
||||
!this.isConfirmCallSession &&
|
||||
!this._noFurtherVerbsExpected
|
||||
) {
|
||||
try {
|
||||
await this._awaitCommandsOrHangup();
|
||||
@@ -3189,6 +3191,19 @@ Duration=${duration} `
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A finished task telling us the application has no follow-on verbs coming, so
|
||||
* don't park a websocket app waiting for them. Cleared each task iteration.
|
||||
*/
|
||||
expectNoFurtherVerbs(reason) {
|
||||
/* http apps end the call here anyway, so that path is log noise */
|
||||
if (this.appIsUsingWebsockets) {
|
||||
this.logger.info(`CallSession:expectNoFurtherVerbs - ${reason}; ending call rather than awaiting commands`);
|
||||
}
|
||||
else this.logger.debug(`CallSession:expectNoFurtherVerbs - ${reason}`);
|
||||
this._noFurtherVerbsExpected = true;
|
||||
}
|
||||
|
||||
_awaitCommandsOrHangup() {
|
||||
assert(!this.wakeupResolver);
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
@@ -209,6 +209,10 @@ class TaskDial extends Task {
|
||||
'Dial:exec - exitMediaPath is set so features such as transcribe and record will not work on this call');
|
||||
}
|
||||
|
||||
/* the catch covers all of exec, and plenty of paths throw before the hook
|
||||
is sent; don't blame the hook for those */
|
||||
let hookAttempted = false;
|
||||
|
||||
try {
|
||||
if (this.listenTask) {
|
||||
const {span, ctx} = this.startChildSpan(`nested:${this.listenTask.summary}`);
|
||||
@@ -246,16 +250,31 @@ class TaskDial extends Task {
|
||||
if (!this.killed) await this._attemptCalls(cs);
|
||||
await this.awaitTaskDone();
|
||||
this.logger.debug({callSid: this.cs.callSid}, 'Dial:exec task is done, sending actionHook if any');
|
||||
if (this.actionHook) hookAttempted = true;
|
||||
else this._endSessionUnlessHandedOff(cs, 'dial completed with no actionHook');
|
||||
await this.performAction(this.results, this.killReason !== KillReason.Replaced);
|
||||
this._removeDtmfDetection(cs.dlg);
|
||||
this._removeDtmfDetection(this.dlg);
|
||||
this._removeSipIndialogRequestListener(this.dlg);
|
||||
} catch (err) {
|
||||
this.logger.error({err}, 'TaskDial:exec terminating with error');
|
||||
this._endSessionUnlessHandedOff(cs,
|
||||
hookAttempted ? 'dial actionHook failed' : 'dial failed before any actionHook was sent');
|
||||
this.kill(cs);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dial is over with nothing to do next, so don't leave a websocket app parked.
|
||||
*/
|
||||
_endSessionUnlessHandedOff(cs, reason) {
|
||||
/* a nested dial reports to its parent verb - only the session's own task may end it */
|
||||
if (cs.currentTask !== this) return;
|
||||
/* the app already took the call elsewhere */
|
||||
if ([KillReason.Replaced, KillReason.ReferComplete].includes(this.killReason)) return;
|
||||
cs.expectNoFurtherVerbs(reason);
|
||||
}
|
||||
|
||||
async kill(cs, reason) {
|
||||
super.kill(cs);
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user