lua-cli 3.38.0 → 3.39.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.
@@ -359,6 +359,155 @@ function foldRichPartsIntoMessages(messages, records, makeMessage, sameTurnGroup
359
359
  while (next < synthetic.length) combined.push(synthetic[next++].message);
360
360
  return mergeRichPartMirrorMessages(combined, sameTurnGroup);
361
361
  }
362
+ function utf8WidthAt(value3, index) {
363
+ const code = value3.charCodeAt(index);
364
+ if (code < 128) return {
365
+ bytes: 1,
366
+ units: 1
367
+ };
368
+ if (code < 2048) return {
369
+ bytes: 2,
370
+ units: 1
371
+ };
372
+ if (code >= 55296 && code <= 56319) {
373
+ const next = value3.charCodeAt(index + 1);
374
+ if (next >= 56320 && next <= 57343) return {
375
+ bytes: 4,
376
+ units: 2
377
+ };
378
+ }
379
+ return {
380
+ bytes: 3,
381
+ units: 1
382
+ };
383
+ }
384
+ function utf8ByteLength(value3) {
385
+ let bytes = 0;
386
+ for (let i = 0; i < value3.length; ) {
387
+ const width = utf8WidthAt(value3, i);
388
+ bytes += width.bytes;
389
+ i += width.units;
390
+ }
391
+ return bytes;
392
+ }
393
+ function clampUtf8(value3, maxBytes) {
394
+ let bytes = 0;
395
+ for (let i = 0; i < value3.length; ) {
396
+ const width = utf8WidthAt(value3, i);
397
+ if (bytes + width.bytes > maxBytes) return value3.slice(0, i);
398
+ bytes += width.bytes;
399
+ i += width.units;
400
+ }
401
+ return value3;
402
+ }
403
+ function mapBytes(fields, withMark) {
404
+ return utf8ByteLength(JSON.stringify(withMark ? {
405
+ ...fields,
406
+ [LOG_FIELDS_CLAMPED_KEY]: true
407
+ } : fields));
408
+ }
409
+ function withValueCap(fields, capBytes) {
410
+ const out = {};
411
+ for (const [key, value3] of Object.entries(fields)) {
412
+ out[key] = typeof value3 === "string" ? clampUtf8(value3, capBytes) : value3;
413
+ }
414
+ return out;
415
+ }
416
+ function describeValue(value3) {
417
+ if (value3 === null) return "null";
418
+ if (value3 === void 0) return "undefined";
419
+ if (Array.isArray(value3)) return "an array";
420
+ return typeof value3 === "object" ? "an object" : `a ${typeof value3}`;
421
+ }
422
+ function assertValidLogFieldKey(key) {
423
+ if (!LOG_FIELD_KEY_PATTERN.test(key)) {
424
+ throw new LuaLogFieldsError(`log fields: key \`${key}\` is not allowed \u2014 a key must match ${LOG_FIELD_KEY_PATTERN.source} (lower-case, starts with a letter, then letters/digits/underscore, at most 64 characters)`);
425
+ }
426
+ if (LOG_FIELD_SECRET_SHAPED_KEY_PATTERNS.some((pattern) => pattern.test(key))) {
427
+ throw new LuaLogFieldsError(`log fields: key \`${key}\` is not allowed \u2014 it has the shape of a credential that the log-drain scrubber masks in values; a field KEY is never scrubbed, so it may never carry a secret shape`);
428
+ }
429
+ if (key === LOG_FIELDS_CLAMPED_KEY) {
430
+ throw new LuaLogFieldsError(`log fields: key \`${key}\` is reserved \u2014 the platform sets it to mark a field map it had to clamp`);
431
+ }
432
+ }
433
+ function normalizeLogFields(fields) {
434
+ if (fields === void 0 || fields === null) return void 0;
435
+ if (typeof fields !== "object" || Array.isArray(fields)) {
436
+ throw new LuaLogFieldsError(`log fields: expected a plain object of fields, received ${describeValue(fields)}`);
437
+ }
438
+ const entries = Object.entries(fields);
439
+ if (entries.length > LOG_FIELDS_MAX_KEYS) {
440
+ throw new LuaLogFieldsError(`log fields: ${entries.length} keys exceeds the cap of ${LOG_FIELDS_MAX_KEYS} keys per call`);
441
+ }
442
+ const out = {};
443
+ for (const [key, value3] of entries) {
444
+ assertValidLogFieldKey(key);
445
+ if (typeof value3 === "string" || typeof value3 === "boolean") {
446
+ out[key] = value3;
447
+ continue;
448
+ }
449
+ if (typeof value3 === "number") {
450
+ if (!Number.isFinite(value3)) {
451
+ throw new LuaLogFieldsError(`log fields: \`${key}\` must be a finite number \u2014 NaN and Infinity have no JSON form`);
452
+ }
453
+ out[key] = value3;
454
+ continue;
455
+ }
456
+ if (Array.isArray(value3)) {
457
+ const parts = [];
458
+ for (const element of value3) {
459
+ if (typeof element !== "string") {
460
+ throw new LuaLogFieldsError(`log fields: \`${key}\` is an array, so every element must be a string (found ${describeValue(element)})`);
461
+ }
462
+ if (element.includes(",")) {
463
+ throw new LuaLogFieldsError(`log fields: \`${key}\` \u2014 an array element may not contain a comma; elements are comma-joined into one value (with \`${key}${LOG_FIELD_ARRAY_COUNT_SUFFIX}\` beside it), so a comma inside one would be indistinguishable from the separator`);
464
+ }
465
+ parts.push(element);
466
+ }
467
+ out[key] = parts.join(",");
468
+ out[`${key}${LOG_FIELD_ARRAY_COUNT_SUFFIX}`] = parts.length;
469
+ continue;
470
+ }
471
+ throw new LuaLogFieldsError(`log fields: \`${key}\` must be a string, number, boolean or string[] \u2014 received ${describeValue(value3)}` + (value3 !== null && typeof value3 === "object" ? " (nested objects are not supported)" : ""));
472
+ }
473
+ const keys = Object.keys(out);
474
+ if (keys.length === 0) return void 0;
475
+ if (keys.length > LOG_FIELDS_MAX_KEYS) {
476
+ throw new LuaLogFieldsError(`log fields: ${keys.length} keys exceeds the cap of ${LOG_FIELDS_MAX_KEYS} keys per call \u2014 an array field also contributes its \`${LOG_FIELD_ARRAY_COUNT_SUFFIX.slice(1)}\` sibling`);
477
+ }
478
+ if (clampLogFields(out)) out[LOG_FIELDS_CLAMPED_KEY] = true;
479
+ return out;
480
+ }
481
+ function clampLogFields(out) {
482
+ let clamped = false;
483
+ for (const [key, value3] of Object.entries(out)) {
484
+ if (typeof value3 !== "string") continue;
485
+ const capped = clampUtf8(value3, LOG_FIELD_MAX_VALUE_BYTES);
486
+ if (capped !== value3) {
487
+ out[key] = capped;
488
+ clamped = true;
489
+ }
490
+ }
491
+ if (mapBytes(out, clamped) <= LOG_FIELDS_MAX_BYTES) return clamped;
492
+ const base = {
493
+ ...out
494
+ };
495
+ let low = 0;
496
+ let high = LOG_FIELD_MAX_VALUE_BYTES;
497
+ let best = 0;
498
+ while (low <= high) {
499
+ const mid = Math.floor((low + high) / 2);
500
+ if (mapBytes(withValueCap(base, mid), true) <= LOG_FIELDS_MAX_BYTES) {
501
+ best = mid;
502
+ low = mid + 1;
503
+ } else {
504
+ high = mid - 1;
505
+ }
506
+ }
507
+ const fitted = withValueCap(base, best);
508
+ for (const key of Object.keys(out)) out[key] = fitted[key];
509
+ return true;
510
+ }
362
511
  function isDesktopFileCommandName(value3) {
363
512
  return typeof value3 === "string" && DESKTOP_FILE_COMMAND_SET.has(value3);
364
513
  }
