@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.
Files changed (138) hide show
  1. package/README.md +14 -51
  2. package/dist/index.d.ts +0 -5
  3. package/dist/index.js +0 -5
  4. package/dist/knowledge/AbstractKnowledgeBase.d.ts +3 -2
  5. package/dist/knowledge/AbstractKnowledgeBase.js +2 -7
  6. package/dist/knowledge/FilesKnowledgeBase.d.ts +1 -1
  7. package/dist/knowledge/types.d.ts +1 -1
  8. package/dist/models/LanguageModelFactory.d.ts +5 -24
  9. package/dist/models/LanguageModelFactory.js +0 -1
  10. package/dist/models/createLanguageModel.d.ts +2 -2
  11. package/dist/models/evaluations/createEvalEffectiveConfig.d.ts +10 -106
  12. package/dist/models/evaluations/createEvalEffectiveConfig.js +47 -7
  13. package/dist/models/evaluations/createEvaluateIntelligence.d.ts +10 -20
  14. package/dist/models/evaluations/createEvaluateIntelligence.js +32 -23
  15. package/dist/models/evaluations/evalTestCases.d.ts +2 -2
  16. package/dist/models/evaluations/extractAiResponse.d.ts +2 -3
  17. package/dist/models/evaluations/index.d.ts +1 -0
  18. package/dist/models/evaluations/index.js +1 -0
  19. package/dist/models/evaluations/runEval.d.ts +2 -20
  20. package/dist/models/evaluations/session/EvalSession.d.ts +73 -0
  21. package/dist/models/evaluations/session/EvalSession.js +134 -0
  22. package/dist/models/evaluations/session/evaluateSessionScenario.d.ts +58 -0
  23. package/dist/models/evaluations/session/evaluateSessionScenario.js +182 -0
  24. package/dist/{vad → models/evaluations/session}/index.d.ts +3 -2
  25. package/dist/{vad → models/evaluations/session}/index.js +3 -2
  26. package/dist/models/evaluations/session/runSessionEval.d.ts +17 -0
  27. package/dist/models/evaluations/session/runSessionEval.js +59 -0
  28. package/dist/models/evaluations/types.d.ts +7 -6
  29. package/dist/models/types.d.ts +19 -1
  30. package/dist/session/Agent.d.ts +138 -0
  31. package/dist/session/Agent.js +474 -0
  32. package/dist/{loadAssistantFromAPI.d.ts → session/AsyncQueue.d.ts} +12 -7
  33. package/dist/session/AsyncQueue.js +67 -0
  34. package/dist/{machine/guards/index.d.ts → session/config.d.ts} +2 -2
  35. package/dist/session/config.js +27 -0
  36. package/dist/session/convertLegacyAssistant.d.ts +77 -0
  37. package/dist/session/convertLegacyAssistant.js +118 -0
  38. package/dist/session/createAutopilot.d.ts +47 -0
  39. package/dist/session/createAutopilot.js +119 -0
  40. package/dist/session/hosted.d.ts +456 -0
  41. package/dist/session/hosted.js +506 -0
  42. package/dist/{assistants → session}/index.d.ts +11 -2
  43. package/dist/{machine/actors → session}/index.js +11 -3
  44. package/dist/{machine/actions/appendSpeech.js → session/knowledge.d.ts} +14 -17
  45. package/dist/session/knowledge.js +10 -0
  46. package/dist/session/llm.d.ts +86 -0
  47. package/dist/session/llm.js +83 -0
  48. package/dist/session/machine.d.ts +206 -0
  49. package/dist/session/machine.js +126 -0
  50. package/dist/session/main.js +44 -0
  51. package/dist/session/sentences.d.ts +5 -0
  52. package/dist/session/sentences.js +53 -0
  53. package/dist/{machine/actors/index.d.ts → session/think.d.ts} +2 -3
  54. package/dist/{machine/actions/announceSystemError.js → session/think.js} +8 -10
  55. package/dist/session/tools.d.ts +22 -0
  56. package/dist/session/tools.js +83 -0
  57. package/dist/tools/builtin/transferToolDefinition.js +1 -1
  58. package/dist/voice/index.d.ts +0 -1
  59. package/dist/voice/index.js +0 -1
  60. package/dist/voice/types.d.ts +5 -18
  61. package/package.json +13 -14
  62. package/dist/Autopilot.d.ts +0 -14
  63. package/dist/Autopilot.js +0 -133
  64. package/dist/assistants/index.js +0 -36
  65. package/dist/assistants/loadAndValidateAssistant.d.ts +0 -3
  66. package/dist/assistants/loadAndValidateAssistant.js +0 -49
  67. package/dist/assistants/types.d.ts +0 -24
  68. package/dist/assistants/types.js +0 -2
  69. package/dist/envs.d.ts +0 -15
  70. package/dist/envs.js +0 -78
  71. package/dist/handleVoiceRequest.d.ts +0 -3
  72. package/dist/handleVoiceRequest.js +0 -167
  73. package/dist/loadAssistantConfigFromFile.d.ts +0 -3
  74. package/dist/loadAssistantConfigFromFile.js +0 -68
  75. package/dist/loadAssistantFromAPI.js +0 -98
  76. package/dist/machine/actions/announceSystemError.d.ts +0 -4
  77. package/dist/machine/actions/appendSpeech.d.ts +0 -1
  78. package/dist/machine/actions/cleanSpeech.d.ts +0 -1
  79. package/dist/machine/actions/cleanSpeech.js +0 -23
  80. package/dist/machine/actions/goodbye.d.ts +0 -4
  81. package/dist/machine/actions/goodbye.js +0 -31
  82. package/dist/machine/actions/increaseIdleTimeoutCount.d.ts +0 -1
  83. package/dist/machine/actions/increaseIdleTimeoutCount.js +0 -31
  84. package/dist/machine/actions/index.d.ts +0 -26
  85. package/dist/machine/actions/index.js +0 -37
  86. package/dist/machine/actions/interruptPlayback.d.ts +0 -4
  87. package/dist/machine/actions/interruptPlayback.js +0 -43
  88. package/dist/machine/actions/resetIdleTimeoutCount.d.ts +0 -1
  89. package/dist/machine/actions/resetIdleTimeoutCount.js +0 -31
  90. package/dist/machine/actions/resetState.d.ts +0 -1
  91. package/dist/machine/actions/resetState.js +0 -32
  92. package/dist/machine/actors/doAnnounceIdleTimeout.d.ts +0 -4
  93. package/dist/machine/actors/doAnnounceIdleTimeout.js +0 -41
  94. package/dist/machine/actors/doGreetUser.d.ts +0 -4
  95. package/dist/machine/actors/doGreetUser.js +0 -45
  96. package/dist/machine/actors/doProcessUserRequest.d.ts +0 -4
  97. package/dist/machine/actors/doProcessUserRequest.js +0 -75
  98. package/dist/machine/context.d.ts +0 -52
  99. package/dist/machine/context.js +0 -28
  100. package/dist/machine/delays.d.ts +0 -30
  101. package/dist/machine/delays.js +0 -37
  102. package/dist/machine/guards/hasSpeechResult.d.ts +0 -4
  103. package/dist/machine/guards/hasSpeechResult.js +0 -30
  104. package/dist/machine/guards/idleTimeoutCountExceedsMax.d.ts +0 -4
  105. package/dist/machine/guards/idleTimeoutCountExceedsMax.js +0 -31
  106. package/dist/machine/guards/index.js +0 -25
  107. package/dist/machine/index.d.ts +0 -19
  108. package/dist/machine/index.js +0 -23
  109. package/dist/machine/machine.d.ts +0 -408
  110. package/dist/machine/machine.js +0 -206
  111. package/dist/machine/setup.d.ts +0 -328
  112. package/dist/machine/setup.js +0 -116
  113. package/dist/machine/types.d.ts +0 -54
  114. package/dist/machine/types.js +0 -2
  115. package/dist/sendConversationEndedEvent.d.ts +0 -32
  116. package/dist/sendConversationEndedEvent.js +0 -66
  117. package/dist/sendConversationStartedEvent.d.ts +0 -6
  118. package/dist/sendConversationStartedEvent.js +0 -63
  119. package/dist/server.d.ts +0 -2
  120. package/dist/server.js +0 -25
  121. package/dist/types.d.ts +0 -40
  122. package/dist/types.js +0 -8
  123. package/dist/vad/SileroVad.d.ts +0 -18
  124. package/dist/vad/SileroVad.js +0 -40
  125. package/dist/vad/SileroVadModel.d.ts +0 -14
  126. package/dist/vad/SileroVadModel.js +0 -83
  127. package/dist/vad/chunkToFloat32Array.d.ts +0 -20
  128. package/dist/vad/chunkToFloat32Array.js +0 -34
  129. package/dist/vad/createVad.d.ts +0 -3
  130. package/dist/vad/createVad.js +0 -126
  131. package/dist/vad/types.d.ts +0 -60
  132. package/dist/vad/types.js +0 -2
  133. package/dist/vadWorker.js +0 -36
  134. package/dist/voice/Voice.d.ts +0 -61
  135. package/dist/voice/Voice.js +0 -96
  136. package/dist/voiceServerSetup.d.ts +0 -2
  137. package/dist/voiceServerSetup.js +0 -29
  138. /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 { idleTimeoutCountExceedsMax } from "./idleTimeoutCountExceedsMax";
20
- export { hasSpeechResult } from "./hasSpeechResult";
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
+ }