getpatter 0.6.4 → 0.6.6
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/dist/{chunk-7IIV3BY4.mjs → chunk-YJX2EKON.mjs} +658 -79
- package/dist/cli.js +492 -2
- package/dist/index.d.mts +607 -6
- package/dist/index.d.ts +607 -6
- package/dist/index.js +1839 -189
- package/dist/index.mjs +1114 -70
- package/dist/{test-mode-4QLLWYVV.mjs → test-mode-XFOADUNE.mjs} +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -57,7 +57,7 @@ import {
|
|
|
57
57
|
openclawPostCallNotifier,
|
|
58
58
|
resolveLogRoot,
|
|
59
59
|
startSpan
|
|
60
|
-
} from "./chunk-
|
|
60
|
+
} from "./chunk-YJX2EKON.mjs";
|
|
61
61
|
import {
|
|
62
62
|
OpenAIRealtime2Adapter,
|
|
63
63
|
OpenAIRealtimeAdapter,
|
|
@@ -106,6 +106,57 @@ init_esm_shims();
|
|
|
106
106
|
// src/client.ts
|
|
107
107
|
init_esm_shims();
|
|
108
108
|
|
|
109
|
+
// src/telephony/twilio.ts
|
|
110
|
+
init_esm_shims();
|
|
111
|
+
var Carrier2 = class {
|
|
112
|
+
kind = "twilio";
|
|
113
|
+
accountSid;
|
|
114
|
+
authToken;
|
|
115
|
+
constructor(opts = {}) {
|
|
116
|
+
const sid = opts.accountSid ?? process.env.TWILIO_ACCOUNT_SID;
|
|
117
|
+
const tok = opts.authToken ?? process.env.TWILIO_AUTH_TOKEN;
|
|
118
|
+
if (!sid) {
|
|
119
|
+
throw new Error(
|
|
120
|
+
"Twilio carrier requires accountSid. Pass { accountSid: 'AC...' } or set TWILIO_ACCOUNT_SID in the environment."
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
if (!tok) {
|
|
124
|
+
throw new Error(
|
|
125
|
+
"Twilio carrier requires authToken. Pass { authToken: '...' } or set TWILIO_AUTH_TOKEN in the environment."
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
this.accountSid = sid;
|
|
129
|
+
this.authToken = tok;
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
// src/telephony/telnyx.ts
|
|
134
|
+
init_esm_shims();
|
|
135
|
+
var Carrier3 = class {
|
|
136
|
+
kind = "telnyx";
|
|
137
|
+
apiKey;
|
|
138
|
+
connectionId;
|
|
139
|
+
publicKey;
|
|
140
|
+
constructor(opts = {}) {
|
|
141
|
+
const key = opts.apiKey ?? process.env.TELNYX_API_KEY;
|
|
142
|
+
const conn = opts.connectionId ?? process.env.TELNYX_CONNECTION_ID;
|
|
143
|
+
const pub = opts.publicKey ?? process.env.TELNYX_PUBLIC_KEY;
|
|
144
|
+
if (!key) {
|
|
145
|
+
throw new Error(
|
|
146
|
+
"Telnyx carrier requires apiKey. Pass { apiKey: '...' } or set TELNYX_API_KEY in the environment."
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
if (!conn) {
|
|
150
|
+
throw new Error(
|
|
151
|
+
"Telnyx carrier requires connectionId. Pass { connectionId: '...' } or set TELNYX_CONNECTION_ID in the environment."
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
this.apiKey = key;
|
|
155
|
+
this.connectionId = conn;
|
|
156
|
+
this.publicKey = pub;
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
|
|
109
160
|
// src/engines/openai.ts
|
|
110
161
|
init_esm_shims();
|
|
111
162
|
var Realtime = class {
|
|
@@ -324,6 +375,566 @@ function validateAllToolSchemas(tools) {
|
|
|
324
375
|
}
|
|
325
376
|
}
|
|
326
377
|
|
|
378
|
+
// src/telemetry/index.ts
|
|
379
|
+
init_esm_shims();
|
|
380
|
+
|
|
381
|
+
// src/telemetry/client.ts
|
|
382
|
+
init_esm_shims();
|
|
383
|
+
|
|
384
|
+
// src/telemetry/consent.ts
|
|
385
|
+
init_esm_shims();
|
|
386
|
+
|
|
387
|
+
// src/telemetry/env.ts
|
|
388
|
+
init_esm_shims();
|
|
389
|
+
var CI_ENV_VARS = [
|
|
390
|
+
"CI",
|
|
391
|
+
"CONTINUOUS_INTEGRATION",
|
|
392
|
+
"GITHUB_ACTIONS",
|
|
393
|
+
"GITLAB_CI",
|
|
394
|
+
"TRAVIS",
|
|
395
|
+
"CIRCLECI",
|
|
396
|
+
"APPVEYOR",
|
|
397
|
+
"TF_BUILD",
|
|
398
|
+
"TEAMCITY_VERSION",
|
|
399
|
+
"BUILDKITE",
|
|
400
|
+
"DRONE",
|
|
401
|
+
"JENKINS_URL",
|
|
402
|
+
"HUDSON_URL",
|
|
403
|
+
"BAMBOO_BUILDKEY",
|
|
404
|
+
"CODEBUILD_BUILD_ID"
|
|
405
|
+
];
|
|
406
|
+
var TEST_ENV_VARS = ["VITEST", "JEST_WORKER_ID"];
|
|
407
|
+
function isTruthy(value) {
|
|
408
|
+
if (value === void 0) return false;
|
|
409
|
+
const v = value.trim().toLowerCase();
|
|
410
|
+
return v !== "" && v !== "0" && v !== "false" && v !== "no" && v !== "off";
|
|
411
|
+
}
|
|
412
|
+
function isCi() {
|
|
413
|
+
return CI_ENV_VARS.some((name) => isTruthy(process.env[name]));
|
|
414
|
+
}
|
|
415
|
+
function isTest() {
|
|
416
|
+
if (TEST_ENV_VARS.some((name) => process.env[name] !== void 0)) return true;
|
|
417
|
+
const node = (process.env.NODE_ENV ?? "").trim().toLowerCase();
|
|
418
|
+
const patter = (process.env.PATTER_ENV ?? "").trim().toLowerCase();
|
|
419
|
+
return node === "test" || patter === "test";
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// src/telemetry/install-id.ts
|
|
423
|
+
init_esm_shims();
|
|
424
|
+
import { randomUUID } from "crypto";
|
|
425
|
+
import * as fs from "fs";
|
|
426
|
+
import * as os from "os";
|
|
427
|
+
import * as path from "path";
|
|
428
|
+
var RUN_ID = randomUUID().replace(/-/g, "");
|
|
429
|
+
var HEX32 = /^[0-9a-f]{32}$/;
|
|
430
|
+
var VERSION_RE = /^[0-9][0-9a-z.+-]{0,31}$/;
|
|
431
|
+
var cachedInstallId = null;
|
|
432
|
+
function runId() {
|
|
433
|
+
return RUN_ID;
|
|
434
|
+
}
|
|
435
|
+
function statePath() {
|
|
436
|
+
const base = process.env.PATTER_TELEMETRY_STATE_DIR || process.env.XDG_STATE_HOME;
|
|
437
|
+
const root = base && base.length > 0 ? base : path.join(os.homedir(), ".getpatter");
|
|
438
|
+
return path.join(root, "install-id");
|
|
439
|
+
}
|
|
440
|
+
function installId() {
|
|
441
|
+
if (cachedInstallId !== null) return cachedInstallId;
|
|
442
|
+
const p = statePath();
|
|
443
|
+
try {
|
|
444
|
+
const existing = fs.readFileSync(p, "utf8").trim();
|
|
445
|
+
if (HEX32.test(existing)) {
|
|
446
|
+
cachedInstallId = existing;
|
|
447
|
+
return cachedInstallId;
|
|
448
|
+
}
|
|
449
|
+
} catch {
|
|
450
|
+
}
|
|
451
|
+
const newId = randomUUID().replace(/-/g, "");
|
|
452
|
+
try {
|
|
453
|
+
fs.mkdirSync(path.dirname(p), { recursive: true });
|
|
454
|
+
fs.writeFileSync(p, newId, "utf8");
|
|
455
|
+
cachedInstallId = newId;
|
|
456
|
+
} catch {
|
|
457
|
+
cachedInstallId = RUN_ID;
|
|
458
|
+
}
|
|
459
|
+
return cachedInstallId;
|
|
460
|
+
}
|
|
461
|
+
function versionPath() {
|
|
462
|
+
return path.join(path.dirname(statePath()), "version");
|
|
463
|
+
}
|
|
464
|
+
function previousVersion(current) {
|
|
465
|
+
const p = versionPath();
|
|
466
|
+
let prev = "";
|
|
467
|
+
try {
|
|
468
|
+
prev = fs.readFileSync(p, "utf8").trim();
|
|
469
|
+
} catch {
|
|
470
|
+
prev = "";
|
|
471
|
+
}
|
|
472
|
+
try {
|
|
473
|
+
fs.mkdirSync(path.dirname(p), { recursive: true });
|
|
474
|
+
fs.writeFileSync(p, current, "utf8");
|
|
475
|
+
} catch {
|
|
476
|
+
}
|
|
477
|
+
return VERSION_RE.test(prev) ? prev : "";
|
|
478
|
+
}
|
|
479
|
+
function daysSinceInstallBucket() {
|
|
480
|
+
let mtimeMs;
|
|
481
|
+
try {
|
|
482
|
+
mtimeMs = fs.statSync(statePath()).mtimeMs;
|
|
483
|
+
} catch {
|
|
484
|
+
return "0";
|
|
485
|
+
}
|
|
486
|
+
const days = Math.max(0, Math.floor((Date.now() - mtimeMs) / 864e5));
|
|
487
|
+
if (days === 0) return "0";
|
|
488
|
+
if (days <= 7) return "1_7";
|
|
489
|
+
if (days <= 30) return "8_30";
|
|
490
|
+
return "30_plus";
|
|
491
|
+
}
|
|
492
|
+
function firstRunPath() {
|
|
493
|
+
return path.join(path.dirname(statePath()), "first-run");
|
|
494
|
+
}
|
|
495
|
+
function isFirstRun() {
|
|
496
|
+
const p = firstRunPath();
|
|
497
|
+
try {
|
|
498
|
+
if (fs.existsSync(p)) return false;
|
|
499
|
+
} catch {
|
|
500
|
+
return false;
|
|
501
|
+
}
|
|
502
|
+
try {
|
|
503
|
+
fs.mkdirSync(path.dirname(p), { recursive: true });
|
|
504
|
+
fs.writeFileSync(p, "1", "utf8");
|
|
505
|
+
return true;
|
|
506
|
+
} catch {
|
|
507
|
+
return false;
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
function optOutPath() {
|
|
511
|
+
return path.join(path.dirname(statePath()), "telemetry-disabled");
|
|
512
|
+
}
|
|
513
|
+
function isOptedOut() {
|
|
514
|
+
try {
|
|
515
|
+
return fs.existsSync(optOutPath());
|
|
516
|
+
} catch {
|
|
517
|
+
return false;
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// src/telemetry/consent.ts
|
|
522
|
+
function isEnabled(flag) {
|
|
523
|
+
if (isTruthy(process.env.DO_NOT_TRACK)) return false;
|
|
524
|
+
if (isTruthy(process.env.PATTER_TELEMETRY_DISABLED)) return false;
|
|
525
|
+
if (isOptedOut()) return false;
|
|
526
|
+
if (flag === false) return false;
|
|
527
|
+
if (isCi() || isTest()) return false;
|
|
528
|
+
return true;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
// src/telemetry/events.ts
|
|
532
|
+
init_esm_shims();
|
|
533
|
+
import * as os2 from "os";
|
|
534
|
+
|
|
535
|
+
// src/telemetry/stack.ts
|
|
536
|
+
init_esm_shims();
|
|
537
|
+
var STACK_VENDORS = /* @__PURE__ */ new Set([
|
|
538
|
+
"openai",
|
|
539
|
+
"anthropic",
|
|
540
|
+
"google",
|
|
541
|
+
"cerebras",
|
|
542
|
+
"groq",
|
|
543
|
+
"deepgram",
|
|
544
|
+
"elevenlabs",
|
|
545
|
+
"cartesia",
|
|
546
|
+
"whisper",
|
|
547
|
+
"soniox",
|
|
548
|
+
"assemblyai",
|
|
549
|
+
"speechmatics",
|
|
550
|
+
"lmnt",
|
|
551
|
+
"rime",
|
|
552
|
+
"inworld",
|
|
553
|
+
"telnyx",
|
|
554
|
+
"other"
|
|
555
|
+
]);
|
|
556
|
+
var VENDOR_ALIASES = {
|
|
557
|
+
cartesia_stt: "cartesia",
|
|
558
|
+
cartesia_tts: "cartesia",
|
|
559
|
+
openai_tts: "openai",
|
|
560
|
+
openai_transcribe: "openai",
|
|
561
|
+
elevenlabs_ws: "elevenlabs",
|
|
562
|
+
telnyx_stt: "telnyx",
|
|
563
|
+
telnyx_tts: "telnyx"
|
|
564
|
+
};
|
|
565
|
+
var RAW_UNSAFE_RE = /[^a-z0-9._-]/;
|
|
566
|
+
var DATE_SUFFIX_RE = /-\d{8}$/;
|
|
567
|
+
function vendorOf(providerKey) {
|
|
568
|
+
if (!providerKey) return "other";
|
|
569
|
+
const v = VENDOR_ALIASES[providerKey] ?? providerKey;
|
|
570
|
+
return STACK_VENDORS.has(v) ? v : "other";
|
|
571
|
+
}
|
|
572
|
+
function modelToken(vendor, rawModel) {
|
|
573
|
+
if (!rawModel) return `${vendor}-other`;
|
|
574
|
+
const m = rawModel.trim().toLowerCase();
|
|
575
|
+
if (m.length > 40 || RAW_UNSAFE_RE.test(m)) return `${vendor}-other`;
|
|
576
|
+
const token = m.replace(/_/g, "-").replace(DATE_SUFFIX_RE, "").replace(/^[-.]+|[-.]+$/g, "");
|
|
577
|
+
return token ? `${vendor}-${token}` : `${vendor}-other`;
|
|
578
|
+
}
|
|
579
|
+
function readProviderKey(obj) {
|
|
580
|
+
const ctor = obj?.constructor;
|
|
581
|
+
const key = ctor?.providerKey;
|
|
582
|
+
return typeof key === "string" && key ? key : null;
|
|
583
|
+
}
|
|
584
|
+
function readModel(obj) {
|
|
585
|
+
const rec = obj;
|
|
586
|
+
for (const attr of ["model", "modelId", "_model"]) {
|
|
587
|
+
const v = rec?.[attr];
|
|
588
|
+
if (typeof v === "string" && v) return v;
|
|
589
|
+
}
|
|
590
|
+
return "";
|
|
591
|
+
}
|
|
592
|
+
function layerDims(obj, providerField, modelField) {
|
|
593
|
+
if (obj === null || obj === void 0) return {};
|
|
594
|
+
const vendor = vendorOf(readProviderKey(obj));
|
|
595
|
+
return { [providerField]: vendor, [modelField]: modelToken(vendor, readModel(obj)) };
|
|
596
|
+
}
|
|
597
|
+
function stackDimensions(stt, tts, llm) {
|
|
598
|
+
return {
|
|
599
|
+
...layerDims(stt, "stt_provider", "stt_model"),
|
|
600
|
+
...layerDims(tts, "tts_provider", "tts_model"),
|
|
601
|
+
...layerDims(llm, "llm_provider", "llm_model")
|
|
602
|
+
};
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
// src/telemetry/events.ts
|
|
606
|
+
var SCHEMA_VERSION = 5;
|
|
607
|
+
var EVENT_SDK_INITIALIZED = "sdk_initialized";
|
|
608
|
+
var EVENT_FIRST_RUN = "first_run";
|
|
609
|
+
var EVENT_CLI_COMMAND = "cli_command";
|
|
610
|
+
var EVENT_FEATURE_USED = "feature_used";
|
|
611
|
+
var EVENT_AGENT_CONFIGURED = "agent_configured";
|
|
612
|
+
var EVENT_CALL_STARTED = "call_started";
|
|
613
|
+
var EVENT_CALL_COMPLETED = "call_completed";
|
|
614
|
+
var ALLOWED_EVENTS = /* @__PURE__ */ new Set([
|
|
615
|
+
EVENT_SDK_INITIALIZED,
|
|
616
|
+
EVENT_FIRST_RUN,
|
|
617
|
+
EVENT_CLI_COMMAND,
|
|
618
|
+
EVENT_FEATURE_USED,
|
|
619
|
+
EVENT_AGENT_CONFIGURED,
|
|
620
|
+
EVENT_CALL_STARTED,
|
|
621
|
+
EVENT_CALL_COMPLETED
|
|
622
|
+
]);
|
|
623
|
+
var DIMENSION_VALUES = {
|
|
624
|
+
carrier: /* @__PURE__ */ new Set(["twilio", "telnyx", "plivo", "none"]),
|
|
625
|
+
tunnel: /* @__PURE__ */ new Set(["static", "configured", "none"]),
|
|
626
|
+
engine: /* @__PURE__ */ new Set(["realtime", "convai", "pipeline"]),
|
|
627
|
+
provider: /* @__PURE__ */ new Set([
|
|
628
|
+
"openai",
|
|
629
|
+
"elevenlabs",
|
|
630
|
+
"deepgram",
|
|
631
|
+
"cartesia",
|
|
632
|
+
"cerebras",
|
|
633
|
+
"anthropic",
|
|
634
|
+
"google",
|
|
635
|
+
"whisper",
|
|
636
|
+
"other"
|
|
637
|
+
]),
|
|
638
|
+
// agent_configured dimensions
|
|
639
|
+
custom_tool_count_bucket: /* @__PURE__ */ new Set(["0", "1", "2_3", "4_6", "7_12", "13_plus"]),
|
|
640
|
+
integration: /* @__PURE__ */ new Set(["openclaw", "mcp", "hermes", "other", "none"]),
|
|
641
|
+
integration_kind: /* @__PURE__ */ new Set(["consult", "mcp", "none"]),
|
|
642
|
+
mcp_server_count_bucket: /* @__PURE__ */ new Set(["0", "1", "2_3", "4_plus"]),
|
|
643
|
+
// call_started / call_completed: inbound vs outbound — a core usage split.
|
|
644
|
+
direction: /* @__PURE__ */ new Set(["inbound", "outbound", "none"]),
|
|
645
|
+
// cli_command: which CLI subcommand was invoked (never args/flags values).
|
|
646
|
+
cli_command: /* @__PURE__ */ new Set(["dashboard", "eval", "telemetry", "none", "other"]),
|
|
647
|
+
// call_completed: the call's terminal outcome
|
|
648
|
+
outcome: /* @__PURE__ */ new Set(["completed", "error", "no_answer", "busy", "failed"]),
|
|
649
|
+
// call_completed: terminal error code (mirrors ErrorCode, plus "other"). Never
|
|
650
|
+
// the error message.
|
|
651
|
+
error_code: /* @__PURE__ */ new Set([
|
|
652
|
+
"config",
|
|
653
|
+
"connection",
|
|
654
|
+
"auth",
|
|
655
|
+
"timeout",
|
|
656
|
+
"rate_limit",
|
|
657
|
+
"webhook_verification",
|
|
658
|
+
"input_validation",
|
|
659
|
+
"provider_error",
|
|
660
|
+
"provision",
|
|
661
|
+
"internal",
|
|
662
|
+
"other"
|
|
663
|
+
]),
|
|
664
|
+
// feature_used (pipeline): per-layer vendor of the composed stack. A
|
|
665
|
+
// providerKey not on the closed allowlist collapses to "other"; an absent layer
|
|
666
|
+
// is omitted (the value set keeps "none" only as a safety token).
|
|
667
|
+
stt_provider: /* @__PURE__ */ new Set([...STACK_VENDORS, "none"]),
|
|
668
|
+
tts_provider: /* @__PURE__ */ new Set([...STACK_VENDORS, "none"]),
|
|
669
|
+
llm_provider: /* @__PURE__ */ new Set([...STACK_VENDORS, "none"]),
|
|
670
|
+
// sdk_initialized: anonymous deploy-shape (presence-only env/file probes).
|
|
671
|
+
invoked_by_agent: /* @__PURE__ */ new Set(["claude", "cursor", "copilot", "gemini", "windsurf", "other", "none"]),
|
|
672
|
+
serverless: /* @__PURE__ */ new Set(["lambda", "cloud_run", "vercel", "azure_functions", "none"]),
|
|
673
|
+
cloud: /* @__PURE__ */ new Set(["aws", "gcp", "azure", "fly", "none"]),
|
|
674
|
+
package_manager: /* @__PURE__ */ new Set(["npm", "pnpm", "yarn", "bun", "pip", "uv", "poetry", "pipenv", "conda", "none"]),
|
|
675
|
+
days_since_install_bucket: /* @__PURE__ */ new Set(["0", "1_7", "8_30", "30_plus"]),
|
|
676
|
+
// agent_configured: feature-adoption (Realtime tuning).
|
|
677
|
+
noise_reduction: /* @__PURE__ */ new Set(["near_field", "far_field", "none"]),
|
|
678
|
+
turn_detection: /* @__PURE__ */ new Set(["default", "custom", "none"]),
|
|
679
|
+
// call_completed: how many conversational turns the call had.
|
|
680
|
+
turn_count_bucket: /* @__PURE__ */ new Set(["0", "1", "2_3", "4_6", "7_12", "13_plus"])
|
|
681
|
+
};
|
|
682
|
+
var NUMERIC_DIMENSIONS = /* @__PURE__ */ new Set([
|
|
683
|
+
"builtin_tool_count",
|
|
684
|
+
"latency_ms",
|
|
685
|
+
"duration_seconds",
|
|
686
|
+
"cost_usd"
|
|
687
|
+
]);
|
|
688
|
+
var STRING_DIMENSIONS = /* @__PURE__ */ new Set([
|
|
689
|
+
"stt_model",
|
|
690
|
+
"tts_model",
|
|
691
|
+
"llm_model",
|
|
692
|
+
"previous_sdk_version"
|
|
693
|
+
]);
|
|
694
|
+
var MODEL_TOKEN_RE = /^[a-z0-9][a-z0-9.-]{0,40}$/;
|
|
695
|
+
var BOOL_DIMENSIONS = /* @__PURE__ */ new Set([
|
|
696
|
+
"container",
|
|
697
|
+
"preambles_used",
|
|
698
|
+
"per_tool_timeouts_set",
|
|
699
|
+
"llm_fallback_configured"
|
|
700
|
+
]);
|
|
701
|
+
var ALLOWED_DIMENSIONS = /* @__PURE__ */ new Set([
|
|
702
|
+
...Object.keys(DIMENSION_VALUES),
|
|
703
|
+
...NUMERIC_DIMENSIONS,
|
|
704
|
+
...STRING_DIMENSIONS,
|
|
705
|
+
...BOOL_DIMENSIONS
|
|
706
|
+
]);
|
|
707
|
+
function osFamily() {
|
|
708
|
+
const p = os2.platform();
|
|
709
|
+
if (p === "win32") return "windows";
|
|
710
|
+
return p || "unknown";
|
|
711
|
+
}
|
|
712
|
+
function arch2() {
|
|
713
|
+
const a = os2.arch();
|
|
714
|
+
if (a === "x64") return "x86_64";
|
|
715
|
+
if (a === "arm64") return "arm64";
|
|
716
|
+
return "other";
|
|
717
|
+
}
|
|
718
|
+
function runtimeVersion() {
|
|
719
|
+
const parts = (process.versions.node ?? "0.0").split(".");
|
|
720
|
+
return `${parts[0] ?? "0"}.${parts[1] ?? "0"}`;
|
|
721
|
+
}
|
|
722
|
+
function buildEvent(name, opts) {
|
|
723
|
+
if (!ALLOWED_EVENTS.has(name)) {
|
|
724
|
+
throw new Error(`unknown telemetry event: ${name}`);
|
|
725
|
+
}
|
|
726
|
+
const event = {
|
|
727
|
+
event: name,
|
|
728
|
+
schema_version: SCHEMA_VERSION,
|
|
729
|
+
run_id: runId(),
|
|
730
|
+
install_id: installId(),
|
|
731
|
+
sdk: "typescript",
|
|
732
|
+
sdk_version: opts.sdkVersion,
|
|
733
|
+
os: osFamily(),
|
|
734
|
+
arch: arch2(),
|
|
735
|
+
runtime: "node",
|
|
736
|
+
runtime_version: runtimeVersion(),
|
|
737
|
+
ci: isCi() || isTest()
|
|
738
|
+
};
|
|
739
|
+
for (const [key, raw] of Object.entries(opts.dimensions ?? {})) {
|
|
740
|
+
if (!ALLOWED_DIMENSIONS.has(key) || raw === null || raw === void 0) {
|
|
741
|
+
continue;
|
|
742
|
+
}
|
|
743
|
+
let value = raw;
|
|
744
|
+
const allowed = DIMENSION_VALUES[key];
|
|
745
|
+
if (allowed && !(typeof value === "string" && allowed.has(value))) {
|
|
746
|
+
value = "other";
|
|
747
|
+
} else if (STRING_DIMENSIONS.has(key)) {
|
|
748
|
+
if (!(typeof value === "string" && MODEL_TOKEN_RE.test(value))) {
|
|
749
|
+
continue;
|
|
750
|
+
}
|
|
751
|
+
} else if (BOOL_DIMENSIONS.has(key) && typeof value !== "boolean") {
|
|
752
|
+
continue;
|
|
753
|
+
}
|
|
754
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
755
|
+
event[key] = value;
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
return event;
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
// src/telemetry/client.ts
|
|
762
|
+
var DEFAULT_ENDPOINT = "https://telemetry.getpatter.com/v1/ingest";
|
|
763
|
+
var TIMEOUT_MS = 3e3;
|
|
764
|
+
var BUFFER_MAX = 256;
|
|
765
|
+
var noticeShown = false;
|
|
766
|
+
var liveClients = /* @__PURE__ */ new Set();
|
|
767
|
+
var exitHookRegistered = false;
|
|
768
|
+
function showNoticeOnce() {
|
|
769
|
+
if (noticeShown) return;
|
|
770
|
+
noticeShown = true;
|
|
771
|
+
getLogger().info(
|
|
772
|
+
"Anonymous usage telemetry is on (no PII, no call content). Collected: a random anonymous install id, SDK version, language, OS family, runtime version, coarse feature flags, the composed stack (provider + model per layer), tool counts, integration category, and per-call duration, latency, cost, and error codes (no call content, no message text). Disable with PATTER_TELEMETRY_DISABLED=1, DO_NOT_TRACK=1, or telemetry: false. Details: https://docs.getpatter.com/telemetry"
|
|
773
|
+
);
|
|
774
|
+
}
|
|
775
|
+
function registerExitHook() {
|
|
776
|
+
if (exitHookRegistered) return;
|
|
777
|
+
exitHookRegistered = true;
|
|
778
|
+
process.once("beforeExit", () => {
|
|
779
|
+
for (const ref of [...liveClients]) {
|
|
780
|
+
const client = ref.deref();
|
|
781
|
+
if (client) void client.close();
|
|
782
|
+
else liveClients.delete(ref);
|
|
783
|
+
}
|
|
784
|
+
});
|
|
785
|
+
}
|
|
786
|
+
var TelemetryClient = class {
|
|
787
|
+
sdkVersion;
|
|
788
|
+
enabledFlag;
|
|
789
|
+
endpoint;
|
|
790
|
+
debug;
|
|
791
|
+
buffer = [];
|
|
792
|
+
flushing = false;
|
|
793
|
+
closed = false;
|
|
794
|
+
selfRef = new WeakRef(this);
|
|
795
|
+
constructor(options) {
|
|
796
|
+
this.sdkVersion = options.sdkVersion;
|
|
797
|
+
this.enabledFlag = isEnabled(options.flag);
|
|
798
|
+
this.endpoint = options.endpoint ?? process.env.PATTER_TELEMETRY_ENDPOINT ?? DEFAULT_ENDPOINT;
|
|
799
|
+
this.debug = isTruthy(process.env.PATTER_TELEMETRY_DEBUG);
|
|
800
|
+
if (this.enabledFlag && !this.debug) {
|
|
801
|
+
showNoticeOnce();
|
|
802
|
+
registerExitHook();
|
|
803
|
+
liveClients.add(this.selfRef);
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
get enabled() {
|
|
807
|
+
return this.enabledFlag;
|
|
808
|
+
}
|
|
809
|
+
/** Enqueue an event. Fire-and-forget; never throws, never blocks. */
|
|
810
|
+
record(name, dimensions) {
|
|
811
|
+
if (!this.enabledFlag || this.closed) return;
|
|
812
|
+
let event;
|
|
813
|
+
try {
|
|
814
|
+
event = buildEvent(name, { sdkVersion: this.sdkVersion, dimensions });
|
|
815
|
+
} catch (err) {
|
|
816
|
+
getLogger().debug("telemetry buildEvent failed", err);
|
|
817
|
+
return;
|
|
818
|
+
}
|
|
819
|
+
if (this.debug) {
|
|
820
|
+
try {
|
|
821
|
+
process.stderr.write(`[patter telemetry] ${JSON.stringify(event)}
|
|
822
|
+
`);
|
|
823
|
+
} catch {
|
|
824
|
+
}
|
|
825
|
+
return;
|
|
826
|
+
}
|
|
827
|
+
try {
|
|
828
|
+
if (this.buffer.length >= BUFFER_MAX) this.buffer.shift();
|
|
829
|
+
this.buffer.push(event);
|
|
830
|
+
this.scheduleFlush();
|
|
831
|
+
} catch (err) {
|
|
832
|
+
getLogger().debug("telemetry enqueue failed", err);
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
/**
|
|
836
|
+
* Schedule a flush of any buffered events. Events recorded before the server
|
|
837
|
+
* is running (e.g. at `new Patter(...)`) sit in the buffer; call this once the
|
|
838
|
+
* server is up so they ship promptly. Cheap when disabled or buffer is empty.
|
|
839
|
+
*/
|
|
840
|
+
flushPending() {
|
|
841
|
+
if (!this.enabledFlag || this.debug) return;
|
|
842
|
+
try {
|
|
843
|
+
this.scheduleFlush();
|
|
844
|
+
} catch (err) {
|
|
845
|
+
getLogger().debug("telemetry flushPending failed", err);
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
/** Flush remaining events (graceful shutdown). Never throws. */
|
|
849
|
+
async close() {
|
|
850
|
+
if (this.closed) return;
|
|
851
|
+
this.closed = true;
|
|
852
|
+
liveClients.delete(this.selfRef);
|
|
853
|
+
if (!this.enabledFlag || this.debug) return;
|
|
854
|
+
try {
|
|
855
|
+
await this.flush();
|
|
856
|
+
} catch (err) {
|
|
857
|
+
getLogger().debug("telemetry close flush failed", err);
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
scheduleFlush() {
|
|
861
|
+
if (this.flushing) return;
|
|
862
|
+
this.flushing = true;
|
|
863
|
+
void this.flush().finally(() => {
|
|
864
|
+
this.flushing = false;
|
|
865
|
+
});
|
|
866
|
+
}
|
|
867
|
+
async flush() {
|
|
868
|
+
if (this.buffer.length === 0) return;
|
|
869
|
+
const events = this.buffer.splice(0, this.buffer.length);
|
|
870
|
+
const controller = new AbortController();
|
|
871
|
+
const timer = setTimeout(() => controller.abort(), TIMEOUT_MS);
|
|
872
|
+
timer.unref?.();
|
|
873
|
+
try {
|
|
874
|
+
await fetch(this.endpoint, {
|
|
875
|
+
method: "POST",
|
|
876
|
+
headers: { "content-type": "application/json" },
|
|
877
|
+
body: JSON.stringify(events),
|
|
878
|
+
signal: controller.signal
|
|
879
|
+
});
|
|
880
|
+
} catch (err) {
|
|
881
|
+
getLogger().debug("telemetry flush failed", err);
|
|
882
|
+
} finally {
|
|
883
|
+
clearTimeout(timer);
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
};
|
|
887
|
+
|
|
888
|
+
// src/telemetry/environment.ts
|
|
889
|
+
init_esm_shims();
|
|
890
|
+
import * as fs2 from "fs";
|
|
891
|
+
var env = process.env;
|
|
892
|
+
function invokedByAgent() {
|
|
893
|
+
if ("CLAUDECODE" in env || "CLAUDE_CODE" in env || "CLAUDE_CODE_ENTRYPOINT" in env)
|
|
894
|
+
return "claude";
|
|
895
|
+
if ("CURSOR_TRACE_ID" in env || "CURSOR_AGENT" in env) return "cursor";
|
|
896
|
+
if ("GITHUB_COPILOT_AGENT" in env || "COPILOT_AGENT_ID" in env) return "copilot";
|
|
897
|
+
if ("GEMINI_CLI" in env || "GEMINI_AGENT" in env) return "gemini";
|
|
898
|
+
if ("WINDSURF" in env || "WINDSURF_AGENT" in env) return "windsurf";
|
|
899
|
+
if ("AIDER" in env || "OPENAI_AGENT" in env) return "other";
|
|
900
|
+
return "none";
|
|
901
|
+
}
|
|
902
|
+
function inContainer() {
|
|
903
|
+
try {
|
|
904
|
+
if (fs2.existsSync("/.dockerenv")) return true;
|
|
905
|
+
} catch {
|
|
906
|
+
}
|
|
907
|
+
if (env.KUBERNETES_SERVICE_HOST) return true;
|
|
908
|
+
try {
|
|
909
|
+
const blob = fs2.readFileSync("/proc/1/cgroup", "utf8");
|
|
910
|
+
return blob.includes("docker") || blob.includes("containerd") || blob.includes("kubepods");
|
|
911
|
+
} catch {
|
|
912
|
+
return false;
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
function serverless() {
|
|
916
|
+
if (env.AWS_LAMBDA_FUNCTION_NAME) return "lambda";
|
|
917
|
+
if (env.K_SERVICE) return "cloud_run";
|
|
918
|
+
if (env.VERCEL) return "vercel";
|
|
919
|
+
if (env.AZURE_FUNCTIONS_ENVIRONMENT || env.FUNCTIONS_WORKER_RUNTIME) return "azure_functions";
|
|
920
|
+
return "none";
|
|
921
|
+
}
|
|
922
|
+
function cloud() {
|
|
923
|
+
if (env.AWS_REGION || env.AWS_EXECUTION_ENV || env.AWS_LAMBDA_FUNCTION_NAME) return "aws";
|
|
924
|
+
if (env.K_SERVICE || env.GOOGLE_CLOUD_PROJECT || env.GCP_PROJECT) return "gcp";
|
|
925
|
+
if (env.WEBSITE_INSTANCE_ID || env.AZURE_FUNCTIONS_ENVIRONMENT) return "azure";
|
|
926
|
+
if (env.FLY_APP_NAME) return "fly";
|
|
927
|
+
return "none";
|
|
928
|
+
}
|
|
929
|
+
function packageManager() {
|
|
930
|
+
const ua = env.npm_config_user_agent ?? "";
|
|
931
|
+
if (ua.startsWith("pnpm")) return "pnpm";
|
|
932
|
+
if (ua.startsWith("yarn")) return "yarn";
|
|
933
|
+
if (ua.startsWith("bun")) return "bun";
|
|
934
|
+
if (ua.startsWith("npm")) return "npm";
|
|
935
|
+
return "none";
|
|
936
|
+
}
|
|
937
|
+
|
|
327
938
|
// src/_speech-events.ts
|
|
328
939
|
init_esm_shims();
|
|
329
940
|
var logger = getLogger();
|
|
@@ -628,6 +1239,79 @@ function closeParkedConnections(slot) {
|
|
|
628
1239
|
}
|
|
629
1240
|
}
|
|
630
1241
|
}
|
|
1242
|
+
function carrierFamily(carrier) {
|
|
1243
|
+
if (carrier instanceof Carrier2) return "twilio";
|
|
1244
|
+
if (carrier instanceof Carrier3) return "telnyx";
|
|
1245
|
+
if (carrier instanceof Carrier) return "plivo";
|
|
1246
|
+
return "none";
|
|
1247
|
+
}
|
|
1248
|
+
function telemetryEngineFamily(opts) {
|
|
1249
|
+
if (opts.engine) {
|
|
1250
|
+
return opts.engine.constructor.name.toLowerCase().includes("convai") ? "convai" : "realtime";
|
|
1251
|
+
}
|
|
1252
|
+
if (opts.provider === "elevenlabs_convai") return "convai";
|
|
1253
|
+
if (opts.provider === "pipeline") return "pipeline";
|
|
1254
|
+
if (opts.provider === "openai_realtime") return "realtime";
|
|
1255
|
+
if (opts.stt || opts.tts) return "pipeline";
|
|
1256
|
+
return "realtime";
|
|
1257
|
+
}
|
|
1258
|
+
function telemetryProviderFamily(family) {
|
|
1259
|
+
if (family === "realtime") return "openai";
|
|
1260
|
+
if (family === "convai") return "elevenlabs";
|
|
1261
|
+
return "other";
|
|
1262
|
+
}
|
|
1263
|
+
function telemetryBucketCustomTools(n) {
|
|
1264
|
+
if (n <= 0) return "0";
|
|
1265
|
+
if (n === 1) return "1";
|
|
1266
|
+
if (n <= 3) return "2_3";
|
|
1267
|
+
if (n <= 6) return "4_6";
|
|
1268
|
+
if (n <= 12) return "7_12";
|
|
1269
|
+
return "13_plus";
|
|
1270
|
+
}
|
|
1271
|
+
function telemetryBucketMcp(n) {
|
|
1272
|
+
if (n <= 0) return "0";
|
|
1273
|
+
if (n === 1) return "1";
|
|
1274
|
+
if (n <= 3) return "2_3";
|
|
1275
|
+
return "4_plus";
|
|
1276
|
+
}
|
|
1277
|
+
function telemetryIntegration(opts) {
|
|
1278
|
+
const nMcp = opts.mcpServers?.length ?? 0;
|
|
1279
|
+
if (nMcp > 0) {
|
|
1280
|
+
return { integration: "mcp", integrationKind: "mcp", mcpBucket: telemetryBucketMcp(nMcp) };
|
|
1281
|
+
}
|
|
1282
|
+
if (opts.consult) {
|
|
1283
|
+
let isOpenclaw = false;
|
|
1284
|
+
const oc = opts.consult.openaiCompatible;
|
|
1285
|
+
if (oc) {
|
|
1286
|
+
const model = oc.model ?? "";
|
|
1287
|
+
const baseUrl = oc.baseUrl ?? "";
|
|
1288
|
+
isOpenclaw = model.startsWith("openclaw/") || baseUrl.includes(":18789");
|
|
1289
|
+
}
|
|
1290
|
+
return {
|
|
1291
|
+
integration: isOpenclaw ? "openclaw" : "other",
|
|
1292
|
+
integrationKind: "consult",
|
|
1293
|
+
mcpBucket: "0"
|
|
1294
|
+
};
|
|
1295
|
+
}
|
|
1296
|
+
return { integration: "none", integrationKind: "none", mcpBucket: "0" };
|
|
1297
|
+
}
|
|
1298
|
+
function telemetryEnvironmentDims() {
|
|
1299
|
+
try {
|
|
1300
|
+
const dims = {
|
|
1301
|
+
invoked_by_agent: invokedByAgent(),
|
|
1302
|
+
container: inContainer(),
|
|
1303
|
+
serverless: serverless(),
|
|
1304
|
+
cloud: cloud(),
|
|
1305
|
+
package_manager: packageManager(),
|
|
1306
|
+
days_since_install_bucket: daysSinceInstallBucket()
|
|
1307
|
+
};
|
|
1308
|
+
const prev = previousVersion(VERSION);
|
|
1309
|
+
if (prev) dims.previous_sdk_version = prev;
|
|
1310
|
+
return dims;
|
|
1311
|
+
} catch {
|
|
1312
|
+
return {};
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
631
1315
|
var Patter = class {
|
|
632
1316
|
localConfig;
|
|
633
1317
|
embeddedServer = null;
|
|
@@ -648,6 +1332,14 @@ var Patter = class {
|
|
|
648
1332
|
* ``Cannot use both tunnel: true and webhookUrl``.
|
|
649
1333
|
*/
|
|
650
1334
|
tunnelOwnsWebhookUrl = false;
|
|
1335
|
+
/**
|
|
1336
|
+
* Anonymous usage telemetry (opt-out, default ON). Separate from
|
|
1337
|
+
* ``./observability`` (user-facing OTel). Fire-and-forget and fail-safe — it
|
|
1338
|
+
* can never block or break a call. See ``./telemetry``.
|
|
1339
|
+
*/
|
|
1340
|
+
telemetry;
|
|
1341
|
+
telemetrySeenEngines = /* @__PURE__ */ new Set();
|
|
1342
|
+
telemetrySeenAgentShapes = /* @__PURE__ */ new Set();
|
|
651
1343
|
/**
|
|
652
1344
|
* Pre-rendered first-message TTS audio per outbound call_id. Populated
|
|
653
1345
|
* by :meth:`call` when ``agent.prewarmFirstMessage`` is true; consumed
|
|
@@ -853,6 +1545,22 @@ var Patter = class {
|
|
|
853
1545
|
openaiKey: options.openaiKey,
|
|
854
1546
|
persistRoot: resolvePersistRoot(options.persist)
|
|
855
1547
|
};
|
|
1548
|
+
this.telemetry = new TelemetryClient({
|
|
1549
|
+
sdkVersion: VERSION,
|
|
1550
|
+
flag: options.telemetry
|
|
1551
|
+
});
|
|
1552
|
+
const initDims = {
|
|
1553
|
+
carrier: carrierFamily(carrier),
|
|
1554
|
+
tunnel: tunnel instanceof Static ? "static" : options.tunnel ? "configured" : "none",
|
|
1555
|
+
...telemetryEnvironmentDims()
|
|
1556
|
+
};
|
|
1557
|
+
if (this.telemetry.enabled) {
|
|
1558
|
+
try {
|
|
1559
|
+
if (isFirstRun()) this.telemetry.record("first_run", initDims);
|
|
1560
|
+
} catch {
|
|
1561
|
+
}
|
|
1562
|
+
}
|
|
1563
|
+
this.telemetry.record("sdk_initialized", initDims);
|
|
856
1564
|
this._tunnelReady = new Promise((resolve, reject) => {
|
|
857
1565
|
this._tunnelReadyResolve = resolve;
|
|
858
1566
|
this._tunnelReadyReject = reject;
|
|
@@ -872,6 +1580,55 @@ var Patter = class {
|
|
|
872
1580
|
// === Agent definition ===
|
|
873
1581
|
/** Resolve user-supplied agent options against engine defaults and return the merged config. */
|
|
874
1582
|
agent(opts) {
|
|
1583
|
+
const family = telemetryEngineFamily(opts);
|
|
1584
|
+
const stack = stackDimensions(opts.stt, opts.tts, opts.llm);
|
|
1585
|
+
const featureKey = family + "|" + Object.entries(stack).sort().map(([k, v]) => `${k}=${v}`).join(",");
|
|
1586
|
+
if (!this.telemetrySeenEngines.has(featureKey)) {
|
|
1587
|
+
this.telemetrySeenEngines.add(featureKey);
|
|
1588
|
+
this.telemetry.record("feature_used", {
|
|
1589
|
+
engine: family,
|
|
1590
|
+
provider: telemetryProviderFamily(family),
|
|
1591
|
+
...stack
|
|
1592
|
+
});
|
|
1593
|
+
}
|
|
1594
|
+
const builtin = opts.consult ? 1 : 0;
|
|
1595
|
+
const customBucket = telemetryBucketCustomTools(opts.tools?.length ?? 0);
|
|
1596
|
+
const { integration, integrationKind, mcpBucket } = telemetryIntegration(opts);
|
|
1597
|
+
const engineObj = opts.engine;
|
|
1598
|
+
const nr = opts.openaiRealtimeNoiseReduction ?? engineObj?.noiseReduction;
|
|
1599
|
+
const noiseReduction = nr === "near_field" || nr === "far_field" ? nr : "none";
|
|
1600
|
+
const td = opts.realtimeTurnDetection ?? engineObj?.turnDetection;
|
|
1601
|
+
const turnDetection = td != null ? "custom" : "none";
|
|
1602
|
+
const preamblesUsed = Boolean(opts.toolCallPreambles);
|
|
1603
|
+
const perToolTimeoutsSet = Array.isArray(opts.tools) && opts.tools.some((t) => t.timeoutMs !== void 0);
|
|
1604
|
+
const llmFallbackConfigured = typeof opts.llm?.getAvailability === "function";
|
|
1605
|
+
const shapeKey = [
|
|
1606
|
+
builtin,
|
|
1607
|
+
customBucket,
|
|
1608
|
+
integration,
|
|
1609
|
+
integrationKind,
|
|
1610
|
+
mcpBucket,
|
|
1611
|
+
noiseReduction,
|
|
1612
|
+
turnDetection,
|
|
1613
|
+
preamblesUsed,
|
|
1614
|
+
perToolTimeoutsSet,
|
|
1615
|
+
llmFallbackConfigured
|
|
1616
|
+
].join("|");
|
|
1617
|
+
if (!this.telemetrySeenAgentShapes.has(shapeKey)) {
|
|
1618
|
+
this.telemetrySeenAgentShapes.add(shapeKey);
|
|
1619
|
+
this.telemetry.record("agent_configured", {
|
|
1620
|
+
builtin_tool_count: builtin,
|
|
1621
|
+
custom_tool_count_bucket: customBucket,
|
|
1622
|
+
integration,
|
|
1623
|
+
integration_kind: integrationKind,
|
|
1624
|
+
mcp_server_count_bucket: mcpBucket,
|
|
1625
|
+
noise_reduction: noiseReduction,
|
|
1626
|
+
turn_detection: turnDetection,
|
|
1627
|
+
preambles_used: preamblesUsed,
|
|
1628
|
+
per_tool_timeouts_set: perToolTimeoutsSet,
|
|
1629
|
+
llm_fallback_configured: llmFallbackConfigured
|
|
1630
|
+
});
|
|
1631
|
+
}
|
|
875
1632
|
let working = { ...opts };
|
|
876
1633
|
if (opts.engine) {
|
|
877
1634
|
if (opts.provider) {
|
|
@@ -1061,6 +1818,7 @@ var Patter = class {
|
|
|
1061
1818
|
opts.dashboardToken ?? "",
|
|
1062
1819
|
opts.allowInsecureDashboard ?? false
|
|
1063
1820
|
);
|
|
1821
|
+
this.embeddedServer.telemetry = this.telemetry;
|
|
1064
1822
|
this.embeddedServer.popPrewarmAudio = this.popPrewarmAudio;
|
|
1065
1823
|
this.embeddedServer.popPrewarmedConnections = this.popPrewarmedConnections;
|
|
1066
1824
|
this.embeddedServer.recordPrewarmWaste = this.recordPrewarmWaste;
|
|
@@ -1070,6 +1828,7 @@ var Patter = class {
|
|
|
1070
1828
|
await waitForTunnelPubliclyReachable(webhookUrl);
|
|
1071
1829
|
}
|
|
1072
1830
|
this._readyResolve(webhookUrl);
|
|
1831
|
+
this.telemetry.flushPending();
|
|
1073
1832
|
} catch (err) {
|
|
1074
1833
|
const e = err instanceof Error ? err : new Error(String(err));
|
|
1075
1834
|
this._readyReject(e);
|
|
@@ -1078,7 +1837,7 @@ var Patter = class {
|
|
|
1078
1837
|
}
|
|
1079
1838
|
/** Run the agent in interactive terminal-test mode (no real telephony). */
|
|
1080
1839
|
async test(opts) {
|
|
1081
|
-
const { TestSession: TestSession2 } = await import("./test-mode-
|
|
1840
|
+
const { TestSession: TestSession2 } = await import("./test-mode-XFOADUNE.mjs");
|
|
1082
1841
|
const session = new TestSession2();
|
|
1083
1842
|
await session.run({
|
|
1084
1843
|
agent: opts.agent,
|
|
@@ -1752,6 +2511,7 @@ var Patter = class {
|
|
|
1752
2511
|
* entries leak across ``serve`` / ``disconnect`` cycles. See FIX #93.
|
|
1753
2512
|
*/
|
|
1754
2513
|
async disconnect() {
|
|
2514
|
+
this.telemetry.flushPending();
|
|
1755
2515
|
for (const handle of this.prewarmTtlTimers.values()) {
|
|
1756
2516
|
clearTimeout(handle);
|
|
1757
2517
|
}
|
|
@@ -2329,6 +3089,7 @@ var PatterTool = class {
|
|
|
2329
3089
|
maxDurationSec;
|
|
2330
3090
|
recording;
|
|
2331
3091
|
started = false;
|
|
3092
|
+
hermesTelemetryEmitted = false;
|
|
2332
3093
|
/** Cached in-progress (or completed) start promise so concurrent execute()
|
|
2333
3094
|
* callers all await the same boot sequence instead of each racing into
|
|
2334
3095
|
* phone.serve(). Reset to null on failure so callers can retry after a
|
|
@@ -2485,6 +3246,20 @@ var PatterTool = class {
|
|
|
2485
3246
|
* the same wire contract.
|
|
2486
3247
|
*/
|
|
2487
3248
|
hermesHandler() {
|
|
3249
|
+
if (!this.hermesTelemetryEmitted) {
|
|
3250
|
+
this.hermesTelemetryEmitted = true;
|
|
3251
|
+
try {
|
|
3252
|
+
const tel = this.phone.telemetry;
|
|
3253
|
+
tel?.record("agent_configured", {
|
|
3254
|
+
builtin_tool_count: 0,
|
|
3255
|
+
custom_tool_count_bucket: "0",
|
|
3256
|
+
integration: "hermes",
|
|
3257
|
+
integration_kind: "none",
|
|
3258
|
+
mcp_server_count_bucket: "0"
|
|
3259
|
+
});
|
|
3260
|
+
} catch {
|
|
3261
|
+
}
|
|
3262
|
+
}
|
|
2488
3263
|
return async (args) => {
|
|
2489
3264
|
try {
|
|
2490
3265
|
const result = await this.execute(args);
|
|
@@ -7539,6 +8314,310 @@ var LLM5 = class extends GoogleLLMProvider {
|
|
|
7539
8314
|
}
|
|
7540
8315
|
};
|
|
7541
8316
|
|
|
8317
|
+
// src/llm/openai-compatible.ts
|
|
8318
|
+
init_esm_shims();
|
|
8319
|
+
import { createHash } from "crypto";
|
|
8320
|
+
var DEFAULT_TIMEOUT_S = 60;
|
|
8321
|
+
function hashCaller(caller) {
|
|
8322
|
+
if (!caller) return void 0;
|
|
8323
|
+
return createHash("sha256").update(caller, "utf8").digest("hex").slice(0, 16);
|
|
8324
|
+
}
|
|
8325
|
+
var OpenAICompatibleLLMProvider = class {
|
|
8326
|
+
/**
|
|
8327
|
+
* Stable pricing/dashboard key — read by stream-handler/metrics. Typed as
|
|
8328
|
+
* ``string`` (not the narrowed literal) so the Hermes / OpenClaw presets can
|
|
8329
|
+
* override it with their own key while still extending this class.
|
|
8330
|
+
*/
|
|
8331
|
+
static providerKey = "openai_compatible";
|
|
8332
|
+
/** Resolved bearer; undefined for keyless gateways. */
|
|
8333
|
+
apiKey;
|
|
8334
|
+
model;
|
|
8335
|
+
baseUrl;
|
|
8336
|
+
timeoutMs;
|
|
8337
|
+
extraHeaders;
|
|
8338
|
+
sessionUserPrefix;
|
|
8339
|
+
sessionIdHeader;
|
|
8340
|
+
sessionIdPrefix;
|
|
8341
|
+
sessionKeyHeader;
|
|
8342
|
+
sessionKey;
|
|
8343
|
+
sessionKeyFactory;
|
|
8344
|
+
temperature;
|
|
8345
|
+
maxTokens;
|
|
8346
|
+
responseFormat;
|
|
8347
|
+
parallelToolCalls;
|
|
8348
|
+
toolChoice;
|
|
8349
|
+
seed;
|
|
8350
|
+
topP;
|
|
8351
|
+
frequencyPenalty;
|
|
8352
|
+
presencePenalty;
|
|
8353
|
+
stop;
|
|
8354
|
+
constructor(options) {
|
|
8355
|
+
if (!options.baseUrl) {
|
|
8356
|
+
throw new Error(
|
|
8357
|
+
'OpenAICompatibleLLMProvider requires a baseUrl (e.g. "http://127.0.0.1:11434/v1").'
|
|
8358
|
+
);
|
|
8359
|
+
}
|
|
8360
|
+
if (!options.model) {
|
|
8361
|
+
throw new Error("OpenAICompatibleLLMProvider requires a model.");
|
|
8362
|
+
}
|
|
8363
|
+
this.apiKey = options.apiKey ?? (options.apiKeyEnv ? process.env[options.apiKeyEnv] : void 0);
|
|
8364
|
+
this.model = options.model;
|
|
8365
|
+
this.baseUrl = options.baseUrl;
|
|
8366
|
+
this.timeoutMs = (options.timeout ?? DEFAULT_TIMEOUT_S) * 1e3;
|
|
8367
|
+
this.extraHeaders = options.extraHeaders;
|
|
8368
|
+
this.sessionUserPrefix = options.sessionUserPrefix;
|
|
8369
|
+
this.sessionIdHeader = options.sessionIdHeader;
|
|
8370
|
+
this.sessionIdPrefix = options.sessionIdPrefix;
|
|
8371
|
+
this.sessionKeyHeader = options.sessionKeyHeader;
|
|
8372
|
+
this.sessionKey = options.sessionKey;
|
|
8373
|
+
let sessionKeyFactory = options.sessionKeyFactory;
|
|
8374
|
+
if (!sessionKeyFactory && options.sessionKeyFrom === "caller_hash") {
|
|
8375
|
+
sessionKeyFactory = (ctx) => ctx.callerHash ? `patter-caller-${ctx.callerHash}` : void 0;
|
|
8376
|
+
} else if (options.sessionKeyFrom !== void 0 && options.sessionKeyFrom !== "caller_hash") {
|
|
8377
|
+
throw new Error(
|
|
8378
|
+
`sessionKeyFrom must be 'caller_hash' or undefined, got ${JSON.stringify(
|
|
8379
|
+
options.sessionKeyFrom
|
|
8380
|
+
)}`
|
|
8381
|
+
);
|
|
8382
|
+
}
|
|
8383
|
+
this.sessionKeyFactory = sessionKeyFactory;
|
|
8384
|
+
this.temperature = options.temperature;
|
|
8385
|
+
this.maxTokens = options.maxTokens;
|
|
8386
|
+
this.responseFormat = options.responseFormat;
|
|
8387
|
+
this.parallelToolCalls = options.parallelToolCalls;
|
|
8388
|
+
this.toolChoice = options.toolChoice;
|
|
8389
|
+
this.seed = options.seed;
|
|
8390
|
+
this.topP = options.topP;
|
|
8391
|
+
this.frequencyPenalty = options.frequencyPenalty;
|
|
8392
|
+
this.presencePenalty = options.presencePenalty;
|
|
8393
|
+
this.stop = options.stop;
|
|
8394
|
+
}
|
|
8395
|
+
/**
|
|
8396
|
+
* Assemble the request headers. ``User-Agent`` is set first so any
|
|
8397
|
+
* ``extraHeaders`` (and the per-call session headers) layer on top without
|
|
8398
|
+
* silently dropping the SDK attribution, and the ``Authorization`` header is
|
|
8399
|
+
* only added when a key is present (keyless gateways omit it).
|
|
8400
|
+
*
|
|
8401
|
+
* The two session headers are emitted INDEPENDENTLY, each gated on its own
|
|
8402
|
+
* config (decoupled from ``sessionUserPrefix`` and from each other):
|
|
8403
|
+
* - ``sessionIdHeader`` (+ ``callId``) → ``` `${sessionIdPrefix}${callId}` ```
|
|
8404
|
+
* - ``sessionKeyHeader`` (+ ``sessionKey``) → the static ``sessionKey`` value.
|
|
8405
|
+
* ``sessionKey`` is a credential-grade memory scope and is never logged.
|
|
8406
|
+
*/
|
|
8407
|
+
buildHeaders(callId, caller, callee) {
|
|
8408
|
+
const headers = {
|
|
8409
|
+
"Content-Type": "application/json",
|
|
8410
|
+
"User-Agent": `getpatter/${VERSION}`,
|
|
8411
|
+
...this.extraHeaders ?? {}
|
|
8412
|
+
};
|
|
8413
|
+
if (this.apiKey) {
|
|
8414
|
+
headers.Authorization = `Bearer ${this.apiKey}`;
|
|
8415
|
+
}
|
|
8416
|
+
if (this.sessionIdHeader && callId) {
|
|
8417
|
+
headers[this.sessionIdHeader] = `${this.sessionIdPrefix ?? ""}${callId}`;
|
|
8418
|
+
}
|
|
8419
|
+
if (this.sessionKeyHeader) {
|
|
8420
|
+
const sessionKeyValue = this.resolveSessionKey(callId, caller, callee);
|
|
8421
|
+
if (sessionKeyValue) {
|
|
8422
|
+
headers[this.sessionKeyHeader] = sessionKeyValue;
|
|
8423
|
+
}
|
|
8424
|
+
}
|
|
8425
|
+
return headers;
|
|
8426
|
+
}
|
|
8427
|
+
/**
|
|
8428
|
+
* Resolve the ``sessionKeyHeader`` VALUE for this call. When a
|
|
8429
|
+
* ``sessionKeyFactory`` is configured it is called with a
|
|
8430
|
+
* {@link SessionContext} (the raw ``caller`` plus its non-reversible
|
|
8431
|
+
* {@link hashCaller}) and its return value wins — a falsy return omits the
|
|
8432
|
+
* header. Otherwise the static ``sessionKey`` is used. Never logged.
|
|
8433
|
+
*/
|
|
8434
|
+
resolveSessionKey(callId, caller, callee) {
|
|
8435
|
+
if (this.sessionKeyFactory) {
|
|
8436
|
+
const ctx = {
|
|
8437
|
+
callId,
|
|
8438
|
+
caller,
|
|
8439
|
+
callee,
|
|
8440
|
+
callerHash: hashCaller(caller)
|
|
8441
|
+
};
|
|
8442
|
+
return this.sessionKeyFactory(ctx);
|
|
8443
|
+
}
|
|
8444
|
+
return this.sessionKey;
|
|
8445
|
+
}
|
|
8446
|
+
/**
|
|
8447
|
+
* Pre-call DNS / TLS warmup for the configured endpoint. Best-effort:
|
|
8448
|
+
* 5 s timeout, all exceptions swallowed at debug level. The ``Authorization``
|
|
8449
|
+
* header is only sent when a key is present so the operator-grade bearer is
|
|
8450
|
+
* never echoed for keyless gateways (and the key is never logged).
|
|
8451
|
+
*/
|
|
8452
|
+
async warmup() {
|
|
8453
|
+
try {
|
|
8454
|
+
const headers = {};
|
|
8455
|
+
if (this.apiKey) headers.Authorization = `Bearer ${this.apiKey}`;
|
|
8456
|
+
await fetch(`${this.baseUrl}/models`, {
|
|
8457
|
+
method: "GET",
|
|
8458
|
+
headers,
|
|
8459
|
+
signal: AbortSignal.timeout(5e3)
|
|
8460
|
+
});
|
|
8461
|
+
} catch (err) {
|
|
8462
|
+
getLogger().debug(
|
|
8463
|
+
`OpenAI-compatible LLM warmup failed (best-effort): ${String(err)}`
|
|
8464
|
+
);
|
|
8465
|
+
}
|
|
8466
|
+
}
|
|
8467
|
+
/**
|
|
8468
|
+
* Build the request body. Mirrors the base OpenAI provider's sampling-kwarg
|
|
8469
|
+
* assembly and additionally sets ``user`` for session continuity when
|
|
8470
|
+
* ``sessionUserPrefix`` is set AND a ``callId`` is available — so the default
|
|
8471
|
+
* (prefix unset) behaviour is byte-identical to the base provider.
|
|
8472
|
+
*/
|
|
8473
|
+
buildBody(messages, tools, callId) {
|
|
8474
|
+
const body = {
|
|
8475
|
+
model: this.model,
|
|
8476
|
+
messages,
|
|
8477
|
+
stream: true,
|
|
8478
|
+
stream_options: { include_usage: true }
|
|
8479
|
+
};
|
|
8480
|
+
if (this.temperature !== void 0) body.temperature = this.temperature;
|
|
8481
|
+
if (this.maxTokens !== void 0) body.max_completion_tokens = this.maxTokens;
|
|
8482
|
+
if (this.responseFormat !== void 0) body.response_format = this.responseFormat;
|
|
8483
|
+
if (this.parallelToolCalls !== void 0) body.parallel_tool_calls = this.parallelToolCalls;
|
|
8484
|
+
if (this.toolChoice !== void 0) body.tool_choice = this.toolChoice;
|
|
8485
|
+
if (this.seed !== void 0) body.seed = this.seed;
|
|
8486
|
+
if (this.topP !== void 0) body.top_p = this.topP;
|
|
8487
|
+
if (this.frequencyPenalty !== void 0) body.frequency_penalty = this.frequencyPenalty;
|
|
8488
|
+
if (this.presencePenalty !== void 0) body.presence_penalty = this.presencePenalty;
|
|
8489
|
+
if (this.stop !== void 0) body.stop = this.stop;
|
|
8490
|
+
if (tools) body.tools = tools;
|
|
8491
|
+
if (this.sessionUserPrefix !== void 0 && callId) {
|
|
8492
|
+
body.user = `${this.sessionUserPrefix}${callId}`;
|
|
8493
|
+
}
|
|
8494
|
+
return body;
|
|
8495
|
+
}
|
|
8496
|
+
/** Stream Patter-format LLM chunks from the configured chat completions API. */
|
|
8497
|
+
async *stream(messages, tools, opts) {
|
|
8498
|
+
const callId = opts?.callId;
|
|
8499
|
+
const caller = opts?.caller;
|
|
8500
|
+
const callee = opts?.callee;
|
|
8501
|
+
const body = this.buildBody(messages, tools, callId);
|
|
8502
|
+
const response = await fetch(`${this.baseUrl}/chat/completions`, {
|
|
8503
|
+
method: "POST",
|
|
8504
|
+
headers: this.buildHeaders(callId, caller, callee),
|
|
8505
|
+
body: JSON.stringify(body),
|
|
8506
|
+
signal: mergeAbortSignals(opts?.signal, AbortSignal.timeout(this.timeoutMs))
|
|
8507
|
+
});
|
|
8508
|
+
if (!response.ok) {
|
|
8509
|
+
const errText = await response.text();
|
|
8510
|
+
getLogger().error(
|
|
8511
|
+
`OpenAI-compatible API error: ${response.status} ${errText}`
|
|
8512
|
+
);
|
|
8513
|
+
throw new PatterConnectionError(
|
|
8514
|
+
`LLM API returned ${response.status}: ${errText.slice(0, 200)}`
|
|
8515
|
+
);
|
|
8516
|
+
}
|
|
8517
|
+
yield* parseOpenAISseStream(response);
|
|
8518
|
+
}
|
|
8519
|
+
};
|
|
8520
|
+
var LLM6 = class extends OpenAICompatibleLLMProvider {
|
|
8521
|
+
static providerKey = "openai_compatible";
|
|
8522
|
+
};
|
|
8523
|
+
|
|
8524
|
+
// src/llm/custom.ts
|
|
8525
|
+
init_esm_shims();
|
|
8526
|
+
var LLM7 = class extends OpenAICompatibleLLMProvider {
|
|
8527
|
+
/** Stable pricing/dashboard key — read by stream-handler/metrics. */
|
|
8528
|
+
static providerKey = "custom";
|
|
8529
|
+
};
|
|
8530
|
+
|
|
8531
|
+
// src/llm/hermes.ts
|
|
8532
|
+
init_esm_shims();
|
|
8533
|
+
var BASE_URL = "http://127.0.0.1:8642/v1";
|
|
8534
|
+
var DEFAULT_MODEL5 = "hermes-agent";
|
|
8535
|
+
var API_KEY_ENV = "API_SERVER_KEY";
|
|
8536
|
+
var MODEL_ENV = "API_SERVER_MODEL_NAME";
|
|
8537
|
+
var SESSION_USER_PREFIX = "patter-call-";
|
|
8538
|
+
var SESSION_ID_HEADER = "X-Hermes-Session-Id";
|
|
8539
|
+
var SESSION_ID_PREFIX = "patter-call-";
|
|
8540
|
+
var SESSION_KEY_HEADER = "X-Hermes-Session-Key";
|
|
8541
|
+
var DEFAULT_TIMEOUT_S2 = 120;
|
|
8542
|
+
var LLM8 = class extends OpenAICompatibleLLMProvider {
|
|
8543
|
+
static providerKey = "hermes";
|
|
8544
|
+
constructor(opts = {}) {
|
|
8545
|
+
const model = opts.model ?? process.env[MODEL_ENV] ?? DEFAULT_MODEL5;
|
|
8546
|
+
const options = {
|
|
8547
|
+
apiKey: opts.apiKey,
|
|
8548
|
+
apiKeyEnv: API_KEY_ENV,
|
|
8549
|
+
baseUrl: opts.baseUrl ?? BASE_URL,
|
|
8550
|
+
model,
|
|
8551
|
+
timeout: opts.timeout ?? DEFAULT_TIMEOUT_S2,
|
|
8552
|
+
sessionUserPrefix: SESSION_USER_PREFIX,
|
|
8553
|
+
sessionIdHeader: SESSION_ID_HEADER,
|
|
8554
|
+
sessionIdPrefix: SESSION_ID_PREFIX,
|
|
8555
|
+
sessionKeyHeader: SESSION_KEY_HEADER,
|
|
8556
|
+
sessionKey: opts.sessionKey,
|
|
8557
|
+
sessionKeyFrom: opts.sessionKeyFrom,
|
|
8558
|
+
sessionKeyFactory: opts.sessionKeyFactory,
|
|
8559
|
+
extraHeaders: opts.extraHeaders,
|
|
8560
|
+
temperature: opts.temperature,
|
|
8561
|
+
maxTokens: opts.maxTokens,
|
|
8562
|
+
responseFormat: opts.responseFormat,
|
|
8563
|
+
parallelToolCalls: opts.parallelToolCalls,
|
|
8564
|
+
toolChoice: opts.toolChoice,
|
|
8565
|
+
seed: opts.seed,
|
|
8566
|
+
topP: opts.topP,
|
|
8567
|
+
frequencyPenalty: opts.frequencyPenalty,
|
|
8568
|
+
presencePenalty: opts.presencePenalty,
|
|
8569
|
+
stop: opts.stop
|
|
8570
|
+
};
|
|
8571
|
+
super(options);
|
|
8572
|
+
}
|
|
8573
|
+
};
|
|
8574
|
+
|
|
8575
|
+
// src/llm/openclaw.ts
|
|
8576
|
+
init_esm_shims();
|
|
8577
|
+
var BASE_URL2 = "http://127.0.0.1:18789/v1";
|
|
8578
|
+
var API_KEY_ENV2 = "OPENCLAW_API_KEY";
|
|
8579
|
+
var SESSION_HEADER = "x-openclaw-session-key";
|
|
8580
|
+
var SESSION_USER_PREFIX2 = "patter-call-";
|
|
8581
|
+
var DEFAULT_TIMEOUT_S3 = 120;
|
|
8582
|
+
var OPENCLAW_AGENT_RE = /^[A-Za-z0-9._:/-]+$/;
|
|
8583
|
+
var LLM9 = class extends OpenAICompatibleLLMProvider {
|
|
8584
|
+
static providerKey = "openclaw";
|
|
8585
|
+
constructor(opts) {
|
|
8586
|
+
const agent = opts?.agent;
|
|
8587
|
+
if (!agent || !OPENCLAW_AGENT_RE.test(agent)) {
|
|
8588
|
+
throw new Error(
|
|
8589
|
+
`Invalid OpenClaw agent id: ${JSON.stringify(agent)}. Allowed characters: letters, digits, dot, underscore, colon, slash, dash.`
|
|
8590
|
+
);
|
|
8591
|
+
}
|
|
8592
|
+
const model = agent.includes("/") || agent.includes(":") ? agent : `openclaw/${agent}`;
|
|
8593
|
+
const options = {
|
|
8594
|
+
apiKey: opts.apiKey,
|
|
8595
|
+
apiKeyEnv: API_KEY_ENV2,
|
|
8596
|
+
baseUrl: opts.baseUrl ?? BASE_URL2,
|
|
8597
|
+
model,
|
|
8598
|
+
timeout: opts.timeout ?? DEFAULT_TIMEOUT_S3,
|
|
8599
|
+
sessionUserPrefix: SESSION_USER_PREFIX2,
|
|
8600
|
+
// Wire-identical to the prior behaviour: header value is the raw call id
|
|
8601
|
+
// (empty prefix), and OpenClaw's gateway also derives the session from
|
|
8602
|
+
// the ``user`` field above. No separate memory-scope header.
|
|
8603
|
+
sessionIdHeader: SESSION_HEADER,
|
|
8604
|
+
sessionIdPrefix: "",
|
|
8605
|
+
extraHeaders: opts.extraHeaders,
|
|
8606
|
+
temperature: opts.temperature,
|
|
8607
|
+
maxTokens: opts.maxTokens,
|
|
8608
|
+
responseFormat: opts.responseFormat,
|
|
8609
|
+
parallelToolCalls: opts.parallelToolCalls,
|
|
8610
|
+
toolChoice: opts.toolChoice,
|
|
8611
|
+
seed: opts.seed,
|
|
8612
|
+
topP: opts.topP,
|
|
8613
|
+
frequencyPenalty: opts.frequencyPenalty,
|
|
8614
|
+
presencePenalty: opts.presencePenalty,
|
|
8615
|
+
stop: opts.stop
|
|
8616
|
+
};
|
|
8617
|
+
super(options);
|
|
8618
|
+
}
|
|
8619
|
+
};
|
|
8620
|
+
|
|
7542
8621
|
// src/providers/deepfilternet-filter.ts
|
|
7543
8622
|
init_esm_shims();
|
|
7544
8623
|
function log() {
|
|
@@ -7771,57 +8850,6 @@ var KrispVivaFilter = class {
|
|
|
7771
8850
|
}
|
|
7772
8851
|
};
|
|
7773
8852
|
|
|
7774
|
-
// src/telephony/twilio.ts
|
|
7775
|
-
init_esm_shims();
|
|
7776
|
-
var Carrier2 = class {
|
|
7777
|
-
kind = "twilio";
|
|
7778
|
-
accountSid;
|
|
7779
|
-
authToken;
|
|
7780
|
-
constructor(opts = {}) {
|
|
7781
|
-
const sid = opts.accountSid ?? process.env.TWILIO_ACCOUNT_SID;
|
|
7782
|
-
const tok = opts.authToken ?? process.env.TWILIO_AUTH_TOKEN;
|
|
7783
|
-
if (!sid) {
|
|
7784
|
-
throw new Error(
|
|
7785
|
-
"Twilio carrier requires accountSid. Pass { accountSid: 'AC...' } or set TWILIO_ACCOUNT_SID in the environment."
|
|
7786
|
-
);
|
|
7787
|
-
}
|
|
7788
|
-
if (!tok) {
|
|
7789
|
-
throw new Error(
|
|
7790
|
-
"Twilio carrier requires authToken. Pass { authToken: '...' } or set TWILIO_AUTH_TOKEN in the environment."
|
|
7791
|
-
);
|
|
7792
|
-
}
|
|
7793
|
-
this.accountSid = sid;
|
|
7794
|
-
this.authToken = tok;
|
|
7795
|
-
}
|
|
7796
|
-
};
|
|
7797
|
-
|
|
7798
|
-
// src/telephony/telnyx.ts
|
|
7799
|
-
init_esm_shims();
|
|
7800
|
-
var Carrier3 = class {
|
|
7801
|
-
kind = "telnyx";
|
|
7802
|
-
apiKey;
|
|
7803
|
-
connectionId;
|
|
7804
|
-
publicKey;
|
|
7805
|
-
constructor(opts = {}) {
|
|
7806
|
-
const key = opts.apiKey ?? process.env.TELNYX_API_KEY;
|
|
7807
|
-
const conn = opts.connectionId ?? process.env.TELNYX_CONNECTION_ID;
|
|
7808
|
-
const pub = opts.publicKey ?? process.env.TELNYX_PUBLIC_KEY;
|
|
7809
|
-
if (!key) {
|
|
7810
|
-
throw new Error(
|
|
7811
|
-
"Telnyx carrier requires apiKey. Pass { apiKey: '...' } or set TELNYX_API_KEY in the environment."
|
|
7812
|
-
);
|
|
7813
|
-
}
|
|
7814
|
-
if (!conn) {
|
|
7815
|
-
throw new Error(
|
|
7816
|
-
"Telnyx carrier requires connectionId. Pass { connectionId: '...' } or set TELNYX_CONNECTION_ID in the environment."
|
|
7817
|
-
);
|
|
7818
|
-
}
|
|
7819
|
-
this.apiKey = key;
|
|
7820
|
-
this.connectionId = conn;
|
|
7821
|
-
this.publicKey = pub;
|
|
7822
|
-
}
|
|
7823
|
-
};
|
|
7824
|
-
|
|
7825
8853
|
// src/public-api.ts
|
|
7826
8854
|
init_esm_shims();
|
|
7827
8855
|
var DEFAULT_GUARDRAIL_REPLACEMENT = "I'm sorry, I can't respond to that.";
|
|
@@ -7888,9 +8916,9 @@ function tool(opts) {
|
|
|
7888
8916
|
|
|
7889
8917
|
// src/chat-context.ts
|
|
7890
8918
|
init_esm_shims();
|
|
7891
|
-
import { randomUUID } from "crypto";
|
|
8919
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
7892
8920
|
function generateId() {
|
|
7893
|
-
return
|
|
8921
|
+
return randomUUID2().replace(/-/g, "").slice(0, 12);
|
|
7894
8922
|
}
|
|
7895
8923
|
function createMessage(role, content, options) {
|
|
7896
8924
|
return Object.freeze({
|
|
@@ -8305,8 +9333,8 @@ var IVRActivity = class {
|
|
|
8305
9333
|
|
|
8306
9334
|
// src/audio/background-audio.ts
|
|
8307
9335
|
init_esm_shims();
|
|
8308
|
-
import { promises as
|
|
8309
|
-
import
|
|
9336
|
+
import { promises as fs3 } from "fs";
|
|
9337
|
+
import path2 from "path";
|
|
8310
9338
|
import { fileURLToPath } from "url";
|
|
8311
9339
|
var BuiltinAudioClip = {
|
|
8312
9340
|
CITY_AMBIENCE: "city-ambience.ogg",
|
|
@@ -8319,8 +9347,8 @@ var BuiltinAudioClip = {
|
|
|
8319
9347
|
};
|
|
8320
9348
|
function builtinClipPath(clip) {
|
|
8321
9349
|
const meta = typeof import.meta !== "undefined" ? import.meta : void 0;
|
|
8322
|
-
const here = meta?.url ?
|
|
8323
|
-
return
|
|
9350
|
+
const here = meta?.url ? path2.dirname(fileURLToPath(meta.url)) : typeof __dirname !== "undefined" ? __dirname : process.cwd();
|
|
9351
|
+
return path2.resolve(here, "..", "resources", "audio", clip);
|
|
8324
9352
|
}
|
|
8325
9353
|
var INT16_MIN = -32768;
|
|
8326
9354
|
var INT16_MAX = 32767;
|
|
@@ -8489,7 +9517,7 @@ var BackgroundAudioPlayer = class {
|
|
|
8489
9517
|
return source.decode(source.path);
|
|
8490
9518
|
case "builtin": {
|
|
8491
9519
|
const p = builtinClipPath(source.clip);
|
|
8492
|
-
const header = await
|
|
9520
|
+
const header = await fs3.readFile(p, { flag: "r" }).then((buf) => buf.subarray(0, 4));
|
|
8493
9521
|
if (header.toString("ascii") !== "OggS") {
|
|
8494
9522
|
throw new Error(`Bundled clip ${source.clip} is not a valid Ogg file`);
|
|
8495
9523
|
}
|
|
@@ -8536,8 +9564,8 @@ var TwilioAdapter = class _TwilioAdapter {
|
|
|
8536
9564
|
this.baseUrl = opts.region ? `https://api.${opts.region}.twilio.com/2010-04-01` : TWILIO_API_BASE;
|
|
8537
9565
|
this.authHeader = `Basic ${Buffer.from(`${accountSid}:${authToken}`).toString("base64")}`;
|
|
8538
9566
|
}
|
|
8539
|
-
async request(method,
|
|
8540
|
-
const url = `${this.baseUrl}/Accounts/${encodeURIComponent(this.accountSid)}${
|
|
9567
|
+
async request(method, path3, body) {
|
|
9568
|
+
const url = `${this.baseUrl}/Accounts/${encodeURIComponent(this.accountSid)}${path3}`;
|
|
8541
9569
|
const headers = { Authorization: this.authHeader };
|
|
8542
9570
|
if (body) headers["Content-Type"] = "application/x-www-form-urlencoded";
|
|
8543
9571
|
const response = await fetch(url, {
|
|
@@ -8548,7 +9576,7 @@ var TwilioAdapter = class _TwilioAdapter {
|
|
|
8548
9576
|
});
|
|
8549
9577
|
const text = await response.text();
|
|
8550
9578
|
if (!response.ok) {
|
|
8551
|
-
throw new Error(`Twilio ${method} ${
|
|
9579
|
+
throw new Error(`Twilio ${method} ${path3} failed: ${response.status} ${text}`);
|
|
8552
9580
|
}
|
|
8553
9581
|
if (!text) return {};
|
|
8554
9582
|
try {
|
|
@@ -8566,8 +9594,8 @@ var TwilioAdapter = class _TwilioAdapter {
|
|
|
8566
9594
|
const country = encodeURIComponent(opts.countryCode);
|
|
8567
9595
|
const queryParts = ["PageSize=1"];
|
|
8568
9596
|
if (opts.areaCode) queryParts.push(`AreaCode=${encodeURIComponent(opts.areaCode)}`);
|
|
8569
|
-
const
|
|
8570
|
-
const available = await this.request("GET",
|
|
9597
|
+
const path3 = `/AvailablePhoneNumbers/${country}/Local.json?${queryParts.join("&")}`;
|
|
9598
|
+
const available = await this.request("GET", path3);
|
|
8571
9599
|
const first = available.available_phone_numbers?.[0]?.phone_number;
|
|
8572
9600
|
if (!first) {
|
|
8573
9601
|
throw new Error(`TwilioAdapter: no numbers available for country ${opts.countryCode}`);
|
|
@@ -8667,7 +9695,7 @@ var TwilioAdapter = class _TwilioAdapter {
|
|
|
8667
9695
|
|
|
8668
9696
|
// src/providers/telnyx-adapter.ts
|
|
8669
9697
|
init_esm_shims();
|
|
8670
|
-
import { randomUUID as
|
|
9698
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
8671
9699
|
var TELNYX_API_BASE = "https://api.telnyx.com/v2";
|
|
8672
9700
|
var TelnyxAdapter = class {
|
|
8673
9701
|
apiKey;
|
|
@@ -8678,8 +9706,8 @@ var TelnyxAdapter = class {
|
|
|
8678
9706
|
this.apiKey = apiKey;
|
|
8679
9707
|
this.connectionId = connectionId;
|
|
8680
9708
|
}
|
|
8681
|
-
async request(method,
|
|
8682
|
-
const url = `${this.baseUrl}${
|
|
9709
|
+
async request(method, path3, body) {
|
|
9710
|
+
const url = `${this.baseUrl}${path3}`;
|
|
8683
9711
|
const headers = {
|
|
8684
9712
|
Authorization: `Bearer ${this.apiKey}`
|
|
8685
9713
|
};
|
|
@@ -8692,7 +9720,7 @@ var TelnyxAdapter = class {
|
|
|
8692
9720
|
});
|
|
8693
9721
|
const text = await response.text();
|
|
8694
9722
|
if (!response.ok) {
|
|
8695
|
-
throw new Error(`Telnyx ${method} ${
|
|
9723
|
+
throw new Error(`Telnyx ${method} ${path3} failed: ${response.status} ${text}`);
|
|
8696
9724
|
}
|
|
8697
9725
|
if (!text) return {};
|
|
8698
9726
|
try {
|
|
@@ -8776,7 +9804,7 @@ var TelnyxAdapter = class {
|
|
|
8776
9804
|
if (!callControlId) throw new Error("TelnyxAdapter: callControlId is required");
|
|
8777
9805
|
const encoded = encodeURIComponent(callControlId);
|
|
8778
9806
|
const body = {
|
|
8779
|
-
command_id: opts.commandId ??
|
|
9807
|
+
command_id: opts.commandId ?? randomUUID3()
|
|
8780
9808
|
};
|
|
8781
9809
|
try {
|
|
8782
9810
|
await this.request(
|
|
@@ -9050,6 +10078,12 @@ var TelnyxTTS = class {
|
|
|
9050
10078
|
|
|
9051
10079
|
// src/observability/index.ts
|
|
9052
10080
|
init_esm_shims();
|
|
10081
|
+
|
|
10082
|
+
// src/index.ts
|
|
10083
|
+
var hermes = Object.freeze({ LLM: LLM8 });
|
|
10084
|
+
var openclaw = Object.freeze({ LLM: LLM9 });
|
|
10085
|
+
var openaiCompatible = Object.freeze({ LLM: LLM6 });
|
|
10086
|
+
var custom = Object.freeze({ LLM: LLM7 });
|
|
9053
10087
|
export {
|
|
9054
10088
|
AllProvidersFailedError,
|
|
9055
10089
|
LLM2 as AnthropicLLM,
|
|
@@ -9065,6 +10099,7 @@ export {
|
|
|
9065
10099
|
LLM4 as CerebrasLLM,
|
|
9066
10100
|
ChatContext,
|
|
9067
10101
|
CloudflareTunnel,
|
|
10102
|
+
LLM7 as CustomLLM,
|
|
9068
10103
|
DEFAULT_MIN_SENTENCE_LEN,
|
|
9069
10104
|
DEFAULT_PRICING,
|
|
9070
10105
|
DTMF_EVENTS,
|
|
@@ -9088,6 +10123,7 @@ export {
|
|
|
9088
10123
|
LLM5 as GoogleLLM,
|
|
9089
10124
|
LLM3 as GroqLLM,
|
|
9090
10125
|
Guardrail,
|
|
10126
|
+
LLM8 as HermesLLM,
|
|
9091
10127
|
IVRActivity,
|
|
9092
10128
|
TTS7 as InworldTTS,
|
|
9093
10129
|
KrispFrameDuration,
|
|
@@ -9098,6 +10134,8 @@ export {
|
|
|
9098
10134
|
MetricsStore,
|
|
9099
10135
|
MinWordsStrategy,
|
|
9100
10136
|
Ngrok,
|
|
10137
|
+
LLM6 as OpenAICompatibleLLM,
|
|
10138
|
+
OpenAICompatibleLLMProvider,
|
|
9101
10139
|
LLM as OpenAILLM,
|
|
9102
10140
|
OpenAILLMProvider,
|
|
9103
10141
|
Realtime as OpenAIRealtime,
|
|
@@ -9111,6 +10149,7 @@ export {
|
|
|
9111
10149
|
STT3 as OpenAITranscribeSTT,
|
|
9112
10150
|
OpenAITranscriptionModel,
|
|
9113
10151
|
OpenAIVoice,
|
|
10152
|
+
LLM9 as OpenClawLLM,
|
|
9114
10153
|
PRICING_LAST_UPDATED,
|
|
9115
10154
|
PRICING_VERSION,
|
|
9116
10155
|
PartialStreamError,
|
|
@@ -9179,6 +10218,7 @@ export {
|
|
|
9179
10218
|
createResampler24kTo16k,
|
|
9180
10219
|
createResampler24kTo8k,
|
|
9181
10220
|
createResampler8kTo16k,
|
|
10221
|
+
custom,
|
|
9182
10222
|
deepgram,
|
|
9183
10223
|
defineTool,
|
|
9184
10224
|
elevenlabs,
|
|
@@ -9190,6 +10230,8 @@ export {
|
|
|
9190
10230
|
geminiLive,
|
|
9191
10231
|
getLogger,
|
|
9192
10232
|
guardrail,
|
|
10233
|
+
hashCaller,
|
|
10234
|
+
hermes,
|
|
9193
10235
|
initTracing,
|
|
9194
10236
|
isRemoteUrl,
|
|
9195
10237
|
isTracingEnabled,
|
|
@@ -9202,7 +10244,9 @@ export {
|
|
|
9202
10244
|
mountDashboard,
|
|
9203
10245
|
mulawToPcm16,
|
|
9204
10246
|
notifyDashboard,
|
|
10247
|
+
openaiCompatible,
|
|
9205
10248
|
openaiTts,
|
|
10249
|
+
openclaw,
|
|
9206
10250
|
openclawConsult,
|
|
9207
10251
|
openclawPostCallNotifier,
|
|
9208
10252
|
pcm16ToMulaw,
|