@komaa/standin-sdk 0.1.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/LICENSE +21 -0
- package/README.md +141 -0
- package/dist/ambient.d.ts +80 -0
- package/dist/ambient.d.ts.map +1 -0
- package/dist/ambient.js +281 -0
- package/dist/ambient.js.map +1 -0
- package/dist/attachments.d.ts +149 -0
- package/dist/attachments.d.ts.map +1 -0
- package/dist/attachments.js +451 -0
- package/dist/attachments.js.map +1 -0
- package/dist/audio.d.ts +80 -0
- package/dist/audio.d.ts.map +1 -0
- package/dist/audio.js +170 -0
- package/dist/audio.js.map +1 -0
- package/dist/avatar.d.ts +111 -0
- package/dist/avatar.d.ts.map +1 -0
- package/dist/avatar.js +201 -0
- package/dist/avatar.js.map +1 -0
- package/dist/callServer.d.ts +151 -0
- package/dist/callServer.d.ts.map +1 -0
- package/dist/callServer.js +1097 -0
- package/dist/callServer.js.map +1 -0
- package/dist/callTools.d.ts +154 -0
- package/dist/callTools.d.ts.map +1 -0
- package/dist/callTools.js +255 -0
- package/dist/callTools.js.map +1 -0
- package/dist/chat.d.ts +226 -0
- package/dist/chat.d.ts.map +1 -0
- package/dist/chat.js +574 -0
- package/dist/chat.js.map +1 -0
- package/dist/config.d.ts +32 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +86 -0
- package/dist/config.js.map +1 -0
- package/dist/consult.d.ts +147 -0
- package/dist/consult.d.ts.map +1 -0
- package/dist/consult.js +388 -0
- package/dist/consult.js.map +1 -0
- package/dist/delivery.d.ts +81 -0
- package/dist/delivery.d.ts.map +1 -0
- package/dist/delivery.js +187 -0
- package/dist/delivery.js.map +1 -0
- package/dist/echoGuard.d.ts +56 -0
- package/dist/echoGuard.d.ts.map +1 -0
- package/dist/echoGuard.js +57 -0
- package/dist/echoGuard.js.map +1 -0
- package/dist/errors.d.ts +5 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +10 -0
- package/dist/errors.js.map +1 -0
- package/dist/fetch.d.ts +40 -0
- package/dist/fetch.d.ts.map +1 -0
- package/dist/fetch.js +240 -0
- package/dist/fetch.js.map +1 -0
- package/dist/gate.d.ts +80 -0
- package/dist/gate.d.ts.map +1 -0
- package/dist/gate.js +249 -0
- package/dist/gate.js.map +1 -0
- package/dist/handler.d.ts +283 -0
- package/dist/handler.d.ts.map +1 -0
- package/dist/handler.js +4 -0
- package/dist/handler.js.map +1 -0
- package/dist/hmac.d.ts +32 -0
- package/dist/hmac.d.ts.map +1 -0
- package/dist/hmac.js +108 -0
- package/dist/hmac.js.map +1 -0
- package/dist/index.d.ts +59 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +60 -0
- package/dist/index.js.map +1 -0
- package/dist/lane.d.ts +92 -0
- package/dist/lane.d.ts.map +1 -0
- package/dist/lane.js +269 -0
- package/dist/lane.js.map +1 -0
- package/dist/lipsync.d.ts +136 -0
- package/dist/lipsync.d.ts.map +1 -0
- package/dist/lipsync.js +310 -0
- package/dist/lipsync.js.map +1 -0
- package/dist/log.d.ts +18 -0
- package/dist/log.d.ts.map +1 -0
- package/dist/log.js +20 -0
- package/dist/log.js.map +1 -0
- package/dist/media.d.ts +43 -0
- package/dist/media.d.ts.map +1 -0
- package/dist/media.js +190 -0
- package/dist/media.js.map +1 -0
- package/dist/minutes.d.ts +398 -0
- package/dist/minutes.d.ts.map +1 -0
- package/dist/minutes.js +805 -0
- package/dist/minutes.js.map +1 -0
- package/dist/outbound.d.ts +388 -0
- package/dist/outbound.d.ts.map +1 -0
- package/dist/outbound.js +933 -0
- package/dist/outbound.js.map +1 -0
- package/dist/plugins/cartesia/agent.d.ts +58 -0
- package/dist/plugins/cartesia/agent.d.ts.map +1 -0
- package/dist/plugins/cartesia/agent.js +254 -0
- package/dist/plugins/cartesia/agent.js.map +1 -0
- package/dist/plugins/cartesia/cli.d.ts +3 -0
- package/dist/plugins/cartesia/cli.d.ts.map +1 -0
- package/dist/plugins/cartesia/cli.js +10 -0
- package/dist/plugins/cartesia/cli.js.map +1 -0
- package/dist/plugins/cartesia/config.d.ts +30 -0
- package/dist/plugins/cartesia/config.d.ts.map +1 -0
- package/dist/plugins/cartesia/config.js +29 -0
- package/dist/plugins/cartesia/config.js.map +1 -0
- package/dist/plugins/cartesia/handler.d.ts +40 -0
- package/dist/plugins/cartesia/handler.d.ts.map +1 -0
- package/dist/plugins/cartesia/handler.js +142 -0
- package/dist/plugins/cartesia/handler.js.map +1 -0
- package/dist/plugins/cartesia/index.d.ts +6 -0
- package/dist/plugins/cartesia/index.d.ts.map +1 -0
- package/dist/plugins/cartesia/index.js +54 -0
- package/dist/plugins/cartesia/index.js.map +1 -0
- package/dist/plugins/deepgram/agent.d.ts +60 -0
- package/dist/plugins/deepgram/agent.d.ts.map +1 -0
- package/dist/plugins/deepgram/agent.js +302 -0
- package/dist/plugins/deepgram/agent.js.map +1 -0
- package/dist/plugins/deepgram/cli.d.ts +3 -0
- package/dist/plugins/deepgram/cli.d.ts.map +1 -0
- package/dist/plugins/deepgram/cli.js +10 -0
- package/dist/plugins/deepgram/cli.js.map +1 -0
- package/dist/plugins/deepgram/config.d.ts +39 -0
- package/dist/plugins/deepgram/config.d.ts.map +1 -0
- package/dist/plugins/deepgram/config.js +66 -0
- package/dist/plugins/deepgram/config.js.map +1 -0
- package/dist/plugins/deepgram/handler.d.ts +87 -0
- package/dist/plugins/deepgram/handler.d.ts.map +1 -0
- package/dist/plugins/deepgram/handler.js +268 -0
- package/dist/plugins/deepgram/handler.js.map +1 -0
- package/dist/plugins/deepgram/index.d.ts +11 -0
- package/dist/plugins/deepgram/index.d.ts.map +1 -0
- package/dist/plugins/deepgram/index.js +76 -0
- package/dist/plugins/deepgram/index.js.map +1 -0
- package/dist/plugins/echo/cli.d.ts +3 -0
- package/dist/plugins/echo/cli.d.ts.map +1 -0
- package/dist/plugins/echo/cli.js +10 -0
- package/dist/plugins/echo/cli.js.map +1 -0
- package/dist/plugins/echo/index.d.ts +37 -0
- package/dist/plugins/echo/index.d.ts.map +1 -0
- package/dist/plugins/echo/index.js +61 -0
- package/dist/plugins/echo/index.js.map +1 -0
- package/dist/plugins/elevenlabs/agent.d.ts +72 -0
- package/dist/plugins/elevenlabs/agent.d.ts.map +1 -0
- package/dist/plugins/elevenlabs/agent.js +305 -0
- package/dist/plugins/elevenlabs/agent.js.map +1 -0
- package/dist/plugins/elevenlabs/cli.d.ts +3 -0
- package/dist/plugins/elevenlabs/cli.d.ts.map +1 -0
- package/dist/plugins/elevenlabs/cli.js +10 -0
- package/dist/plugins/elevenlabs/cli.js.map +1 -0
- package/dist/plugins/elevenlabs/config.d.ts +27 -0
- package/dist/plugins/elevenlabs/config.d.ts.map +1 -0
- package/dist/plugins/elevenlabs/config.js +42 -0
- package/dist/plugins/elevenlabs/config.js.map +1 -0
- package/dist/plugins/elevenlabs/handler.d.ts +67 -0
- package/dist/plugins/elevenlabs/handler.d.ts.map +1 -0
- package/dist/plugins/elevenlabs/handler.js +419 -0
- package/dist/plugins/elevenlabs/handler.js.map +1 -0
- package/dist/plugins/elevenlabs/index.d.ts +11 -0
- package/dist/plugins/elevenlabs/index.d.ts.map +1 -0
- package/dist/plugins/elevenlabs/index.js +72 -0
- package/dist/plugins/elevenlabs/index.js.map +1 -0
- package/dist/plugins/livekit/cli.d.ts +3 -0
- package/dist/plugins/livekit/cli.d.ts.map +1 -0
- package/dist/plugins/livekit/cli.js +10 -0
- package/dist/plugins/livekit/cli.js.map +1 -0
- package/dist/plugins/livekit/config.d.ts +47 -0
- package/dist/plugins/livekit/config.d.ts.map +1 -0
- package/dist/plugins/livekit/config.js +46 -0
- package/dist/plugins/livekit/config.js.map +1 -0
- package/dist/plugins/livekit/handler.d.ts +59 -0
- package/dist/plugins/livekit/handler.d.ts.map +1 -0
- package/dist/plugins/livekit/handler.js +211 -0
- package/dist/plugins/livekit/handler.js.map +1 -0
- package/dist/plugins/livekit/index.d.ts +6 -0
- package/dist/plugins/livekit/index.d.ts.map +1 -0
- package/dist/plugins/livekit/index.js +68 -0
- package/dist/plugins/livekit/index.js.map +1 -0
- package/dist/plugins/livekit/room.d.ts +117 -0
- package/dist/plugins/livekit/room.d.ts.map +1 -0
- package/dist/plugins/livekit/room.js +449 -0
- package/dist/plugins/livekit/room.js.map +1 -0
- package/dist/plugins/openai/agent.d.ts +74 -0
- package/dist/plugins/openai/agent.d.ts.map +1 -0
- package/dist/plugins/openai/agent.js +264 -0
- package/dist/plugins/openai/agent.js.map +1 -0
- package/dist/plugins/openai/cli.d.ts +3 -0
- package/dist/plugins/openai/cli.d.ts.map +1 -0
- package/dist/plugins/openai/cli.js +10 -0
- package/dist/plugins/openai/cli.js.map +1 -0
- package/dist/plugins/openai/config.d.ts +29 -0
- package/dist/plugins/openai/config.d.ts.map +1 -0
- package/dist/plugins/openai/config.js +37 -0
- package/dist/plugins/openai/config.js.map +1 -0
- package/dist/plugins/openai/handler.d.ts +65 -0
- package/dist/plugins/openai/handler.d.ts.map +1 -0
- package/dist/plugins/openai/handler.js +297 -0
- package/dist/plugins/openai/handler.js.map +1 -0
- package/dist/plugins/openai/index.d.ts +6 -0
- package/dist/plugins/openai/index.d.ts.map +1 -0
- package/dist/plugins/openai/index.js +72 -0
- package/dist/plugins/openai/index.js.map +1 -0
- package/dist/plugins/openclaw/allowlist.d.ts +9 -0
- package/dist/plugins/openclaw/allowlist.d.ts.map +1 -0
- package/dist/plugins/openclaw/allowlist.js +11 -0
- package/dist/plugins/openclaw/allowlist.js.map +1 -0
- package/dist/plugins/openclaw/config.d.ts +50 -0
- package/dist/plugins/openclaw/config.d.ts.map +1 -0
- package/dist/plugins/openclaw/config.js +53 -0
- package/dist/plugins/openclaw/config.js.map +1 -0
- package/dist/plugins/openclaw/handler.d.ts +90 -0
- package/dist/plugins/openclaw/handler.d.ts.map +1 -0
- package/dist/plugins/openclaw/handler.js +146 -0
- package/dist/plugins/openclaw/handler.js.map +1 -0
- package/dist/plugins/openclaw/index.d.ts +55 -0
- package/dist/plugins/openclaw/index.d.ts.map +1 -0
- package/dist/plugins/openclaw/index.js +89 -0
- package/dist/plugins/openclaw/index.js.map +1 -0
- package/dist/plugins/openclaw/openclaw.plugin.json +74 -0
- package/dist/plugins/openclaw/package.json +14 -0
- package/dist/plugins/openclaw/realtime.d.ts +82 -0
- package/dist/plugins/openclaw/realtime.d.ts.map +1 -0
- package/dist/plugins/openclaw/realtime.js +238 -0
- package/dist/plugins/openclaw/realtime.js.map +1 -0
- package/dist/plugins/openclaw/runtime.d.ts +19 -0
- package/dist/plugins/openclaw/runtime.d.ts.map +1 -0
- package/dist/plugins/openclaw/runtime.js +160 -0
- package/dist/plugins/openclaw/runtime.js.map +1 -0
- package/dist/policy.d.ts +38 -0
- package/dist/policy.d.ts.map +1 -0
- package/dist/policy.js +95 -0
- package/dist/policy.js.map +1 -0
- package/dist/protocol.d.ts +44 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +77 -0
- package/dist/protocol.js.map +1 -0
- package/dist/protocolRuntime.d.ts +19 -0
- package/dist/protocolRuntime.d.ts.map +1 -0
- package/dist/protocolRuntime.js +78 -0
- package/dist/protocolRuntime.js.map +1 -0
- package/dist/smoke.d.ts +65 -0
- package/dist/smoke.d.ts.map +1 -0
- package/dist/smoke.js +293 -0
- package/dist/smoke.js.map +1 -0
- package/dist/startup.d.ts +74 -0
- package/dist/startup.d.ts.map +1 -0
- package/dist/startup.js +112 -0
- package/dist/startup.js.map +1 -0
- package/dist/tile.d.ts +101 -0
- package/dist/tile.d.ts.map +1 -0
- package/dist/tile.js +195 -0
- package/dist/tile.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +4 -0
- package/dist/version.js.map +1 -0
- package/dist/vision.d.ts +160 -0
- package/dist/vision.d.ts.map +1 -0
- package/dist/vision.js +301 -0
- package/dist/vision.js.map +1 -0
- package/dist/visionTools.d.ts +237 -0
- package/dist/visionTools.d.ts.map +1 -0
- package/dist/visionTools.js +578 -0
- package/dist/visionTools.js.map +1 -0
- package/dist/voice.d.ts +143 -0
- package/dist/voice.d.ts.map +1 -0
- package/dist/voice.js +403 -0
- package/dist/voice.js.map +1 -0
- package/package.json +129 -0
package/dist/protocol.js
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// Copyright (c) 2026 Komaa DigiTech
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// GENERATED from protocol/schema.yaml; do not hand-edit.
|
|
4
|
+
// Schema SHA-256: 9157d78287022cc97566640007ea61c5336cb11c731ba5fcb15b0833b8048d5b
|
|
5
|
+
// Regenerate with: python3 protocol/generate.py
|
|
6
|
+
import { clean, normalizePongTimestamp } from "./protocolRuntime.js";
|
|
7
|
+
export { contextSentences, decodePcm, parseMessage } from "./protocolRuntime.js";
|
|
8
|
+
export const SAMPLE_RATE_HZ = 16_000;
|
|
9
|
+
export const NUM_CHANNELS = 1;
|
|
10
|
+
export const TYPE_SESSION_START = "session.start";
|
|
11
|
+
export const TYPE_SESSION_END = "session.end";
|
|
12
|
+
export const TYPE_RECORDING_STATUS = "recording.status";
|
|
13
|
+
export const TYPE_AUDIO_FRAME = "audio.frame";
|
|
14
|
+
export const TYPE_VIDEO_FRAME = "video.frame";
|
|
15
|
+
export const TYPE_PARTICIPANTS = "participants";
|
|
16
|
+
export const TYPE_DTMF = "dtmf";
|
|
17
|
+
export const TYPE_PING = "ping";
|
|
18
|
+
export const TYPE_ASSISTANT_SAY = "assistant.say";
|
|
19
|
+
export const TYPE_ASSISTANT_CANCEL = "assistant.cancel";
|
|
20
|
+
export const TYPE_EXPRESSION = "expression";
|
|
21
|
+
export const TYPE_SPEECH_MARKS = "speech.marks";
|
|
22
|
+
export const TYPE_DISPLAY_IMAGE = "display.image";
|
|
23
|
+
export const TYPE_DISPLAY_FRAME = "display.frame";
|
|
24
|
+
export const TYPE_PONG = "pong";
|
|
25
|
+
/** Read call context; only callId lacks a safe default. */
|
|
26
|
+
export function parseSessionStart(msg) {
|
|
27
|
+
const callId = clean(msg.callId);
|
|
28
|
+
if (!callId)
|
|
29
|
+
throw new Error("session.start is missing callId");
|
|
30
|
+
const rawCaller = msg.caller;
|
|
31
|
+
const callerObj = typeof rawCaller === "object" && rawCaller !== null && !Array.isArray(rawCaller)
|
|
32
|
+
? rawCaller : {};
|
|
33
|
+
const direction = clean(msg.direction) ?? "inbound";
|
|
34
|
+
return {
|
|
35
|
+
callId: callId,
|
|
36
|
+
threadId: clean(msg.threadId) ?? "",
|
|
37
|
+
caller: {
|
|
38
|
+
aadId: clean(callerObj.aadId),
|
|
39
|
+
displayName: clean(callerObj.displayName),
|
|
40
|
+
tenantId: clean(callerObj.tenantId),
|
|
41
|
+
},
|
|
42
|
+
direction: direction === "inbound" || direction === "outbound" ? direction : "inbound",
|
|
43
|
+
recordingStatus: clean(msg.recordingStatus),
|
|
44
|
+
tenantId: clean(msg.tenantId),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/** Build an outbound `audio.frame` JSON frame. */
|
|
48
|
+
export function audioFrame(seq, timestampMs, pcm) {
|
|
49
|
+
return JSON.stringify({
|
|
50
|
+
type: TYPE_AUDIO_FRAME,
|
|
51
|
+
seq: seq,
|
|
52
|
+
timestampMs: timestampMs,
|
|
53
|
+
payloadBase64: pcm.toString("base64"),
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
/** Build an outbound `pong` JSON frame. */
|
|
57
|
+
export function pong(ts) {
|
|
58
|
+
return JSON.stringify({
|
|
59
|
+
type: TYPE_PONG,
|
|
60
|
+
ts: normalizePongTimestamp(ts),
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
/** Build an outbound `assistant.cancel` JSON frame. */
|
|
64
|
+
export function assistantCancel(turnId) {
|
|
65
|
+
return JSON.stringify({
|
|
66
|
+
type: TYPE_ASSISTANT_CANCEL,
|
|
67
|
+
turnId: turnId,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
/** Build an outbound `session.end` JSON frame. */
|
|
71
|
+
export function sessionEnd(reason) {
|
|
72
|
+
return JSON.stringify({
|
|
73
|
+
type: TYPE_SESSION_END,
|
|
74
|
+
reason: reason,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=protocol.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** Read blank strings and non-string identity fields as absent. */
|
|
2
|
+
export declare function clean(value: unknown): string | undefined;
|
|
3
|
+
/** Drop malformed JSON while preserving unknown message types for the receive loop. */
|
|
4
|
+
export declare function parseMessage(raw: string | Buffer): Record<string, unknown> | undefined;
|
|
5
|
+
/** Decode canonical standard base64 containing complete, nonempty PCM16 samples. */
|
|
6
|
+
export declare function decodePcm(payloadBase64: unknown): Buffer;
|
|
7
|
+
/** Encode raw PCM bytes using canonical standard base64. */
|
|
8
|
+
export declare function encodePcm(pcm: Buffer): string;
|
|
9
|
+
/** Serialize an outbound message as compact JSON. */
|
|
10
|
+
export declare function encode(message: Record<string, unknown>): string;
|
|
11
|
+
/** Echo safe integer timestamps, using zero for malformed or imprecise values. */
|
|
12
|
+
export declare function normalizePongTimestamp(value: unknown): number;
|
|
13
|
+
/** The exact context sentences presented to agents by both SDKs. */
|
|
14
|
+
export declare const contextSentences: {
|
|
15
|
+
readonly participants: (count: number) => string;
|
|
16
|
+
readonly dtmf: (digit: string) => string;
|
|
17
|
+
readonly recording: (status: string) => string;
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=protocolRuntime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocolRuntime.d.ts","sourceRoot":"","sources":["../src/protocolRuntime.ts"],"names":[],"mappings":"AAQA,mEAAmE;AACnE,wBAAgB,KAAK,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAGxD;AAED,uFAAuF;AACvF,wBAAgB,YAAY,CAC1B,GAAG,EAAE,MAAM,GAAG,MAAM,GACnB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAkBrC;AAED,oFAAoF;AACpF,wBAAgB,SAAS,CAAC,aAAa,EAAE,OAAO,GAAG,MAAM,CAiBxD;AAED,4DAA4D;AAC5D,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE7C;AAED,qDAAqD;AACrD,wBAAgB,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAE/D;AAED,kFAAkF;AAClF,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAE7D;AAED,oEAAoE;AACpE,eAAO,MAAM,gBAAgB;mCACP,MAAM,KAAG,MAAM;2BAMvB,MAAM,KAAG,MAAM;iCAGT,MAAM,KAAG,MAAM;CAKzB,CAAC"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// Copyright (c) 2026 Komaa DigiTech
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
/** Runtime helpers shared by the generated StandIn call protocol bindings. */
|
|
4
|
+
const BASE64 = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
|
|
5
|
+
/** Read blank strings and non-string identity fields as absent. */
|
|
6
|
+
export function clean(value) {
|
|
7
|
+
if (typeof value !== "string")
|
|
8
|
+
return undefined;
|
|
9
|
+
return value.trim() || undefined;
|
|
10
|
+
}
|
|
11
|
+
/** Drop malformed JSON while preserving unknown message types for the receive loop. */
|
|
12
|
+
export function parseMessage(raw) {
|
|
13
|
+
let obj;
|
|
14
|
+
try {
|
|
15
|
+
const text = typeof raw === "string"
|
|
16
|
+
? raw
|
|
17
|
+
: new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(raw);
|
|
18
|
+
obj = JSON.parse(text);
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
if (typeof obj !== "object" || obj === null || Array.isArray(obj))
|
|
24
|
+
return undefined;
|
|
25
|
+
const rec = obj;
|
|
26
|
+
if (typeof rec.type !== "string")
|
|
27
|
+
return undefined;
|
|
28
|
+
return rec;
|
|
29
|
+
}
|
|
30
|
+
/** Decode canonical standard base64 containing complete, nonempty PCM16 samples. */
|
|
31
|
+
export function decodePcm(payloadBase64) {
|
|
32
|
+
if (typeof payloadBase64 !== "string" || payloadBase64 === "") {
|
|
33
|
+
throw new Error("audio.frame carries no payloadBase64");
|
|
34
|
+
}
|
|
35
|
+
if (!BASE64.test(payloadBase64)) {
|
|
36
|
+
throw new Error("audio.frame payloadBase64 is not valid base64");
|
|
37
|
+
}
|
|
38
|
+
const pcm = Buffer.from(payloadBase64, "base64");
|
|
39
|
+
// Buffer's decoder tolerates invalid padding and nonzero padding bits.
|
|
40
|
+
// Require the same canonical representation as the Python runtime.
|
|
41
|
+
if (encodePcm(pcm) !== payloadBase64) {
|
|
42
|
+
throw new Error("audio.frame payloadBase64 is not valid base64");
|
|
43
|
+
}
|
|
44
|
+
if (pcm.length < 2 || pcm.length % 2 !== 0) {
|
|
45
|
+
throw new Error(`malformed PCM16 payload (${pcm.length} bytes)`);
|
|
46
|
+
}
|
|
47
|
+
return pcm;
|
|
48
|
+
}
|
|
49
|
+
/** Encode raw PCM bytes using canonical standard base64. */
|
|
50
|
+
export function encodePcm(pcm) {
|
|
51
|
+
return pcm.toString("base64");
|
|
52
|
+
}
|
|
53
|
+
/** Serialize an outbound message as compact JSON. */
|
|
54
|
+
export function encode(message) {
|
|
55
|
+
return JSON.stringify(message);
|
|
56
|
+
}
|
|
57
|
+
/** Echo safe integer timestamps, using zero for malformed or imprecise values. */
|
|
58
|
+
export function normalizePongTimestamp(value) {
|
|
59
|
+
return typeof value === "number" && Number.isSafeInteger(value) ? value : 0;
|
|
60
|
+
}
|
|
61
|
+
/** The exact context sentences presented to agents by both SDKs. */
|
|
62
|
+
export const contextSentences = {
|
|
63
|
+
participants(count) {
|
|
64
|
+
return count <= 1
|
|
65
|
+
? "This is a 1:1 call with a single human caller."
|
|
66
|
+
: `There are ${count} human participants on this call. ` +
|
|
67
|
+
"Stay quiet unless directly addressed.";
|
|
68
|
+
},
|
|
69
|
+
dtmf(digit) {
|
|
70
|
+
return `The caller pressed the "${digit}" key on their keypad.`;
|
|
71
|
+
},
|
|
72
|
+
recording(status) {
|
|
73
|
+
return status === "active"
|
|
74
|
+
? "The Microsoft Teams call recording is now ACTIVE."
|
|
75
|
+
: "The Microsoft Teams call recording is not active.";
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
//# sourceMappingURL=protocolRuntime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocolRuntime.js","sourceRoot":"","sources":["../src/protocolRuntime.ts"],"names":[],"mappings":"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"}
|
package/dist/smoke.d.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { HandlerFactory } from "./handler.js";
|
|
2
|
+
/** PCM16 mono at 16 kHz for 20 ms. The cadence a real call arrives at. */
|
|
3
|
+
export declare const FRAME_MS = 20;
|
|
4
|
+
export declare const FRAME_BYTES: number;
|
|
5
|
+
/**
|
|
6
|
+
* How long the whole run may take before it is a recorded failure rather than a
|
|
7
|
+
* hang. A listener that accepts and then stops talking would otherwise hang CI,
|
|
8
|
+
* and hang a status tool call for ever.
|
|
9
|
+
*/
|
|
10
|
+
export declare const RUN_TIMEOUT_MS = 15000;
|
|
11
|
+
export declare const CONNECT_TIMEOUT_MS = 5000;
|
|
12
|
+
/** One thing that either works here or does not. */
|
|
13
|
+
export interface SmokeCheck {
|
|
14
|
+
readonly name: string;
|
|
15
|
+
readonly ok: boolean;
|
|
16
|
+
readonly detail?: string;
|
|
17
|
+
/**
|
|
18
|
+
* What stops working when this fails. A report that names the missing surface
|
|
19
|
+
* without naming the consequence sends the operator to the source.
|
|
20
|
+
*/
|
|
21
|
+
readonly cost?: string;
|
|
22
|
+
/**
|
|
23
|
+
* Whether `ok` depends on it. Counted by default, so a check a plugin adds
|
|
24
|
+
* counts unless it passes `required: false`.
|
|
25
|
+
*/
|
|
26
|
+
readonly required?: boolean;
|
|
27
|
+
}
|
|
28
|
+
/** What the run found. */
|
|
29
|
+
export interface SmokeResult {
|
|
30
|
+
readonly checks: SmokeCheck[];
|
|
31
|
+
readonly echoFrames: number;
|
|
32
|
+
readonly error?: string;
|
|
33
|
+
/**
|
|
34
|
+
* True only when every mandatory check passed.
|
|
35
|
+
*
|
|
36
|
+
* Never true when nothing echoed: the point of the run is that audio made the
|
|
37
|
+
* round trip, and a run that proves nothing must not read as a pass.
|
|
38
|
+
*/
|
|
39
|
+
readonly ok: boolean;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* A client that speaks the call wire, for one call that nobody is on.
|
|
43
|
+
*
|
|
44
|
+
* Used by {@link runSmoke}. Exported because a plugin with its own listener may
|
|
45
|
+
* want to point one at it.
|
|
46
|
+
*/
|
|
47
|
+
export declare class SyntheticCall {
|
|
48
|
+
#private;
|
|
49
|
+
echoFrames: number;
|
|
50
|
+
constructor(url: string, secret: string, callId: string, frames?: number, connectTimeoutMs?: number);
|
|
51
|
+
/** Connect, greet, stream, hang up. Rejects on anything that fails. */
|
|
52
|
+
run(): Promise<void>;
|
|
53
|
+
}
|
|
54
|
+
/** Extra checks a plugin runs after the call. */
|
|
55
|
+
export type ExtraChecks = () => Promise<SmokeCheck[]>;
|
|
56
|
+
/**
|
|
57
|
+
* Ring this worker's own handler and report what worked.
|
|
58
|
+
*
|
|
59
|
+
* `extra` is a plugin's own checks, run after the call. Each one counts
|
|
60
|
+
* towards `ok` unless it says `required: false`.
|
|
61
|
+
*/
|
|
62
|
+
export declare function runSmoke(handlerFactory: HandlerFactory, frames?: number, extra?: ExtraChecks, timeoutMs?: number): Promise<SmokeResult>;
|
|
63
|
+
/** The result as something to print. */
|
|
64
|
+
export declare function report(result: SmokeResult): string;
|
|
65
|
+
//# sourceMappingURL=smoke.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"smoke.d.ts","sourceRoot":"","sources":["../src/smoke.ts"],"names":[],"mappings":"AAoCA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AASnD,0EAA0E;AAC1E,eAAO,MAAM,QAAQ,KAAK,CAAC;AAC3B,eAAO,MAAM,WAAW,QAAmC,CAAC;AAE5D;;;;GAIG;AACH,eAAO,MAAM,cAAc,QAAS,CAAC;AACrC,eAAO,MAAM,kBAAkB,OAAQ,CAAC;AAExC,oDAAoD;AACpD,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,0BAA0B;AAC1B,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,MAAM,EAAE,UAAU,EAAE,CAAC;IAC9B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;CACtB;AAED;;;;;GAKG;AACH,qBAAa,aAAa;;IACxB,UAAU,SAAK;gBAQb,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,MAAM,SAAK,EACX,gBAAgB,SAAqB;IASvC,uEAAuE;IACjE,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAmF3B;AAED,iDAAiD;AACjD,MAAM,MAAM,WAAW,GAAG,MAAM,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;AAEtD;;;;;GAKG;AACH,wBAAsB,QAAQ,CAC5B,cAAc,EAAE,cAAc,EAC9B,MAAM,SAAK,EACX,KAAK,CAAC,EAAE,WAAW,EACnB,SAAS,SAAiB,GACzB,OAAO,CAAC,WAAW,CAAC,CAwGtB;AAED,wCAAwC;AACxC,wBAAgB,MAAM,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAWlD"}
|
package/dist/smoke.js
ADDED
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
// Copyright (c) 2026 Komaa DigiTech
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
/**
|
|
4
|
+
* Proving an install works, without a bill, a tunnel or a Microsoft tenant.
|
|
5
|
+
*
|
|
6
|
+
* The question every plugin has to answer before anybody trusts it with a real
|
|
7
|
+
* call is "does this actually work here?", and until now the only way to find
|
|
8
|
+
* out was to place a real call: a provider bill, a public tunnel, a Teams
|
|
9
|
+
* tenant and somebody's afternoon.
|
|
10
|
+
*
|
|
11
|
+
* So this rings the worker itself. It binds an ephemeral listener on loopback,
|
|
12
|
+
* connects a real client that speaks the real call wire, streams a few frames
|
|
13
|
+
* of silence, and reports what came back. Everything it exercises is the thing
|
|
14
|
+
* that breaks in the field: the secret, the bind, the handshake, the session,
|
|
15
|
+
* and whether audio makes the round trip.
|
|
16
|
+
*
|
|
17
|
+
* The client lives next to the wire it speaks, deliberately. A copy of it kept
|
|
18
|
+
* in a plugin drifts, and the copy that used to exist kept passing against a
|
|
19
|
+
* path that no longer existed, which is exactly the failure a smoke check is
|
|
20
|
+
* for.
|
|
21
|
+
*
|
|
22
|
+
* **Nothing that delivers is started.** No pending-message sweep, no no-answer
|
|
23
|
+
* reaper, no outbound caller, no chat lane. A verification command that quietly
|
|
24
|
+
* resumed durable jobs would place real calls and post real messages as a side
|
|
25
|
+
* effect of somebody typing `smoke`, which is the single worst thing this could
|
|
26
|
+
* possibly do.
|
|
27
|
+
*
|
|
28
|
+
* Identical in shape to the Python SDK's `standin.smoke`.
|
|
29
|
+
*/
|
|
30
|
+
import { randomBytes } from "node:crypto";
|
|
31
|
+
import WebSocket from "ws";
|
|
32
|
+
import { CallServer } from "./callServer.js";
|
|
33
|
+
import { SIGNATURE_HEADER, TIMESTAMP_HEADER, nowMs, signHandshake, } from "./hmac.js";
|
|
34
|
+
import { logger } from "./log.js";
|
|
35
|
+
/** PCM16 mono at 16 kHz for 20 ms. The cadence a real call arrives at. */
|
|
36
|
+
export const FRAME_MS = 20;
|
|
37
|
+
export const FRAME_BYTES = ((16_000 * FRAME_MS) / 1000) * 2;
|
|
38
|
+
/**
|
|
39
|
+
* How long the whole run may take before it is a recorded failure rather than a
|
|
40
|
+
* hang. A listener that accepts and then stops talking would otherwise hang CI,
|
|
41
|
+
* and hang a status tool call for ever.
|
|
42
|
+
*/
|
|
43
|
+
export const RUN_TIMEOUT_MS = 15_000;
|
|
44
|
+
export const CONNECT_TIMEOUT_MS = 5_000;
|
|
45
|
+
/**
|
|
46
|
+
* A client that speaks the call wire, for one call that nobody is on.
|
|
47
|
+
*
|
|
48
|
+
* Used by {@link runSmoke}. Exported because a plugin with its own listener may
|
|
49
|
+
* want to point one at it.
|
|
50
|
+
*/
|
|
51
|
+
export class SyntheticCall {
|
|
52
|
+
echoFrames = 0;
|
|
53
|
+
#url;
|
|
54
|
+
#secret;
|
|
55
|
+
#callId;
|
|
56
|
+
#frames;
|
|
57
|
+
#connectTimeoutMs;
|
|
58
|
+
constructor(url, secret, callId, frames = 10, connectTimeoutMs = CONNECT_TIMEOUT_MS) {
|
|
59
|
+
this.#url = url;
|
|
60
|
+
this.#secret = secret;
|
|
61
|
+
this.#callId = callId;
|
|
62
|
+
this.#frames = Math.max(1, frames);
|
|
63
|
+
this.#connectTimeoutMs = connectTimeoutMs;
|
|
64
|
+
}
|
|
65
|
+
/** Connect, greet, stream, hang up. Rejects on anything that fails. */
|
|
66
|
+
async run() {
|
|
67
|
+
// Signed freshly for this connect. The listener enforces single use inside
|
|
68
|
+
// the freshness window, so a retry that reused these headers would loop on
|
|
69
|
+
// 401 and read as a wrong secret.
|
|
70
|
+
const stamp = nowMs();
|
|
71
|
+
const ws = new WebSocket(this.#url, {
|
|
72
|
+
headers: {
|
|
73
|
+
[TIMESTAMP_HEADER]: String(stamp),
|
|
74
|
+
[SIGNATURE_HEADER]: signHandshake(this.#secret, stamp, this.#callId),
|
|
75
|
+
},
|
|
76
|
+
handshakeTimeout: this.#connectTimeoutMs,
|
|
77
|
+
});
|
|
78
|
+
ws.on("message", (data) => {
|
|
79
|
+
try {
|
|
80
|
+
if (JSON.parse(String(data)).type === "audio.frame")
|
|
81
|
+
this.echoFrames += 1;
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
// Not ours to interpret. A frame this cannot read is not an echo.
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
try {
|
|
88
|
+
await new Promise((resolve, reject) => {
|
|
89
|
+
ws.once("open", resolve);
|
|
90
|
+
ws.once("error", reject);
|
|
91
|
+
ws.once("close", (code) => reject(new Error(`the socket closed with ${code}`)));
|
|
92
|
+
});
|
|
93
|
+
ws.removeAllListeners("close");
|
|
94
|
+
ws.removeAllListeners("error");
|
|
95
|
+
let closed = "";
|
|
96
|
+
ws.on("close", (code) => {
|
|
97
|
+
closed = `the socket closed with ${code}`;
|
|
98
|
+
});
|
|
99
|
+
ws.on("error", (err) => {
|
|
100
|
+
closed = String(err);
|
|
101
|
+
});
|
|
102
|
+
ws.send(JSON.stringify({
|
|
103
|
+
type: "session.start",
|
|
104
|
+
// Identical to the id in the path. The listener refuses a start that
|
|
105
|
+
// disagrees with the authenticated path, and the refusal would read
|
|
106
|
+
// as a wire fault.
|
|
107
|
+
callId: this.#callId,
|
|
108
|
+
threadId: "",
|
|
109
|
+
direction: "inbound",
|
|
110
|
+
caller: { displayName: "StandIn smoke check" },
|
|
111
|
+
}));
|
|
112
|
+
// Handlers commonly gate output on the call being recorded, so without
|
|
113
|
+
// this a recording-gated handler stays silent and the run reports a false
|
|
114
|
+
// negative.
|
|
115
|
+
ws.send(JSON.stringify({ type: "recording.status", status: "active" }));
|
|
116
|
+
const silence = Buffer.alloc(FRAME_BYTES).toString("base64");
|
|
117
|
+
for (let seq = 0; seq < this.#frames; seq += 1) {
|
|
118
|
+
if (closed !== "")
|
|
119
|
+
throw new Error(closed);
|
|
120
|
+
ws.send(JSON.stringify({
|
|
121
|
+
type: "audio.frame",
|
|
122
|
+
seq,
|
|
123
|
+
timestampMs: seq * FRAME_MS,
|
|
124
|
+
payloadBase64: silence,
|
|
125
|
+
}));
|
|
126
|
+
await new Promise((resolve) => setTimeout(resolve, FRAME_MS));
|
|
127
|
+
}
|
|
128
|
+
ws.send(JSON.stringify({ type: "session.end", reason: "smoke-done" }));
|
|
129
|
+
// One cadence beat for whatever the handler said last to arrive.
|
|
130
|
+
await new Promise((resolve) => setTimeout(resolve, FRAME_MS * 2));
|
|
131
|
+
}
|
|
132
|
+
finally {
|
|
133
|
+
// On every path. An error path that skipped this leaks a socket per run
|
|
134
|
+
// inside a long-lived host that exposes the check.
|
|
135
|
+
try {
|
|
136
|
+
ws.close();
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
// Never connected, or already gone.
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Ring this worker's own handler and report what worked.
|
|
146
|
+
*
|
|
147
|
+
* `extra` is a plugin's own checks, run after the call. Each one counts
|
|
148
|
+
* towards `ok` unless it says `required: false`.
|
|
149
|
+
*/
|
|
150
|
+
export async function runSmoke(handlerFactory, frames = 10, extra, timeoutMs = RUN_TIMEOUT_MS) {
|
|
151
|
+
const checks = [];
|
|
152
|
+
let error = "";
|
|
153
|
+
let echoFrames = 0;
|
|
154
|
+
// Its own credential, never the operator's. A fixed string would be a
|
|
155
|
+
// predictable one on a live listener, and borrowing the operator's makes the
|
|
156
|
+
// run pass or fail for reasons that have nothing to do with the wire. It also
|
|
157
|
+
// makes the check runnable before the secret is configured, which is exactly
|
|
158
|
+
// when people run it.
|
|
159
|
+
const secret = randomBytes(16).toString("hex");
|
|
160
|
+
checks.push({
|
|
161
|
+
name: "secret",
|
|
162
|
+
ok: true,
|
|
163
|
+
detail: "generated for this run",
|
|
164
|
+
cost: "nothing could authenticate",
|
|
165
|
+
});
|
|
166
|
+
const server = new CallServer({
|
|
167
|
+
handlerFactory,
|
|
168
|
+
secret,
|
|
169
|
+
host: "127.0.0.1",
|
|
170
|
+
port: 0,
|
|
171
|
+
staleCallReaperMs: 0,
|
|
172
|
+
});
|
|
173
|
+
try {
|
|
174
|
+
await server.start();
|
|
175
|
+
}
|
|
176
|
+
catch (err) {
|
|
177
|
+
return finish(checks, 0, String(err), [
|
|
178
|
+
{
|
|
179
|
+
name: "listener",
|
|
180
|
+
ok: false,
|
|
181
|
+
detail: String(err),
|
|
182
|
+
cost: "no call could ever reach this worker",
|
|
183
|
+
},
|
|
184
|
+
]);
|
|
185
|
+
}
|
|
186
|
+
checks.push({
|
|
187
|
+
name: "listener",
|
|
188
|
+
ok: true,
|
|
189
|
+
detail: `127.0.0.1:${server.port}${server.wsPath}`,
|
|
190
|
+
cost: "no call could ever reach this worker",
|
|
191
|
+
});
|
|
192
|
+
const callId = `smoke-${randomBytes(4).toString("hex")}`;
|
|
193
|
+
const url = `ws://127.0.0.1:${server.port}${server.wsPath}/${callId}`;
|
|
194
|
+
const call = new SyntheticCall(url, secret, callId, frames);
|
|
195
|
+
const started = Date.now();
|
|
196
|
+
try {
|
|
197
|
+
await withTimeout(call.run(), timeoutMs);
|
|
198
|
+
checks.push({
|
|
199
|
+
name: "call",
|
|
200
|
+
ok: true,
|
|
201
|
+
detail: `${frames} frames in ${Date.now() - started} ms`,
|
|
202
|
+
cost: "calls do not connect",
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
catch (err) {
|
|
206
|
+
const timedOut = err === TIMED_OUT;
|
|
207
|
+
error = timedOut
|
|
208
|
+
? `the call did not finish within ${timeoutMs} ms`
|
|
209
|
+
: String(err);
|
|
210
|
+
checks.push({
|
|
211
|
+
name: "call",
|
|
212
|
+
ok: false,
|
|
213
|
+
detail: error,
|
|
214
|
+
// A hang and a refusal are different things to go and look at, so they
|
|
215
|
+
// are told apart here rather than both reading as "it did not work".
|
|
216
|
+
cost: timedOut
|
|
217
|
+
? "a real call would hang the same way"
|
|
218
|
+
: "a real call would fail the same way",
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
finally {
|
|
222
|
+
try {
|
|
223
|
+
await server.aclose();
|
|
224
|
+
}
|
|
225
|
+
catch {
|
|
226
|
+
// Already down. Nothing to report about a teardown nobody waited on.
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
echoFrames = call.echoFrames;
|
|
230
|
+
checks.push({
|
|
231
|
+
name: "audio",
|
|
232
|
+
ok: echoFrames > 0,
|
|
233
|
+
detail: `${echoFrames} frames came back`,
|
|
234
|
+
cost: "the caller would hear nothing",
|
|
235
|
+
});
|
|
236
|
+
if (extra !== undefined) {
|
|
237
|
+
try {
|
|
238
|
+
checks.push(...(await extra()));
|
|
239
|
+
}
|
|
240
|
+
catch (err) {
|
|
241
|
+
logger.debug(`standin: a plugin smoke check failed: ${String(err)}`);
|
|
242
|
+
// Advisory: the plugin's own checks failing to RUN says nothing about
|
|
243
|
+
// whether a call works, which is what this command answers.
|
|
244
|
+
checks.push({
|
|
245
|
+
name: "plugin checks",
|
|
246
|
+
ok: false,
|
|
247
|
+
detail: String(err),
|
|
248
|
+
cost: "this plugin's own checks did not run",
|
|
249
|
+
required: false,
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
return finish(checks, echoFrames, error);
|
|
254
|
+
}
|
|
255
|
+
/** The result as something to print. */
|
|
256
|
+
export function report(result) {
|
|
257
|
+
const lines = [`standin: ${result.ok ? "ok" : "NOT ok"}`];
|
|
258
|
+
for (const check of result.checks) {
|
|
259
|
+
const mark = check.ok ? "ok " : "FAIL";
|
|
260
|
+
lines.push(` ${mark} ${check.name}${check.detail ? ` (${check.detail})` : ""}`);
|
|
261
|
+
if (!check.ok && check.cost)
|
|
262
|
+
lines.push(` without it: ${check.cost}`);
|
|
263
|
+
}
|
|
264
|
+
if (result.error)
|
|
265
|
+
lines.push(` error: ${result.error}`);
|
|
266
|
+
return lines.join("\n");
|
|
267
|
+
}
|
|
268
|
+
function finish(checks, echoFrames, error, extra = []) {
|
|
269
|
+
const all = [...checks, ...extra];
|
|
270
|
+
return {
|
|
271
|
+
checks: all,
|
|
272
|
+
echoFrames,
|
|
273
|
+
error: error || undefined,
|
|
274
|
+
ok: all.every((check) => check.required === false || check.ok),
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
const TIMED_OUT = Symbol("standin.smokeTimeout");
|
|
278
|
+
async function withTimeout(work, ms) {
|
|
279
|
+
let timer;
|
|
280
|
+
try {
|
|
281
|
+
return await Promise.race([
|
|
282
|
+
work,
|
|
283
|
+
new Promise((_, reject) => {
|
|
284
|
+
timer = setTimeout(() => reject(TIMED_OUT), ms);
|
|
285
|
+
}),
|
|
286
|
+
]);
|
|
287
|
+
}
|
|
288
|
+
finally {
|
|
289
|
+
if (timer !== undefined)
|
|
290
|
+
clearTimeout(timer);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
//# sourceMappingURL=smoke.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"smoke.js","sourceRoot":"","sources":["../src/smoke.ts"],"names":[],"mappings":"AAAA,oCAAoC;AACpC,+BAA+B;AAE/B;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,OAAO,SAAS,MAAM,IAAI,CAAC;AAE3B,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,KAAK,EACL,aAAa,GACd,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAElC,0EAA0E;AAC1E,MAAM,CAAC,MAAM,QAAQ,GAAG,EAAE,CAAC;AAC3B,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,CAAC,MAAM,GAAG,QAAQ,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC;AAE5D;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC;AACrC,MAAM,CAAC,MAAM,kBAAkB,GAAG,KAAK,CAAC;AAiCxC;;;;;GAKG;AACH,MAAM,OAAO,aAAa;IACxB,UAAU,GAAG,CAAC,CAAC;IACN,IAAI,CAAS;IACb,OAAO,CAAS;IAChB,OAAO,CAAS;IAChB,OAAO,CAAS;IAChB,iBAAiB,CAAS;IAEnC,YACE,GAAW,EACX,MAAc,EACd,MAAc,EACd,MAAM,GAAG,EAAE,EACX,gBAAgB,GAAG,kBAAkB;QAErC,IAAI,CAAC,IAAI,GAAG,GAAG,CAAC;QAChB,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;QACtB,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;QACtB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;QACnC,IAAI,CAAC,iBAAiB,GAAG,gBAAgB,CAAC;IAC5C,CAAC;IAED,uEAAuE;IACvE,KAAK,CAAC,GAAG;QACP,2EAA2E;QAC3E,2EAA2E;QAC3E,kCAAkC;QAClC,MAAM,KAAK,GAAG,KAAK,EAAE,CAAC;QACtB,MAAM,EAAE,GAAG,IAAI,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE;YAClC,OAAO,EAAE;gBACP,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC,KAAK,CAAC;gBACjC,CAAC,gBAAgB,CAAC,EAAE,aAAa,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC;aACrE;YACD,gBAAgB,EAAE,IAAI,CAAC,iBAAiB;SACzC,CAAC,CAAC;QACH,EAAE,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,EAAE;YACxB,IAAI,CAAC;gBACH,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,KAAK,aAAa;oBACjD,IAAI,CAAC,UAAU,IAAI,CAAC,CAAC;YACzB,CAAC;YAAC,MAAM,CAAC;gBACP,kEAAkE;YACpE,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC;YACH,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;gBAC1C,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;gBACzB,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;gBACzB,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,IAAY,EAAE,EAAE,CAChC,MAAM,CAAC,IAAI,KAAK,CAAC,0BAA0B,IAAI,EAAE,CAAC,CAAC,CACpD,CAAC;YACJ,CAAC,CAAC,CAAC;YACH,EAAE,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC;YAC/B,EAAE,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC;YAE/B,IAAI,MAAM,GAAG,EAAE,CAAC;YAChB,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAY,EAAE,EAAE;gBAC9B,MAAM,GAAG,0BAA0B,IAAI,EAAE,CAAC;YAC5C,CAAC,CAAC,CAAC;YACH,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAU,EAAE,EAAE;gBAC5B,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;YACvB,CAAC,CAAC,CAAC;YAEH,EAAE,CAAC,IAAI,CACL,IAAI,CAAC,SAAS,CAAC;gBACb,IAAI,EAAE,eAAe;gBACrB,qEAAqE;gBACrE,oEAAoE;gBACpE,mBAAmB;gBACnB,MAAM,EAAE,IAAI,CAAC,OAAO;gBACpB,QAAQ,EAAE,EAAE;gBACZ,SAAS,EAAE,SAAS;gBACpB,MAAM,EAAE,EAAE,WAAW,EAAE,qBAAqB,EAAE;aAC/C,CAAC,CACH,CAAC;YACF,uEAAuE;YACvE,0EAA0E;YAC1E,YAAY;YACZ,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,kBAAkB,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC;YAExE,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;YAC7D,KAAK,IAAI,GAAG,GAAG,CAAC,EAAE,GAAG,GAAG,IAAI,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC;gBAC/C,IAAI,MAAM,KAAK,EAAE;oBAAE,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC;gBAC3C,EAAE,CAAC,IAAI,CACL,IAAI,CAAC,SAAS,CAAC;oBACb,IAAI,EAAE,aAAa;oBACnB,GAAG;oBACH,WAAW,EAAE,GAAG,GAAG,QAAQ;oBAC3B,aAAa,EAAE,OAAO;iBACvB,CAAC,CACH,CAAC;gBACF,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;YAChE,CAAC;YACD,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC;YACvE,iEAAiE;YACjE,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC;QACpE,CAAC;gBAAS,CAAC;YACT,wEAAwE;YACxE,mDAAmD;YACnD,IAAI,CAAC;gBACH,EAAE,CAAC,KAAK,EAAE,CAAC;YACb,CAAC;YAAC,MAAM,CAAC;gBACP,oCAAoC;YACtC,CAAC;QACH,CAAC;IACH,CAAC;CACF;AAKD;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,cAA8B,EAC9B,MAAM,GAAG,EAAE,EACX,KAAmB,EACnB,SAAS,GAAG,cAAc;IAE1B,MAAM,MAAM,GAAiB,EAAE,CAAC;IAChC,IAAI,KAAK,GAAG,EAAE,CAAC;IACf,IAAI,UAAU,GAAG,CAAC,CAAC;IAEnB,sEAAsE;IACtE,6EAA6E;IAC7E,8EAA8E;IAC9E,6EAA6E;IAC7E,sBAAsB;IACtB,MAAM,MAAM,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC/C,MAAM,CAAC,IAAI,CAAC;QACV,IAAI,EAAE,QAAQ;QACd,EAAE,EAAE,IAAI;QACR,MAAM,EAAE,wBAAwB;QAChC,IAAI,EAAE,4BAA4B;KACnC,CAAC,CAAC;IAEH,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC;QAC5B,cAAc;QACd,MAAM;QACN,IAAI,EAAE,WAAW;QACjB,IAAI,EAAE,CAAC;QACP,iBAAiB,EAAE,CAAC;KACrB,CAAC,CAAC;IACH,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE;YACpC;gBACE,IAAI,EAAE,UAAU;gBAChB,EAAE,EAAE,KAAK;gBACT,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC;gBACnB,IAAI,EAAE,sCAAsC;aAC7C;SACF,CAAC,CAAC;IACL,CAAC;IACD,MAAM,CAAC,IAAI,CAAC;QACV,IAAI,EAAE,UAAU;QAChB,EAAE,EAAE,IAAI;QACR,MAAM,EAAE,aAAa,MAAM,CAAC,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE;QAClD,IAAI,EAAE,sCAAsC;KAC7C,CAAC,CAAC;IAEH,MAAM,MAAM,GAAG,SAAS,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;IACzD,MAAM,GAAG,GAAG,kBAAkB,MAAM,CAAC,IAAI,GAAG,MAAM,CAAC,MAAM,IAAI,MAAM,EAAE,CAAC;IACtE,MAAM,IAAI,GAAG,IAAI,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5D,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC3B,IAAI,CAAC;QACH,MAAM,WAAW,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,SAAS,CAAC,CAAC;QACzC,MAAM,CAAC,IAAI,CAAC;YACV,IAAI,EAAE,MAAM;YACZ,EAAE,EAAE,IAAI;YACR,MAAM,EAAE,GAAG,MAAM,cAAc,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,KAAK;YACxD,IAAI,EAAE,sBAAsB;SAC7B,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,QAAQ,GAAG,GAAG,KAAK,SAAS,CAAC;QACnC,KAAK,GAAG,QAAQ;YACd,CAAC,CAAC,kCAAkC,SAAS,KAAK;YAClD,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAChB,MAAM,CAAC,IAAI,CAAC;YACV,IAAI,EAAE,MAAM;YACZ,EAAE,EAAE,KAAK;YACT,MAAM,EAAE,KAAK;YACb,uEAAuE;YACvE,qEAAqE;YACrE,IAAI,EAAE,QAAQ;gBACZ,CAAC,CAAC,qCAAqC;gBACvC,CAAC,CAAC,qCAAqC;SAC1C,CAAC,CAAC;IACL,CAAC;YAAS,CAAC;QACT,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,MAAM,EAAE,CAAC;QACxB,CAAC;QAAC,MAAM,CAAC;YACP,qEAAqE;QACvE,CAAC;IACH,CAAC;IAED,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC;IAC7B,MAAM,CAAC,IAAI,CAAC;QACV,IAAI,EAAE,OAAO;QACb,EAAE,EAAE,UAAU,GAAG,CAAC;QAClB,MAAM,EAAE,GAAG,UAAU,mBAAmB;QACxC,IAAI,EAAE,+BAA+B;KACtC,CAAC,CAAC;IAEH,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,IAAI,CAAC;YACH,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,KAAK,EAAE,CAAC,CAAC,CAAC;QAClC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,CAAC,KAAK,CAAC,yCAAyC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACrE,sEAAsE;YACtE,4DAA4D;YAC5D,MAAM,CAAC,IAAI,CAAC;gBACV,IAAI,EAAE,eAAe;gBACrB,EAAE,EAAE,KAAK;gBACT,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC;gBACnB,IAAI,EAAE,sCAAsC;gBAC5C,QAAQ,EAAE,KAAK;aAChB,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,CAAC,CAAC;AAC3C,CAAC;AAED,wCAAwC;AACxC,MAAM,UAAU,MAAM,CAAC,MAAmB;IACxC,MAAM,KAAK,GAAG,CAAC,YAAY,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC1D,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QAClC,MAAM,IAAI,GAAG,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC;QACxC,KAAK,CAAC,IAAI,CACR,KAAK,IAAI,IAAI,KAAK,CAAC,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CACrE,CAAC;QACF,IAAI,CAAC,KAAK,CAAC,EAAE,IAAI,KAAK,CAAC,IAAI;YAAE,KAAK,CAAC,IAAI,CAAC,sBAAsB,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;IAC9E,CAAC;IACD,IAAI,MAAM,CAAC,KAAK;QAAE,KAAK,CAAC,IAAI,CAAC,YAAY,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;IACzD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,SAAS,MAAM,CACb,MAAoB,EACpB,UAAkB,EAClB,KAAa,EACb,QAAsB,EAAE;IAExB,MAAM,GAAG,GAAG,CAAC,GAAG,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC;IAClC,OAAO;QACL,MAAM,EAAE,GAAG;QACX,UAAU;QACV,KAAK,EAAE,KAAK,IAAI,SAAS;QACzB,EAAE,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,KAAK,KAAK,IAAI,KAAK,CAAC,EAAE,CAAC;KAC/D,CAAC;AACJ,CAAC;AAED,MAAM,SAAS,GAAG,MAAM,CAAC,sBAAsB,CAAC,CAAC;AAEjD,KAAK,UAAU,WAAW,CAAI,IAAgB,EAAE,EAAU;IACxD,IAAI,KAAiC,CAAC;IACtC,IAAI,CAAC;QACH,OAAO,MAAM,OAAO,CAAC,IAAI,CAAC;YACxB,IAAI;YACJ,IAAI,OAAO,CAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE;gBAC/B,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,EAAE,CAAC,CAAC;YAClD,CAAC,CAAC;SACH,CAAC,CAAC;IACL,CAAC;YAAS,CAAC;QACT,IAAI,KAAK,KAAK,SAAS;YAAE,YAAY,CAAC,KAAK,CAAC,CAAC;IAC/C,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What arrived before your agent was ready to hear it.
|
|
3
|
+
*
|
|
4
|
+
* A call starts the moment StandIn dials. Your agent starts a little later: a
|
|
5
|
+
* socket has to open, a room has to be joined, a session has to be configured.
|
|
6
|
+
* Everything the caller does in that gap still arrives, and if nothing holds it,
|
|
7
|
+
* it is gone.
|
|
8
|
+
*
|
|
9
|
+
* Two things arrive in that gap and both matter:
|
|
10
|
+
*
|
|
11
|
+
* **The caller's first words.** People start talking the instant the call
|
|
12
|
+
* connects, and often the first thing they say is the reason they called. Drop
|
|
13
|
+
* it and the agent opens by asking a question that was already answered.
|
|
14
|
+
*
|
|
15
|
+
* **The first context.** The "there are three people here, stay quiet unless
|
|
16
|
+
* addressed" sentence and the recording-status change both land within the first
|
|
17
|
+
* moment of a meeting. Drop those and a group-call gate never engages and a
|
|
18
|
+
* recording gate never opens.
|
|
19
|
+
*
|
|
20
|
+
* This exists because every plugin was solving half of it. Of the nine plugins
|
|
21
|
+
* in this SDK, two held both, and five held one and silently lost the other.
|
|
22
|
+
*
|
|
23
|
+
* Bounded on purpose. A socket that never opens must not grow a buffer for the
|
|
24
|
+
* length of the call, so the oldest entries are dropped rather than the newest.
|
|
25
|
+
*
|
|
26
|
+
* Identical in shape to the Python SDK's `standin.startup`.
|
|
27
|
+
*/
|
|
28
|
+
/**
|
|
29
|
+
* About four seconds of speech at the wire's frame size. Enough to hold an
|
|
30
|
+
* opening sentence, far short of enough to hide a socket that never opened.
|
|
31
|
+
*/
|
|
32
|
+
export declare const MAX_PENDING_AUDIO = 200;
|
|
33
|
+
/** Context sentences are rare and each one is small. This is generous. */
|
|
34
|
+
export declare const MAX_PENDING_CONTEXT = 20;
|
|
35
|
+
/**
|
|
36
|
+
* Holds caller audio and call context until the agent can take them.
|
|
37
|
+
*
|
|
38
|
+
* Order is preserved within each lane, and audio is released before context,
|
|
39
|
+
* because the provider needs the caller's words in the order they were said and
|
|
40
|
+
* the context is a note about the call rather than part of the conversation.
|
|
41
|
+
*/
|
|
42
|
+
export declare class StartupBuffer {
|
|
43
|
+
#private;
|
|
44
|
+
constructor(maxAudio?: number, maxContext?: number);
|
|
45
|
+
/** Whether the agent is still being set up. */
|
|
46
|
+
get holding(): boolean;
|
|
47
|
+
/**
|
|
48
|
+
* How much was lost to the bounds.
|
|
49
|
+
*
|
|
50
|
+
* Worth logging when it is not zero: it means the agent took long enough to
|
|
51
|
+
* start that the caller outran it.
|
|
52
|
+
*/
|
|
53
|
+
get dropped(): {
|
|
54
|
+
audio: number;
|
|
55
|
+
context: number;
|
|
56
|
+
};
|
|
57
|
+
/** Hold one frame of the caller's voice. */
|
|
58
|
+
audio(pcm: Buffer): void;
|
|
59
|
+
/** Hold one line of call context. */
|
|
60
|
+
context(text: string): void;
|
|
61
|
+
/**
|
|
62
|
+
* Hand everything over, in order, and stop holding.
|
|
63
|
+
*
|
|
64
|
+
* Both callbacks may be sync or async, because a provider's send is often
|
|
65
|
+
* fire-and-forget. Safe to call twice: the second call releases nothing.
|
|
66
|
+
*/
|
|
67
|
+
release(sendAudio?: (pcm: Buffer) => void | Promise<void>, sendContext?: (text: string) => void | Promise<void>): Promise<{
|
|
68
|
+
audio: number;
|
|
69
|
+
context: number;
|
|
70
|
+
}>;
|
|
71
|
+
/** Throw it away. For a call that ended before the agent was ready. */
|
|
72
|
+
discard(): void;
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=startup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"startup.d.ts","sourceRoot":"","sources":["../src/startup.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH;;;GAGG;AACH,eAAO,MAAM,iBAAiB,MAAM,CAAC;AAErC,0EAA0E;AAC1E,eAAO,MAAM,mBAAmB,KAAK,CAAC;AAEtC;;;;;;GAMG;AACH,qBAAa,aAAa;;gBASZ,QAAQ,SAAoB,EAAE,UAAU,SAAsB;IAK1E,+CAA+C;IAC/C,IAAI,OAAO,IAAI,OAAO,CAErB;IAED;;;;;OAKG;IACH,IAAI,OAAO,IAAI;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAEhD;IAED,4CAA4C;IAC5C,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IASxB,qCAAqC;IACrC,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAS3B;;;;;OAKG;IACG,OAAO,CACX,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,EACjD,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GACnD,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAS9C,uEAAuE;IACvE,OAAO,IAAI,IAAI;CAKhB"}
|