@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
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RegistryLlmResolver = exports.LlmResolutionError = exports.OPENAI_COMPATIBLE = exports.LayeredCredentials = exports.ConfigCredentials = void 0;
4
+ const community_1 = require("@fonoster/community");
5
+ const openai_1 = require("@fonoster/community/openai");
6
+ Object.defineProperty(exports, "OPENAI_COMPATIBLE", { enumerable: true, get: function () { return openai_1.OPENAI_COMPATIBLE; } });
7
+ /**
8
+ * Platform credentials from operator config: `{ "<credentialRef>": { env } | { value } }`. The
9
+ * variable names are operator choices; Autopilot never reads a vendor variable by itself.
10
+ */
11
+ class ConfigCredentials {
12
+ constructor(refs) {
13
+ this.refs = refs;
14
+ }
15
+ get(ref) {
16
+ const c = this.refs[ref];
17
+ return c?.value ?? (c?.env ? process.env[c.env] : undefined);
18
+ }
19
+ }
20
+ exports.ConfigCredentials = ConfigCredentials;
21
+ /** Workspace Secrets (BYOK) in front of platform credentials. */
22
+ class LayeredCredentials {
23
+ constructor(sources) {
24
+ this.sources = sources;
25
+ }
26
+ async get(ref, scope) {
27
+ for (const s of this.sources) {
28
+ const v = await s.get(ref, scope);
29
+ if (v)
30
+ return v;
31
+ }
32
+ return undefined;
33
+ }
34
+ }
35
+ exports.LayeredCredentials = LayeredCredentials;
36
+ class LlmResolutionError extends Error {
37
+ }
38
+ exports.LlmResolutionError = LlmResolutionError;
39
+ class RegistryLlmResolver {
40
+ constructor(options) {
41
+ this.options = options;
42
+ }
43
+ async resolve(catalogId, settings, scope = {}) {
44
+ const { registry } = this.options;
45
+ const resolution = registry.resolve(catalogId);
46
+ const id = resolution.id;
47
+ const entries = registry.list();
48
+ const model = entries.find((e) => e.id === id);
49
+ if (!model || model.kind !== "model" || model.type !== "llm")
50
+ throw new LlmResolutionError(`${catalogId} is not a language model in the catalog`);
51
+ const providerId = id.slice(0, id.indexOf("/"));
52
+ const provider = entries.find((e) => e.id === providerId);
53
+ if (!provider || provider.kind !== "provider")
54
+ throw new LlmResolutionError(`no provider ${providerId} for ${id}`);
55
+ if (provider.state === "retired")
56
+ throw new LlmResolutionError(`provider ${providerId} is retired`);
57
+ const llm = (0, community_1.adapterFor)(provider.adapter)?.llm;
58
+ if (!llm)
59
+ throw new LlmResolutionError(`adapter ${provider.adapter} has no language model implementation in Autopilot yet`);
60
+ const baseUrl = this.options.endpoints?.[provider.id] ??
61
+ this.options.endpoints?.[provider.adapter] ??
62
+ llm.defaultEndpoint;
63
+ if (!baseUrl)
64
+ throw new LlmResolutionError(`no endpoint for provider ${provider.id} (adapter ${provider.adapter})`);
65
+ const apiKey = await this.options.credentials.get(provider.credentialRef, scope);
66
+ if (!apiKey && !(0, community_1.adapterFor)(provider.adapter)?.keyless)
67
+ throw new LlmResolutionError(`no credential ${provider.credentialRef} for provider ${provider.id}`);
68
+ return {
69
+ id,
70
+ think: llm.create({ baseUrl, apiKey }, id.slice(providerId.length + 1), settings),
71
+ ...(resolution.event
72
+ ? {
73
+ event: {
74
+ type: resolution.event,
75
+ id: catalogId,
76
+ replacement: resolution.replacement
77
+ }
78
+ }
79
+ : {})
80
+ };
81
+ }
82
+ }
83
+ exports.RegistryLlmResolver = RegistryLlmResolver;
@@ -0,0 +1,206 @@
1
+ import { Agent, AgentEvent } from "./Agent";
2
+ /**
3
+ * Autopilot's states (rework-autopilot A4). The machine decides; the Agent acts on the session.
4
+ * The idle clock runs only in listening.
5
+ */
6
+ export declare const autopilotMachine: import("xstate").StateMachine<{
7
+ agent: Agent;
8
+ }, AgentEvent, {}, never, import("xstate").Values<{
9
+ transfer: {
10
+ type: "transfer";
11
+ params: unknown;
12
+ };
13
+ stop: {
14
+ type: "stop";
15
+ params: unknown;
16
+ };
17
+ prepare: {
18
+ type: "prepare";
19
+ params: unknown;
20
+ };
21
+ greet: {
22
+ type: "greet";
23
+ params: unknown;
24
+ };
25
+ armIdle: {
26
+ type: "armIdle";
27
+ params: unknown;
28
+ };
29
+ disarmIdle: {
30
+ type: "disarmIdle";
31
+ params: unknown;
32
+ };
33
+ sayIdle: {
34
+ type: "sayIdle";
35
+ params: unknown;
36
+ };
37
+ resumed: {
38
+ type: "resumed";
39
+ params: unknown;
40
+ };
41
+ answer: {
42
+ type: "answer";
43
+ params: unknown;
44
+ };
45
+ abortReply: {
46
+ type: "abortReply";
47
+ params: unknown;
48
+ };
49
+ clear: {
50
+ type: "clear";
51
+ params: unknown;
52
+ };
53
+ goodbye: {
54
+ type: "goodbye";
55
+ params: unknown;
56
+ };
57
+ }>, import("xstate").Values<{
58
+ spokenFrom: {
59
+ type: "spokenFrom";
60
+ params: string[];
61
+ };
62
+ bargeIn: {
63
+ type: "bargeIn";
64
+ params: unknown;
65
+ };
66
+ idleExhausted: {
67
+ type: "idleExhausted";
68
+ params: unknown;
69
+ };
70
+ }>, never, "greeting" | "listening" | "thinking" | "ending" | "speaking" | "transferring" | "ended", string, {
71
+ agent: Agent;
72
+ }, {}, import("xstate").EventObject, import("xstate").MetaObject, {
73
+ readonly id: "autopilot";
74
+ readonly initial: "greeting";
75
+ readonly context: ({ input }: {
76
+ spawn: {
77
+ <TSrc extends never>(logic: TSrc, ...[options]: never): import("xstate").ActorRefFromLogic<never>;
78
+ <TLogic extends import("xstate").AnyActorLogic>(src: TLogic, options?: {
79
+ id?: never;
80
+ systemId?: string;
81
+ input?: import("xstate").InputFrom<TLogic>;
82
+ syncSnapshot?: boolean;
83
+ }): import("xstate").ActorRefFromLogic<TLogic>;
84
+ };
85
+ input: {
86
+ agent: Agent;
87
+ };
88
+ self: import("xstate").ActorRef<import("xstate").MachineSnapshot<{
89
+ agent: Agent;
90
+ }, AgentEvent, Record<string, import("xstate").AnyActorRef>, import("xstate").StateValue, string, unknown, any, any>, AgentEvent, import("xstate").AnyEventObject>;
91
+ }) => {
92
+ agent: Agent;
93
+ };
94
+ readonly on: {
95
+ readonly SESSION_END: ".ended";
96
+ readonly MAX_DURATION: ".ending";
97
+ readonly HANGUP_REQUESTED: ".ending";
98
+ readonly TRANSFER_REQUESTED: ".transferring";
99
+ readonly TURN_BACKCHANNEL: {};
100
+ };
101
+ readonly states: {
102
+ readonly greeting: {
103
+ readonly entry: "greet";
104
+ readonly on: {
105
+ readonly SPOKEN: {
106
+ readonly guard: {
107
+ readonly type: "spokenFrom";
108
+ readonly params: ["greeting"];
109
+ };
110
+ readonly target: "listening";
111
+ };
112
+ readonly TURN_USER_STARTED: {
113
+ readonly guard: "bargeIn";
114
+ readonly actions: "clear";
115
+ };
116
+ readonly TURN_LIKELY_ENDED: {
117
+ readonly actions: "prepare";
118
+ };
119
+ readonly TURN_RESUMED: {
120
+ readonly actions: "resumed";
121
+ };
122
+ readonly TURN_ENDED: "thinking";
123
+ };
124
+ };
125
+ readonly listening: {
126
+ readonly entry: "armIdle";
127
+ readonly exit: "disarmIdle";
128
+ readonly on: {
129
+ readonly TURN_LIKELY_ENDED: {
130
+ readonly actions: "prepare";
131
+ };
132
+ readonly TURN_RESUMED: {
133
+ readonly actions: "resumed";
134
+ };
135
+ readonly TURN_ENDED: "thinking";
136
+ readonly IDLE: readonly [{
137
+ readonly guard: "idleExhausted";
138
+ readonly target: "ending";
139
+ }, {
140
+ readonly target: "speaking";
141
+ readonly actions: "sayIdle";
142
+ }];
143
+ };
144
+ };
145
+ readonly thinking: {
146
+ readonly entry: "answer";
147
+ readonly on: {
148
+ readonly REPLY_SPEAKING: "speaking";
149
+ readonly SPOKEN: {
150
+ readonly guard: {
151
+ readonly type: "spokenFrom";
152
+ readonly params: ["reply", "error"];
153
+ };
154
+ readonly target: "listening";
155
+ };
156
+ readonly TURN_USER_STARTED: {
157
+ readonly guard: "bargeIn";
158
+ readonly actions: "clear";
159
+ };
160
+ readonly TURN_ENDED: {
161
+ readonly target: "thinking";
162
+ readonly reenter: true;
163
+ readonly actions: "abortReply";
164
+ };
165
+ };
166
+ };
167
+ readonly speaking: {
168
+ readonly on: {
169
+ readonly SPOKEN: {
170
+ readonly guard: {
171
+ readonly type: "spokenFrom";
172
+ readonly params: ["reply", "idle", "error"];
173
+ };
174
+ readonly target: "listening";
175
+ };
176
+ readonly TURN_USER_STARTED: {
177
+ readonly guard: "bargeIn";
178
+ readonly actions: "clear";
179
+ };
180
+ readonly TURN_ENDED: {
181
+ readonly target: "thinking";
182
+ readonly actions: "abortReply";
183
+ };
184
+ };
185
+ };
186
+ readonly transferring: {
187
+ readonly entry: "transfer";
188
+ readonly on: {
189
+ readonly SPOKEN: {
190
+ readonly guard: {
191
+ readonly type: "spokenFrom";
192
+ readonly params: ["error"];
193
+ };
194
+ readonly target: "listening";
195
+ };
196
+ };
197
+ };
198
+ readonly ending: {
199
+ readonly entry: "goodbye";
200
+ };
201
+ readonly ended: {
202
+ readonly type: "final";
203
+ readonly entry: "stop";
204
+ };
205
+ };
206
+ }>;
@@ -0,0 +1,126 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.autopilotMachine = 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
+ const xstate_1 = require("xstate");
23
+ /**
24
+ * Autopilot's states (rework-autopilot A4). The machine decides; the Agent acts on the session.
25
+ * The idle clock runs only in listening.
26
+ */
27
+ exports.autopilotMachine = (0, xstate_1.setup)({
28
+ types: {
29
+ context: {},
30
+ input: {},
31
+ events: {}
32
+ },
33
+ guards: {
34
+ spokenFrom: ({ event }, sources) => event.type === "SPOKEN" && sources.includes(event.source),
35
+ bargeIn: ({ context }) => context.agent.settings.allowUserBargeIn,
36
+ idleExhausted: ({ context }) => context.agent.idleExhausted()
37
+ },
38
+ actions: {
39
+ greet: ({ context }) => void context.agent.greet(),
40
+ armIdle: ({ context }) => context.agent.armIdle(),
41
+ disarmIdle: ({ context }) => context.agent.disarmIdle(),
42
+ sayIdle: ({ context }) => void context.agent.sayIdle(),
43
+ prepare: ({ context, event }) => context.agent.prepare(event.text),
44
+ resumed: ({ context }) => context.agent.resumed(),
45
+ answer: ({ context, event }) => void context.agent.answer(event.text),
46
+ abortReply: ({ context }) => context.agent.abortReply(),
47
+ clear: ({ context }) => void context.agent.clear(),
48
+ goodbye: ({ context }) => void context.agent.goodbye(),
49
+ transfer: ({ context }) => void context.agent.transfer(),
50
+ stop: ({ context }) => context.agent.stop()
51
+ }
52
+ }).createMachine({
53
+ id: "autopilot",
54
+ initial: "greeting",
55
+ context: ({ input }) => ({ agent: input.agent }),
56
+ on: {
57
+ SESSION_END: ".ended",
58
+ MAX_DURATION: ".ending",
59
+ HANGUP_REQUESTED: ".ending",
60
+ TRANSFER_REQUESTED: ".transferring",
61
+ // Backchannels are not interruptions: nothing happens in any state.
62
+ TURN_BACKCHANNEL: {}
63
+ },
64
+ states: {
65
+ greeting: {
66
+ entry: "greet",
67
+ on: {
68
+ SPOKEN: {
69
+ guard: { type: "spokenFrom", params: ["greeting"] },
70
+ target: "listening"
71
+ },
72
+ TURN_USER_STARTED: { guard: "bargeIn", actions: "clear" },
73
+ TURN_LIKELY_ENDED: { actions: "prepare" },
74
+ TURN_RESUMED: { actions: "resumed" },
75
+ TURN_ENDED: "thinking"
76
+ }
77
+ },
78
+ listening: {
79
+ entry: "armIdle",
80
+ exit: "disarmIdle",
81
+ on: {
82
+ TURN_LIKELY_ENDED: { actions: "prepare" },
83
+ TURN_RESUMED: { actions: "resumed" },
84
+ TURN_ENDED: "thinking",
85
+ IDLE: [
86
+ { guard: "idleExhausted", target: "ending" },
87
+ { target: "speaking", actions: "sayIdle" }
88
+ ]
89
+ }
90
+ },
91
+ thinking: {
92
+ entry: "answer",
93
+ on: {
94
+ REPLY_SPEAKING: "speaking",
95
+ SPOKEN: {
96
+ guard: { type: "spokenFrom", params: ["reply", "error"] },
97
+ target: "listening"
98
+ },
99
+ TURN_USER_STARTED: { guard: "bargeIn", actions: "clear" },
100
+ // A new turn cancels the reply in flight; its user text stays in the history.
101
+ TURN_ENDED: { target: "thinking", reenter: true, actions: "abortReply" }
102
+ }
103
+ },
104
+ speaking: {
105
+ on: {
106
+ SPOKEN: {
107
+ guard: { type: "spokenFrom", params: ["reply", "idle", "error"] },
108
+ target: "listening"
109
+ },
110
+ TURN_USER_STARTED: { guard: "bargeIn", actions: "clear" },
111
+ TURN_ENDED: { target: "thinking", actions: "abortReply" }
112
+ }
113
+ },
114
+ transferring: {
115
+ entry: "transfer",
116
+ on: {
117
+ SPOKEN: {
118
+ guard: { type: "spokenFrom", params: ["error"] },
119
+ target: "listening"
120
+ }
121
+ }
122
+ },
123
+ ending: { entry: "goodbye" },
124
+ ended: { type: "final", entry: "stop" }
125
+ }
126
+ });
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ /**
4
+ * Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
5
+ * http://github.com/fonoster/fonoster
6
+ *
7
+ * This file is part of Fonoster
8
+ *
9
+ * Licensed under the MIT License (the "License");
10
+ * you may not use this file except in compliance with
11
+ * the License. You may obtain a copy of the License at
12
+ *
13
+ * https://opensource.org/licenses/MIT
14
+ *
15
+ * Unless required by applicable law or agreed to in writing, software
16
+ * distributed under the License is distributed on an "AS IS" BASIS,
17
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
+ * See the License for the specific language governing permissions and
19
+ * limitations under the License.
20
+ */
21
+ // Autopilot's entry point (rework-autopilot task 4.3; the only engine since cleanbreak CB-B5):
22
+ //
23
+ // node dist/session/main.js --config /config/autopilot.yaml
24
+ const common_1 = require("@fonoster/common");
25
+ const logger_1 = require("@fonoster/logger");
26
+ const hosted_1 = require("./hosted");
27
+ const logger = (0, logger_1.getLogger)({ service: "autopilot", filePath: __filename });
28
+ (0, common_1.logUnhandledRejections)(logger);
29
+ // --config, AUTOPILOT_SESSION_CONFIG, else /config/autopilot.yaml (or an older location).
30
+ const at = process.argv.indexOf("--config");
31
+ const file = at >= 0
32
+ ? process.argv[at + 1]
33
+ : (process.env.AUTOPILOT_SESSION_CONFIG ?? (0, hosted_1.findHostedConfig)());
34
+ if (!file) {
35
+ logger.error("no Autopilot config file: mount /config/autopilot.yaml or pass --config <file>");
36
+ process.exit(1);
37
+ }
38
+ const config = (0, hosted_1.loadHostedConfig)(file);
39
+ // Off unless an OTLP endpoint is configured (telemetry.otlpEndpoint or OTEL_EXPORTER_OTLP_*).
40
+ (0, common_1.initTelemetry)("fonoster-autopilot", undefined, (0, common_1.telemetryEnv)(config.telemetry));
41
+ (0, hosted_1.startHostedAutopilot)(config).catch((e) => {
42
+ logger.error(`hosted autopilot failed to start: ${e.message}`);
43
+ process.exit(1);
44
+ });
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Groups streamed tokens into sentence-sized pieces, so each speak piece is a natural unit for TTS
3
+ * while the first sentence still starts before the reply is complete (rework-autopilot A5).
4
+ */
5
+ export declare function sentences(tokens: AsyncIterable<string>): AsyncIterable<string>;
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.sentences = sentences;
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 SENTENCE_END = /[.!?](["')\]]*)\s+/;
23
+ const MAX_PIECE = 160;
24
+ /**
25
+ * Groups streamed tokens into sentence-sized pieces, so each speak piece is a natural unit for TTS
26
+ * while the first sentence still starts before the reply is complete (rework-autopilot A5).
27
+ */
28
+ async function* sentences(tokens) {
29
+ let buffer = "";
30
+ for await (const token of tokens) {
31
+ buffer += token;
32
+ for (;;) {
33
+ const m = SENTENCE_END.exec(buffer);
34
+ if (m) {
35
+ const end = m.index + m[0].length;
36
+ yield buffer.slice(0, end);
37
+ buffer = buffer.slice(end);
38
+ continue;
39
+ }
40
+ if (buffer.length > MAX_PIECE) {
41
+ const cut = buffer.lastIndexOf(" ", MAX_PIECE);
42
+ if (cut > 0) {
43
+ yield buffer.slice(0, cut + 1);
44
+ buffer = buffer.slice(cut + 1);
45
+ continue;
46
+ }
47
+ }
48
+ break;
49
+ }
50
+ }
51
+ if (buffer.trim())
52
+ yield buffer;
53
+ }
@@ -16,6 +16,5 @@
16
16
  * See the License for the specific language governing permissions and
17
17
  * limitations under the License.
18
18
  */
19
- export * from "./doProcessUserRequest";
20
- export * from "./doGreetUser";
21
- export * from "./doAnnounceIdleTimeout";
19
+ export { ChatMessage, Think, ThinkChunk, ThinkError, ThinkProvider, ToolCall, ToolSpec } from "@fonoster/community";
20
+ export { OpenAiCompatibleThink, resolveThink } from "@fonoster/community/openai";
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.announceSystemError = void 0;
3
+ exports.resolveThink = exports.OpenAiCompatibleThink = exports.ThinkError = void 0;
4
4
  /**
5
5
  * Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
6
6
  * http://github.com/fonoster/fonoster
@@ -19,12 +19,10 @@ exports.announceSystemError = void 0;
19
19
  * See the License for the specific language governing permissions and
20
20
  * limitations under the License.
21
21
  */
22
- const logger_1 = require("@fonoster/logger");
23
- const logger = (0, logger_1.getLogger)({ service: "autopilot", filePath: __filename });
24
- const announceSystemError = async ({ context }) => {
25
- logger.verbose("called the announceSystemError action", {
26
- systemErrorMessage: context.systemErrorMessage
27
- });
28
- await context.voice.say(context.systemErrorMessage);
29
- };
30
- exports.announceSystemError = announceSystemError;
22
+ // The LLM adapter interface and the OpenAI-compatible adapter live in @fonoster/community with
23
+ // the other vendor adapters (registry 3.1).
24
+ var community_1 = require("@fonoster/community");
25
+ Object.defineProperty(exports, "ThinkError", { enumerable: true, get: function () { return community_1.ThinkError; } });
26
+ var openai_1 = require("@fonoster/community/openai");
27
+ Object.defineProperty(exports, "OpenAiCompatibleThink", { enumerable: true, get: function () { return openai_1.OpenAiCompatibleThink; } });
28
+ Object.defineProperty(exports, "resolveThink", { enumerable: true, get: function () { return openai_1.resolveThink; } });
@@ -0,0 +1,22 @@
1
+ import { DestinationPolicy } from "@fonoster/common";
2
+ import { ToolDefinition } from "./config";
3
+ export type ToolOutcome = {
4
+ ok: boolean;
5
+ result?: unknown;
6
+ error?: string;
7
+ };
8
+ /** The tool port (rework-autopilot A7): one operation, run a tool with inputs. */
9
+ export interface ToolPort {
10
+ run(name: string, args: Record<string, unknown>, signal: AbortSignal): Promise<ToolOutcome>;
11
+ }
12
+ /**
13
+ * HTTP tools from the assistant config (`operation.method`, `url`, `headers`). Tool URLs are
14
+ * tenant input: they go through safeFetch (public destinations only, redirects re-checked,
15
+ * 10 s timeout; RV-B2).
16
+ */
17
+ export declare class HttpToolPort implements ToolPort {
18
+ private readonly tools;
19
+ private readonly policy?;
20
+ constructor(tools: ToolDefinition[], policy?: DestinationPolicy);
21
+ run(name: string, args: Record<string, unknown>, signal: AbortSignal): Promise<ToolOutcome>;
22
+ }
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HttpToolPort = 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
+ const url_1 = require("url");
23
+ const common_1 = require("@fonoster/common");
24
+ /**
25
+ * HTTP tools from the assistant config (`operation.method`, `url`, `headers`). Tool URLs are
26
+ * tenant input: they go through safeFetch (public destinations only, redirects re-checked,
27
+ * 10 s timeout; RV-B2).
28
+ */
29
+ class HttpToolPort {
30
+ constructor(tools, policy) {
31
+ this.tools = tools;
32
+ this.policy = policy;
33
+ }
34
+ async run(name, args, signal) {
35
+ const tool = this.tools.find((t) => t.name === name);
36
+ if (!tool)
37
+ return { ok: false, error: `unknown tool ${name}` };
38
+ const method = String(tool.operation.method ?? "get").toUpperCase();
39
+ const url = new url_1.URL(tool.operation.url);
40
+ let body;
41
+ if (method === "GET") {
42
+ for (const [k, v] of Object.entries(args))
43
+ url.searchParams.set(k, String(v));
44
+ }
45
+ else {
46
+ body = JSON.stringify(args);
47
+ }
48
+ try {
49
+ const request = (0, common_1.safeFetch)(url, {
50
+ method,
51
+ headers: {
52
+ accept: "application/json",
53
+ "content-type": "application/json",
54
+ ...(tool.operation.headers ?? {})
55
+ },
56
+ body,
57
+ signal,
58
+ policy: this.policy?.allow ? this.policy : undefined
59
+ });
60
+ if (tool.operation.waitForResponse === false) {
61
+ request.catch(() => undefined);
62
+ return { ok: true, result: { status: "sent" } };
63
+ }
64
+ const res = await request;
65
+ const text = await res.text();
66
+ if (!res.ok)
67
+ return {
68
+ ok: false,
69
+ error: `HTTP ${res.status}: ${text.slice(0, 200)}`
70
+ };
71
+ try {
72
+ return { ok: true, result: JSON.parse(text) };
73
+ }
74
+ catch {
75
+ return { ok: true, result: text };
76
+ }
77
+ }
78
+ catch (e) {
79
+ return { ok: false, error: e.message };
80
+ }
81
+ }
82
+ }
83
+ exports.HttpToolPort = HttpToolPort;
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.transferToolDefinition = void 0;
4
4
  const transferToolDefinition = {
5
5
  name: "transfer",
6
- description: "Transfer the call to a live agent",
6
+ description: "Transfer the call to an operator (a person)",
7
7
  parameters: {
8
8
  type: "object",
9
9
  properties: {
@@ -16,5 +16,4 @@
16
16
  * See the License for the specific language governing permissions and
17
17
  * limitations under the License.
18
18
  */
19
- export * from "./Voice";
20
19
  export * from "./types";