@@ -586,6 +735,81 @@ function buildDefaultPersona(agentName) {
586
735
  function buildPersonalSpaceStartingPersona(agentName) {
587
736
  return PERSONAL_SPACE_STARTING_PERSONA.replace(new RegExp(AGENT_NAME_TOKEN.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g"), () => agentName || "Your assistant");
588
737
  }
738
+ function utf8Bytes(value3) {
739
+ let bytes = 0;
740
+ for (let i = 0; i < value3.length; ) {
741
+ const code = value3.charCodeAt(i);
742
+ if (code < 128) {
743
+ bytes += 1;
744
+ i += 1;
745
+ } else if (code < 2048) {
746
+ bytes += 2;
747
+ i += 1;
748
+ } else if (code >= 55296 && code <= 56319) {
749
+ const next = value3.charCodeAt(i + 1);
750
+ if (next >= 56320 && next <= 57343) {
751
+ bytes += 4;
752
+ i += 2;
753
+ } else {
754
+ bytes += 3;
755
+ i += 1;
756
+ }
757
+ } else {
758
+ bytes += 3;
759
+ i += 1;
760
+ }
761
+ }
762
+ return bytes;
763
+ }
764
+ function isPlainFieldKey(key) {
765
+ if (!LOG_FIELD_KEY_PATTERN.test(key)) return false;
766
+ return !LOG_FIELD_SECRET_SHAPED_KEY_PATTERNS.some((pattern) => pattern.test(key));
767
+ }
768
+ function describeAgentLogFieldsViolation(value3) {
769
+ if (typeof value3 !== "object" || value3 === null || Array.isArray(value3)) {
770
+ return "fields must be an object";
771
+ }
772
+ const entries = Object.entries(value3);
773
+ let userKeys = 0;
774
+ for (const [key, entry] of entries) {
775
+ if (key === LOG_FIELDS_CLAMPED_KEY) {
776
+ if (entry !== true) return `fields.${key} must be true`;
777
+ continue;
778
+ }
779
+ userKeys += 1;
780
+ if (key.endsWith(LOG_FIELD_ARRAY_COUNT_SUFFIX)) {
781
+ const base = key.slice(0, -LOG_FIELD_ARRAY_COUNT_SUFFIX.length);
782
+ if (!isPlainFieldKey(base)) return `fields key \`${key}\` is not a legal field key`;
783
+ if (typeof entry !== "number" || !Number.isFinite(entry)) {
784
+ return `fields.${key} must be a finite number`;
785
+ }
786
+ continue;
787
+ }
788
+ if (!isPlainFieldKey(key)) return `fields key \`${key}\` is not a legal field key`;
789
+ if (typeof entry === "string") {
790
+ if (utf8Bytes(entry) > LOG_FIELD_MAX_VALUE_BYTES) {
791
+ return `fields.${key} exceeds ${LOG_FIELD_MAX_VALUE_BYTES} bytes`;
792
+ }
793
+ continue;
794
+ }
795
+ if (typeof entry === "boolean") continue;
796
+ if (typeof entry === "number") {
797
+ if (!Number.isFinite(entry)) return `fields.${key} must be a finite number`;
798
+ continue;
799
+ }
800
+ return `fields.${key} must be a string, number or boolean`;
801
+ }
802
+ if (userKeys > LOG_FIELDS_MAX_KEYS) {
803
+ return `fields has ${userKeys} keys, over the cap of ${LOG_FIELDS_MAX_KEYS}`;
804
+ }
805
+ if (utf8Bytes(JSON.stringify(value3)) > LOG_FIELDS_MAX_BYTES) {
806
+ return `fields exceeds ${LOG_FIELDS_MAX_BYTES} bytes`;
807
+ }
808
+ return void 0;
809
+ }
810
+ function isAgentLogFields(value3) {
811
+ return describeAgentLogFieldsViolation(value3) === void 0;
812
+ }
589
813
  function maxDrainsForTier(tier) {
590
814
  return tier === "enterprise" ? LOG_DRAIN_MAX_PER_ORG_ENTERPRISE : LOG_DRAIN_MAX_PER_ORG;
591
815
  }
@@ -594,6 +818,148 @@ function resolvePresetEndpoint(type, site) {
594
818
  if (!template3 || !site) return void 0;
595
819
  return template3.replace("{site}", site);
596
820
  }
821
+ function isLogDrainBucketHostAllowed(provider, host) {
822
+ const normalized = host.trim().toLowerCase().replace(/^\[|\]$/g, "");
823
+ if (!normalized) return false;
824
+ return (LOG_DRAIN_BUCKET_HOST_PATTERNS[provider] ?? []).some((pattern) => pattern.test(normalized));
825
+ }
826
+ function resolveDrainBucketEndpoint(bucket) {
827
+ if (!LOG_DRAIN_BUCKET_NAME_PATTERN.test(bucket.bucket)) {
828
+ throw new Error("log drain bucket name is not a valid S3 bucket label");
829
+ }
830
+ if (bucket.provider === "aws") {
831
+ const region = bucket.region.trim().toLowerCase();
832
+ if (!/^[a-z0-9-]{1,32}$/.test(region)) throw new Error("log drain bucket region is not an AWS region");
833
+ return `https://${bucket.bucket}.s3.${region}.amazonaws.com`;
834
+ }
835
+ if (!bucket.endpoint) throw new Error(`log drain '${bucket.provider}' bucket requires an endpoint`);
836
+ let url;
837
+ try {
838
+ url = new URL(bucket.endpoint);
839
+ } catch {
840
+ throw new Error(`log drain '${bucket.provider}' bucket endpoint is not an absolute URL`);
841
+ }
842
+ if (url.protocol !== "https:") throw new Error(`log drain '${bucket.provider}' bucket endpoint must be https`);
843
+ return `https://${url.host}/${bucket.bucket}`;
844
+ }
845
+ function drainBucketUsesPathStyle(provider) {
846
+ return provider !== "aws";
847
+ }
848
+ function presetIntakeHosts(type) {
849
+ const template3 = LOG_DRAIN_ENDPOINT_TEMPLATES[type];
850
+ const sites = LOG_DRAIN_PRESET_SITES[type];
851
+ if (!template3 || !sites) return [];
852
+ const hosts = [];
853
+ for (const site of sites) {
854
+ try {
855
+ hosts.push(new URL(template3.replace("{site}", site)).hostname.toLowerCase());
856
+ } catch {
857
+ }
858
+ }
859
+ return hosts;
860
+ }
861
+ function presetHostAllowlist(type) {
862
+ return [
863
+ ...presetIntakeHosts(type),
864
+ ...LOG_DRAIN_PRESET_HOST_SUFFIXES[type] ?? []
865
+ ];
866
+ }
867
+ function drainHostAllowlistAllows(allowlist, host) {
868
+ const needle = host.toLowerCase();
869
+ return allowlist.some((raw) => {
870
+ const entry = raw.toLowerCase();
871
+ if (entry.startsWith(".")) return needle === entry.slice(1) || needle.endsWith(entry);
872
+ return entry === needle;
873
+ });
874
+ }
875
+ function drainDestinationIsVendorPinned(type) {
876
+ if (LOG_DRAIN_ENDPOINT_TEMPLATES[type]) return true;
877
+ return presetHostAllowlist(type).length > 0;
878
+ }
879
+ function drainVerificationOwnership(input) {
880
+ if (input.proved === true) return "verified";
881
+ if (input.mode === "test-post" && drainDestinationIsVendorPinned(input.type)) return "verified";
882
+ return "reachability-only";
883
+ }
884
+ function drainOwnershipOrDefault(ownership) {
885
+ return ownership === "verified" ? "verified" : "reachability-only";
886
+ }
887
+ function assertDrainContentAck(input) {
888
+ const turningOn = input.next === true && input.was !== true;
889
+ if (!turningOn) return void 0;
890
+ if (input.ack !== true) throw new DrainContentAckRequiredError();
891
+ return {
892
+ by: input.actor,
893
+ source: input.source,
894
+ at: input.at,
895
+ ...input.inheritedFrom ? {
896
+ inheritedFrom: input.inheritedFrom
897
+ } : {}
898
+ };
899
+ }
900
+ function composePresetEndpoint(type, endpoint) {
901
+ const rule = LOG_DRAIN_PRESET_PATHS[type];
902
+ if (!rule) return void 0;
903
+ let url;
904
+ try {
905
+ url = new URL(endpoint);
906
+ } catch {
907
+ return {
908
+ ok: false,
909
+ reason: "invalid_url"
910
+ };
911
+ }
912
+ if (url.protocol !== "https:" || !url.hostname) return {
913
+ ok: false,
914
+ reason: "invalid_url"
915
+ };
916
+ const origin = url.origin;
917
+ const path3 = url.pathname.replace(/\/+$/, "");
918
+ if (!rule.segment) {
919
+ const base2 = path3.endsWith(rule.path) ? path3.slice(0, path3.length - rule.path.length) : path3;
920
+ return {
921
+ ok: true,
922
+ endpoint: `${origin}${base2}${rule.path}`
923
+ };
924
+ }
925
+ const marker = `${rule.path}/`;
926
+ const at = path3.indexOf(marker);
927
+ if (at < 0) {
928
+ return {
929
+ ok: false,
930
+ reason: rule.segment === "dataset" ? "missing_dataset" : "missing_token"
931
+ };
932
+ }
933
+ const base = path3.slice(0, at);
934
+ const afterMarker = path3.slice(at + marker.length);
935
+ const rest = rule.suffix && afterMarker.endsWith(rule.suffix) ? afterMarker.slice(0, afterMarker.length - rule.suffix.length) : afterMarker;
936
+ if (!rest || rest.includes("/")) {
937
+ return {
938
+ ok: false,
939
+ reason: rule.segment === "dataset" ? "missing_dataset" : "missing_token"
940
+ };
941
+ }
942
+ if (!PRESET_SEGMENT_RE.test(rest)) return {
943
+ ok: false,
944
+ reason: "bad_segment"
945
+ };
946
+ if (rule.segment === "dataset") {
947
+ return {
948
+ ok: true,
949
+ endpoint: `${origin}${base}${rule.path}/${rest}${rule.suffix ?? ""}`,
950
+ dataset: rest
951
+ };
952
+ }
953
+ return {
954
+ ok: true,
955
+ endpoint: `${origin}${base}${rule.path}/`,
956
+ token: rest
957
+ };
958
+ }
959
+ function joinPresetEndpointToken(endpoint, token) {
960
+ if (typeof token !== "string" || !PRESET_SEGMENT_RE.test(token)) return endpoint;
961
+ return endpoint.endsWith("/") ? `${endpoint}${token}` : `${endpoint}/${token}`;
962
+ }
597
963
  function nextLogDrainQuotaResetAt(now) {
598
964
  return new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate() + 1)).toISOString();
599
965
  }
@@ -640,6 +1006,264 @@ function logDrainQuotaDroppedSeverities(degradation) {
640
1006
  return [];
641
1007
  }
642
1008
  }
