@fonoster/autopilot 0.27.0 → 0.31.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 (153) hide show
  1. package/README.md +15 -52
  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/MeteredEmbeddings.d.ts +47 -0
  8. package/dist/knowledge/MeteredEmbeddings.js +58 -0
  9. package/dist/knowledge/types.d.ts +1 -1
  10. package/dist/models/LanguageModelFactory.d.ts +5 -24
  11. package/dist/models/LanguageModelFactory.js +0 -1
  12. package/dist/models/createLanguageModel.d.ts +2 -2
  13. package/dist/models/createLanguageModel.js +2 -2
  14. package/dist/models/evaluations/createEvalEffectiveConfig.d.ts +10 -106
  15. package/dist/models/evaluations/createEvalEffectiveConfig.js +47 -7
  16. package/dist/models/evaluations/createEvaluateIntelligence.d.ts +21 -20
  17. package/dist/models/evaluations/createEvaluateIntelligence.js +53 -23
  18. package/dist/models/evaluations/createTestTextSimilarity.d.ts +7 -1
  19. package/dist/models/evaluations/createTestTextSimilarity.js +19 -1
  20. package/dist/models/evaluations/evalTestCases.d.ts +2 -2
  21. package/dist/models/evaluations/evalTestCases.js +7 -7
  22. package/dist/models/evaluations/evaluateScenario.js +2 -2
  23. package/dist/models/evaluations/evaluateStep.d.ts +1 -1
  24. package/dist/models/evaluations/evaluateStep.js +2 -2
  25. package/dist/models/evaluations/extractAiResponse.d.ts +2 -3
  26. package/dist/models/evaluations/extractAiResponse.js +3 -3
  27. package/dist/models/evaluations/index.d.ts +1 -0
  28. package/dist/models/evaluations/index.js +1 -0
  29. package/dist/models/evaluations/runEval.d.ts +2 -20
  30. package/dist/models/evaluations/runEval.js +7 -7
  31. package/dist/models/evaluations/session/EvalSession.d.ts +73 -0
  32. package/dist/models/evaluations/session/EvalSession.js +134 -0
  33. package/dist/models/evaluations/session/evaluateSessionScenario.d.ts +58 -0
  34. package/dist/models/evaluations/session/evaluateSessionScenario.js +182 -0
  35. package/dist/{vad → models/evaluations/session}/index.d.ts +3 -2
  36. package/dist/{vad → models/evaluations/session}/index.js +3 -2
  37. package/dist/models/evaluations/session/runSessionEval.d.ts +20 -0
  38. package/dist/models/evaluations/session/runSessionEval.js +59 -0
  39. package/dist/models/evaluations/textSimilaryPrompt.d.ts +1 -1
  40. package/dist/models/evaluations/textSimilaryPrompt.js +1 -1
  41. package/dist/models/evaluations/types.d.ts +7 -6
  42. package/dist/models/types.d.ts +19 -1
  43. package/dist/session/Agent.d.ts +173 -0
  44. package/dist/session/Agent.js +548 -0
  45. package/dist/{loadAssistantFromAPI.d.ts → session/AsyncQueue.d.ts} +12 -7
  46. package/dist/session/AsyncQueue.js +67 -0
  47. package/dist/{machine/guards/index.d.ts → session/config.d.ts} +2 -2
  48. package/dist/session/config.js +27 -0
  49. package/dist/session/createAutopilot.d.ts +48 -0
  50. package/dist/session/createAutopilot.js +151 -0
  51. package/dist/session/hosted.d.ts +454 -0
  52. package/dist/session/hosted.js +501 -0
  53. package/dist/{assistants → session}/index.d.ts +11 -2
  54. package/dist/{machine/actors → session}/index.js +11 -3
  55. package/dist/{machine/actions/appendSpeech.js → session/knowledge.d.ts} +14 -17
  56. package/dist/session/knowledge.js +10 -0
  57. package/dist/session/llm.d.ts +113 -0
  58. package/dist/session/llm.js +127 -0
  59. package/dist/session/machine.d.ts +206 -0
  60. package/dist/session/machine.js +126 -0
  61. package/dist/session/main.js +45 -0
  62. package/dist/session/meteredThink.d.ts +45 -0
  63. package/dist/session/meteredThink.js +125 -0
  64. package/dist/session/sentences.d.ts +5 -0
  65. package/dist/session/sentences.js +53 -0
  66. package/dist/session/telemetry.d.ts +34 -0
  67. package/dist/session/telemetry.js +55 -0
  68. package/dist/{machine/actors/index.d.ts → session/think.d.ts} +2 -3
  69. package/dist/{machine/actions/announceSystemError.js → session/think.js} +8 -10
  70. package/dist/session/tools.d.ts +22 -0
  71. package/dist/session/tools.js +83 -0
  72. package/dist/tools/builtin/transferToolDefinition.js +1 -1
  73. package/dist/voice/index.d.ts +0 -1
  74. package/dist/voice/index.js +0 -1
  75. package/dist/voice/types.d.ts +5 -18
  76. package/package.json +15 -16
  77. package/dist/Autopilot.d.ts +0 -14
  78. package/dist/Autopilot.js +0 -133
  79. package/dist/assistants/index.js +0 -36
  80. package/dist/assistants/loadAndValidateAssistant.d.ts +0 -3
  81. package/dist/assistants/loadAndValidateAssistant.js +0 -49
  82. package/dist/assistants/types.d.ts +0 -24
  83. package/dist/assistants/types.js +0 -2
  84. package/dist/envs.d.ts +0 -15
  85. package/dist/envs.js +0 -78
  86. package/dist/handleVoiceRequest.d.ts +0 -3
  87. package/dist/handleVoiceRequest.js +0 -167
  88. package/dist/loadAssistantConfigFromFile.d.ts +0 -3
  89. package/dist/loadAssistantConfigFromFile.js +0 -68
  90. package/dist/loadAssistantFromAPI.js +0 -98
  91. package/dist/machine/actions/announceSystemError.d.ts +0 -4
  92. package/dist/machine/actions/appendSpeech.d.ts +0 -1
  93. package/dist/machine/actions/cleanSpeech.d.ts +0 -1
  94. package/dist/machine/actions/cleanSpeech.js +0 -23
  95. package/dist/machine/actions/goodbye.d.ts +0 -4
  96. package/dist/machine/actions/goodbye.js +0 -31
  97. package/dist/machine/actions/increaseIdleTimeoutCount.d.ts +0 -1
  98. package/dist/machine/actions/increaseIdleTimeoutCount.js +0 -31
  99. package/dist/machine/actions/index.d.ts +0 -26
  100. package/dist/machine/actions/index.js +0 -37
  101. package/dist/machine/actions/interruptPlayback.d.ts +0 -4
  102. package/dist/machine/actions/interruptPlayback.js +0 -43
  103. package/dist/machine/actions/resetIdleTimeoutCount.d.ts +0 -1
  104. package/dist/machine/actions/resetIdleTimeoutCount.js +0 -31
  105. package/dist/machine/actions/resetState.d.ts +0 -1
  106. package/dist/machine/actions/resetState.js +0 -32
  107. package/dist/machine/actors/doAnnounceIdleTimeout.d.ts +0 -4
  108. package/dist/machine/actors/doAnnounceIdleTimeout.js +0 -41
  109. package/dist/machine/actors/doGreetUser.d.ts +0 -4
  110. package/dist/machine/actors/doGreetUser.js +0 -45
  111. package/dist/machine/actors/doProcessUserRequest.d.ts +0 -4
  112. package/dist/machine/actors/doProcessUserRequest.js +0 -75
  113. package/dist/machine/context.d.ts +0 -52
  114. package/dist/machine/context.js +0 -28
  115. package/dist/machine/delays.d.ts +0 -30
  116. package/dist/machine/delays.js +0 -37
  117. package/dist/machine/guards/hasSpeechResult.d.ts +0 -4
  118. package/dist/machine/guards/hasSpeechResult.js +0 -30
  119. package/dist/machine/guards/idleTimeoutCountExceedsMax.d.ts +0 -4
  120. package/dist/machine/guards/idleTimeoutCountExceedsMax.js +0 -31
  121. package/dist/machine/guards/index.js +0 -25
  122. package/dist/machine/index.d.ts +0 -19
  123. package/dist/machine/index.js +0 -23
  124. package/dist/machine/machine.d.ts +0 -408
  125. package/dist/machine/machine.js +0 -206
  126. package/dist/machine/setup.d.ts +0 -328
  127. package/dist/machine/setup.js +0 -116
  128. package/dist/machine/types.d.ts +0 -54
  129. package/dist/machine/types.js +0 -2
  130. package/dist/sendConversationEndedEvent.d.ts +0 -32
  131. package/dist/sendConversationEndedEvent.js +0 -66
  132. package/dist/sendConversationStartedEvent.d.ts +0 -6
  133. package/dist/sendConversationStartedEvent.js +0 -63
  134. package/dist/server.d.ts +0 -2
  135. package/dist/server.js +0 -25
  136. package/dist/types.d.ts +0 -40
  137. package/dist/types.js +0 -8
  138. package/dist/vad/SileroVad.d.ts +0 -18
  139. package/dist/vad/SileroVad.js +0 -40
  140. package/dist/vad/SileroVadModel.d.ts +0 -14
  141. package/dist/vad/SileroVadModel.js +0 -83
  142. package/dist/vad/chunkToFloat32Array.d.ts +0 -20
  143. package/dist/vad/chunkToFloat32Array.js +0 -34
  144. package/dist/vad/createVad.d.ts +0 -3
  145. package/dist/vad/createVad.js +0 -126
  146. package/dist/vad/types.d.ts +0 -60
  147. package/dist/vad/types.js +0 -2
  148. package/dist/vadWorker.js +0 -36
  149. package/dist/voice/Voice.d.ts +0 -61
  150. package/dist/voice/Voice.js +0 -96
  151. package/dist/voiceServerSetup.d.ts +0 -2
  152. package/dist/voiceServerSetup.js +0 -29
  153. /package/dist/{vadWorker.d.ts → session/main.d.ts} +0 -0
