@fonoster/autopilot 0.27.0 → 0.30.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.
- package/README.md +14 -51
- package/dist/index.d.ts +0 -5
- package/dist/index.js +0 -5
- package/dist/knowledge/AbstractKnowledgeBase.d.ts +3 -2
- package/dist/knowledge/AbstractKnowledgeBase.js +2 -7
- package/dist/knowledge/FilesKnowledgeBase.d.ts +1 -1
- package/dist/knowledge/types.d.ts +1 -1
- package/dist/models/LanguageModelFactory.d.ts +5 -24
- package/dist/models/LanguageModelFactory.js +0 -1
- package/dist/models/createLanguageModel.d.ts +2 -2
- package/dist/models/evaluations/createEvalEffectiveConfig.d.ts +10 -106
- package/dist/models/evaluations/createEvalEffectiveConfig.js +47 -7
- package/dist/models/evaluations/createEvaluateIntelligence.d.ts +10 -20
- package/dist/models/evaluations/createEvaluateIntelligence.js +32 -23
- package/dist/models/evaluations/evalTestCases.d.ts +2 -2
- package/dist/models/evaluations/extractAiResponse.d.ts +2 -3
- package/dist/models/evaluations/index.d.ts +1 -0
- package/dist/models/evaluations/index.js +1 -0
- package/dist/models/evaluations/runEval.d.ts +2 -20
- package/dist/models/evaluations/session/EvalSession.d.ts +73 -0
- package/dist/models/evaluations/session/EvalSession.js +134 -0
- package/dist/models/evaluations/session/evaluateSessionScenario.d.ts +58 -0
- package/dist/models/evaluations/session/evaluateSessionScenario.js +182 -0
- package/dist/{vad → models/evaluations/session}/index.d.ts +3 -2
- package/dist/{vad → models/evaluations/session}/index.js +3 -2
- package/dist/models/evaluations/session/runSessionEval.d.ts +17 -0
- package/dist/models/evaluations/session/runSessionEval.js +59 -0
- package/dist/models/evaluations/types.d.ts +7 -6
- package/dist/models/types.d.ts +19 -1
- package/dist/session/Agent.d.ts +138 -0
- package/dist/session/Agent.js +474 -0
- package/dist/{loadAssistantFromAPI.d.ts → session/AsyncQueue.d.ts} +12 -7
- package/dist/session/AsyncQueue.js +67 -0
- package/dist/{machine/guards/index.d.ts → session/config.d.ts} +2 -2
- package/dist/session/config.js +27 -0
- package/dist/session/convertLegacyAssistant.d.ts +77 -0
- package/dist/session/convertLegacyAssistant.js +118 -0
- package/dist/session/createAutopilot.d.ts +47 -0
- package/dist/session/createAutopilot.js +119 -0
- package/dist/session/hosted.d.ts +456 -0
- package/dist/session/hosted.js +506 -0
- package/dist/{assistants → session}/index.d.ts +11 -2
- package/dist/{machine/actors → session}/index.js +11 -3
- package/dist/{machine/actions/appendSpeech.js → session/knowledge.d.ts} +14 -17
- package/dist/session/knowledge.js +10 -0
- package/dist/session/llm.d.ts +86 -0
- package/dist/session/llm.js +83 -0
- package/dist/session/machine.d.ts +206 -0
- package/dist/session/machine.js +126 -0
- package/dist/session/main.js +44 -0
- package/dist/session/sentences.d.ts +5 -0
- package/dist/session/sentences.js +53 -0
- package/dist/{machine/actors/index.d.ts → session/think.d.ts} +2 -3
- package/dist/{machine/actions/announceSystemError.js → session/think.js} +8 -10
- package/dist/session/tools.d.ts +22 -0
- package/dist/session/tools.js +83 -0
- package/dist/tools/builtin/transferToolDefinition.js +1 -1
- package/dist/voice/index.d.ts +0 -1
- package/dist/voice/index.js +0 -1
- package/dist/voice/types.d.ts +5 -18
- package/package.json +13 -14
- package/dist/Autopilot.d.ts +0 -14
- package/dist/Autopilot.js +0 -133
- package/dist/assistants/index.js +0 -36
- package/dist/assistants/loadAndValidateAssistant.d.ts +0 -3
- package/dist/assistants/loadAndValidateAssistant.js +0 -49
- package/dist/assistants/types.d.ts +0 -24
- package/dist/assistants/types.js +0 -2
- package/dist/envs.d.ts +0 -15
- package/dist/envs.js +0 -78
- package/dist/handleVoiceRequest.d.ts +0 -3
- package/dist/handleVoiceRequest.js +0 -167
- package/dist/loadAssistantConfigFromFile.d.ts +0 -3
- package/dist/loadAssistantConfigFromFile.js +0 -68
- package/dist/loadAssistantFromAPI.js +0 -98
- package/dist/machine/actions/announceSystemError.d.ts +0 -4
- package/dist/machine/actions/appendSpeech.d.ts +0 -1
- package/dist/machine/actions/cleanSpeech.d.ts +0 -1
- package/dist/machine/actions/cleanSpeech.js +0 -23
- package/dist/machine/actions/goodbye.d.ts +0 -4
- package/dist/machine/actions/goodbye.js +0 -31
- package/dist/machine/actions/increaseIdleTimeoutCount.d.ts +0 -1
- package/dist/machine/actions/increaseIdleTimeoutCount.js +0 -31
- package/dist/machine/actions/index.d.ts +0 -26
- package/dist/machine/actions/index.js +0 -37
- package/dist/machine/actions/interruptPlayback.d.ts +0 -4
- package/dist/machine/actions/interruptPlayback.js +0 -43
- package/dist/machine/actions/resetIdleTimeoutCount.d.ts +0 -1
- package/dist/machine/actions/resetIdleTimeoutCount.js +0 -31
- package/dist/machine/actions/resetState.d.ts +0 -1
- package/dist/machine/actions/resetState.js +0 -32
- package/dist/machine/actors/doAnnounceIdleTimeout.d.ts +0 -4
- package/dist/machine/actors/doAnnounceIdleTimeout.js +0 -41
- package/dist/machine/actors/doGreetUser.d.ts +0 -4
- package/dist/machine/actors/doGreetUser.js +0 -45
- package/dist/machine/actors/doProcessUserRequest.d.ts +0 -4
- package/dist/machine/actors/doProcessUserRequest.js +0 -75
- package/dist/machine/context.d.ts +0 -52
- package/dist/machine/context.js +0 -28
- package/dist/machine/delays.d.ts +0 -30
- package/dist/machine/delays.js +0 -37
- package/dist/machine/guards/hasSpeechResult.d.ts +0 -4
- package/dist/machine/guards/hasSpeechResult.js +0 -30
- package/dist/machine/guards/idleTimeoutCountExceedsMax.d.ts +0 -4
- package/dist/machine/guards/idleTimeoutCountExceedsMax.js +0 -31
- package/dist/machine/guards/index.js +0 -25
- package/dist/machine/index.d.ts +0 -19
- package/dist/machine/index.js +0 -23
- package/dist/machine/machine.d.ts +0 -408
- package/dist/machine/machine.js +0 -206
- package/dist/machine/setup.d.ts +0 -328
- package/dist/machine/setup.js +0 -116
- package/dist/machine/types.d.ts +0 -54
- package/dist/machine/types.js +0 -2
- package/dist/sendConversationEndedEvent.d.ts +0 -32
- package/dist/sendConversationEndedEvent.js +0 -66
- package/dist/sendConversationStartedEvent.d.ts +0 -6
- package/dist/sendConversationStartedEvent.js +0 -63
- package/dist/server.d.ts +0 -2
- package/dist/server.js +0 -25
- package/dist/types.d.ts +0 -40
- package/dist/types.js +0 -8
- package/dist/vad/SileroVad.d.ts +0 -18
- package/dist/vad/SileroVad.js +0 -40
- package/dist/vad/SileroVadModel.d.ts +0 -14
- package/dist/vad/SileroVadModel.js +0 -83
- package/dist/vad/chunkToFloat32Array.d.ts +0 -20
- package/dist/vad/chunkToFloat32Array.js +0 -34
- package/dist/vad/createVad.d.ts +0 -3
- package/dist/vad/createVad.js +0 -126
- package/dist/vad/types.d.ts +0 -60
- package/dist/vad/types.js +0 -2
- package/dist/vadWorker.js +0 -36
- package/dist/voice/Voice.d.ts +0 -61
- package/dist/voice/Voice.js +0 -96
- package/dist/voiceServerSetup.d.ts +0 -2
- package/dist/voiceServerSetup.js +0 -29
- /package/dist/{vadWorker.d.ts → session/main.d.ts} +0 -0
|
@@ -16,5 +16,5 @@
|
|
|
16
16
|
* See the License for the specific language governing permissions and
|
|
17
17
|
* limitations under the License.
|
|
18
18
|
*/
|
|
19
|
-
export {
|
|
20
|
-
export {
|
|
19
|
+
export { sessionAssistantSchema, sessionConversationSettingsSchema, sessionLanguageModelSchema } from "@fonoster/common";
|
|
20
|
+
export type { SessionAssistant, SessionConversationSettings, SessionLanguageModel, ToolDefinition } from "@fonoster/common";
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.sessionLanguageModelSchema = exports.sessionConversationSettingsSchema = exports.sessionAssistantSchema = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
|
|
6
|
+
* http://github.com/fonoster/fonoster
|
|
7
|
+
*
|
|
8
|
+
* This file is part of Fonoster
|
|
9
|
+
*
|
|
10
|
+
* Licensed under the MIT License (the "License");
|
|
11
|
+
* you may not use this file except in compliance with
|
|
12
|
+
* the License. You may obtain a copy of the License at
|
|
13
|
+
*
|
|
14
|
+
* https://opensource.org/licenses/MIT
|
|
15
|
+
*
|
|
16
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
17
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
18
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
19
|
+
* See the License for the specific language governing permissions and
|
|
20
|
+
* limitations under the License.
|
|
21
|
+
*/
|
|
22
|
+
// The session assistant schema lives in @fonoster/common (rework-autopilot 5.1), so the control
|
|
23
|
+
// plane validates assistants with the same rules Autopilot runs with.
|
|
24
|
+
var common_1 = require("@fonoster/common");
|
|
25
|
+
Object.defineProperty(exports, "sessionAssistantSchema", { enumerable: true, get: function () { return common_1.sessionAssistantSchema; } });
|
|
26
|
+
Object.defineProperty(exports, "sessionConversationSettingsSchema", { enumerable: true, get: function () { return common_1.sessionConversationSettingsSchema; } });
|
|
27
|
+
Object.defineProperty(exports, "sessionLanguageModelSchema", { enumerable: true, get: function () { return common_1.sessionLanguageModelSchema; } });
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
|
|
3
|
+
* http://github.com/fonoster/fonoster
|
|
4
|
+
*
|
|
5
|
+
* This file is part of Fonoster
|
|
6
|
+
*
|
|
7
|
+
* Licensed under the MIT License (the "License");
|
|
8
|
+
* you may not use this file except in compliance with
|
|
9
|
+
* the License. You may obtain a copy of the License at
|
|
10
|
+
*
|
|
11
|
+
* https://opensource.org/licenses/MIT
|
|
12
|
+
*
|
|
13
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
* See the License for the specific language governing permissions and
|
|
17
|
+
* limitations under the License.
|
|
18
|
+
*/
|
|
19
|
+
import { SessionAssistant } from "./config";
|
|
20
|
+
type Legacy = {
|
|
21
|
+
conversationSettings?: Record<string, unknown> & {
|
|
22
|
+
maxSpeechWaitTimeout?: number;
|
|
23
|
+
vad?: unknown;
|
|
24
|
+
transferOptions?: {
|
|
25
|
+
phoneNumber: string;
|
|
26
|
+
message: string;
|
|
27
|
+
timeout?: number;
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
languageModel?: Record<string, unknown> & {
|
|
31
|
+
provider?: string;
|
|
32
|
+
model?: string;
|
|
33
|
+
apiKey?: string;
|
|
34
|
+
baseUrl?: string;
|
|
35
|
+
};
|
|
36
|
+
eventsHook?: {
|
|
37
|
+
url: string;
|
|
38
|
+
events?: string[];
|
|
39
|
+
headers?: Record<string, string>;
|
|
40
|
+
};
|
|
41
|
+
audioFilters?: unknown;
|
|
42
|
+
testCases?: unknown;
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* VAD-era settings → the patience knob (add-turn-taking 3.2, AP-B3). maxSpeechWaitTimeout was
|
|
46
|
+
* extra waiting after the caller's speech before answering; 0 (the old default) is today's
|
|
47
|
+
* behaviour, normal. Any extra wait becomes high (about 1.6 × the gateway's silence timeout, and
|
|
48
|
+
* more as the gateway adapts to a caller who pauses). Nothing maps to low: the old settings had
|
|
49
|
+
* no way to answer faster than the default. allowUserBargeIn keeps its meaning: it sets
|
|
50
|
+
* `interruptible` on every speak and whether Autopilot sends clear on turn.user_started.
|
|
51
|
+
*/
|
|
52
|
+
export declare function patienceFor(maxSpeechWaitTimeout?: number): "low" | "normal" | "high";
|
|
53
|
+
export type AssistantConversion = {
|
|
54
|
+
assistant: SessionAssistant;
|
|
55
|
+
/**
|
|
56
|
+
* An inline key to store as the workspace's key for `providerId` (the config no longer holds
|
|
57
|
+
* it). The migration job binds it like the dashboard's Vendor keys page does (a Secret named
|
|
58
|
+
* `provider-key/<provider>` in workspace_provider_keys), which is where the registry's
|
|
59
|
+
* credentialRef lookup finds workspace keys (AP-B4, LG-B1).
|
|
60
|
+
*/
|
|
61
|
+
secret?: {
|
|
62
|
+
providerId: string;
|
|
63
|
+
name: string;
|
|
64
|
+
value: string;
|
|
65
|
+
};
|
|
66
|
+
/** The retired eventsHook URL, for creating a workspace webhook endpoint. */
|
|
67
|
+
webhook?: {
|
|
68
|
+
url: string;
|
|
69
|
+
};
|
|
70
|
+
/** Audio filters that now belong in the app's configuration. */
|
|
71
|
+
appFilters?: unknown;
|
|
72
|
+
notices: string[];
|
|
73
|
+
};
|
|
74
|
+
export declare function convertLegacyAssistant(old: Legacy, options: {
|
|
75
|
+
assistantRef: string;
|
|
76
|
+
}): AssistantConversion;
|
|
77
|
+
export {};
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.patienceFor = patienceFor;
|
|
4
|
+
exports.convertLegacyAssistant = convertLegacyAssistant;
|
|
5
|
+
/**
|
|
6
|
+
* Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
|
|
7
|
+
* http://github.com/fonoster/fonoster
|
|
8
|
+
*
|
|
9
|
+
* This file is part of Fonoster
|
|
10
|
+
*
|
|
11
|
+
* Licensed under the MIT License (the "License");
|
|
12
|
+
* you may not use this file except in compliance with
|
|
13
|
+
* the License. You may obtain a copy of the License at
|
|
14
|
+
*
|
|
15
|
+
* https://opensource.org/licenses/MIT
|
|
16
|
+
*
|
|
17
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
18
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
19
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
20
|
+
* See the License for the specific language governing permissions and
|
|
21
|
+
* limitations under the License.
|
|
22
|
+
*/
|
|
23
|
+
const config_1 = require("./config");
|
|
24
|
+
// The one-time conversion of stored assistants at cutover (owner decision, rework-autopilot 5.3).
|
|
25
|
+
// It never writes anything itself: it returns the new config plus what the migration job must do
|
|
26
|
+
// around it (create a workspace Secret, a webhook endpoint, app audio filters) and notices for the
|
|
27
|
+
// workspace.
|
|
28
|
+
const PROVIDERS = ["openai", "groq", "ollama", "google", "anthropic"];
|
|
29
|
+
const PLACEHOLDER_KEYS = new Set(["", "your-api-key", "changeme", "xxx"]);
|
|
30
|
+
/**
|
|
31
|
+
* VAD-era settings → the patience knob (add-turn-taking 3.2, AP-B3). maxSpeechWaitTimeout was
|
|
32
|
+
* extra waiting after the caller's speech before answering; 0 (the old default) is today's
|
|
33
|
+
* behaviour, normal. Any extra wait becomes high (about 1.6 × the gateway's silence timeout, and
|
|
34
|
+
* more as the gateway adapts to a caller who pauses). Nothing maps to low: the old settings had
|
|
35
|
+
* no way to answer faster than the default. allowUserBargeIn keeps its meaning: it sets
|
|
36
|
+
* `interruptible` on every speak and whether Autopilot sends clear on turn.user_started.
|
|
37
|
+
*/
|
|
38
|
+
function patienceFor(maxSpeechWaitTimeout) {
|
|
39
|
+
return (maxSpeechWaitTimeout ?? 0) > 0 ? "high" : "normal";
|
|
40
|
+
}
|
|
41
|
+
const DEFAULT_VAD = {
|
|
42
|
+
activationThreshold: 0.4,
|
|
43
|
+
deactivationThreshold: 0.25,
|
|
44
|
+
debounceFrames: 4
|
|
45
|
+
};
|
|
46
|
+
function isDefaultVad(vad) {
|
|
47
|
+
if (!vad || typeof vad !== "object")
|
|
48
|
+
return true;
|
|
49
|
+
const v = vad;
|
|
50
|
+
return Object.keys(DEFAULT_VAD).every((k) => v[k] === undefined || v[k] === DEFAULT_VAD[k]);
|
|
51
|
+
}
|
|
52
|
+
function convertLegacyAssistant(old, options) {
|
|
53
|
+
const notices = [];
|
|
54
|
+
const lm = { ...(old.languageModel ?? {}) };
|
|
55
|
+
const cs = { ...(old.conversationSettings ?? {}) };
|
|
56
|
+
const result = { notices };
|
|
57
|
+
const provider = String(lm.provider ?? "");
|
|
58
|
+
if (!PROVIDERS.includes(provider))
|
|
59
|
+
throw new Error(`unknown language model provider "${provider}"`);
|
|
60
|
+
const model = `${provider}/${lm.model}`;
|
|
61
|
+
if (typeof lm.apiKey === "string" &&
|
|
62
|
+
!PLACEHOLDER_KEYS.has(lm.apiKey.trim())) {
|
|
63
|
+
void options.assistantRef;
|
|
64
|
+
result.secret = {
|
|
65
|
+
providerId: provider,
|
|
66
|
+
name: `provider-key/${provider}`,
|
|
67
|
+
value: lm.apiKey
|
|
68
|
+
};
|
|
69
|
+
notices.push(`The ${provider} key moved out of the assistant into the workspace's ${provider} vendor key (Secret "${result.secret.name}").`);
|
|
70
|
+
}
|
|
71
|
+
if (lm.baseUrl) {
|
|
72
|
+
notices.push(`The custom model endpoint ${lm.baseUrl} was removed; endpoints now come from the provider registry.`);
|
|
73
|
+
}
|
|
74
|
+
const patience = patienceFor(cs.maxSpeechWaitTimeout);
|
|
75
|
+
if (patience !== "normal") {
|
|
76
|
+
notices.push(`maxSpeechWaitTimeout (${cs.maxSpeechWaitTimeout} ms) became patience "${patience}"; turn timing now comes from the gateway.`);
|
|
77
|
+
}
|
|
78
|
+
if (cs.vad !== undefined && !isDefaultVad(cs.vad))
|
|
79
|
+
notices.push("Custom VAD settings were removed; turn detection now runs in the gateway. For noisy lines use the app's audio filters or a more patient setting.");
|
|
80
|
+
if (cs.transferOptions?.timeout !== undefined &&
|
|
81
|
+
cs.transferOptions.timeout !== 30000) {
|
|
82
|
+
notices.push(`The transfer timeout (${cs.transferOptions.timeout} ms) is no longer configurable per assistant.`);
|
|
83
|
+
}
|
|
84
|
+
if (old.eventsHook?.url) {
|
|
85
|
+
result.webhook = { url: old.eventsHook.url };
|
|
86
|
+
notices.push(`Assistant webhooks are retired; create a workspace webhook for ${old.eventsHook.url} (call.started, call.ended, call_record.sealed).`);
|
|
87
|
+
}
|
|
88
|
+
if (old.audioFilters !== undefined) {
|
|
89
|
+
result.appFilters = old.audioFilters;
|
|
90
|
+
notices.push("Audio filters moved to the app's configuration.");
|
|
91
|
+
}
|
|
92
|
+
const { maxSpeechWaitTimeout, vad, transferOptions, ...settings } = cs;
|
|
93
|
+
void vad;
|
|
94
|
+
void maxSpeechWaitTimeout;
|
|
95
|
+
const assistant = config_1.sessionAssistantSchema.parse({
|
|
96
|
+
conversationSettings: {
|
|
97
|
+
...settings,
|
|
98
|
+
...(transferOptions
|
|
99
|
+
? {
|
|
100
|
+
transferOptions: {
|
|
101
|
+
phoneNumber: transferOptions.phoneNumber,
|
|
102
|
+
message: transferOptions.message
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
: {}),
|
|
106
|
+
...(patience !== "normal" ? { patience } : {})
|
|
107
|
+
},
|
|
108
|
+
languageModel: {
|
|
109
|
+
model,
|
|
110
|
+
temperature: lm.temperature,
|
|
111
|
+
maxTokens: lm.maxTokens,
|
|
112
|
+
tools: lm.tools,
|
|
113
|
+
knowledgeBase: lm.knowledgeBase
|
|
114
|
+
},
|
|
115
|
+
...(old.testCases !== undefined ? { testCases: old.testCases } : {})
|
|
116
|
+
});
|
|
117
|
+
return { ...result, assistant };
|
|
118
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Agent, Recorder } from "./Agent";
|
|
2
|
+
import { SessionAssistant } from "./config";
|
|
3
|
+
import { KnowledgePort } from "./knowledge";
|
|
4
|
+
import { LlmResolver } from "./llm";
|
|
5
|
+
import { Think, ThinkProvider } from "./think";
|
|
6
|
+
import { ToolPort } from "./tools";
|
|
7
|
+
import type { Session } from "@fonoster/voice";
|
|
8
|
+
/** What the hosted Autopilot knows about the app a session belongs to. */
|
|
9
|
+
export type LoadedAssistant = {
|
|
10
|
+
assistant: unknown;
|
|
11
|
+
/** The workspace that owns the app: scope for its own keys (BYOK) and knowledge. */
|
|
12
|
+
workspaceRef?: string;
|
|
13
|
+
};
|
|
14
|
+
export type AutopilotOptions = {
|
|
15
|
+
/** The assistant for a session (validated with the session assistant schema). */
|
|
16
|
+
assistant?: unknown | ((session: Session) => Promise<unknown> | unknown);
|
|
17
|
+
/**
|
|
18
|
+
* The hosted path: loads the app's assistant and workspace for a session; undefined means the
|
|
19
|
+
* app has no assistant (the call is ended).
|
|
20
|
+
*/
|
|
21
|
+
load?: (session: Session) => Promise<LoadedAssistant | undefined>;
|
|
22
|
+
/** Language models through the provider registry (task 3.1). */
|
|
23
|
+
llm?: LlmResolver;
|
|
24
|
+
/** Language model endpoints by provider name (the stand-in used before the registry). */
|
|
25
|
+
providers?: Record<string, ThinkProvider>;
|
|
26
|
+
think?: Think;
|
|
27
|
+
tools?: ToolPort;
|
|
28
|
+
/**
|
|
29
|
+
* Private destinations HTTP tools may call (host names, IPs, CIDRs); defaults to
|
|
30
|
+
* FONOSTER_ALLOWED_DESTINATIONS. Everything else must be public (RV-B2).
|
|
31
|
+
*/
|
|
32
|
+
allowedDestinations?: string[];
|
|
33
|
+
/** Finds what is relevant to the caller's question (the knowledge port). */
|
|
34
|
+
knowledge?: KnowledgePort;
|
|
35
|
+
/** Builds the knowledge port for an assistant (operator wiring, task 4.1). */
|
|
36
|
+
knowledgeFor?: (assistant: SessionAssistant, scope: {
|
|
37
|
+
workspaceRef?: string;
|
|
38
|
+
}) => KnowledgePort | undefined;
|
|
39
|
+
recorder?: Recorder;
|
|
40
|
+
/** Called with each session's Agent, for tests and observability. */
|
|
41
|
+
onAgent?: (agent: Agent) => void;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Autopilot as an ordinary application (rework-autopilot A2): returns a VoiceServer handler that
|
|
45
|
+
* runs the state machine on the session's public messages only.
|
|
46
|
+
*/
|
|
47
|
+
export declare function createAutopilotHandler(options: AutopilotOptions): (session: Session) => Promise<void>;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createAutopilotHandler = createAutopilotHandler;
|
|
4
|
+
/**
|
|
5
|
+
* Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
|
|
6
|
+
* http://github.com/fonoster/fonoster
|
|
7
|
+
*
|
|
8
|
+
* This file is part of Fonoster
|
|
9
|
+
*
|
|
10
|
+
* Licensed under the MIT License (the "License");
|
|
11
|
+
* you may not use this file except in compliance with
|
|
12
|
+
* the License. You may obtain a copy of the License at
|
|
13
|
+
*
|
|
14
|
+
* https://opensource.org/licenses/MIT
|
|
15
|
+
*
|
|
16
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
17
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
18
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
19
|
+
* See the License for the specific language governing permissions and
|
|
20
|
+
* limitations under the License.
|
|
21
|
+
*/
|
|
22
|
+
const logger_1 = require("@fonoster/logger");
|
|
23
|
+
const xstate_1 = require("xstate");
|
|
24
|
+
const Agent_1 = require("./Agent");
|
|
25
|
+
const config_1 = require("./config");
|
|
26
|
+
const machine_1 = require("./machine");
|
|
27
|
+
const think_1 = require("./think");
|
|
28
|
+
const tools_1 = require("./tools");
|
|
29
|
+
const logger = (0, logger_1.getLogger)({ service: "autopilot", filePath: __filename });
|
|
30
|
+
/**
|
|
31
|
+
* Autopilot as an ordinary application (rework-autopilot A2): returns a VoiceServer handler that
|
|
32
|
+
* runs the state machine on the session's public messages only.
|
|
33
|
+
*/
|
|
34
|
+
function createAutopilotHandler(options) {
|
|
35
|
+
return async (session) => {
|
|
36
|
+
// Listen before any await: turn events that arrive while the assistant and model load are
|
|
37
|
+
// held and delivered once the machine runs.
|
|
38
|
+
const early = [];
|
|
39
|
+
let deliver = (event) => early.push(event);
|
|
40
|
+
session.on("turn.user_started", () => deliver({ type: "TURN_USER_STARTED" }));
|
|
41
|
+
session.on("turn.likely_ended", (e) => deliver({ type: "TURN_LIKELY_ENDED", text: e.text }));
|
|
42
|
+
session.on("turn.resumed", () => deliver({ type: "TURN_RESUMED" }));
|
|
43
|
+
// A turn with no words (line noise, an abandoned start) is not an answer: no reply, and the
|
|
44
|
+
// idle count is kept, so a silent call still ends.
|
|
45
|
+
session.on("turn.ended", (e) => {
|
|
46
|
+
if (e.text.trim())
|
|
47
|
+
deliver({ type: "TURN_ENDED", text: e.text });
|
|
48
|
+
});
|
|
49
|
+
session.on("turn.backchannel", () => deliver({ type: "TURN_BACKCHANNEL" }));
|
|
50
|
+
const ended = new Promise((resolve) => session.on("end", () => {
|
|
51
|
+
deliver({ type: "SESSION_END" });
|
|
52
|
+
resolve();
|
|
53
|
+
}));
|
|
54
|
+
const loaded = options.load
|
|
55
|
+
? await options.load(session)
|
|
56
|
+
: {
|
|
57
|
+
assistant: typeof options.assistant === "function"
|
|
58
|
+
? await options.assistant(session)
|
|
59
|
+
: options.assistant
|
|
60
|
+
};
|
|
61
|
+
if (!loaded) {
|
|
62
|
+
logger.warn("no assistant for this app; ending the call", {
|
|
63
|
+
appRef: session.appRef,
|
|
64
|
+
callRef: session.callRef
|
|
65
|
+
});
|
|
66
|
+
await session.hangup().catch(() => undefined);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const assistant = config_1.sessionAssistantSchema.parse(loaded.assistant);
|
|
70
|
+
const scope = { workspaceRef: loaded.workspaceRef };
|
|
71
|
+
const recorder = options.recorder ?? (0, Agent_1.sessionRecorder)(session);
|
|
72
|
+
const settings = {
|
|
73
|
+
temperature: assistant.languageModel.temperature,
|
|
74
|
+
maxTokens: assistant.languageModel.maxTokens
|
|
75
|
+
};
|
|
76
|
+
let think = options.think;
|
|
77
|
+
if (!think && options.llm) {
|
|
78
|
+
try {
|
|
79
|
+
const resolved = await options.llm.resolve(assistant.languageModel.model, settings, scope);
|
|
80
|
+
think = resolved.think;
|
|
81
|
+
if (resolved.event)
|
|
82
|
+
recorder.note(resolved.event);
|
|
83
|
+
}
|
|
84
|
+
catch (e) {
|
|
85
|
+
// A model the registry cannot serve: apologise and end the call, never a silent line.
|
|
86
|
+
logger.error(`autopilot cannot reach its language model: ${e.message}`);
|
|
87
|
+
recorder.note({
|
|
88
|
+
type: "llm.unavailable",
|
|
89
|
+
reason: e.message
|
|
90
|
+
});
|
|
91
|
+
await session
|
|
92
|
+
.say(assistant.conversationSettings.systemErrorMessage, {
|
|
93
|
+
interruptible: false
|
|
94
|
+
})
|
|
95
|
+
.catch(() => undefined);
|
|
96
|
+
await session.hangup().catch(() => undefined);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
think ?? (think = (0, think_1.resolveThink)(assistant.languageModel.model, options.providers ?? {}, settings));
|
|
101
|
+
const agent = new Agent_1.Agent(session, assistant, think, options.tools ??
|
|
102
|
+
new tools_1.HttpToolPort(assistant.languageModel.tools, {
|
|
103
|
+
allow: options.allowedDestinations
|
|
104
|
+
}), recorder, options.knowledge ?? options.knowledgeFor?.(assistant, scope));
|
|
105
|
+
options.onAgent?.(agent);
|
|
106
|
+
const actor = (0, xstate_1.createActor)(machine_1.autopilotMachine, { input: { agent } });
|
|
107
|
+
agent.send = (event) => actor.send(event);
|
|
108
|
+
actor.subscribe((state) => logger.verbose("autopilot state", {
|
|
109
|
+
callRef: session.callRef,
|
|
110
|
+
state: state.value
|
|
111
|
+
}));
|
|
112
|
+
actor.start();
|
|
113
|
+
deliver = (event) => actor.send(event);
|
|
114
|
+
for (const event of early.splice(0))
|
|
115
|
+
actor.send(event);
|
|
116
|
+
await ended;
|
|
117
|
+
actor.stop();
|
|
118
|
+
};
|
|
119
|
+
}
|