1009
+ function isLogExportSource(source) {
1010
+ return LOG_EXPORT_SOURCES.includes(source);
1011
+ }
1012
+ function clamp(value3, max) {
1013
+ if (typeof value3 !== "string") return void 0;
1014
+ const trimmed = value3.trim();
1015
+ if (!trimmed) return void 0;
1016
+ return trimmed.length <= max ? trimmed : `${trimmed.slice(0, max - 1)}\u2026`;
1017
+ }
1018
+ function numberOr(value3, fallback) {
1019
+ return typeof value3 === "number" && Number.isFinite(value3) ? value3 : fallback;
1020
+ }
1021
+ function isoOr(value3) {
1022
+ return typeof value3 === "string" && value3.length > 0 ? value3 : void 0;
1023
+ }
1024
+ function logDrainNoticeView(drain, now) {
1025
+ const health = drain.health ?? {};
1026
+ const quota = drain.quota;
1027
+ const view = {
1028
+ id: String(drain.id ?? ""),
1029
+ name: String(drain.name ?? ""),
1030
+ type: drain.type,
1031
+ state: drain.state,
1032
+ deliveredCount24h: numberOr(health.deliveredCount24h, 0),
1033
+ droppedCount24h: numberOr(health.droppedCount24h, 0),
1034
+ rejectedCount24h: numberOr(health.rejectedCount24h, 0),
1035
+ backlog: numberOr(health.backlog, 0),
1036
+ quotaRatio: quota ? logDrainQuotaRatio(quota, now) : 0
1037
+ };
1038
+ if (health.pauseReason) view.pauseReason = health.pauseReason;
1039
+ const lastError = clamp(health.lastError, LOG_DRAIN_NOTICE_ERROR_MAX_CHARS);
1040
+ if (lastError) view.lastError = lastError;
1041
+ if (typeof health.lastStatusCode === "number") view.lastStatusCode = health.lastStatusCode;
1042
+ const lastSuccessAt = isoOr(health.lastSuccessAt);
1043
+ if (lastSuccessAt) view.lastSuccessAt = lastSuccessAt;
1044
+ const lastFailureAt = isoOr(health.lastFailureAt);
1045
+ if (lastFailureAt) view.lastFailureAt = lastFailureAt;
1046
+ const resetsAt = isoOr(quota?.resetsAt);
1047
+ if (resetsAt) view.quotaResetsAt = resetsAt;
1048
+ return view;
1049
+ }
1050
+ function logDrainHealthCardPath(orgId, drainId) {
1051
+ return `/orgs/${encodeURIComponent(orgId)}/log-drains/${encodeURIComponent(drainId)}`;
1052
+ }
1053
+ function percent(ratio) {
1054
+ if (!Number.isFinite(ratio) || ratio <= 0) return "0%";
1055
+ return `${Math.round(ratio * 100)}%`;
1056
+ }
1057
+ function becauseClause(view) {
1058
+ const bits = [];
1059
+ if (view.lastStatusCode) bits.push(`HTTP ${view.lastStatusCode}`);
1060
+ if (view.lastError) bits.push(view.lastError);
1061
+ return bits.length ? ` Last error: ${bits.join(" \u2014 ")}.` : "";
1062
+ }
1063
+ function logDrainNoticeCopy(payload) {
1064
+ const view = payload.drain;
1065
+ const name = view.name || view.id;
1066
+ const backlog = view.backlog > 0 ? ` ${view.backlog.toLocaleString("en-US")} records are buffered.` : "";
1067
+ switch (payload.kind) {
1068
+ case "failing":
1069
+ return {
1070
+ title: name,
1071
+ subject: `Log drain "${name}" is failing`,
1072
+ previewLine: "Failing \u2014 15 minutes without a successful delivery",
1073
+ summary: `The log drain "${name}" (${view.type}) has gone 15 minutes without a successful delivery and is now failing.${becauseClause(view)}${backlog} Records keep buffering; the drain pauses itself after 24 hours without a success.`,
1074
+ status: "failed",
1075
+ priority: "high"
1076
+ };
1077
+ case "paused_auto":
1078
+ return {
1079
+ title: name,
1080
+ subject: `Log drain "${name}" was paused`,
1081
+ previewLine: "Paused \u2014 24 hours without a successful delivery",
1082
+ summary: `The log drain "${name}" (${view.type}) was paused automatically after 24 hours without a successful delivery.${becauseClause(view)}${backlog} Nothing is being sent while it is paused, and the buffer is delivered on resume. Fix the destination, then resume it from the drain's health card or with \`lua drains resume ${view.id}\`.`,
1083
+ status: "failed",
1084
+ priority: "high"
1085
+ };
1086
+ case "paused_quota":
1087
+ return {
1088
+ title: name,
1089
+ subject: `Log drain "${name}" was paused \u2014 daily quota exhausted`,
1090
+ previewLine: `Paused \u2014 ${percent(view.quotaRatio)} of the daily allowance used`,
1091
+ summary: `The log drain "${name}" (${view.type}) reached ${percent(view.quotaRatio)} of its daily allowance and was paused.${backlog} Records keep buffering. The allowance resets${view.quotaResetsAt ? ` at ${view.quotaResetsAt}` : " at 00:00 UTC"}; narrow the drain's sources or raise the plan's limit to stop it happening again, then resume it with \`lua drains resume ${view.id}\`.`,
1092
+ status: "failed",
1093
+ priority: "high"
1094
+ };
1095
+ case "quota_warning":
1096
+ return {
1097
+ title: name,
1098
+ subject: `Log drain "${name}" has used 80% of its daily allowance`,
1099
+ previewLine: `${percent(view.quotaRatio)} of the daily allowance used`,
1100
+ summary: `The log drain "${name}" (${view.type}) has used ${percent(view.quotaRatio)} of today's allowance. Past 100% debug records are dropped, past 125% info records as well, and at 150% the drain pauses. The allowance resets${view.quotaResetsAt ? ` at ${view.quotaResetsAt}` : " at 00:00 UTC"}.`,
1101
+ status: "failed",
1102
+ priority: "normal"
1103
+ };
1104
+ case "dropped": {
1105
+ const dropped = payload.droppedCount ?? view.droppedCount24h;
1106
+ return {
1107
+ title: name,
1108
+ subject: `Log drain "${name}" is dropping records`,
1109
+ previewLine: `${dropped.toLocaleString("en-US")} records dropped`,
1110
+ summary: `The log drain "${name}" (${view.type}) dropped ${dropped.toLocaleString("en-US")} records because its buffer filled up faster than the destination accepted them.${becauseClause(view)} The gap is reported in band on the next successful batch, so the range is recoverable from the destination itself.`,
1111
+ status: "failed",
1112
+ priority: "high"
1113
+ };
1114
+ }
1115
+ case "verification_pending":
1116
+ return {
1117
+ title: name,
1118
+ subject: `Log drain "${name}" is still unverified`,
1119
+ previewLine: "Unverified after 24 hours \u2014 nothing is being delivered",
1120
+ summary: `The log drain "${name}" (${view.type}) was created more than 24 hours ago and has never completed verification, so nothing is being delivered to it.${becauseClause(view)} Run \`lua drains verify ${view.id}\` once the destination echoes the verification token.`,
1121
+ status: "failed",
1122
+ priority: "normal"
1123
+ };
1124
+ default: {
1125
+ const exhaustive = payload.kind;
1126
+ throw new Error(`unhandled log-drain notice kind: ${String(exhaustive)}`);
1127
+ }
1128
+ }
1129
+ }
1130
+ function logDrainNoticeLabel(kind) {
1131
+ switch (kind) {
1132
+ case "failing":
1133
+ return "failing";
1134
+ case "paused_auto":
1135
+ return "paused (no success for 24 h)";
1136
+ case "paused_quota":
1137
+ return "paused (daily allowance exhausted)";
1138
+ case "quota_warning":
1139
+ return "80% of the daily allowance used";
1140
+ case "dropped":
1141
+ return "dropping records";
1142
+ case "verification_pending":
1143
+ return "still unverified";
1144
+ default: {
1145
+ const exhaustive = kind;
1146
+ throw new Error(`unhandled log-drain notice kind: ${String(exhaustive)}`);
1147
+ }
1148
+ }
1149
+ }
1150
+ function logDrainNoticeDigestCopy(payload) {
1151
+ const entries = [
1152
+ ...payload.entries ?? []
1153
+ ].sort((a, b) => a.at < b.at ? -1 : a.at > b.at ? 1 : 0).slice(0, LOG_DRAIN_DIGEST_MAX_ENTRIES);
1154
+ const drains = new Set(entries.map((entry) => entry.drainId));
1155
+ const noun = `${entries.length} notice${entries.length === 1 ? "" : "s"}`;
1156
+ const across = `${drains.size} drain${drains.size === 1 ? "" : "s"}`;
1157
+ const lines = entries.map((entry) => `${entry.drainName || entry.drainId} \u2014 ${logDrainNoticeLabel(entry.kind)}`);
1158
+ return {
1159
+ title: "Log drains \u2014 daily summary",
1160
+ subject: `Log drains \u2014 ${noun} in the last day`,
1161
+ previewLine: `${noun} across ${across}`,
1162
+ summary: `${noun} across ${across} in the last day: ${lines.join("; ")}.`,
1163
+ status: "failed",
1164
+ priority: "normal"
1165
+ };
1166
+ }
1167
+ function finiteNonNegative(value3) {
1168
+ return typeof value3 === "number" && Number.isFinite(value3) && value3 >= 0 ? value3 : void 0;
1169
+ }
1170
+ function mergeLimits(base, stored) {
1171
+ const patch = stored ?? {};
1172
+ return {
1173
+ eventsPerDay: finiteNonNegative(patch.eventsPerDay) ?? base.eventsPerDay,
1174
+ bytesPerDay: finiteNonNegative(patch.bytesPerDay) ?? base.bytesPerDay
1175
+ };
1176
+ }
1177
+ function mergeCard(base, stored) {
1178
+ const patch = stored ?? {};
1179
+ return {
1180
+ includedEventsPerMonth: finiteNonNegative(patch.includedEventsPerMonth) ?? base.includedEventsPerMonth,
1181
+ includedBytesPerMonth: finiteNonNegative(patch.includedBytesPerMonth) ?? base.includedBytesPerMonth,
1182
+ usdPerMillionEvents: finiteNonNegative(patch.usdPerMillionEvents) ?? base.usdPerMillionEvents,
1183
+ usdPerGiB: finiteNonNegative(patch.usdPerGiB) ?? base.usdPerGiB
1184
+ };
1185
+ }
1186
+ function resolveStoredLogDrainPlanConfig(stored) {
1187
+ const values = stored;
1188
+ if (!values || typeof values.version !== "number" || !Number.isFinite(values.version)) {
1189
+ return DEFAULT_LOG_DRAIN_PLAN_CONFIG;
1190
+ }
1191
+ const tiers = {};
1192
+ const pricing = {};
1193
+ for (const tier of TIERS) {
1194
+ tiers[tier] = mergeLimits(DEFAULT_LOG_DRAIN_PLAN_CONFIG.tiers[tier], values.tiers?.[tier]);
1195
+ pricing[tier] = mergeCard(DEFAULT_LOG_DRAIN_PLAN_CONFIG.pricing[tier], values.pricing?.[tier]);
1196
+ }
1197
+ const orgs = {};
1198
+ for (const [orgId, override] of Object.entries(values.orgs ?? {})) {
1199
+ if (!orgId || !override || typeof override !== "object") continue;
1200
+ const row = {};
1201
+ if (override.tier && TIERS.includes(override.tier)) row.tier = override.tier;
1202
+ const events = finiteNonNegative(override.eventsPerDay);
1203
+ const bytes = finiteNonNegative(override.bytesPerDay);
1204
+ if (events !== void 0) row.eventsPerDay = events;
1205
+ if (bytes !== void 0) row.bytesPerDay = bytes;
1206
+ if (override.pricing && typeof override.pricing === "object") row.pricing = {
1207
+ ...override.pricing
1208
+ };
1209
+ if (Object.keys(row).length) orgs[orgId] = row;
1210
+ }
1211
+ return {
1212
+ version: values.version,
1213
+ tiers,
1214
+ pricing,
1215
+ ...Object.keys(orgs).length ? {
1216
+ orgs
1217
+ } : {}
1218
+ };
1219
+ }
1220
+ function logDrainPlanTierOverride(config, orgId) {
1221
+ return orgId ? config.orgs?.[orgId]?.tier : void 0;
1222
+ }
1223
+ function logDrainQuotaLimitsFor(config, orgId, tier) {
1224
+ const base = config.tiers[tier] ?? config.tiers.team ?? LOG_DRAIN_QUOTAS.team;
1225
+ const override = orgId ? config.orgs?.[orgId] : void 0;
1226
+ if (!override) return {
1227
+ ...base
1228
+ };
1229
+ return {
1230
+ eventsPerDay: override.eventsPerDay ?? base.eventsPerDay,
1231
+ bytesPerDay: override.bytesPerDay ?? base.bytesPerDay
1232
+ };
1233
+ }
1234
+ function logDrainPriceCardFor(config, orgId, tier) {
1235
+ const base = config.pricing[tier] ?? config.pricing.team ?? DEFAULT_LOG_DRAIN_PLAN_CONFIG.pricing.team;
1236
+ const override = orgId ? config.orgs?.[orgId]?.pricing : void 0;
1237
+ return override ? mergeCard(base, override) : {
1238
+ ...base
1239
+ };
1240
+ }
1241
+ function priceDrainUsage(totals, card, tier) {
1242
+ const billableRecords = Math.max(0, Math.floor(finiteNonNegative(totals?.billableRecords) ?? 0));
1243
+ const billableBytes = Math.max(0, Math.floor(finiteNonNegative(totals?.billableBytes) ?? 0));
1244
+ const includedEvents = card.includedEventsPerMonth;
1245
+ const includedBytes = card.includedBytesPerMonth;
1246
+ const overageEvents = Math.max(0, billableRecords - includedEvents);
1247
+ const overageBytes = Math.max(0, billableBytes - includedBytes);
1248
+ const eventsUsd = overageEvents / 1e6 * card.usdPerMillionEvents;
1249
+ const bytesUsd = overageBytes / LOG_DRAIN_BYTES_PER_GIB * card.usdPerGiB;
1250
+ const unpriced = card.usdPerMillionEvents === 0 && card.usdPerGiB === 0;
1251
+ const round = /* @__PURE__ */ __name2((usd) => Math.round(usd * 100) / 100, "round");
1252
+ return {
1253
+ lineItem: LOG_DRAIN_USAGE_LINE_ITEM,
1254
+ tier,
1255
+ billableRecords,
1256
+ billableBytes,
1257
+ includedEvents,
1258
+ includedBytes,
1259
+ overageEvents,
1260
+ overageBytes,
1261
+ eventsUsd: round(eventsUsd),
1262
+ bytesUsd: round(bytesUsd),
1263
+ usd: round(Math.max(eventsUsd, bytesUsd)),
1264
+ unpriced
1265
+ };
1266
+ }
643
1267
  function encodeLogCursor(c) {
644
1268
  return Buffer.from(JSON.stringify({
645
1269
  t: c.t,
@@ -1358,7 +1982,7 @@ function effectiveAgentFeatures(base, override, rule) {
1358
1982
  if (rule === "wholesale") return override || base || void 0;
1359
1983
  return effectiveAgentFeatureRows(base, override).rows;
1360
1984
  }
1361
- var __defProp2, __name2, PLUMBING_THREAD_CHANNELS, CHANNEL_CONVERSATION_CHANNELS, HIDDEN_THREAD_CHANNELS, CHANNEL_SEND_CHANNELS, REVIEWABLE_ACTION_EXECUTE_TOOL_ALLOWLIST, REVIEWABLE_STANDING_START_TOOLS, REVIEWABLE_MCP_SEND_TOOL_SUFFIX, MCP_TOOL_READ_VERB_RE, MCP_DRAFT_CREATE_VERBS, NON_INTERACTIVE_CHANNELS, RICH_PARTS_MESSAGE_ID_PREFIX, SCREENSHOT_MESSAGE_ID_PREFIX, BROWSER_COMMANDS, BROWSER_COMMAND_NAMES, DESKTOP_FILE_COMMANDS, DESKTOP_FILE_COMMAND_SET, WORKFLOW_RETRY_BACKOFFS, WORKFLOW_RETRY_MIN_ATTEMPTS, WORKFLOW_RETRY_POLICY_KEYS, WORKFLOW_RETRY_MAX_ATTEMPTS, WORKFLOW_RETRY_ENGINE_KEYS, WORKFLOW_JOB_RESOURCES, WORKFLOW_SIDE_EFFECTS, WORKFLOW_MODEL_CLASSES, WORKFLOW_DEFAULT_TASK_CLASS_MAP, WORKFLOW_MODELS_MAX_CLASS_DEFAULT, WORKFLOW_AUTONOMY_NUMERIC_KEYS, WORKFLOW_AUTONOMY_ENVELOPE_KEYS, WORKFLOW_AUTONOMY_DEFAULTS, WORKFLOW_AUTONOMY_CEILINGS, WORKFLOW_AUTONOMY_MAX_RUNS_PER_HOUR_ENV, WORKFLOW_AUTONOMY_MAX_RUNS_PER_HOUR_CEILING, WORKFLOW_AUTONOMY_FORMS_DEFAULT, WORKFLOW_AUTONOMY_MIN_VALUE, WORKFLOW_JOB_RANGES, WORKFLOW_JOB_RANGE_MEMBERS, WORKFLOW_SINGLE_STEP_TYPES, WORKFLOW_HITL_ENTRY_TYPES, WORKFLOW_ARM_ENTRY_TYPES, WORKFLOW_HITL_ARM_CONTAINERS, WORKFLOW_GRAPH_ENTRY_STEP_KINDS, WORKFLOW_ARM_ENTRY_STEP_KINDS, WORKFLOW_BUDGET_MAX_DURATION_SECONDS, MODEL_ID_BYOK_PROVIDERS, MODEL_CLASS_REF_PREFIX, MODEL_SNAPSHOT_SUFFIX, REASONING_EFFORT_VALUES, IMPLICIT_MODEL_SELECTION_SOURCES, PLATFORM_FALLBACK_MODEL_SOURCE, AGENT_NAME_TOKEN, DEFAULT_PERSONA_GUIDE, PERSONAL_SPACE_STARTING_PERSONA, AGENT_LOG_SOURCES, LOG_DRAIN_SOURCES, LOG_DRAIN_MAX_PER_ORG, LOG_DRAIN_MAX_PER_ORG_ENTERPRISE, LOG_DRAIN_ENDPOINT_TEMPLATES, LOG_DRAIN_QUOTAS, LOG_DRAIN_QUOTA_WARN_RATIO, LOG_DRAIN_QUOTA_DROP_DEBUG_RATIO, LOG_DRAIN_QUOTA_DROP_INFO_RATIO, LOG_DRAIN_QUOTA_PAUSE_RATIO, LOG_CURSOR_MAX_LENGTH, LOG_CURSOR_MAX_ID_LENGTH, LOG_RELATIVE_TIME_PATTERN, LOG_ISO_TIME_PATTERN, RELATIVE_UNIT_MS, CORE_DRAINING_CODE, CORE_DRAINING_DEFAULT_RETRY_MS, CORE_DRAINING_MAX_RETRY_MS, VoiceNameSchema, PluginProviderSchema, RealtimeProviderSchema, PluginClassSchema, ModelDescriptorSchema, InferenceModelSchema, PluginModelSchema, RealtimeModelSchema, LuaVoiceModelSchema, TurnDetectionSchema, InterruptionSchema, BuiltinAudioClipSchema, AudioConfigSchema, BackgroundAudioEntrySchema, BackgroundAudioSchema, LuaVoiceConfigInnerSchema, LuaVoiceConfigSchema, LuaVoiceRefSchema, LUA_JOB_DEFAULT_TIMEOUT_SECONDS, LUA_JOB_MIN_TIMEOUT_SECONDS, LUA_JOB_MAX_TIMEOUT_SECONDS, KEYS, MAX_BODY_BYTES, MAX_ID_LENGTH, UTC_ISO, TEMPLATE_TRIGGER_URL_ENV_PREFIX, TEMPLATE_INSTALL_POLICY_PER_WORKSPACE_VALUES, SUBJECT_TYPES, SubjectTypeSchema, CREDENTIAL_TYPES, CredentialTypeSchema, DEVICE_OPERATIONS, DeviceOperationSchema, DEVICE_SCOPE_BY_OPERATION, DeviceBindingSchema, IdSchema, SESSION_AUTH_TIME_MAX_S, PrincipalDescriptorSchema, ActorDescriptorSchema, PrincipalOwnerSchema, CredentialLifecycleSchema, GeneralCredentialDescriptorSchema, DeviceCredentialDescriptorSchema, GeneralPrincipalContextSchema, DeviceCredentialPrincipalContextSchema, RawPrincipalContextSchema, PrincipalContextSchema, DeviceCredentialClaimSchema, LUA_CLIENT_HEADER, LUA_CLIENT_APPS, SEMVER_PATTERN, WEB_RELEASE_PATTERN, CLIENT_HEADER_PATTERN, LUA_SESSION_ID_CLAIM, SESSION_REVOKED_CODE, AUTHZ_PROJECTION_VERSION, ProjectedScopeSchema, DisplayRoleSchema, AuthorizationPrincipalSchema, CredentialContextSchema, ProjectionAnomalySchema, ProjectedOrgSchema, ProjectedResourceSchema, CapabilityProfilesSchema, RoleCatalogSchema, EffectiveAuthorizationSchema, ResourcePageSchema, SYSTEM_USER_PREFIX, WORKFLOW_RUN_IN_FLIGHT, WORKFLOW_RUN_IDLE, WORKFLOW_RUN_TERMINAL, WORKFLOW_RUN_STATUSES, WORKFLOW_RUN_GATE_KINDS, WORKFLOW_STEP_STATUSES, WORKFLOW_STEP_IN_FLIGHT, WORKFLOW_SIGNAL_EVENT_SITES, ARCHIVE_WINDOW_MARGIN_DAYS, WORKFLOW_ORG_PURGING_TTL_S, WORKFLOW_ORG_PURGE_FORCE_AFTER_MS, IDEMPOTENCY_HOLDING_STATUSES, WORKFLOW_SCHEDULED_RUN_ID_PREFIX, CLOUD_TASK_RUN_ID_PREFIX, WORKFLOW_SCHEDULE_KEY_MAX, WORKFLOW_SCHEDULE_IDEMPOTENCY_KEY_PREFIX, WORKFLOW_OPERATION_ID_PREFIX, WORKFLOW_CONNECTION_KEY_RE, WORKFLOW_JOB_MODEL_SOURCE_LABELS, WORKFLOW_SUSPEND_KINDS, WORKFLOW_RUN_NOTIFICATION_SUSPENDED_KINDS, WORKFLOW_SIGNAL_PAYLOAD_MAX_BYTES, WORKFLOW_RESOLVE_OUTPUT_MAX_BYTES, WORKFLOW_GOAL_JUDGE_SELF, REDACTED_PLACEHOLDER, PROVIDER_MESSAGE_MAX_CHARS, ERROR_MESSAGE_MAX_CHARS, SECRET_LITERAL_PATTERNS, SECRET_NAME, SECRET_PAIR_PATTERNS, GROUP_COUNT, WORKFLOW_SECRET_KEY_RE, WORKFLOW_RESERVED_SECRET_KEYS, SCRUB_INPUT_MAX_CHARS, SCRUB_CUT_BACKOFF_CHARS, WORKFLOW_AUDIT_EVENTS, WORKFLOW_AUDIT_METADATA_MAX_BYTES, INDENT, WRAP_WIDTH, NOUNS, GET_TOOL_NAMES, PREAMBLE, WORKFLOW_APPROVAL_OUTPUT_DECISIONS, WORKFLOW_APPROVAL_OUTPUT_SCHEMA, JSON_FENCE_RE, DEFAULT_ON_AGENT_FEATURES, SUBAGENT_PER_KEY_FLAG_ENV, PER_KEY_FLAG_VALUES;
1985
+ var __defProp2, __name2, PLUMBING_THREAD_CHANNELS, CHANNEL_CONVERSATION_CHANNELS, HIDDEN_THREAD_CHANNELS, CHANNEL_SEND_CHANNELS, REVIEWABLE_ACTION_EXECUTE_TOOL_ALLOWLIST, REVIEWABLE_STANDING_START_TOOLS, REVIEWABLE_MCP_SEND_TOOL_SUFFIX, MCP_TOOL_READ_VERB_RE, MCP_DRAFT_CREATE_VERBS, NON_INTERACTIVE_CHANNELS, RICH_PARTS_MESSAGE_ID_PREFIX, SCREENSHOT_MESSAGE_ID_PREFIX, BROWSER_COMMANDS, BROWSER_COMMAND_NAMES, LOG_FIELDS_MAX_KEYS, LOG_FIELD_MAX_VALUE_BYTES, LOG_FIELDS_MAX_BYTES, LOG_FIELD_KEY_PATTERN, LOG_FIELD_SECRET_SHAPED_KEY_PATTERNS, LOG_FIELDS_CLAMPED_KEY, LOG_FIELD_ARRAY_COUNT_SUFFIX, LuaLogFieldsError, DESKTOP_FILE_COMMANDS, DESKTOP_FILE_COMMAND_SET, WORKFLOW_RETRY_BACKOFFS, WORKFLOW_RETRY_MIN_ATTEMPTS, WORKFLOW_RETRY_POLICY_KEYS, WORKFLOW_RETRY_MAX_ATTEMPTS, WORKFLOW_RETRY_ENGINE_KEYS, WORKFLOW_JOB_RESOURCES, WORKFLOW_SIDE_EFFECTS, WORKFLOW_MODEL_CLASSES, WORKFLOW_DEFAULT_TASK_CLASS_MAP, WORKFLOW_MODELS_MAX_CLASS_DEFAULT, WORKFLOW_AUTONOMY_NUMERIC_KEYS, WORKFLOW_AUTONOMY_ENVELOPE_KEYS, WORKFLOW_AUTONOMY_DEFAULTS, WORKFLOW_AUTONOMY_CEILINGS, WORKFLOW_AUTONOMY_MAX_RUNS_PER_HOUR_ENV, WORKFLOW_AUTONOMY_MAX_RUNS_PER_HOUR_CEILING, WORKFLOW_AUTONOMY_FORMS_DEFAULT, WORKFLOW_AUTONOMY_MIN_VALUE, WORKFLOW_JOB_RANGES, WORKFLOW_JOB_RANGE_MEMBERS, WORKFLOW_SINGLE_STEP_TYPES, WORKFLOW_HITL_ENTRY_TYPES, WORKFLOW_ARM_ENTRY_TYPES, WORKFLOW_HITL_ARM_CONTAINERS, WORKFLOW_GRAPH_ENTRY_STEP_KINDS, WORKFLOW_ARM_ENTRY_STEP_KINDS, WORKFLOW_BUDGET_MAX_DURATION_SECONDS, MODEL_ID_BYOK_PROVIDERS, MODEL_CLASS_REF_PREFIX, MODEL_SNAPSHOT_SUFFIX, REASONING_EFFORT_VALUES, IMPLICIT_MODEL_SELECTION_SOURCES, PLATFORM_FALLBACK_MODEL_SOURCE, AGENT_NAME_TOKEN, DEFAULT_PERSONA_GUIDE, PERSONAL_SPACE_STARTING_PERSONA, AGENT_LOG_SOURCES, AGENT_LOG_FIELDS_MAX_WIRE_KEYS, LOG_DRAIN_TYPES_PHASE1, LOG_DRAIN_TYPES_PHASE2, LOG_DRAIN_TYPES_IMPLEMENTED, LOG_DRAIN_SOURCES, LOG_DRAIN_MAX_PER_ORG, LOG_DRAIN_MAX_PER_ORG_ENTERPRISE, LOG_DRAIN_DATADOG_SITES, LOG_DRAIN_NEWRELIC_SITES, LOG_DRAIN_ENDPOINT_TEMPLATES, LOG_DRAIN_PRESET_SITES, LOG_DRAIN_BUCKET_NAME_PATTERN, LOG_DRAIN_BUCKET_HOST_PATTERNS, LOG_DRAIN_PRESET_HOST_SUFFIXES, DRAIN_CONTENT_ACK_ERROR_CODE, DRAIN_CONTENT_ACK_FIELD, DrainContentAckRequiredError, LOG_DRAIN_PRESET_PATHS, PRESET_SEGMENT_RE, LOG_DRAIN_QUOTAS, LOG_DRAIN_QUOTA_WARN_RATIO, LOG_DRAIN_QUOTA_DROP_DEBUG_RATIO, LOG_DRAIN_QUOTA_DROP_INFO_RATIO, LOG_DRAIN_QUOTA_PAUSE_RATIO, LOG_EXPORT_SOURCES, LOG_EXPORT_MAX_WINDOW_DAYS, LOG_EXPORT_MAX_WINDOW_MS, LOG_EXPORT_URL_TTL_SECONDS, LOG_EXPORT_FILE_RETENTION_DAYS, LOG_EXPORT_FILE_RETENTION_MS, LOG_EXPORT_PART_MAX_BYTES, LOG_DRAIN_NOTICE_RATE_LIMIT_MS, LOG_DRAIN_VERIFICATION_STALE_MS, LOG_DRAIN_DIGEST_INTERVAL_MS, LOG_DRAIN_DIGEST_MAX_ENTRIES, LOG_DRAIN_NOTICE_ERROR_MAX_CHARS, LOG_DRAIN_USAGE_LINE_ITEM, LOG_DRAIN_BYTES_PER_GIB, DEFAULT_LOG_DRAIN_PLAN_CONFIG, TIERS, LOG_CURSOR_MAX_LENGTH, LOG_CURSOR_MAX_ID_LENGTH, LOG_RELATIVE_TIME_PATTERN, LOG_ISO_TIME_PATTERN, RELATIVE_UNIT_MS, CORE_DRAINING_CODE, CORE_DRAINING_DEFAULT_RETRY_MS, CORE_DRAINING_MAX_RETRY_MS, VoiceNameSchema, PluginProviderSchema, RealtimeProviderSchema, PluginClassSchema, ModelDescriptorSchema, InferenceModelSchema, PluginModelSchema, RealtimeModelSchema, LuaVoiceModelSchema, TurnDetectionSchema, InterruptionSchema, BuiltinAudioClipSchema, AudioConfigSchema, BackgroundAudioEntrySchema, BackgroundAudioSchema, LuaVoiceConfigInnerSchema, LuaVoiceConfigSchema, LuaVoiceRefSchema, LUA_JOB_DEFAULT_TIMEOUT_SECONDS, LUA_JOB_MIN_TIMEOUT_SECONDS, LUA_JOB_MAX_TIMEOUT_SECONDS, KEYS, MAX_BODY_BYTES, MAX_ID_LENGTH, UTC_ISO, TEMPLATE_TRIGGER_URL_ENV_PREFIX, TEMPLATE_INSTALL_POLICY_PER_WORKSPACE_VALUES, SUBJECT_TYPES, SubjectTypeSchema, CREDENTIAL_TYPES, CredentialTypeSchema, DEVICE_OPERATIONS, DeviceOperationSchema, DEVICE_SCOPE_BY_OPERATION, DeviceBindingSchema, IdSchema, SESSION_AUTH_TIME_MAX_S, PrincipalDescriptorSchema, ActorDescriptorSchema, PrincipalOwnerSchema, CredentialLifecycleSchema, GeneralCredentialDescriptorSchema, DeviceCredentialDescriptorSchema, GeneralPrincipalContextSchema, DeviceCredentialPrincipalContextSchema, RawPrincipalContextSchema, PrincipalContextSchema, DeviceCredentialClaimSchema, LUA_CLIENT_HEADER, LUA_CLIENT_APPS, SEMVER_PATTERN, WEB_RELEASE_PATTERN, CLIENT_HEADER_PATTERN, LUA_SESSION_ID_CLAIM, SESSION_REVOKED_CODE, AUTHZ_PROJECTION_VERSION, ProjectedScopeSchema, DisplayRoleSchema, AuthorizationPrincipalSchema, CredentialContextSchema, ProjectionAnomalySchema, ProjectedOrgSchema, ProjectedResourceSchema, CapabilityProfilesSchema, RoleCatalogSchema, EffectiveAuthorizationSchema, ResourcePageSchema, SYSTEM_USER_PREFIX, WORKFLOW_RUN_IN_FLIGHT, WORKFLOW_RUN_IDLE, WORKFLOW_RUN_TERMINAL, WORKFLOW_RUN_STATUSES, WORKFLOW_RUN_GATE_KINDS, WORKFLOW_STEP_STATUSES, WORKFLOW_STEP_IN_FLIGHT, WORKFLOW_SIGNAL_EVENT_SITES, ARCHIVE_WINDOW_MARGIN_DAYS, WORKFLOW_ORG_PURGING_TTL_S, WORKFLOW_ORG_PURGE_FORCE_AFTER_MS, IDEMPOTENCY_HOLDING_STATUSES, WORKFLOW_SCHEDULED_RUN_ID_PREFIX, CLOUD_TASK_RUN_ID_PREFIX, WORKFLOW_SCHEDULE_KEY_MAX, WORKFLOW_SCHEDULE_IDEMPOTENCY_KEY_PREFIX, WORKFLOW_OPERATION_ID_PREFIX, WORKFLOW_CONNECTION_KEY_RE, WORKFLOW_JOB_MODEL_SOURCE_LABELS, WORKFLOW_SUSPEND_KINDS, WORKFLOW_RUN_NOTIFICATION_SUSPENDED_KINDS, WORKFLOW_SIGNAL_PAYLOAD_MAX_BYTES, WORKFLOW_RESOLVE_OUTPUT_MAX_BYTES, WORKFLOW_GOAL_JUDGE_SELF, REDACTED_PLACEHOLDER, PROVIDER_MESSAGE_MAX_CHARS, ERROR_MESSAGE_MAX_CHARS, SECRET_LITERAL_PATTERNS, SECRET_NAME, SECRET_PAIR_PATTERNS, GROUP_COUNT, WORKFLOW_SECRET_KEY_RE, WORKFLOW_RESERVED_SECRET_KEYS, SCRUB_INPUT_MAX_CHARS, SCRUB_CUT_BACKOFF_CHARS, WORKFLOW_AUDIT_EVENTS, WORKFLOW_AUDIT_METADATA_MAX_BYTES, INDENT, WRAP_WIDTH, NOUNS, GET_TOOL_NAMES, PREAMBLE, WORKFLOW_APPROVAL_OUTPUT_DECISIONS, WORKFLOW_APPROVAL_OUTPUT_SCHEMA, JSON_FENCE_RE, DEFAULT_ON_AGENT_FEATURES, SUBAGENT_PER_KEY_FLAG_ENV, PER_KEY_FLAG_VALUES;
1362
1986
  var init_dist = __esm({
1363
1987
  "../shared-types/dist/index.mjs"() {
1364
1988
  "use strict";
@@ -1686,6 +2310,50 @@ var init_dist = __esm({
1686
2310
  }
1687
2311
  ];
1688
2312
  BROWSER_COMMAND_NAMES = BROWSER_COMMANDS.map((c) => c.name);
2313
+ LOG_FIELDS_MAX_KEYS = 32;
2314
+ LOG_FIELD_MAX_VALUE_BYTES = 1024;
2315
+ LOG_FIELDS_MAX_BYTES = 8192;
2316
+ LOG_FIELD_KEY_PATTERN = /^[a-z][a-z0-9_]{0,63}$/;
2317
+ LOG_FIELD_SECRET_SHAPED_KEY_PATTERNS = Object.freeze([
2318
+ /^api_[0-9a-f]{24,}$/,
2319
+ /^gh[pousr]_[a-z0-9]{36}$/,
2320
+ /^(?:sk|rk|pk)_(?:live|test)_[a-z0-9]{16,}$/,
2321
+ /^lua_hc_[a-z0-9]{20,}/,
2322
+ /^lua_sk_[a-z0-9]{20,}/
2323
+ ]);
2324
+ LOG_FIELDS_CLAMPED_KEY = "lua_fields_clamped";
2325
+ LOG_FIELD_ARRAY_COUNT_SUFFIX = ".count";
2326
+ LuaLogFieldsError = class _LuaLogFieldsError extends Error {
2327
+ static {
2328
+ __name(this, "_LuaLogFieldsError");
2329
+ }
2330
+ static {
2331
+ __name2(this, "LuaLogFieldsError");
2332
+ }
2333
+ constructor(message) {
2334
+ super(message);
2335
+ this.name = "LuaLogFieldsError";
2336
+ Object.setPrototypeOf(this, _LuaLogFieldsError.prototype);
2337
+ }
2338
+ };
2339
+ __name(utf8WidthAt, "utf8WidthAt");
2340
+ __name2(utf8WidthAt, "utf8WidthAt");
2341
+ __name(utf8ByteLength, "utf8ByteLength");
2342
+ __name2(utf8ByteLength, "utf8ByteLength");
2343
+ __name(clampUtf8, "clampUtf8");
2344
+ __name2(clampUtf8, "clampUtf8");
2345
+ __name(mapBytes, "mapBytes");
2346
+ __name2(mapBytes, "mapBytes");
2347
+ __name(withValueCap, "withValueCap");
2348
+ __name2(withValueCap, "withValueCap");
2349
+ __name(describeValue, "describeValue");
2350
+ __name2(describeValue, "describeValue");
2351
+ __name(assertValidLogFieldKey, "assertValidLogFieldKey");
2352
+ __name2(assertValidLogFieldKey, "assertValidLogFieldKey");
2353
+ __name(normalizeLogFields, "normalizeLogFields");
2354
+ __name2(normalizeLogFields, "normalizeLogFields");
2355
+ __name(clampLogFields, "clampLogFields");
2356
+ __name2(clampLogFields, "clampLogFields");
1689
2357
  DESKTOP_FILE_COMMANDS = [
1690
2358
  "files_roots",
1691
2359
  "files_stat",
@@ -2030,6 +2698,37 @@ This text is who you are for this person. As you learn them, their name, their w
2030
2698
  "workflow-script",
2031
2699
  "workflow"
2032
2700
  ];
2701
+ AGENT_LOG_FIELDS_MAX_WIRE_KEYS = LOG_FIELDS_MAX_KEYS + 1;
2702
+ __name(utf8Bytes, "utf8Bytes");
2703
+ __name2(utf8Bytes, "utf8Bytes");
2704
+ __name(isPlainFieldKey, "isPlainFieldKey");
2705
+ __name2(isPlainFieldKey, "isPlainFieldKey");
2706
+ __name(describeAgentLogFieldsViolation, "describeAgentLogFieldsViolation");
2707
+ __name2(describeAgentLogFieldsViolation, "describeAgentLogFieldsViolation");
2708
+ __name(isAgentLogFields, "isAgentLogFields");
2709
+ __name2(isAgentLogFields, "isAgentLogFields");
2710
+ LOG_DRAIN_TYPES_PHASE1 = [
2711
+ "http",
2712
+ "otlp",
2713
+ "datadog",
2714
+ "betterstack"
2715
+ ];
2716
+ LOG_DRAIN_TYPES_PHASE2 = [
2717
+ "splunk",
2718
+ "loki",
2719
+ "axiom",
2720
+ "newrelic",
2721
+ "sumologic"
2722
+ ];
2723
+ LOG_DRAIN_TYPES_IMPLEMENTED = [
2724
+ ...LOG_DRAIN_TYPES_PHASE1,
2725
+ ...LOG_DRAIN_TYPES_PHASE2,
2726
+ // RECONCILE-4 (W4-4). `s3` is implemented (H4 / PRO-1887) but is NOT in
2727
+ // `LOG_DRAIN_TYPES_PHASE2`, because its own flag opens it and `*` must not. It
2728
+ // belongs here all the same: this list is what a CLIENT may offer, and the server
2729
+ // remains the authority that answers 422 with the live list.
2730
+ "s3"
2731
+ ];
2033
2732
  LOG_DRAIN_SOURCES = [
2034
2733
  ...AGENT_LOG_SOURCES,
2035
2734
  "execution",
@@ -2038,17 +2737,123 @@ This text is who you are for this person. As you learn them, their name, their w
2038
2737
  "trigger-execution",
2039
2738
  "job-execution",
2040
2739
  "workflow-run",
2041
- "workflow-audit"
2740
+ "workflow-audit",
2741
+ // PRO-1884 (H2). Appended AFTER 'workflow-audit' so the enum stays additive and
2742
+ // no existing member changes position.
2743
+ "voice-call"
2042
2744
  ];
2043
2745
  LOG_DRAIN_MAX_PER_ORG = 5;
2044
2746
  LOG_DRAIN_MAX_PER_ORG_ENTERPRISE = 10;
2045
2747
  __name(maxDrainsForTier, "maxDrainsForTier");
2046
2748
  __name2(maxDrainsForTier, "maxDrainsForTier");
2749
+ LOG_DRAIN_DATADOG_SITES = [
2750
+ "datadoghq.com",
2751
+ "datadoghq.eu",
2752
+ "us3.datadoghq.com",
2753
+ "us5.datadoghq.com",
2754
+ "ap1.datadoghq.com",
2755
+ "ddog-gov.com"
2756
+ ];
2757
+ LOG_DRAIN_NEWRELIC_SITES = [
2758
+ "log-api.newrelic.com",
2759
+ "log-api.eu.newrelic.com",
2760
+ "log-api.jp.newrelic.com"
2761
+ ];
2047
2762
  LOG_DRAIN_ENDPOINT_TEMPLATES = {
2048
- datadog: "https://http-intake.logs.{site}/api/v2/logs"
2763
+ datadog: "https://http-intake.logs.{site}/api/v2/logs",
2764
+ newrelic: "https://{site}/log/v1"
2765
+ };
2766
+ LOG_DRAIN_PRESET_SITES = {
2767
+ datadog: LOG_DRAIN_DATADOG_SITES,
2768
+ newrelic: LOG_DRAIN_NEWRELIC_SITES
2049
2769
  };
2050
2770
  __name(resolvePresetEndpoint, "resolvePresetEndpoint");
2051
2771
  __name2(resolvePresetEndpoint, "resolvePresetEndpoint");
2772
+ LOG_DRAIN_BUCKET_NAME_PATTERN = /^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]$/;
2773
+ LOG_DRAIN_BUCKET_HOST_PATTERNS = Object.freeze({
2774
+ aws: [
2775
+ // virtual-host style, regional and legacy global
2776
+ /^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]\.s3(\.dualstack)?(\.[a-z0-9-]+)?\.amazonaws\.com$/,
2777
+ // path style
2778
+ /^s3(\.dualstack)?(\.[a-z0-9-]+)?\.amazonaws\.com$/
2779
+ ],
2780
+ r2: [
2781
+ /^[a-f0-9]{32}\.r2\.cloudflarestorage\.com$/
2782
+ ],
2783
+ gcs: [
2784
+ /^storage\.googleapis\.com$/
2785
+ ]
2786
+ });
2787
+ __name(isLogDrainBucketHostAllowed, "isLogDrainBucketHostAllowed");
2788
+ __name2(isLogDrainBucketHostAllowed, "isLogDrainBucketHostAllowed");
2789
+ __name(resolveDrainBucketEndpoint, "resolveDrainBucketEndpoint");
2790
+ __name2(resolveDrainBucketEndpoint, "resolveDrainBucketEndpoint");
2791
+ __name(drainBucketUsesPathStyle, "drainBucketUsesPathStyle");
2792
+ __name2(drainBucketUsesPathStyle, "drainBucketUsesPathStyle");
2793
+ __name(presetIntakeHosts, "presetIntakeHosts");
2794
+ __name2(presetIntakeHosts, "presetIntakeHosts");
2795
+ LOG_DRAIN_PRESET_HOST_SUFFIXES = {
2796
+ axiom: [
2797
+ ".axiom.co"
2798
+ ],
2799
+ sumologic: [
2800
+ ".sumologic.com"
2801
+ ]
2802
+ };
2803
+ __name(presetHostAllowlist, "presetHostAllowlist");
2804
+ __name2(presetHostAllowlist, "presetHostAllowlist");
2805
+ __name(drainHostAllowlistAllows, "drainHostAllowlistAllows");
2806
+ __name2(drainHostAllowlistAllows, "drainHostAllowlistAllows");
2807
+ __name(drainDestinationIsVendorPinned, "drainDestinationIsVendorPinned");
2808
+ __name2(drainDestinationIsVendorPinned, "drainDestinationIsVendorPinned");
2809
+ __name(drainVerificationOwnership, "drainVerificationOwnership");
2810
+ __name2(drainVerificationOwnership, "drainVerificationOwnership");
2811
+ __name(drainOwnershipOrDefault, "drainOwnershipOrDefault");
2812
+ __name2(drainOwnershipOrDefault, "drainOwnershipOrDefault");
2813
+ DRAIN_CONTENT_ACK_ERROR_CODE = "DRAIN_CONTENT_ACK_REQUIRED";
2814
+ DRAIN_CONTENT_ACK_FIELD = "includeContentAck";
2815
+ DrainContentAckRequiredError = class extends Error {
2816
+ static {
2817
+ __name(this, "DrainContentAckRequiredError");
2818
+ }
2819
+ static {
2820
+ __name2(this, "DrainContentAckRequiredError");
2821
+ }
2822
+ code = DRAIN_CONTENT_ACK_ERROR_CODE;
2823
+ field = DRAIN_CONTENT_ACK_FIELD;
2824
+ constructor() {
2825
+ super("includeContent ships end-user message content to a third party; set includeContentAck: true to confirm.");
2826
+ this.name = "DrainContentAckRequiredError";
2827
+ }
2828
+ };
2829
+ __name(assertDrainContentAck, "assertDrainContentAck");
2830
+ __name2(assertDrainContentAck, "assertDrainContentAck");
2831
+ LOG_DRAIN_PRESET_PATHS = {
2832
+ // https://docs.splunk.com/Documentation/Splunk/latest/Data/HECExamples
2833
+ splunk: {
2834
+ path: "/services/collector/event"
2835
+ },
2836
+ // https://grafana.com/docs/loki/latest/reference/loki-http-api/#ingest-logs
2837
+ loki: {
2838
+ path: "/loki/api/v1/push"
2839
+ },
2840
+ // https://axiom.co/docs/restapi/endpoints/ingestIntoDataset
2841
+ axiom: {
2842
+ path: "/v1/datasets",
2843
+ segment: "dataset",
2844
+ suffix: "/ingest"
2845
+ },
2846
+ // https://help.sumologic.com/docs/send-data/hosted-collectors/http-source/logs-metrics/upload-logs/
2847
+ sumologic: {
2848
+ path: "/receiver/v1/http",
2849
+ segment: "token"
2850
+ }
2851
+ };
2852
+ PRESET_SEGMENT_RE = /^[A-Za-z0-9._~%=+-]{1,512}$/;
2853
+ __name(composePresetEndpoint, "composePresetEndpoint");
2854
+ __name2(composePresetEndpoint, "composePresetEndpoint");
2855
+ __name(joinPresetEndpointToken, "joinPresetEndpointToken");
2856
+ __name2(joinPresetEndpointToken, "joinPresetEndpointToken");
2052
2857
  LOG_DRAIN_QUOTAS = {
2053
2858
  team: {
2054
2859
  eventsPerDay: 1e6,
@@ -2081,6 +2886,100 @@ This text is who you are for this person. As you learn them, their name, their w
2081
2886
  __name2(logDrainQuotaWarned, "logDrainQuotaWarned");
2082
2887
  __name(logDrainQuotaDroppedSeverities, "logDrainQuotaDroppedSeverities");
2083
2888
  __name2(logDrainQuotaDroppedSeverities, "logDrainQuotaDroppedSeverities");
2889
+ LOG_EXPORT_SOURCES = [
2890
+ ...AGENT_LOG_SOURCES,
2891
+ "execution"
2892
+ ];
2893
+ LOG_EXPORT_MAX_WINDOW_DAYS = 31;
2894
+ LOG_EXPORT_MAX_WINDOW_MS = LOG_EXPORT_MAX_WINDOW_DAYS * 24 * 60 * 60 * 1e3;
2895
+ LOG_EXPORT_URL_TTL_SECONDS = 24 * 60 * 60;
2896
+ LOG_EXPORT_FILE_RETENTION_DAYS = 7;
2897
+ LOG_EXPORT_FILE_RETENTION_MS = LOG_EXPORT_FILE_RETENTION_DAYS * 24 * 60 * 60 * 1e3;
2898
+ LOG_EXPORT_PART_MAX_BYTES = 256 * 1024 * 1024;
2899
+ __name(isLogExportSource, "isLogExportSource");
2900
+ __name2(isLogExportSource, "isLogExportSource");
2901
+ LOG_DRAIN_NOTICE_RATE_LIMIT_MS = 60 * 6e4;
2902
+ LOG_DRAIN_VERIFICATION_STALE_MS = 24 * 60 * 6e4;
2903
+ LOG_DRAIN_DIGEST_INTERVAL_MS = 24 * 60 * 6e4;
2904
+ LOG_DRAIN_DIGEST_MAX_ENTRIES = 50;
2905
+ LOG_DRAIN_NOTICE_ERROR_MAX_CHARS = 200;
2906
+ __name(clamp, "clamp");
2907
+ __name2(clamp, "clamp");
2908
+ __name(numberOr, "numberOr");
2909
+ __name2(numberOr, "numberOr");
2910
+ __name(isoOr, "isoOr");
2911
+ __name2(isoOr, "isoOr");
2912
+ __name(logDrainNoticeView, "logDrainNoticeView");
2913
+ __name2(logDrainNoticeView, "logDrainNoticeView");
2914
+ __name(logDrainHealthCardPath, "logDrainHealthCardPath");
2915
+ __name2(logDrainHealthCardPath, "logDrainHealthCardPath");
2916
+ __name(percent, "percent");
2917
+ __name2(percent, "percent");
2918
+ __name(becauseClause, "becauseClause");
2919
+ __name2(becauseClause, "becauseClause");
2920
+ __name(logDrainNoticeCopy, "logDrainNoticeCopy");
2921
+ __name2(logDrainNoticeCopy, "logDrainNoticeCopy");
2922
+ __name(logDrainNoticeLabel, "logDrainNoticeLabel");
2923
+ __name2(logDrainNoticeLabel, "logDrainNoticeLabel");
2924
+ __name(logDrainNoticeDigestCopy, "logDrainNoticeDigestCopy");
2925
+ __name2(logDrainNoticeDigestCopy, "logDrainNoticeDigestCopy");
2926
+ LOG_DRAIN_USAGE_LINE_ITEM = "log_drain_events";
2927
+ LOG_DRAIN_BYTES_PER_GIB = 1024 ** 3;
2928
+ DEFAULT_LOG_DRAIN_PLAN_CONFIG = {
2929
+ version: 1,
2930
+ tiers: {
2931
+ team: {
2932
+ ...LOG_DRAIN_QUOTAS.team
2933
+ },
2934
+ business: {
2935
+ ...LOG_DRAIN_QUOTAS.business
2936
+ },
2937
+ enterprise: {
2938
+ ...LOG_DRAIN_QUOTAS.enterprise
2939
+ }
2940
+ },
2941
+ pricing: {
2942
+ team: {
2943
+ includedEventsPerMonth: 0,
2944
+ includedBytesPerMonth: 0,
2945
+ usdPerMillionEvents: 0,
2946
+ usdPerGiB: 0
2947
+ },
2948
+ business: {
2949
+ includedEventsPerMonth: 0,
2950
+ includedBytesPerMonth: 0,
2951
+ usdPerMillionEvents: 0,
2952
+ usdPerGiB: 0
2953
+ },
2954
+ enterprise: {
2955
+ includedEventsPerMonth: 0,
2956
+ includedBytesPerMonth: 0,
2957
+ usdPerMillionEvents: 0,
2958
+ usdPerGiB: 0
2959
+ }
2960
+ }
2961
+ };
2962
+ TIERS = [
2963
+ "team",
2964
+ "business",
2965
+ "enterprise"
2966
+ ];
2967
+ __name(finiteNonNegative, "finiteNonNegative");
2968
+ __name2(finiteNonNegative, "finiteNonNegative");
2969
+ __name(mergeLimits, "mergeLimits");
2970
+ __name2(mergeLimits, "mergeLimits");
2971
+ __name(mergeCard, "mergeCard");
2972
+ __name2(mergeCard, "mergeCard");
2973
+ __name(resolveStoredLogDrainPlanConfig, "resolveStoredLogDrainPlanConfig");
2974
+ __name2(resolveStoredLogDrainPlanConfig, "resolveStoredLogDrainPlanConfig");
2975
+ __name(logDrainPlanTierOverride, "logDrainPlanTierOverride");
2976
+ __name2(logDrainPlanTierOverride, "logDrainPlanTierOverride");
2977
+ __name(logDrainQuotaLimitsFor, "logDrainQuotaLimitsFor");
2978
+ __name2(logDrainQuotaLimitsFor, "logDrainQuotaLimitsFor");
2979
+ __name(logDrainPriceCardFor, "logDrainPriceCardFor");
2980
+ __name2(logDrainPriceCardFor, "logDrainPriceCardFor");
2981
+ __name(priceDrainUsage, "priceDrainUsage");
2982
+ __name2(priceDrainUsage, "priceDrainUsage");
2084
2983
  LOG_CURSOR_MAX_LENGTH = 512;
2085
2984
  LOG_CURSOR_MAX_ID_LENGTH = 128;
2086
2985
  LOG_RELATIVE_TIME_PATTERN = /^(\d{1,9})([smhd])$/;
@@ -15859,6 +16758,20 @@ var env = Object.assign((key, _opts) => readEnv(key), {
15859
16758
  };
15860
16759
  }, "template")
15861
16760
  });
16761
+ var log = Object.freeze({
16762
+ debug: /* @__PURE__ */ __name((message, fields) => emitLocalLog("debug", message, fields), "debug"),
16763
+ info: /* @__PURE__ */ __name((message, fields) => emitLocalLog("info", message, fields), "info"),
16764
+ warn: /* @__PURE__ */ __name((message, fields) => emitLocalLog("warn", message, fields), "warn"),
16765
+ error: /* @__PURE__ */ __name((message, fields) => emitLocalLog("error", message, fields), "error")
16766
+ });
16767
+ function emitLocalLog(level, message, fields) {
16768
+ const normalized = normalizeLogFields(fields);
16769
+ const text = typeof message === "object" && message !== null ? JSON.stringify(message) : String(message);
16770
+ const write = level === "error" ? console.error : level === "warn" ? console.warn : level === "info" ? console.info : console.debug;
16771
+ if (normalized) write(text, JSON.stringify(normalized));
16772
+ else write(text);
16773
+ }
16774
+ __name(emitLocalLog, "emitLocalLog");
15862
16775
  var ToolFlag = /* @__PURE__ */ (function(ToolFlag2) {
15863
16776
  ToolFlag2["NONE"] = "none";
15864
16777
  ToolFlag2["IGNORE_ON_ENTER"] = "ignore_on_enter";
@@ -17378,6 +18291,7 @@ export {
17378
18291
  inSet2 as inSet,
17379
18292
  init2 as init,
17380
18293
  lit2 as lit,
18294
+ log,
17381
18295
  lt2 as lt,
17382
18296
  lte2 as lte,
17383
18297
  ne2 as ne,