@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.
- package/README.md +15 -52
- 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/MeteredEmbeddings.d.ts +47 -0
- package/dist/knowledge/MeteredEmbeddings.js +58 -0
- 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/createLanguageModel.js +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 +21 -20
- package/dist/models/evaluations/createEvaluateIntelligence.js +53 -23
- package/dist/models/evaluations/createTestTextSimilarity.d.ts +7 -1
- package/dist/models/evaluations/createTestTextSimilarity.js +19 -1
- package/dist/models/evaluations/evalTestCases.d.ts +2 -2
- package/dist/models/evaluations/evalTestCases.js +7 -7
- package/dist/models/evaluations/evaluateScenario.js +2 -2
- package/dist/models/evaluations/evaluateStep.d.ts +1 -1
- package/dist/models/evaluations/evaluateStep.js +2 -2
- package/dist/models/evaluations/extractAiResponse.d.ts +2 -3
- package/dist/models/evaluations/extractAiResponse.js +3 -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/runEval.js +7 -7
- 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 +20 -0
- package/dist/models/evaluations/session/runSessionEval.js +59 -0
- package/dist/models/evaluations/textSimilaryPrompt.d.ts +1 -1
- package/dist/models/evaluations/textSimilaryPrompt.js +1 -1
- package/dist/models/evaluations/types.d.ts +7 -6
- package/dist/models/types.d.ts +19 -1
- package/dist/session/Agent.d.ts +173 -0
- package/dist/session/Agent.js +548 -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/createAutopilot.d.ts +48 -0
- package/dist/session/createAutopilot.js +151 -0
- package/dist/session/hosted.d.ts +454 -0
- package/dist/session/hosted.js +501 -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 +113 -0
- package/dist/session/llm.js +127 -0
- package/dist/session/machine.d.ts +206 -0
- package/dist/session/machine.js +126 -0
- package/dist/session/main.js +45 -0
- package/dist/session/meteredThink.d.ts +45 -0
- package/dist/session/meteredThink.js +125 -0
- package/dist/session/sentences.d.ts +5 -0
- package/dist/session/sentences.js +53 -0
- package/dist/session/telemetry.d.ts +34 -0
- package/dist/session/telemetry.js +55 -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 +15 -16
- 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
|
@@ -0,0 +1,113 @@
|
|
|
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 { Registry } from "@fonoster/common";
|
|
20
|
+
import { Think } from "@fonoster/community";
|
|
21
|
+
import { OPENAI_COMPATIBLE } from "@fonoster/community/openai";
|
|
22
|
+
/** Where keys come from: workspace Secrets first (bring your own key), then platform keys. */
|
|
23
|
+
export interface CredentialSource {
|
|
24
|
+
get(ref: string, scope: {
|
|
25
|
+
workspaceRef?: string;
|
|
26
|
+
}): Promise<string | undefined> | string | undefined;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Platform credentials from operator config: `{ "<credentialRef>": { env } | { value } }`. The
|
|
30
|
+
* variable names are operator choices; Autopilot never reads a vendor variable by itself.
|
|
31
|
+
*/
|
|
32
|
+
export declare class ConfigCredentials implements CredentialSource {
|
|
33
|
+
private readonly refs;
|
|
34
|
+
constructor(refs: Record<string, {
|
|
35
|
+
env?: string;
|
|
36
|
+
value?: string;
|
|
37
|
+
}>);
|
|
38
|
+
get(ref: string): string;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* A workspace's own key could not be read (its store failed). It never falls back to the platform
|
|
42
|
+
* key (remove-platform-key-fallback): the session ends with reason key_unavailable.
|
|
43
|
+
*/
|
|
44
|
+
export declare class KeyUnavailableError extends Error {
|
|
45
|
+
readonly ref: string;
|
|
46
|
+
readonly reason = "key_unavailable";
|
|
47
|
+
constructor(ref: string, cause?: unknown);
|
|
48
|
+
}
|
|
49
|
+
/** A key and whose it is: the workspace's own (or its private region's), or the platform's. */
|
|
50
|
+
export type ResolvedCredential = {
|
|
51
|
+
key: string;
|
|
52
|
+
source: "workspace" | "platform";
|
|
53
|
+
};
|
|
54
|
+
/** Workspace Secrets (BYOK) in front of platform credentials. */
|
|
55
|
+
export declare class LayeredCredentials implements CredentialSource {
|
|
56
|
+
private readonly sources;
|
|
57
|
+
constructor(sources: CredentialSource[]);
|
|
58
|
+
get(ref: string, scope: {
|
|
59
|
+
workspaceRef?: string;
|
|
60
|
+
}): Promise<string>;
|
|
61
|
+
/**
|
|
62
|
+
* The first key found and whose it is (add-usage-metering): the operator's config holds the
|
|
63
|
+
* platform keys; every other source holds the workspace's own.
|
|
64
|
+
*/
|
|
65
|
+
resolve(ref: string, scope: {
|
|
66
|
+
workspaceRef?: string;
|
|
67
|
+
}): Promise<ResolvedCredential | undefined>;
|
|
68
|
+
}
|
|
69
|
+
/** A credential and its source; a source that cannot tell is the platform's. */
|
|
70
|
+
export declare function resolveCredential(credentials: CredentialSource, ref: string, scope: {
|
|
71
|
+
workspaceRef?: string;
|
|
72
|
+
}): Promise<ResolvedCredential | undefined>;
|
|
73
|
+
/** Adapters whose LLMs speak OpenAI-compatible Chat Completions (moved to @fonoster/community/openai). */
|
|
74
|
+
export { OPENAI_COMPATIBLE };
|
|
75
|
+
export declare class LlmResolutionError extends Error {
|
|
76
|
+
}
|
|
77
|
+
export type ResolvedLlm = {
|
|
78
|
+
think: Think;
|
|
79
|
+
/** The id actually used (a retired id resolves to its replacement). */
|
|
80
|
+
id: string;
|
|
81
|
+
/** Whose key the model is called with; keyless providers count as the platform's. */
|
|
82
|
+
keySource: "workspace" | "platform";
|
|
83
|
+
/** For the call record: provider.deprecated_used / provider.retired_used (registry PR4). */
|
|
84
|
+
event?: {
|
|
85
|
+
type: string;
|
|
86
|
+
id: string;
|
|
87
|
+
replacement?: string;
|
|
88
|
+
};
|
|
89
|
+
};
|
|
90
|
+
export interface LlmResolver {
|
|
91
|
+
resolve(catalogId: string, options: {
|
|
92
|
+
temperature: number;
|
|
93
|
+
maxTokens: number;
|
|
94
|
+
}, scope: {
|
|
95
|
+
workspaceRef?: string;
|
|
96
|
+
}): Promise<ResolvedLlm>;
|
|
97
|
+
}
|
|
98
|
+
export type RegistryLlmResolverOptions = {
|
|
99
|
+
registry: Registry.Registry;
|
|
100
|
+
credentials: CredentialSource;
|
|
101
|
+
/** Endpoint overrides by provider or adapter id (e.g. a local ollama); win over the catalog's. */
|
|
102
|
+
endpoints?: Record<string, string>;
|
|
103
|
+
};
|
|
104
|
+
export declare class RegistryLlmResolver implements LlmResolver {
|
|
105
|
+
private readonly options;
|
|
106
|
+
constructor(options: RegistryLlmResolverOptions);
|
|
107
|
+
resolve(catalogId: string, settings: {
|
|
108
|
+
temperature: number;
|
|
109
|
+
maxTokens: number;
|
|
110
|
+
}, scope?: {
|
|
111
|
+
workspaceRef?: string;
|
|
112
|
+
}): Promise<ResolvedLlm>;
|
|
113
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RegistryLlmResolver = exports.LlmResolutionError = exports.OPENAI_COMPATIBLE = exports.LayeredCredentials = exports.KeyUnavailableError = exports.ConfigCredentials = void 0;
|
|
4
|
+
exports.resolveCredential = resolveCredential;
|
|
5
|
+
const community_1 = require("@fonoster/community");
|
|
6
|
+
const openai_1 = require("@fonoster/community/openai");
|
|
7
|
+
Object.defineProperty(exports, "OPENAI_COMPATIBLE", { enumerable: true, get: function () { return openai_1.OPENAI_COMPATIBLE; } });
|
|
8
|
+
/**
|
|
9
|
+
* Platform credentials from operator config: `{ "<credentialRef>": { env } | { value } }`. The
|
|
10
|
+
* variable names are operator choices; Autopilot never reads a vendor variable by itself.
|
|
11
|
+
*/
|
|
12
|
+
class ConfigCredentials {
|
|
13
|
+
constructor(refs) {
|
|
14
|
+
this.refs = refs;
|
|
15
|
+
}
|
|
16
|
+
get(ref) {
|
|
17
|
+
const c = this.refs[ref];
|
|
18
|
+
return c?.value ?? (c?.env ? process.env[c.env] : undefined);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
exports.ConfigCredentials = ConfigCredentials;
|
|
22
|
+
/**
|
|
23
|
+
* A workspace's own key could not be read (its store failed). It never falls back to the platform
|
|
24
|
+
* key (remove-platform-key-fallback): the session ends with reason key_unavailable.
|
|
25
|
+
*/
|
|
26
|
+
class KeyUnavailableError extends Error {
|
|
27
|
+
constructor(ref, cause) {
|
|
28
|
+
super(`the workspace's key ${ref} could not be read: ${cause?.message ?? cause}`);
|
|
29
|
+
this.ref = ref;
|
|
30
|
+
this.reason = "key_unavailable";
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
exports.KeyUnavailableError = KeyUnavailableError;
|
|
34
|
+
/** Workspace Secrets (BYOK) in front of platform credentials. */
|
|
35
|
+
class LayeredCredentials {
|
|
36
|
+
constructor(sources) {
|
|
37
|
+
this.sources = sources;
|
|
38
|
+
}
|
|
39
|
+
async get(ref, scope) {
|
|
40
|
+
return (await this.resolve(ref, scope))?.key;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The first key found and whose it is (add-usage-metering): the operator's config holds the
|
|
44
|
+
* platform keys; every other source holds the workspace's own.
|
|
45
|
+
*/
|
|
46
|
+
async resolve(ref, scope) {
|
|
47
|
+
for (const s of this.sources) {
|
|
48
|
+
let key;
|
|
49
|
+
try {
|
|
50
|
+
key = await s.get(ref, scope);
|
|
51
|
+
}
|
|
52
|
+
catch (e) {
|
|
53
|
+
if (s instanceof ConfigCredentials)
|
|
54
|
+
throw e;
|
|
55
|
+
throw new KeyUnavailableError(ref, e);
|
|
56
|
+
}
|
|
57
|
+
if (key)
|
|
58
|
+
return {
|
|
59
|
+
key,
|
|
60
|
+
source: s instanceof ConfigCredentials ? "platform" : "workspace"
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
exports.LayeredCredentials = LayeredCredentials;
|
|
67
|
+
/** A credential and its source; a source that cannot tell is the platform's. */
|
|
68
|
+
async function resolveCredential(credentials, ref, scope) {
|
|
69
|
+
if (credentials instanceof LayeredCredentials)
|
|
70
|
+
return credentials.resolve(ref, scope);
|
|
71
|
+
const key = await credentials.get(ref, scope);
|
|
72
|
+
return key ? { key, source: "platform" } : undefined;
|
|
73
|
+
}
|
|
74
|
+
class LlmResolutionError extends Error {
|
|
75
|
+
}
|
|
76
|
+
exports.LlmResolutionError = LlmResolutionError;
|
|
77
|
+
class RegistryLlmResolver {
|
|
78
|
+
constructor(options) {
|
|
79
|
+
this.options = options;
|
|
80
|
+
}
|
|
81
|
+
async resolve(catalogId, settings, scope = {}) {
|
|
82
|
+
const { registry } = this.options;
|
|
83
|
+
const resolution = registry.resolve(catalogId);
|
|
84
|
+
const id = resolution.id;
|
|
85
|
+
const entries = registry.list();
|
|
86
|
+
const model = entries.find((e) => e.id === id);
|
|
87
|
+
if (!model || model.kind !== "model" || model.type !== "llm")
|
|
88
|
+
throw new LlmResolutionError(`${catalogId} is not a language model in the catalog`);
|
|
89
|
+
const providerId = id.slice(0, id.indexOf("/"));
|
|
90
|
+
const provider = entries.find((e) => e.id === providerId);
|
|
91
|
+
if (!provider || provider.kind !== "provider")
|
|
92
|
+
throw new LlmResolutionError(`no provider ${providerId} for ${id}`);
|
|
93
|
+
if (provider.state === "retired")
|
|
94
|
+
throw new LlmResolutionError(`provider ${providerId} is retired`);
|
|
95
|
+
const llm = (0, community_1.adapterFor)(provider.adapter)?.llm;
|
|
96
|
+
if (!llm)
|
|
97
|
+
throw new LlmResolutionError(`adapter ${provider.adapter} has no language model implementation in Autopilot yet`);
|
|
98
|
+
const baseUrl = this.options.endpoints?.[provider.id] ??
|
|
99
|
+
this.options.endpoints?.[provider.adapter] ??
|
|
100
|
+
provider.endpoints?.llm ??
|
|
101
|
+
llm.defaultEndpoint;
|
|
102
|
+
if (!baseUrl)
|
|
103
|
+
throw new LlmResolutionError(`no endpoint for provider ${provider.id} (adapter ${provider.adapter})`);
|
|
104
|
+
// A provider without a credentialRef is keyless (local models, the fakes).
|
|
105
|
+
const credential = provider.credentialRef
|
|
106
|
+
? await resolveCredential(this.options.credentials, provider.credentialRef, scope)
|
|
107
|
+
: undefined;
|
|
108
|
+
const apiKey = credential?.key;
|
|
109
|
+
if (provider.credentialRef && !apiKey)
|
|
110
|
+
throw new LlmResolutionError(`no credential ${provider.credentialRef} for provider ${provider.id}`);
|
|
111
|
+
return {
|
|
112
|
+
id,
|
|
113
|
+
keySource: credential?.source ?? "platform",
|
|
114
|
+
think: llm.create({ baseUrl, apiKey }, id.slice(providerId.length + 1), settings),
|
|
115
|
+
...(resolution.event
|
|
116
|
+
? {
|
|
117
|
+
event: {
|
|
118
|
+
type: resolution.event,
|
|
119
|
+
id: catalogId,
|
|
120
|
+
replacement: resolution.replacement
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
: {})
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
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,45 @@
|
|
|
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 telemetry_1 = require("./telemetry");
|
|
28
|
+
const logger = (0, logger_1.getLogger)({ service: "autopilot", filePath: __filename });
|
|
29
|
+
(0, common_1.logUnhandledRejections)(logger);
|
|
30
|
+
// --config, AUTOPILOT_SESSION_CONFIG, else /config/autopilot.yaml (or an older location).
|
|
31
|
+
const at = process.argv.indexOf("--config");
|
|
32
|
+
const file = at >= 0
|
|
33
|
+
? process.argv[at + 1]
|
|
34
|
+
: (process.env.AUTOPILOT_SESSION_CONFIG ?? (0, hosted_1.findHostedConfig)());
|
|
35
|
+
if (!file) {
|
|
36
|
+
logger.error("no Autopilot config file: mount /config/autopilot.yaml or pass --config <file>");
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
const config = (0, hosted_1.loadHostedConfig)(file);
|
|
40
|
+
// Off unless an OTLP endpoint is configured (telemetry.otlpEndpoint or OTEL_EXPORTER_OTLP_*).
|
|
41
|
+
(0, telemetry_1.startTelemetry)(config.telemetry);
|
|
42
|
+
(0, hosted_1.startHostedAutopilot)(config).catch((e) => {
|
|
43
|
+
logger.error(`hosted autopilot failed to start: ${e.message}`);
|
|
44
|
+
process.exit(1);
|
|
45
|
+
});
|
|
@@ -0,0 +1,45 @@
|
|
|
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 { Usage } from "@fonoster/common";
|
|
20
|
+
import { Think, TokenUsage } from "@fonoster/community";
|
|
21
|
+
/** Where a model's usage goes: Autopilot sends it to the call (usage.report). */
|
|
22
|
+
export type UsageSink = {
|
|
23
|
+
usage(lines: Usage.UsageLine[]): void;
|
|
24
|
+
/** A completed request whose vendor reported no usage (add-usage-metering). */
|
|
25
|
+
missing(catalogId: string): void;
|
|
26
|
+
};
|
|
27
|
+
export type MeteredModel = {
|
|
28
|
+
/** The catalog id (`<provider>/<model>`). */
|
|
29
|
+
catalogId: string;
|
|
30
|
+
keySource: Usage.KeySource;
|
|
31
|
+
};
|
|
32
|
+
/** How long an interrupted reply is read in the background for its usage report (design D6). */
|
|
33
|
+
export declare const DRAIN_DEADLINE_MS = 30000;
|
|
34
|
+
export declare function usageLines(model: MeteredModel, usage: TokenUsage): Usage.UsageLine[];
|
|
35
|
+
/**
|
|
36
|
+
* A Think that meters its model (add-usage-metering D6). Chunks pass through; the vendor's usage
|
|
37
|
+
* chunk is reported, never forwarded. When the caller stops reading (barge-in, a newer turn), the
|
|
38
|
+
* stream ends for the caller at once but keeps being read in the background, so the vendor's usage
|
|
39
|
+
* for the whole request still arrives; after the deadline, or when `closed` aborts (the session
|
|
40
|
+
* ended), the request is reported as missing usage. A request the vendor failed reports nothing.
|
|
41
|
+
*/
|
|
42
|
+
export declare function meteredThink(inner: Think, model: MeteredModel, sink: UsageSink, options?: {
|
|
43
|
+
drainMs?: number;
|
|
44
|
+
closed?: AbortSignal;
|
|
45
|
+
}): Think;
|