@@ -0,0 +1,501 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.FileWorkspaceSecrets = exports.CellSecretCredentials = exports.DatabaseWorkspaceSecrets = exports.PrismaAgentSource = exports.FileAgentSource = exports.AUTOPILOT_CONFIG_DIRS = exports.hostedConfigSchema = void 0;
37
+ exports.findHostedConfig = findHostedConfig;
38
+ exports.loadHostedConfig = loadHostedConfig;
39
+ exports.s3Knowledge = s3Knowledge;
40
+ exports.hostedOptions = hostedOptions;
41
+ exports.eagerKnowledge = eagerKnowledge;
42
+ exports.signedWorkspaceOnly = signedWorkspaceOnly;
43
+ exports.startHostedAutopilot = startHostedAutopilot;
44
+ /**
45
+ * Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
46
+ * http://github.com/fonoster/fonoster
47
+ *
48
+ * This file is part of Fonoster
49
+ *
50
+ * Licensed under the MIT License (the "License");
51
+ * you may not use this file except in compliance with
52
+ * the License. You may obtain a copy of the License at
53
+ *
54
+ * https://opensource.org/licenses/MIT
55
+ *
56
+ * Unless required by applicable law or agreed to in writing, software
57
+ * distributed under the License is distributed on an "AS IS" BASIS,
58
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
59
+ * See the License for the specific language governing permissions and
60
+ * limitations under the License.
61
+ */
62
+ const fs = __importStar(require("fs"));
63
+ const path = __importStar(require("path"));
64
+ const common_1 = require("@fonoster/common");
65
+ const logger_1 = require("@fonoster/logger");
66
+ const voice_1 = require("@fonoster/voice");
67
+ const yaml = __importStar(require("js-yaml"));
68
+ const zod_1 = require("zod");
69
+ const MeteredEmbeddings_1 = require("../knowledge/MeteredEmbeddings");
70
+ const createAutopilot_1 = require("./createAutopilot");
71
+ const knowledge_1 = require("./knowledge");
72
+ const llm_1 = require("./llm");
73
+ // The hosted Autopilot (rework-autopilot A2, task 4.3): an ordinary VoiceServer that AppBridge
74
+ // dials like any app. Everything it needs comes from one operator config file; no vendor key is
75
+ // read from Autopilot's environment by name (A8, A11), only through credential refs.
76
+ const logger = (0, logger_1.getLogger)({ service: "autopilot", filePath: __filename });
77
+ const secretRef = zod_1.z.union([
78
+ zod_1.z.object({ env: zod_1.z.string() }).strict(),
79
+ zod_1.z.object({ value: zod_1.z.string() }).strict()
80
+ ]);
81
+ const wsUrl = zod_1.z.string().regex(/^wss?:\/\//, "must be a ws:// or wss:// URL");
82
+ exports.hostedConfigSchema = zod_1.z
83
+ .object({
84
+ host: zod_1.z.string().default("0.0.0.0"),
85
+ port: zod_1.z.number().int().nonnegative().default(50061),
86
+ /**
87
+ * Private destinations HTTP tools may call (host names, IPs, CIDRs; RV-B2); defaults to
88
+ * FONOSTER_ALLOWED_DESTINATIONS.
89
+ */
90
+ allowedDestinations: zod_1.z.array(zod_1.z.string()).optional(),
91
+ /** The secret AppBridge signs Autopilot sessions with (one for every Autopilot app). */
92
+ signingSecret: secretRef,
93
+ /**
94
+ * The URL Autopilot apps point to, exactly as the apps have it (the apiserver's
95
+ * `apps.autopilotUrl`, e.g. ws://autopilot:3000), or several. Signatures are checked against
96
+ * it, never against the request's Host header.
97
+ */
98
+ publicUrl: zod_1.z.union([wsUrl, zod_1.z.array(wsUrl).min(1)], {
99
+ errorMap: (issue, ctx) => ({
100
+ message: issue.code !== "invalid_union"
101
+ ? ctx.defaultError
102
+ : ctx.data === undefined
103
+ ? "is required: the URL Autopilot apps point to (the apiserver's apps.autopilotUrl)"
104
+ : "must be a ws:// or wss:// URL, or a list of them"
105
+ })
106
+ }),
107
+ /**
108
+ * Apps and their agents: a file `{apps: {<appRef>: {workspaceRef?, agent}}}` (JSON or
109
+ * YAML), or `database`: the apps' Intelligence rows, cached for `cacheMs` (CP-B11).
110
+ */
111
+ agents: zod_1.z.union([
112
+ zod_1.z.object({ file: zod_1.z.string() }).strict(),
113
+ zod_1.z
114
+ .object({
115
+ source: zod_1.z.literal("database"),
116
+ cacheMs: zod_1.z.number().int().nonnegative().default(5000)
117
+ })
118
+ .strict()
119
+ ]),
120
+ /** The provider registry's catalog: the JSON file fonoster-registry writes, or the tables. */
121
+ registry: zod_1.z.union([
122
+ zod_1.z.object({ file: zod_1.z.string() }).strict(),
123
+ zod_1.z
124
+ .object({
125
+ source: zod_1.z.literal("database"),
126
+ refreshMs: zod_1.z.number().int().positive().default(30000)
127
+ })
128
+ .strict()
129
+ ]),
130
+ /** Platform keys by credentialRef. */
131
+ credentials: zod_1.z.record(secretRef).default({}),
132
+ /**
133
+ * Workspace Secrets (bring your own key): a JSON file by workspace and credentialRef, or
134
+ * `database`: the workspace's vendor keys (workspace_provider_keys).
135
+ */
136
+ workspaceSecrets: zod_1.z
137
+ .union([
138
+ zod_1.z.object({ file: zod_1.z.string() }).strict(),
139
+ zod_1.z.object({ source: zod_1.z.literal("database") }).strict(),
140
+ // A private region's cell secrets (add-private-regions PR-4, PR-9): by credentialRef or
141
+ // provider id, set on the cell with `fonoster-region secrets set <name>`.
142
+ zod_1.z
143
+ .object({
144
+ cellSecrets: zod_1.z
145
+ .object({
146
+ file: zod_1.z.string().optional(),
147
+ dir: zod_1.z.string().optional(),
148
+ keyEnv: zod_1.z.string().optional()
149
+ })
150
+ .strict()
151
+ })
152
+ .strict()
153
+ ])
154
+ .optional(),
155
+ /**
156
+ * The control-plane database (the apiserver's), for any `source: database`. The URL comes
157
+ * from this file only, usually as a placeholder: `url: "${APISERVER_DATABASE_URL}"`.
158
+ */
159
+ database: zod_1.z
160
+ .object({
161
+ url: zod_1.z.string().min(1),
162
+ /** A client generated from the apiserver schema (default `@prisma/client`). */
163
+ clientModule: zod_1.z.string().optional()
164
+ })
165
+ .strict()
166
+ .optional(),
167
+ /** Endpoint overrides by provider or adapter id (e.g. a local ollama); the catalog's otherwise. */
168
+ endpoints: zod_1.z.record(zod_1.z.string()).default({}),
169
+ /** The S3 + Unstructured knowledge bases; keys by credentialRef (task 4.1). */
170
+ knowledge: zod_1.z
171
+ .object({
172
+ s3: zod_1.z.object({
173
+ endpoint: zod_1.z.string(),
174
+ region: zod_1.z.string().default("us-east-1"),
175
+ accessKeyIdRef: zod_1.z.string(),
176
+ secretAccessKeyRef: zod_1.z.string()
177
+ }),
178
+ unstructured: zod_1.z.object({ url: zod_1.z.string(), apiKeyRef: zod_1.z.string() }),
179
+ embeddings: zod_1.z.object({ apiKeyRef: zod_1.z.string() })
180
+ })
181
+ .optional(),
182
+ /**
183
+ * The apiserver's encryption key (fonoster.yaml `encryption.key`; vendor keys are encrypted
184
+ * Secrets). Usually "${ENCRYPTION_KEY}".
185
+ */
186
+ encryption: zod_1.z
187
+ .object({ key: zod_1.z.string().min(1) })
188
+ .strict()
189
+ .optional(),
190
+ /** OTLP export (empty = off): `otlpEndpoint: "${OTEL_EXPORTER_OTLP_ENDPOINT:-}"`. */
191
+ telemetry: common_1.telemetrySchema
192
+ })
193
+ .strict();
194
+ function readStructured(file) {
195
+ const text = fs.readFileSync(file, "utf8");
196
+ return /\.ya?ml$/.test(file) ? yaml.load(text) : JSON.parse(text);
197
+ }
198
+ /** Where Autopilot looks for its file when none is named (autopilot.yaml, then the older name). */
199
+ exports.AUTOPILOT_CONFIG_DIRS = ["/config", "/etc/fonoster", "./config"];
200
+ /** The file `loadHostedConfig` reads without `--config`: the first one found, or undefined. */
201
+ function findHostedConfig() {
202
+ for (const base of ["autopilot", "autopilot-session"]) {
203
+ const found = (0, common_1.resolveConfigFile)(base, [], exports.AUTOPILOT_CONFIG_DIRS);
204
+ if (found.path)
205
+ return found.path;
206
+ }
207
+ return undefined;
208
+ }
209
+ /**
210
+ * Loads the hosted Autopilot's file the apiserver's way (unify-config-placeholders): `${VAR}`
211
+ * placeholders from the container environment, `secret://` references, strict validation.
212
+ */
213
+ function loadHostedConfig(file, env = process.env) {
214
+ const config = (0, common_1.loadServiceConfig)({
215
+ service: "autopilot",
216
+ schema: exports.hostedConfigSchema,
217
+ argv: ["--config", file],
218
+ env,
219
+ interpolatePaths: ["*"]
220
+ });
221
+ const dir = path.dirname(file);
222
+ const rel = (p) => (path.isAbsolute(p) ? p : path.join(dir, p));
223
+ const withFile = (c) => "file" in c ? { ...c, file: rel(c.file) } : c;
224
+ const needsDb = !("file" in config.agents) ||
225
+ !("file" in config.registry) ||
226
+ (config.workspaceSecrets &&
227
+ "source" in config.workspaceSecrets &&
228
+ config.workspaceSecrets.source === "database");
229
+ if (needsDb && !config.database)
230
+ throw new Error("source: database needs a database section");
231
+ return {
232
+ ...config,
233
+ agents: withFile(config.agents),
234
+ registry: withFile(config.registry),
235
+ ...(config.workspaceSecrets
236
+ ? { workspaceSecrets: withFile(config.workspaceSecrets) }
237
+ : {})
238
+ };
239
+ }
240
+ const resolveSecret = (s) => "value" in s && s.value !== undefined ? s.value : process.env[s.env ?? ""];
241
+ /** Apps and agents from a file (a stand-in until agents come from the control plane). */
242
+ class FileAgentSource {
243
+ constructor(file) {
244
+ this.file = file;
245
+ }
246
+ async get(appRef) {
247
+ const data = readStructured(this.file);
248
+ const app = data.apps?.[appRef];
249
+ return app
250
+ ? { agent: app.agent, workspaceRef: app.workspaceRef }
251
+ : undefined;
252
+ }
253
+ }
254
+ exports.FileAgentSource = FileAgentSource;
255
+ /**
256
+ * Agents from the control plane: an Autopilot app's Intelligence row holds its agent
257
+ * (CP-B3). Each app is cached for `cacheMs` (misses too), so edits show up within seconds.
258
+ */
259
+ class PrismaAgentSource {
260
+ constructor(
261
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
262
+ prisma, options = {}) {
263
+ this.prisma = prisma;
264
+ this.options = options;
265
+ this.cache = new Map();
266
+ }
267
+ async get(appRef) {
268
+ const now = this.options.now?.() ?? Date.now();
269
+ const hit = this.cache.get(appRef);
270
+ if (hit && now - hit.at < (this.options.cacheMs ?? 5000))
271
+ return hit.value;
272
+ const row = await this.prisma.application.findUnique({
273
+ where: { ref: appRef },
274
+ include: { intelligence: true }
275
+ });
276
+ const value = row && row.type === "AUTOPILOT" && row.intelligence?.config
277
+ ? { agent: row.intelligence.config, workspaceRef: row.accessKeyId }
278
+ : undefined;
279
+ this.cache.set(appRef, { at: now, value });
280
+ return value;
281
+ }
282
+ }
283
+ exports.PrismaAgentSource = PrismaAgentSource;
284
+ /** Workspace vendor keys from the database, looked up by the credentialRef's providers. */
285
+ class DatabaseWorkspaceSecrets {
286
+ constructor(keys, providers) {
287
+ this.keys = keys;
288
+ this.providers = providers;
289
+ }
290
+ get(ref, scope) {
291
+ if (!scope.workspaceRef)
292
+ return undefined;
293
+ return this.keys.forCredentialRef(scope.workspaceRef, ref, this.providers());
294
+ }
295
+ }
296
+ exports.DatabaseWorkspaceSecrets = DatabaseWorkspaceSecrets;
297
+ /** A private region's cell secrets (PR-4): keys never leave the cell. */
298
+ class CellSecretCredentials {
299
+ constructor(keys, providers) {
300
+ this.keys = keys;
301
+ this.providers = providers;
302
+ }
303
+ get(ref) {
304
+ return this.keys.forCredentialRef(ref, this.providers());
305
+ }
306
+ }
307
+ exports.CellSecretCredentials = CellSecretCredentials;
308
+ /** Workspace Secrets from a JSON file: `{<workspaceRef>: {<credentialRef>: value}}`. */
309
+ class FileWorkspaceSecrets {
310
+ constructor(file) {
311
+ this.file = file;
312
+ }
313
+ get(ref, scope) {
314
+ if (!scope.workspaceRef)
315
+ return undefined;
316
+ const data = readStructured(this.file);
317
+ return data[scope.workspaceRef]?.[ref];
318
+ }
319
+ }
320
+ exports.FileWorkspaceSecrets = FileWorkspaceSecrets;
321
+ /**
322
+ * Builds the knowledge port for an agent from the operator's S3 + Unstructured settings,
323
+ * with keys from the credential source (never Autopilot's environment). The bucket is the
324
+ * workspace's, as today. `make` is the knowledge base constructor (the S3 one by default).
325
+ */
326
+ function s3Knowledge(config, credentials, make) {
327
+ return async (agent, scope, usage) => {
328
+ const documents = agent.languageModel.knowledgeBase.map((d) => d.document);
329
+ if (!documents.length || !scope.workspaceRef)
330
+ return undefined;
331
+ const key = (ref) => credentials.get(ref, scope);
332
+ const [accessKeyId, secretAccessKey, unstructuredKey, embeddingsKey] = await Promise.all([
333
+ key(config.s3.accessKeyIdRef),
334
+ key(config.s3.secretAccessKeyRef),
335
+ key(config.unstructured.apiKeyRef),
336
+ (0, llm_1.resolveCredential)(credentials, config.embeddings.apiKeyRef, scope)
337
+ ]);
338
+ const params = {
339
+ bucket: scope.workspaceRef.toLowerCase(),
340
+ documents,
341
+ s3Config: {
342
+ endpoint: config.s3.endpoint,
343
+ region: config.s3.region,
344
+ credentials: {
345
+ accessKeyId: accessKeyId ?? "",
346
+ secretAccessKey: secretAccessKey ?? ""
347
+ },
348
+ forcePathStyle: true
349
+ },
350
+ unstructuredAPIURL: config.unstructured.url,
351
+ unstructuredAPIKey: unstructuredKey ?? "",
352
+ embeddingsApiKey: embeddingsKey?.key ?? "",
353
+ embeddingsKeySource: embeddingsKey?.source ?? "platform",
354
+ usage
355
+ };
356
+ const kb = (make ?? defaultKnowledgeBase)(params);
357
+ kb.load().catch((e) => logger.warn(`knowledge base did not load: ${e.message}`));
358
+ return (0, knowledge_1.fromKnowledgeBase)(kb);
359
+ };
360
+ }
361
+ function defaultKnowledgeBase(params) {
362
+ // Loaded lazily: the S3 loader and embeddings pull in LangChain, which only the knowledge
363
+ // port still uses.
364
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
365
+ const { S3KnowledgeBase } = require("../knowledge/S3KnowledgeBase");
366
+ return new S3KnowledgeBase({
367
+ ...params,
368
+ // Metered: the vendor's token usage reaches the call (add-usage-metering).
369
+ embeddings: new MeteredEmbeddings_1.MeteredEmbeddings({
370
+ apiKey: String(params.embeddingsApiKey ?? ""),
371
+ keySource: params.embeddingsKeySource,
372
+ sink: params.usage
373
+ })
374
+ });
375
+ }
376
+ /** Opens the control-plane database named in the config (once). */
377
+ function databaseOf(config) {
378
+ const d = config.database;
379
+ return common_1.Registry.openPrismaClient({
380
+ url: d.url,
381
+ encryptionKey: config.encryption?.key,
382
+ clientModule: d.clientModule
383
+ });
384
+ }
385
+ /** The registry store: the file, or the tables refreshed every `refreshMs`. */
386
+ function registryStore(config,
387
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
388
+ db) {
389
+ if ("file" in config.registry)
390
+ return new common_1.Registry.FileRegistryStore(config.registry.file);
391
+ const store = new common_1.Registry.MemoryRegistryStore();
392
+ const every = config.registry.refreshMs ?? 30000;
393
+ const load = () => common_1.Registry.PrismaRegistryStore.open(db())
394
+ .then((loaded) => {
395
+ for (const e of loaded.all())
396
+ store.put(e);
397
+ })
398
+ .catch((e) => logger.warn(`registry refresh failed, keeping the last: ${e.message}`));
399
+ void load();
400
+ setInterval(load, every).unref();
401
+ return store;
402
+ }
403
+ /** The handler options the hosted Autopilot runs with, built from its config. */
404
+ function hostedOptions(config,
405
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
406
+ deps = {}) {
407
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
408
+ let prisma = deps.prisma;
409
+ const db = () => (prisma ?? (prisma = databaseOf(config)));
410
+ const store = registryStore(config, db);
411
+ const registry = new common_1.Registry.Registry({ store });
412
+ const providers = () => store
413
+ .all()
414
+ .filter((e) => e.kind === "provider" && Boolean(e.credentialRef))
415
+ .map((e) => ({ id: e.id, credentialRef: e.credentialRef }));
416
+ const workspace = config.workspaceSecrets;
417
+ const credentials = new llm_1.LayeredCredentials([
418
+ ...(workspace
419
+ ? [
420
+ "file" in workspace
421
+ ? new FileWorkspaceSecrets(workspace.file)
422
+ : "cellSecrets" in workspace
423
+ ? new CellSecretCredentials(new common_1.CellSecrets.CellSecretKeys(common_1.CellSecrets.openCellSecrets(workspace.cellSecrets)), providers)
424
+ : new DatabaseWorkspaceSecrets(new common_1.Registry.PrismaWorkspaceKeys(db()), providers)
425
+ ]
426
+ : []),
427
+ new llm_1.ConfigCredentials(config.credentials)
428
+ ]);
429
+ const agents = "file" in config.agents
430
+ ? new FileAgentSource(config.agents.file)
431
+ : new PrismaAgentSource(db(), {
432
+ cacheMs: config.agents.cacheMs
433
+ });
434
+ const knowledge = config.knowledge
435
+ ? s3Knowledge(config.knowledge, credentials)
436
+ : undefined;
437
+ return {
438
+ load: async (session) => signedWorkspaceOnly(session, await agents.get(session.appRef)),
439
+ allowedDestinations: config.allowedDestinations,
440
+ llm: new llm_1.RegistryLlmResolver({
441
+ registry,
442
+ credentials,
443
+ endpoints: config.endpoints
444
+ }),
445
+ knowledgeFor: knowledge ? eagerKnowledge(knowledge) : undefined
446
+ };
447
+ }
448
+ /**
449
+ * One knowledge port per call, built eagerly; a lookup waits for it (and gives up after 800 ms
450
+ * like any). A port that fails to build is logged and answers nothing: the eager promise always
451
+ * has a handler, so a failure never becomes an unhandled rejection (RV-B5).
452
+ */
453
+ function eagerKnowledge(build) {
454
+ return (agent, scope, usage) => {
455
+ const port = Promise.resolve()
456
+ .then(() => build(agent, scope, usage))
457
+ .catch((e) => {
458
+ logger.warn(`knowledge port unavailable: ${e?.message ?? e}`);
459
+ return undefined;
460
+ });
461
+ return {
462
+ find: async (question, signal) => {
463
+ const p = await port;
464
+ return p ? p.find(question, signal) : "";
465
+ }
466
+ };
467
+ };
468
+ }
469
+ /**
470
+ * The app's agent only when the app belongs to the workspace AppBridge signed for: one
471
+ * secret serves every Autopilot app, so a session is bound to its app and workspace
472
+ * (voice-session, "Connection requests are bound to the app").
473
+ */
474
+ function signedWorkspaceOnly(session, loaded) {
475
+ const signed = session.signedFor;
476
+ if (!loaded || !signed || loaded.workspaceRef === undefined)
477
+ return loaded;
478
+ if (signed.workspaceRef === loaded.workspaceRef)
479
+ return loaded;
480
+ logger.warn("refused a session: the app is not in the signed workspace", {
481
+ appRef: session.appRef
482
+ });
483
+ return undefined;
484
+ }
485
+ /** Starts the hosted Autopilot. */
486
+ async function startHostedAutopilot(config) {
487
+ const secret = resolveSecret(config.signingSecret);
488
+ if (!secret)
489
+ throw new Error("signingSecret resolves to nothing");
490
+ const server = await new voice_1.VoiceServer({
491
+ host: config.host,
492
+ port: config.port,
493
+ signingSecret: secret,
494
+ // Every Autopilot app shares this secret: check the configured URL only (never Host) and
495
+ // require the signature bound to the app and workspace.
496
+ publicUrl: config.publicUrl,
497
+ requireAppBinding: true
498
+ }).listen((0, createAutopilot_1.createAutopilotHandler)(hostedOptions(config)));
499
+ logger.info(`hosted autopilot listening on ${config.host}:${server.port}`);
500
+ return server;
501
+ }
@@ -16,5 +16,14 @@
16
16
  * See the License for the specific language governing permissions and
17
17
  * limitations under the License.
18
18
  */
19
- export * from "./loadAndValidateAssistant";
20
- export * from "./types";
19
+ export * from "./Agent";
20
+ export * from "./config";
21
+ export * from "./createAutopilot";
22
+ export * from "./hosted";
23
+ export * from "./knowledge";
24
+ export * from "./llm";
25
+ export * from "./machine";
26
+ export * from "./sentences";
27
+ export * from "./telemetry";
28
+ export * from "./think";
29
+ export * from "./tools";
@@ -32,6 +32,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
32
32
  * See the License for the specific language governing permissions and
33
33
  * limitations under the License.
34
34
  */
35
- __exportStar(require("./doProcessUserRequest"), exports);
36
- __exportStar(require("./doGreetUser"), exports);
37
- __exportStar(require("./doAnnounceIdleTimeout"), exports);
35
+ __exportStar(require("./Agent"), exports);
36
+ __exportStar(require("./config"), exports);
37
+ __exportStar(require("./createAutopilot"), exports);
38
+ __exportStar(require("./hosted"), exports);
39
+ __exportStar(require("./knowledge"), exports);
40
+ __exportStar(require("./llm"), exports);
41
+ __exportStar(require("./machine"), exports);
42
+ __exportStar(require("./sentences"), exports);
43
+ __exportStar(require("./telemetry"), exports);
44
+ __exportStar(require("./think"), exports);
45
+ __exportStar(require("./tools"), exports);
@@ -1,6 +1,3 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.appendSpeech = void 0;
4
1
  /**
5
2
  * Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
6
3
  * http://github.com/fonoster/fonoster
@@ -19,17 +16,17 @@ exports.appendSpeech = void 0;
19
16
  * See the License for the specific language governing permissions and
20
17
  * limitations under the License.
21
18
  */
22
- const logger_1 = require("@fonoster/logger");
23
- const xstate_1 = require("xstate");
24
- const logger = (0, logger_1.getLogger)({ service: "autopilot", filePath: __filename });
25
- exports.appendSpeech = (0, xstate_1.assign)(({ context, event }) => {
26
- const speech = event.speech;
27
- logger.verbose("called the appendSpeech action", { speech });
28
- if (!speech) {
29
- return context;
30
- }
31
- context.speechBuffer = ((context.speechBuffer ?? "") +
32
- " " +
33
- speech).trimStart();
34
- return context;
35
- });
19
+ /**
20
+ * The knowledge port (rework-autopilot A8): one operation, find what is relevant to a question.
21
+ * Implementations (S3 + Unstructured today) are wired by the operator; their keys never live in
22
+ * the agent config.
23
+ */
24
+ export interface KnowledgePort {
25
+ find(question: string, signal: AbortSignal): Promise<string>;
26
+ }
27
+ /** Knowledge lookups never hold a reply back longer than this. */
28
+ export declare const KNOWLEDGE_TIMEOUT_MS = 800;
29
+ /** Adapts the current knowledge bases (mods/autopilot/src/knowledge) to the port. */
30
+ export declare function fromKnowledgeBase(kb: {
31
+ queryKnowledgeBase(query: string, k?: number): Promise<string>;
32
+ }): KnowledgePort;
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.KNOWLEDGE_TIMEOUT_MS = void 0;
4
+ exports.fromKnowledgeBase = fromKnowledgeBase;
5
+ /** Knowledge lookups never hold a reply back longer than this. */
6
+ exports.KNOWLEDGE_TIMEOUT_MS = 800;
7
+ /** Adapts the current knowledge bases (mods/autopilot/src/knowledge) to the port. */
8
+ function fromKnowledgeBase(kb) {
9
+ return { find: (question) => kb.queryKnowledgeBase(question) };
10
+ }