@framers/agentos-ext-endpoint-semantic 0.2.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.
@@ -0,0 +1,82 @@
1
+ /**
2
+ * @file TurnCompletenessClassifier.ts
3
+ * @description LLM-backed binary classifier that decides whether an utterance
4
+ * is a complete conversational turn (COMPLETE) or likely to continue
5
+ * (INCOMPLETE). Results are LRU-cached to avoid redundant LLM round-trips for
6
+ * repeated short phrases.
7
+ *
8
+ * @module endpoint-semantic/TurnCompletenessClassifier
9
+ */
10
+ /**
11
+ * The three possible results from {@link TurnCompletenessClassifier.classify}.
12
+ *
13
+ * - `COMPLETE` — The utterance is a self-contained thought expecting a reply.
14
+ * - `INCOMPLETE` — The speaker is likely to continue before yielding the floor.
15
+ * - `TIMEOUT` — The LLM did not respond within the configured time budget;
16
+ * the caller should fall back to silence-timeout behaviour.
17
+ */
18
+ export type ClassifyResult = 'COMPLETE' | 'INCOMPLETE' | 'TIMEOUT';
19
+ /**
20
+ * Classifies a transcript as {@link ClassifyResult.COMPLETE} or
21
+ * {@link ClassifyResult.INCOMPLETE} using an LLM prompt.
22
+ *
23
+ * ### Caching
24
+ * Results are stored in a fixed-capacity LRU cache keyed on the first 100
25
+ * characters of the transcript. When the cache is full, the oldest entry is
26
+ * evicted (insertion-order eviction via `Map` iteration order, which is
27
+ * guaranteed by the ES2015 spec).
28
+ *
29
+ * ### Prompt format
30
+ * The LLM is asked to respond with exactly `COMPLETE` or `INCOMPLETE` as the
31
+ * first word, followed by one sentence of reasoning. Only the first word is
32
+ * examined — anything after the first space is treated as explanatory text and
33
+ * discarded.
34
+ *
35
+ * @example
36
+ * ```ts
37
+ * const classifier = new TurnCompletenessClassifier(
38
+ * async (prompt) => openai.chat.completions.create({ ... }).then(r => r.choices[0].message.content ?? ''),
39
+ * 400,
40
+ * );
41
+ * const result = await classifier.classify('Tell me about your', 'User: Hello\nAgent: Hi there!');
42
+ * // → 'INCOMPLETE' (sentence is unfinished)
43
+ * ```
44
+ */
45
+ export declare class TurnCompletenessClassifier {
46
+ private readonly llmCall;
47
+ private readonly timeoutMs;
48
+ /**
49
+ * LRU result cache. Keys are the first 100 chars of the transcript; values
50
+ * are the cached classification. `Map` preserves insertion order, so
51
+ * eviction iterates from the oldest entry (`.keys().next().value`).
52
+ */
53
+ private readonly cache;
54
+ /**
55
+ * Maximum number of entries to keep in {@link cache} before evicting the
56
+ * oldest one.
57
+ */
58
+ private readonly maxCacheSize;
59
+ /**
60
+ * @param llmCall — Async function that sends a prompt string to an LLM and
61
+ * resolves with the raw response text.
62
+ * @param timeoutMs — Maximum milliseconds to wait for the LLM before
63
+ * returning `TIMEOUT`. Defaults to 500 ms.
64
+ */
65
+ constructor(llmCall: (prompt: string) => Promise<string>, timeoutMs?: number);
66
+ /**
67
+ * Classify `transcript` as `COMPLETE`, `INCOMPLETE`, or `TIMEOUT`.
68
+ *
69
+ * The result is cached on the first 100 characters of `transcript` so that
70
+ * repeated short phrases (common in voice interaction) never hit the LLM
71
+ * twice.
72
+ *
73
+ * @param transcript — The utterance text to evaluate.
74
+ * @param context — Optional string containing the last 2 conversation turns,
75
+ * formatted as `"Speaker: text\n..."`. When omitted, the
76
+ * prompt uses `'N/A'`.
77
+ * @returns A {@link ClassifyResult} resolving to `COMPLETE`, `INCOMPLETE`,
78
+ * or `TIMEOUT`.
79
+ */
80
+ classify(transcript: string, context?: string): Promise<ClassifyResult>;
81
+ }
82
+ //# sourceMappingURL=TurnCompletenessClassifier.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TurnCompletenessClassifier.d.ts","sourceRoot":"","sources":["../src/TurnCompletenessClassifier.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH;;;;;;;GAOG;AACH,MAAM,MAAM,cAAc,GAAG,UAAU,GAAG,YAAY,GAAG,SAAS,CAAC;AAMnE;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,qBAAa,0BAA0B;IAqBnC,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,SAAS;IArB5B;;;;OAIG;IACH,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAgD;IAEtE;;;OAGG;IACH,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAO;IAEpC;;;;;OAKG;gBAEgB,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,EAC5C,SAAS,GAAE,MAAY;IAO1C;;;;;;;;;;;;;OAaG;IACG,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;CA2D9E"}
@@ -0,0 +1,130 @@
1
+ /**
2
+ * @file TurnCompletenessClassifier.ts
3
+ * @description LLM-backed binary classifier that decides whether an utterance
4
+ * is a complete conversational turn (COMPLETE) or likely to continue
5
+ * (INCOMPLETE). Results are LRU-cached to avoid redundant LLM round-trips for
6
+ * repeated short phrases.
7
+ *
8
+ * @module endpoint-semantic/TurnCompletenessClassifier
9
+ */
10
+ // ---------------------------------------------------------------------------
11
+ // Implementation
12
+ // ---------------------------------------------------------------------------
13
+ /**
14
+ * Classifies a transcript as {@link ClassifyResult.COMPLETE} or
15
+ * {@link ClassifyResult.INCOMPLETE} using an LLM prompt.
16
+ *
17
+ * ### Caching
18
+ * Results are stored in a fixed-capacity LRU cache keyed on the first 100
19
+ * characters of the transcript. When the cache is full, the oldest entry is
20
+ * evicted (insertion-order eviction via `Map` iteration order, which is
21
+ * guaranteed by the ES2015 spec).
22
+ *
23
+ * ### Prompt format
24
+ * The LLM is asked to respond with exactly `COMPLETE` or `INCOMPLETE` as the
25
+ * first word, followed by one sentence of reasoning. Only the first word is
26
+ * examined — anything after the first space is treated as explanatory text and
27
+ * discarded.
28
+ *
29
+ * @example
30
+ * ```ts
31
+ * const classifier = new TurnCompletenessClassifier(
32
+ * async (prompt) => openai.chat.completions.create({ ... }).then(r => r.choices[0].message.content ?? ''),
33
+ * 400,
34
+ * );
35
+ * const result = await classifier.classify('Tell me about your', 'User: Hello\nAgent: Hi there!');
36
+ * // → 'INCOMPLETE' (sentence is unfinished)
37
+ * ```
38
+ */
39
+ export class TurnCompletenessClassifier {
40
+ llmCall;
41
+ timeoutMs;
42
+ /**
43
+ * LRU result cache. Keys are the first 100 chars of the transcript; values
44
+ * are the cached classification. `Map` preserves insertion order, so
45
+ * eviction iterates from the oldest entry (`.keys().next().value`).
46
+ */
47
+ cache = new Map();
48
+ /**
49
+ * Maximum number of entries to keep in {@link cache} before evicting the
50
+ * oldest one.
51
+ */
52
+ maxCacheSize = 100;
53
+ /**
54
+ * @param llmCall — Async function that sends a prompt string to an LLM and
55
+ * resolves with the raw response text.
56
+ * @param timeoutMs — Maximum milliseconds to wait for the LLM before
57
+ * returning `TIMEOUT`. Defaults to 500 ms.
58
+ */
59
+ constructor(llmCall, timeoutMs = 500) {
60
+ this.llmCall = llmCall;
61
+ this.timeoutMs = timeoutMs;
62
+ }
63
+ // ---------------------------------------------------------------------------
64
+ // Public API
65
+ // ---------------------------------------------------------------------------
66
+ /**
67
+ * Classify `transcript` as `COMPLETE`, `INCOMPLETE`, or `TIMEOUT`.
68
+ *
69
+ * The result is cached on the first 100 characters of `transcript` so that
70
+ * repeated short phrases (common in voice interaction) never hit the LLM
71
+ * twice.
72
+ *
73
+ * @param transcript — The utterance text to evaluate.
74
+ * @param context — Optional string containing the last 2 conversation turns,
75
+ * formatted as `"Speaker: text\n..."`. When omitted, the
76
+ * prompt uses `'N/A'`.
77
+ * @returns A {@link ClassifyResult} resolving to `COMPLETE`, `INCOMPLETE`,
78
+ * or `TIMEOUT`.
79
+ */
80
+ async classify(transcript, context) {
81
+ // -----------------------------------------------------------------------
82
+ // Cache lookup
83
+ // -----------------------------------------------------------------------
84
+ const cacheKey = transcript.slice(0, 100);
85
+ if (this.cache.has(cacheKey)) {
86
+ return this.cache.get(cacheKey);
87
+ }
88
+ // -----------------------------------------------------------------------
89
+ // Build prompt
90
+ // -----------------------------------------------------------------------
91
+ const prompt = `Is the following utterance a complete thought that expects a response, ` +
92
+ `or is the speaker likely to continue?\n\n` +
93
+ `Context (last 2 turns):\n${context ?? 'N/A'}\n\n` +
94
+ `Current utterance: "${transcript}"\n\n` +
95
+ `Respond with exactly COMPLETE or INCOMPLETE, then one sentence of reasoning.`;
96
+ // -----------------------------------------------------------------------
97
+ // LLM call with timeout race
98
+ // -----------------------------------------------------------------------
99
+ let rawResult;
100
+ try {
101
+ rawResult = await Promise.race([
102
+ this.llmCall(prompt),
103
+ new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), this.timeoutMs)),
104
+ ]);
105
+ }
106
+ catch {
107
+ // Either the LLM threw or the timeout won — return TIMEOUT without
108
+ // caching so the next call re-attempts the LLM.
109
+ return 'TIMEOUT';
110
+ }
111
+ // -----------------------------------------------------------------------
112
+ // Parse response — only the first word matters
113
+ // -----------------------------------------------------------------------
114
+ const classification = rawResult.trim().startsWith('COMPLETE')
115
+ ? 'COMPLETE'
116
+ : 'INCOMPLETE';
117
+ // -----------------------------------------------------------------------
118
+ // LRU eviction + cache write
119
+ // -----------------------------------------------------------------------
120
+ if (this.cache.size >= this.maxCacheSize) {
121
+ // Map.keys() returns an iterator in insertion order; the first key is the
122
+ // oldest entry.
123
+ const firstKey = this.cache.keys().next().value;
124
+ this.cache.delete(firstKey);
125
+ }
126
+ this.cache.set(cacheKey, classification);
127
+ return classification;
128
+ }
129
+ }
130
+ //# sourceMappingURL=TurnCompletenessClassifier.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TurnCompletenessClassifier.js","sourceRoot":"","sources":["../src/TurnCompletenessClassifier.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAgBH,8EAA8E;AAC9E,iBAAiB;AACjB,8EAA8E;AAE9E;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,OAAO,0BAA0B;IAqBlB;IACA;IArBnB;;;;OAIG;IACc,KAAK,GAAG,IAAI,GAAG,EAAqC,CAAC;IAEtE;;;OAGG;IACc,YAAY,GAAG,GAAG,CAAC;IAEpC;;;;;OAKG;IACH,YACmB,OAA4C,EAC5C,YAAoB,GAAG;QADvB,YAAO,GAAP,OAAO,CAAqC;QAC5C,cAAS,GAAT,SAAS,CAAc;IACvC,CAAC;IAEJ,8EAA8E;IAC9E,aAAa;IACb,8EAA8E;IAE9E;;;;;;;;;;;;;OAaG;IACH,KAAK,CAAC,QAAQ,CAAC,UAAkB,EAAE,OAAgB;QACjD,0EAA0E;QAC1E,eAAe;QACf,0EAA0E;QAC1E,MAAM,QAAQ,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QAE1C,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC7B,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAE,CAAC;QACnC,CAAC;QAED,0EAA0E;QAC1E,eAAe;QACf,0EAA0E;QAC1E,MAAM,MAAM,GACV,yEAAyE;YACzE,2CAA2C;YAC3C,4BAA4B,OAAO,IAAI,KAAK,MAAM;YAClD,uBAAuB,UAAU,OAAO;YACxC,8EAA8E,CAAC;QAEjF,0EAA0E;QAC1E,6BAA6B;QAC7B,0EAA0E;QAC1E,IAAI,SAAiB,CAAC;QAEtB,IAAI,CAAC;YACH,SAAS,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC;gBAC7B,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;gBACpB,IAAI,OAAO,CAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,CAC/B,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,CAC/D;aACF,CAAC,CAAC;QACL,CAAC;QAAC,MAAM,CAAC;YACP,mEAAmE;YACnE,gDAAgD;YAChD,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,0EAA0E;QAC1E,+CAA+C;QAC/C,0EAA0E;QAC1E,MAAM,cAAc,GAA8B,SAAS,CAAC,IAAI,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC;YACvF,CAAC,CAAC,UAAU;YACZ,CAAC,CAAC,YAAY,CAAC;QAEjB,0EAA0E;QAC1E,6BAA6B;QAC7B,0EAA0E;QAC1E,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACzC,0EAA0E;YAC1E,gBAAgB;YAChB,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAe,CAAC;YAC1D,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC9B,CAAC;QAED,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC;QAEzC,OAAO,cAAc,CAAC;IACxB,CAAC;CACF"}
@@ -0,0 +1,75 @@
1
+ /**
2
+ * @file index.ts
3
+ * @description Pack factory for the Semantic Endpoint Detector extension pack.
4
+ *
5
+ * ### Usage (direct)
6
+ * ```ts
7
+ * import { createSemanticEndpointDetector } from '@framers/agentos-ext-endpoint-semantic';
8
+ *
9
+ * const detector = createSemanticEndpointDetector(
10
+ * async (prompt) => myLlm(prompt),
11
+ * { timeoutMs: 400, minSilenceBeforeCheckMs: 600 }
12
+ * );
13
+ * detector.on('turn_complete', (evt) => console.log('Turn done:', evt.reason));
14
+ * ```
15
+ *
16
+ * ### Usage (manifest-driven)
17
+ * ```json
18
+ * { "packs": [{ "module": "@framers/agentos-ext-endpoint-semantic" }] }
19
+ * ```
20
+ *
21
+ * @module endpoint-semantic
22
+ */
23
+ import { SemanticEndpointDetector } from './SemanticEndpointDetector.js';
24
+ import type { SemanticEndpointConfig } from './types.js';
25
+ /** Subset of ExtensionDescriptor required by this pack. */
26
+ interface ExtensionDescriptor {
27
+ id: string;
28
+ kind: string;
29
+ payload: unknown;
30
+ enableByDefault?: boolean;
31
+ metadata?: Record<string, unknown>;
32
+ }
33
+ /** Subset of ExtensionPack required by this pack. */
34
+ interface ExtensionPack {
35
+ id: string;
36
+ descriptors: ExtensionDescriptor[];
37
+ }
38
+ /** Subset of ExtensionPackContext required by this pack. */
39
+ interface ExtensionPackContext {
40
+ getSecret?: (id: string) => string | undefined;
41
+ options?: Record<string, unknown>;
42
+ /** Runtime LLM call function supplied by the AgentOS extension manager. */
43
+ llmCall?: (prompt: string) => Promise<string>;
44
+ }
45
+ /**
46
+ * Create a standalone {@link SemanticEndpointDetector} instance.
47
+ *
48
+ * Use this when composing the detector programmatically outside of the
49
+ * AgentOS extension system.
50
+ *
51
+ * @param llmCall — Async function that sends a prompt string to an LLM and
52
+ * resolves with the raw response text.
53
+ * @param config — Optional configuration overrides.
54
+ * @returns A configured {@link SemanticEndpointDetector}.
55
+ */
56
+ export declare function createSemanticEndpointDetector(llmCall: (prompt: string) => Promise<string>, config?: SemanticEndpointConfig): SemanticEndpointDetector;
57
+ /**
58
+ * AgentOS manifest factory function.
59
+ *
60
+ * Returns an {@link ExtensionPack} containing a single `endpoint-detector`
61
+ * descriptor backed by {@link SemanticEndpointDetector}.
62
+ *
63
+ * The `llmCall` function must be provided via `context.llmCall`. When it is
64
+ * absent a no-op stub is used (the detector will always time out and fall back
65
+ * to silence-timeout behaviour).
66
+ *
67
+ * @param context — Pack context supplied by the extension manager.
68
+ * @returns A fully configured {@link ExtensionPack}.
69
+ */
70
+ export declare function createExtensionPack(context: ExtensionPackContext): ExtensionPack;
71
+ export * from './types.js';
72
+ export { SemanticEndpointDetector } from './SemanticEndpointDetector.js';
73
+ export { TurnCompletenessClassifier } from './TurnCompletenessClassifier.js';
74
+ export type { ClassifyResult } from './TurnCompletenessClassifier.js';
75
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AACzE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAMzD,2DAA2D;AAC3D,UAAU,mBAAmB;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,qDAAqD;AACrD,UAAU,aAAa;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,mBAAmB,EAAE,CAAC;CACpC;AAED,4DAA4D;AAC5D,UAAU,oBAAoB;IAC5B,SAAS,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IAC/C,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,2EAA2E;IAC3E,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CAC/C;AASD;;;;;;;;;;GAUG;AACH,wBAAgB,8BAA8B,CAC5C,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,EAC5C,MAAM,CAAC,EAAE,sBAAsB,GAC9B,wBAAwB,CAE1B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,oBAAoB,GAAG,aAAa,CAwBhF;AAMD,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AACzE,OAAO,EAAE,0BAA0B,EAAE,MAAM,iCAAiC,CAAC;AAC7E,YAAY,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,83 @@
1
+ /**
2
+ * @file index.ts
3
+ * @description Pack factory for the Semantic Endpoint Detector extension pack.
4
+ *
5
+ * ### Usage (direct)
6
+ * ```ts
7
+ * import { createSemanticEndpointDetector } from '@framers/agentos-ext-endpoint-semantic';
8
+ *
9
+ * const detector = createSemanticEndpointDetector(
10
+ * async (prompt) => myLlm(prompt),
11
+ * { timeoutMs: 400, minSilenceBeforeCheckMs: 600 }
12
+ * );
13
+ * detector.on('turn_complete', (evt) => console.log('Turn done:', evt.reason));
14
+ * ```
15
+ *
16
+ * ### Usage (manifest-driven)
17
+ * ```json
18
+ * { "packs": [{ "module": "@framers/agentos-ext-endpoint-semantic" }] }
19
+ * ```
20
+ *
21
+ * @module endpoint-semantic
22
+ */
23
+ import { SemanticEndpointDetector } from './SemanticEndpointDetector.js';
24
+ /** Kind constant matching packages/agentos/src/extensions/types.ts. */
25
+ const EXTENSION_KIND_ENDPOINT_DETECTOR = 'endpoint-detector';
26
+ // ---------------------------------------------------------------------------
27
+ // Factory helpers
28
+ // ---------------------------------------------------------------------------
29
+ /**
30
+ * Create a standalone {@link SemanticEndpointDetector} instance.
31
+ *
32
+ * Use this when composing the detector programmatically outside of the
33
+ * AgentOS extension system.
34
+ *
35
+ * @param llmCall — Async function that sends a prompt string to an LLM and
36
+ * resolves with the raw response text.
37
+ * @param config — Optional configuration overrides.
38
+ * @returns A configured {@link SemanticEndpointDetector}.
39
+ */
40
+ export function createSemanticEndpointDetector(llmCall, config) {
41
+ return new SemanticEndpointDetector(llmCall, config);
42
+ }
43
+ /**
44
+ * AgentOS manifest factory function.
45
+ *
46
+ * Returns an {@link ExtensionPack} containing a single `endpoint-detector`
47
+ * descriptor backed by {@link SemanticEndpointDetector}.
48
+ *
49
+ * The `llmCall` function must be provided via `context.llmCall`. When it is
50
+ * absent a no-op stub is used (the detector will always time out and fall back
51
+ * to silence-timeout behaviour).
52
+ *
53
+ * @param context — Pack context supplied by the extension manager.
54
+ * @returns A fully configured {@link ExtensionPack}.
55
+ */
56
+ export function createExtensionPack(context) {
57
+ const llmCall = context.llmCall ??
58
+ // Stub: always resolves to INCOMPLETE so the silence timeout takes over.
59
+ (() => Promise.resolve('INCOMPLETE (no llmCall provided)'));
60
+ const config = {
61
+ ...context.options,
62
+ };
63
+ const detector = new SemanticEndpointDetector(llmCall, config);
64
+ return {
65
+ id: 'endpoint-semantic',
66
+ descriptors: [
67
+ {
68
+ id: 'endpoint-detector-semantic',
69
+ kind: EXTENSION_KIND_ENDPOINT_DETECTOR,
70
+ payload: detector,
71
+ enableByDefault: true,
72
+ metadata: { mode: 'semantic' },
73
+ },
74
+ ],
75
+ };
76
+ }
77
+ // ---------------------------------------------------------------------------
78
+ // Re-exports
79
+ // ---------------------------------------------------------------------------
80
+ export * from './types.js';
81
+ export { SemanticEndpointDetector } from './SemanticEndpointDetector.js';
82
+ export { TurnCompletenessClassifier } from './TurnCompletenessClassifier.js';
83
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AA8BzE,uEAAuE;AACvE,MAAM,gCAAgC,GAAG,mBAAmB,CAAC;AAE7D,8EAA8E;AAC9E,kBAAkB;AAClB,8EAA8E;AAE9E;;;;;;;;;;GAUG;AACH,MAAM,UAAU,8BAA8B,CAC5C,OAA4C,EAC5C,MAA+B;IAE/B,OAAO,IAAI,wBAAwB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;AACvD,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAA6B;IAC/D,MAAM,OAAO,GACX,OAAO,CAAC,OAAO;QACf,yEAAyE;QACzE,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,kCAAkC,CAAC,CAAC,CAAC;IAE9D,MAAM,MAAM,GAA2B;QACrC,GAAI,OAAO,CAAC,OAA8C;KAC3D,CAAC;IAEF,MAAM,QAAQ,GAAG,IAAI,wBAAwB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAE/D,OAAO;QACL,EAAE,EAAE,mBAAmB;QACvB,WAAW,EAAE;YACX;gBACE,EAAE,EAAE,4BAA4B;gBAChC,IAAI,EAAE,gCAAgC;gBACtC,OAAO,EAAE,QAAQ;gBACjB,eAAe,EAAE,IAAI;gBACrB,QAAQ,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE;aAC/B;SACF;KACF,CAAC;AACJ,CAAC;AAED,8EAA8E;AAC9E,aAAa;AACb,8EAA8E;AAE9E,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AACzE,OAAO,EAAE,0BAA0B,EAAE,MAAM,iCAAiC,CAAC"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * @file types.ts
3
+ * @description Public configuration types for the Semantic Endpoint Detector
4
+ * extension pack.
5
+ *
6
+ * @module endpoint-semantic/types
7
+ */
8
+ /**
9
+ * Configuration options for the {@link SemanticEndpointDetector}.
10
+ *
11
+ * All fields are optional — reasonable defaults are applied when omitted.
12
+ */
13
+ export interface SemanticEndpointConfig {
14
+ /**
15
+ * LLM model identifier forwarded to the runtime LLM provider when performing
16
+ * turn-completeness classification. The exact string is passed through
17
+ * verbatim; its meaning is determined by whichever provider the caller
18
+ * supplies via the `llmCall` constructor argument.
19
+ *
20
+ * @example 'gpt-4o-mini'
21
+ * @example 'claude-haiku-3-5'
22
+ */
23
+ model?: string;
24
+ /**
25
+ * Maximum number of milliseconds to wait for the LLM to return a
26
+ * classification before treating the result as a {@link ClassifyResult.TIMEOUT}.
27
+ * On timeout the detector falls back to the normal silence timeout behaviour.
28
+ *
29
+ * @defaultValue 500
30
+ */
31
+ timeoutMs?: number;
32
+ /**
33
+ * Duration of silence (in ms) after a `speech_end` VAD event before the
34
+ * LLM classifier is invoked. Introducing a small delay avoids calling the
35
+ * LLM on micro-pauses that are immediately followed by resumed speech.
36
+ *
37
+ * @defaultValue 500
38
+ */
39
+ minSilenceBeforeCheckMs?: number;
40
+ /**
41
+ * Duration (in ms) of silence after `speech_end` before the detector emits
42
+ * `turn_complete` with reason `'silence_timeout'` regardless of classifier
43
+ * output. Acts as a hard fallback when the LLM returns `INCOMPLETE` or times
44
+ * out.
45
+ *
46
+ * @defaultValue 1500
47
+ */
48
+ silenceTimeoutMs?: number;
49
+ }
50
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;GAIG;AACH,MAAM,WAAW,sBAAsB;IACrC;;;;;;;;OAQG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;;;;OAMG;IACH,uBAAuB,CAAC,EAAE,MAAM,CAAC;IAEjC;;;;;;;OAOG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B"}
package/dist/types.js ADDED
@@ -0,0 +1,9 @@
1
+ /**
2
+ * @file types.ts
3
+ * @description Public configuration types for the Semantic Endpoint Detector
4
+ * extension pack.
5
+ *
6
+ * @module endpoint-semantic/types
7
+ */
8
+ export {};
9
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
package/manifest.json ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ "name": "@framers/agentos-ext-endpoint-semantic",
3
+ "version": "0.1.0",
4
+ "description": "Semantic endpoint detector with LLM turn-completeness classification",
5
+ "kind": "endpoint-detector",
6
+ "extensionId": "endpoint-semantic",
7
+ "entryPoint": "./dist/index.js"
8
+ }
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@framers/agentos-ext-endpoint-semantic",
3
+ "version": "0.2.0",
4
+ "description": "Semantic endpoint detection extension pack for AgentOS voice pipeline — LLM-based turn-completeness classification",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "src",
17
+ "SKILL.md",
18
+ "manifest.json"
19
+ ],
20
+ "peerDependencies": {
21
+ "@framers/agentos": "^0.1.0"
22
+ },
23
+ "devDependencies": {
24
+ "typescript": "^5.5.0",
25
+ "vitest": "^1.6.0",
26
+ "@framers/agentos": "0.1.94"
27
+ },
28
+ "license": "MIT",
29
+ "author": "Frame.dev",
30
+ "repository": {
31
+ "type": "git",
32
+ "url": "https://github.com/framersai/agentos-extensions.git",
33
+ "directory": "registry/curated/voice/endpoint-semantic"
34
+ },
35
+ "publishConfig": {
36
+ "access": "public"
37
+ },
38
+ "scripts": {
39
+ "build": "tsc -p tsconfig.json",
40
+ "test": "vitest run"
41
+ }
42
+ }