stitchkit 0.115.0 → 0.117.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.
Files changed (140) hide show
  1. package/CHANGELOG.md +67 -0
  2. package/dist/agent-runtime/coding-tool-paths.d.ts.map +1 -1
  3. package/dist/agent-runtime/run-acquisition.d.ts +9 -2
  4. package/dist/agent-runtime/run-acquisition.d.ts.map +1 -1
  5. package/dist/agent-runtime/run-execution.d.ts +3 -0
  6. package/dist/agent-runtime/run-execution.d.ts.map +1 -1
  7. package/dist/agent-runtime/run-failure.d.ts +4 -1
  8. package/dist/agent-runtime/run-failure.d.ts.map +1 -1
  9. package/dist/agent-runtime/run-store-retry.d.ts +8 -0
  10. package/dist/agent-runtime/run-store-retry.d.ts.map +1 -0
  11. package/dist/agent-runtime/run-terminal.d.ts.map +1 -1
  12. package/dist/agent-runtime/run-unacquired.d.ts +19 -0
  13. package/dist/agent-runtime/run-unacquired.d.ts.map +1 -0
  14. package/dist/agent-runtime/store-conformance.d.ts +8 -0
  15. package/dist/agent-runtime/store-conformance.d.ts.map +1 -1
  16. package/dist/agent-runtime/store-driver-contract.d.ts +5 -0
  17. package/dist/agent-runtime/store-driver-contract.d.ts.map +1 -1
  18. package/dist/agent-runtime/terminal-commit.d.ts +34 -4
  19. package/dist/agent-runtime/terminal-commit.d.ts.map +1 -1
  20. package/dist/entrypoints/agent-runtime/coding-tools.js +4 -4
  21. package/dist/entrypoints/agent-runtime/harness.js +4 -4
  22. package/dist/entrypoints/agent-runtime/sandbox.js +4 -4
  23. package/dist/entrypoints/agent-runtime/sqlite/bun.js +7 -7
  24. package/dist/entrypoints/agent-runtime/sqlite/node.js +7 -7
  25. package/dist/entrypoints/agent-runtime/testing.js +26 -8
  26. package/dist/entrypoints/agent-runtime.d.ts +1 -1
  27. package/dist/entrypoints/agent-runtime.d.ts.map +1 -1
  28. package/dist/entrypoints/agent-runtime.js +17 -15
  29. package/dist/entrypoints/application/diagnostic-journal.js +1 -1
  30. package/dist/entrypoints/application/directory-inbox.js +3 -3
  31. package/dist/entrypoints/cli/publish.js +2 -2
  32. package/dist/entrypoints/cli.js +3 -3
  33. package/dist/entrypoints/files.js +2 -2
  34. package/dist/entrypoints/primitives.js +51 -154
  35. package/dist/entrypoints/server.js +6 -6
  36. package/dist/entrypoints/telegram/grammy.js +2 -2
  37. package/dist/entrypoints/telegram.d.ts +1 -1
  38. package/dist/entrypoints/telegram.d.ts.map +1 -1
  39. package/dist/entrypoints/telegram.js +203 -70
  40. package/dist/entrypoints/tools.js +3 -3
  41. package/dist/entrypoints/tracking/contract.d.ts +11 -0
  42. package/dist/entrypoints/tracking/contract.d.ts.map +1 -0
  43. package/dist/entrypoints/tracking/contract.js +162 -0
  44. package/dist/entrypoints/tracking/performance.d.ts +8 -0
  45. package/dist/entrypoints/tracking/performance.d.ts.map +1 -0
  46. package/dist/entrypoints/tracking/performance.js +181 -0
  47. package/dist/entrypoints/tracking/server.d.ts +1 -0
  48. package/dist/entrypoints/tracking/server.d.ts.map +1 -1
  49. package/dist/entrypoints/tracking/server.js +40 -12
  50. package/dist/entrypoints/tracking.d.ts +4 -3
  51. package/dist/entrypoints/tracking.d.ts.map +1 -1
  52. package/dist/entrypoints/tracking.js +43 -143
  53. package/dist/entrypoints/xlsx.d.ts +4 -0
  54. package/dist/entrypoints/xlsx.d.ts.map +1 -0
  55. package/dist/entrypoints/xlsx.js +406 -0
  56. package/dist/{index-crmswbrt.js → index-073nw9qr.js} +8 -8
  57. package/dist/index-1f40z584.js +7 -0
  58. package/dist/{index-nregbbsc.js → index-6f49krb5.js} +1 -1
  59. package/dist/{index-fdk4r4r6.js → index-75ns2ybj.js} +1 -1
  60. package/dist/{index-x0ddbj85.js → index-arjmb6de.js} +36 -1
  61. package/dist/{index-944x38nv.js → index-cycp3r3e.js} +125 -182
  62. package/dist/{index-dx5cm9pc.js → index-ghtpfz4z.js} +9 -9
  63. package/dist/{index-n3v695sb.js → index-hdjmdwsj.js} +277 -96
  64. package/dist/{index-7yx8j6ay.js → index-n5sv4z0w.js} +12 -3
  65. package/dist/{index-zqkz4af9.js → index-nqxrm176.js} +1 -1
  66. package/dist/{index-j8kf56wx.js → index-q968hy6y.js} +1 -1
  67. package/dist/index-qezhmp51.js +242 -0
  68. package/dist/index-vv1sch8a.js +94 -0
  69. package/dist/{index-3qqdqwxe.js → index-w7jq3fz9.js} +25 -5
  70. package/dist/{index-fggxgbrk.js → index-xx1j310k.js} +165 -102
  71. package/dist/internal/contained-files.d.ts +10 -0
  72. package/dist/internal/contained-files.d.ts.map +1 -1
  73. package/dist/internal/exclusive-lock.d.ts.map +1 -1
  74. package/dist/primitives/decimal.d.ts +10 -0
  75. package/dist/primitives/decimal.d.ts.map +1 -1
  76. package/dist/primitives/index.d.ts +3 -1
  77. package/dist/primitives/index.d.ts.map +1 -1
  78. package/dist/primitives/money.d.ts +20 -0
  79. package/dist/primitives/money.d.ts.map +1 -1
  80. package/dist/primitives/quantity.d.ts.map +1 -1
  81. package/dist/primitives/rounding.d.ts +40 -0
  82. package/dist/primitives/rounding.d.ts.map +1 -0
  83. package/dist/telegram/budget-transformer.d.ts +6 -3
  84. package/dist/telegram/budget-transformer.d.ts.map +1 -1
  85. package/dist/telegram/not-dispatched.d.ts +2 -2
  86. package/dist/telegram/not-dispatched.d.ts.map +1 -1
  87. package/dist/telegram/outbound-budget.d.ts +72 -20
  88. package/dist/telegram/outbound-budget.d.ts.map +1 -1
  89. package/dist/telegram/outbound-cost.d.ts +5 -0
  90. package/dist/telegram/outbound-cost.d.ts.map +1 -0
  91. package/dist/telegram/outbound-pauses.d.ts +17 -0
  92. package/dist/telegram/outbound-pauses.d.ts.map +1 -0
  93. package/dist/telegram/outbound-queue.d.ts +17 -0
  94. package/dist/telegram/outbound-queue.d.ts.map +1 -0
  95. package/dist/tools/cli/view.d.ts +7 -1
  96. package/dist/tools/cli/view.d.ts.map +1 -1
  97. package/dist/tracking/attribution.d.ts +1 -1
  98. package/dist/tracking/attribution.d.ts.map +1 -1
  99. package/dist/tracking/client-visit.d.ts +3 -1
  100. package/dist/tracking/client-visit.d.ts.map +1 -1
  101. package/dist/tracking/client.d.ts.map +1 -1
  102. package/dist/tracking/performance/collector.d.ts +61 -0
  103. package/dist/tracking/performance/collector.d.ts.map +1 -0
  104. package/dist/tracking/performance/schema.d.ts +45 -0
  105. package/dist/tracking/performance/schema.d.ts.map +1 -0
  106. package/dist/tracking/performance/summary.d.ts +12 -0
  107. package/dist/tracking/performance/summary.d.ts.map +1 -0
  108. package/dist/tracking/privacy.d.ts +5 -0
  109. package/dist/tracking/privacy.d.ts.map +1 -0
  110. package/dist/tracking/server/telemetry-health.d.ts +31 -0
  111. package/dist/tracking/server/telemetry-health.d.ts.map +1 -0
  112. package/dist/xlsx/cells.d.ts +17 -0
  113. package/dist/xlsx/cells.d.ts.map +1 -0
  114. package/dist/xlsx/render.d.ts +11 -0
  115. package/dist/xlsx/render.d.ts.map +1 -0
  116. package/dist/xlsx/schema.d.ts +119 -0
  117. package/dist/xlsx/schema.d.ts.map +1 -0
  118. package/dist/xlsx/zip.d.ts +13 -0
  119. package/dist/xlsx/zip.d.ts.map +1 -0
  120. package/llms/agent-runtime.2.txt +40 -16
  121. package/llms/agent-runtime.3.txt +17 -0
  122. package/llms/agent-runtime.txt +6 -3
  123. package/llms/cli.txt +3 -1
  124. package/llms/primitives.txt +21 -0
  125. package/llms/stitchkit.txt +9 -6
  126. package/llms/telegram.txt +39 -12
  127. package/llms/tracking-contract.txt +32 -0
  128. package/llms/tracking-performance.txt +21 -0
  129. package/llms/tracking-server.txt +1 -0
  130. package/llms/tracking.txt +75 -12
  131. package/llms/{upgrading-0.104.0-0.115.0.txt → upgrading-0.105.0-0.117.0.txt} +73 -124
  132. package/llms/{upgrading-0.44.0-0.61.0.txt → upgrading-0.44.0-0.62.0.txt} +120 -3
  133. package/llms/{upgrading-0.62.0-0.78.0.txt → upgrading-0.63.0-0.83.0.txt} +181 -120
  134. package/llms/{upgrading-0.79.0-0.103.13.txt → upgrading-0.84.0-0.104.0.txt} +124 -181
  135. package/llms/upgrading.txt +4 -4
  136. package/llms/xlsx.txt +107 -0
  137. package/llms-full.txt +390 -34
  138. package/llms.txt +15 -11
  139. package/package.json +27 -9
  140. package/dist/{index-z3d6smer.js → index-tdpmfks8.js} +5 -5
@@ -5,7 +5,7 @@ import"../index-2kbysk9s.js";
5
5
  import"../index-n8z3rvzw.js";
6
6
  import {
7
7
  attemptExclusiveLock
8
- } from "../index-z3d6smer.js";
8
+ } from "../index-tdpmfks8.js";
9
9
  import {
10
10
  TELEGRAM_SEND_FAILURE_REASONS2,
11
11
  classifyTelegramSendFailure2
@@ -908,11 +908,8 @@ function telegramOperatorSender(config) {
908
908
  }
909
909
  // src/telegram/outbound-budget.ts
910
910
  import { z as z5 } from "zod";
911
- var DEFAULT_MESSAGES_PER_SECOND = 24;
912
- var DEFAULT_RETRY_AFTER_FALLBACK_MS = 1000;
913
- var TOKEN = z5.string().min(1, "token must not be empty");
914
- var MESSAGES_PER_SECOND = z5.number().positive().max(1000);
915
- var RETRY_AFTER_FALLBACK_MS = z5.number().int().nonnegative().max(MAX_TIMER_MS);
911
+
912
+ // src/telegram/outbound-cost.ts
916
913
  var SEND_METHODS = new Set([
917
914
  "sendMessage",
918
915
  "sendPhoto",
@@ -959,15 +956,69 @@ function sendCost(method, payload) {
959
956
  const value = payload[field];
960
957
  return Array.isArray(value) ? Math.max(1, value.length) : 1;
961
958
  }
962
- function createBudget(rate, fallback, now, sleepFor) {
963
- const interval = 1000 / rate;
959
+ function chatOf(payload) {
960
+ if (!isRecord(payload))
961
+ return;
962
+ const chat = payload.chat_id;
963
+ return typeof chat === "number" || typeof chat === "string" ? String(chat) : undefined;
964
+ }
965
+
966
+ // src/telegram/outbound-pauses.ts
967
+ function createOutboundPauses({ tokenPauseChats, tokenPauseWindowMs }, now, onPause) {
968
+ const chatPauses = new Map;
969
+ const refusedChats = new Map;
970
+ let pausedUntil = Number.NEGATIVE_INFINITY;
971
+ const pauseEnd = (milliseconds) => {
972
+ if (Number.isNaN(milliseconds) || milliseconds < 0) {
973
+ throw new RangeError("pause must be a non-negative number of milliseconds");
974
+ }
975
+ return now() + (Number.isFinite(milliseconds) ? milliseconds : Number.MAX_SAFE_INTEGER);
976
+ };
977
+ const pauseToken = (milliseconds) => {
978
+ pausedUntil = Math.max(pausedUntil, pauseEnd(milliseconds));
979
+ onPause();
980
+ };
981
+ const forgetExpired = (current) => {
982
+ for (const [chat, until] of chatPauses)
983
+ if (until <= current)
984
+ chatPauses.delete(chat);
985
+ for (const [chat, at] of refusedChats) {
986
+ if (current - at > tokenPauseWindowMs)
987
+ refusedChats.delete(chat);
988
+ }
989
+ };
990
+ return {
991
+ resumesAt: (chat) => Math.max(pausedUntil, (chat === undefined ? undefined : chatPauses.get(chat)) ?? Number.NEGATIVE_INFINITY),
992
+ pauseToken,
993
+ pauseChat(chat, milliseconds) {
994
+ const until = pauseEnd(milliseconds);
995
+ const current = now();
996
+ forgetExpired(current);
997
+ chatPauses.set(chat, Math.max(chatPauses.get(chat) ?? until, until));
998
+ refusedChats.set(chat, current);
999
+ onPause();
1000
+ if (refusedChats.size < tokenPauseChats)
1001
+ return;
1002
+ refusedChats.clear();
1003
+ pauseToken(milliseconds);
1004
+ },
1005
+ pausedForMs: () => Math.max(0, pausedUntil - now()),
1006
+ pausedChats() {
1007
+ forgetExpired(now());
1008
+ return chatPauses.size;
1009
+ }
1010
+ };
1011
+ }
1012
+
1013
+ // src/telegram/outbound-queue.ts
1014
+ function createOutboundQueue(interval, now, sleepFor, pauses) {
964
1015
  const queue = [];
965
1016
  let nextGrantAt = now();
966
- let pausedUntil = Number.NEGATIVE_INFINITY;
967
1017
  let granted = 0;
968
1018
  let messages = 0;
969
1019
  let draining = false;
970
- let wake;
1020
+ let waking;
1021
+ let wakingAt = Number.POSITIVE_INFINITY;
971
1022
  const wait = async (milliseconds, signal) => {
972
1023
  const delay = Math.min(MAX_TIMER_MS, Math.max(0, milliseconds));
973
1024
  if (delay <= 0)
@@ -978,18 +1029,40 @@ function createBudget(rate, fallback, now, sleepFor) {
978
1029
  else
979
1030
  await pending;
980
1031
  };
981
- const wakeWaiting = () => {
982
- const current = wake;
983
- wake = undefined;
1032
+ const wake = () => {
1033
+ const current = waking;
1034
+ waking = undefined;
984
1035
  current?.abort();
985
1036
  };
1037
+ const nextWaiter = (current) => {
1038
+ let bulk;
1039
+ let held;
1040
+ for (const waiter of queue) {
1041
+ if (pauses.resumesAt(waiter.chat) > current) {
1042
+ if (held === undefined || pauses.resumesAt(waiter.chat) < pauses.resumesAt(held.chat)) {
1043
+ held = waiter;
1044
+ }
1045
+ } else if (waiter.lane === "interactive") {
1046
+ return waiter;
1047
+ } else {
1048
+ bulk ??= waiter;
1049
+ }
1050
+ }
1051
+ return bulk ?? held;
1052
+ };
1053
+ const detach = (waiter) => {
1054
+ if (waiter.onAbort)
1055
+ waiter.signal?.removeEventListener("abort", waiter.onAbort);
1056
+ waiter.expiry?.abort();
1057
+ };
986
1058
  const remove = (waiter) => {
987
1059
  const index = queue.indexOf(waiter);
988
1060
  if (index === -1)
989
1061
  return;
1062
+ const next = nextWaiter(now()) === waiter;
990
1063
  queue.splice(index, 1);
991
- if (index === 0)
992
- wakeWaiting();
1064
+ if (next)
1065
+ wake();
993
1066
  };
994
1067
  const drain = async () => {
995
1068
  if (draining)
@@ -997,38 +1070,38 @@ function createBudget(rate, fallback, now, sleepFor) {
997
1070
  draining = true;
998
1071
  try {
999
1072
  while (queue.length > 0) {
1000
- const head = queue[0];
1073
+ const current = now();
1074
+ const head = nextWaiter(current);
1001
1075
  if (head === undefined)
1002
1076
  break;
1003
1077
  if (head.signal?.aborted) {
1004
1078
  remove(head);
1005
1079
  continue;
1006
1080
  }
1007
- const current = now();
1008
- const at = Math.max(current, nextGrantAt, pausedUntil);
1081
+ const at = Math.max(current, nextGrantAt, pauses.resumesAt(head.chat));
1009
1082
  if (at > current) {
1010
1083
  const controller = new AbortController;
1011
- wake = controller;
1084
+ waking = controller;
1085
+ wakingAt = at;
1012
1086
  try {
1013
1087
  await wait(at - current, controller.signal);
1014
1088
  } catch (error) {
1015
1089
  if (!controller.signal.aborted) {
1016
1090
  for (const waiter of queue.splice(0)) {
1017
- if (waiter.onAbort)
1018
- waiter.signal?.removeEventListener("abort", waiter.onAbort);
1091
+ detach(waiter);
1019
1092
  waiter.reject(error);
1020
1093
  }
1021
1094
  return;
1022
1095
  }
1023
1096
  } finally {
1024
- if (wake === controller)
1025
- wake = undefined;
1097
+ if (waking === controller)
1098
+ waking = undefined;
1099
+ wakingAt = Number.POSITIVE_INFINITY;
1026
1100
  }
1027
1101
  continue;
1028
1102
  }
1029
- queue.shift();
1030
- if (head.onAbort)
1031
- head.signal?.removeEventListener("abort", head.onAbort);
1103
+ queue.splice(queue.indexOf(head), 1);
1104
+ detach(head);
1032
1105
  nextGrantAt = at + head.cost * interval;
1033
1106
  granted += 1;
1034
1107
  messages += head.cost;
@@ -1040,84 +1113,140 @@ function createBudget(rate, fallback, now, sleepFor) {
1040
1113
  drain();
1041
1114
  }
1042
1115
  };
1043
- const acquire = (cost, signal) => {
1044
- if (!Number.isSafeInteger(cost) || cost < 1) {
1045
- return Promise.reject(new RangeError("cost must be an integer of at least one message"));
1046
- }
1047
- if (signal?.aborted)
1048
- return Promise.reject(signal.reason);
1049
- return new Promise((resolve, reject) => {
1050
- const waiter = { cost, resolve, reject, ...signal && { signal } };
1051
- if (signal) {
1052
- const onAbort = () => {
1053
- remove(waiter);
1054
- reject(signal.reason);
1055
- };
1056
- waiter.onAbort = onAbort;
1057
- signal.addEventListener("abort", onAbort, { once: true });
1058
- }
1059
- queue.push(waiter);
1060
- drain();
1116
+ const expireAfter = (waiter, maxWaitMs, reject) => {
1117
+ waiter.expiry = new AbortController;
1118
+ wait(maxWaitMs, waiter.expiry.signal).then(() => {
1119
+ if (!queue.includes(waiter))
1120
+ return;
1121
+ remove(waiter);
1122
+ detach(waiter);
1123
+ reject(new TelegramNotDispatchedError2("queue", "the Telegram request waited longer than maxWaitMs for its turn"));
1124
+ }, () => {
1125
+ return;
1061
1126
  });
1062
1127
  };
1063
- const pauseFor = (milliseconds) => {
1064
- if (Number.isNaN(milliseconds) || milliseconds < 0) {
1065
- throw new RangeError("pause must be a non-negative number of milliseconds");
1066
- }
1067
- const duration = Number.isFinite(milliseconds) ? milliseconds : Number.MAX_SAFE_INTEGER;
1068
- pausedUntil = Math.max(pausedUntil, now() + duration);
1069
- wakeWaiting();
1128
+ return {
1129
+ acquire(cost, signal, options) {
1130
+ if (!Number.isSafeInteger(cost) || cost < 1) {
1131
+ return Promise.reject(new RangeError("cost must be an integer of at least one message"));
1132
+ }
1133
+ if (signal?.aborted)
1134
+ return Promise.reject(signal.reason);
1135
+ const maxWaitMs = options?.maxWaitMs;
1136
+ if (maxWaitMs !== undefined && (!Number.isSafeInteger(maxWaitMs) || maxWaitMs < 1 || maxWaitMs > MAX_TIMER_MS)) {
1137
+ return Promise.reject(new RangeError(`maxWaitMs must be an integer from 1 to ${MAX_TIMER_MS}`));
1138
+ }
1139
+ const chat = options?.chatId === undefined ? undefined : String(options.chatId);
1140
+ return new Promise((resolve, reject) => {
1141
+ const waiter = {
1142
+ cost,
1143
+ lane: options?.lane ?? "interactive",
1144
+ chat,
1145
+ resolve,
1146
+ reject,
1147
+ ...signal && { signal }
1148
+ };
1149
+ if (signal) {
1150
+ const onAbort = () => {
1151
+ remove(waiter);
1152
+ detach(waiter);
1153
+ reject(signal.reason);
1154
+ };
1155
+ waiter.onAbort = onAbort;
1156
+ signal.addEventListener("abort", onAbort, { once: true });
1157
+ }
1158
+ if (maxWaitMs !== undefined)
1159
+ expireAfter(waiter, maxWaitMs, reject);
1160
+ queue.push(waiter);
1161
+ if (Math.max(now(), nextGrantAt, pauses.resumesAt(chat)) < wakingAt)
1162
+ wake();
1163
+ drain();
1164
+ });
1165
+ },
1166
+ wake,
1167
+ wait,
1168
+ counts: () => ({
1169
+ granted,
1170
+ messages,
1171
+ queued: queue.length,
1172
+ queuedBulk: queue.filter((waiter) => waiter.lane === "bulk").length
1173
+ })
1070
1174
  };
1071
- const pauseAfter = (error) => {
1175
+ }
1176
+
1177
+ // src/telegram/outbound-budget.ts
1178
+ var DEFAULT_MESSAGES_PER_SECOND = 24;
1179
+ var DEFAULT_RETRY_AFTER_FALLBACK_MS = 1000;
1180
+ var DEFAULT_TOKEN_PAUSE_CHATS = 3;
1181
+ var DEFAULT_TOKEN_PAUSE_WINDOW_MS = 5000;
1182
+ var TOKEN = z5.string().min(1, "token must not be empty");
1183
+ var MESSAGES_PER_SECOND = z5.number().positive().max(1000);
1184
+ var RETRY_AFTER_FALLBACK_MS = z5.number().int().nonnegative().max(MAX_TIMER_MS);
1185
+ var TOKEN_PAUSE_CHATS = z5.number().int().min(2).max(1000);
1186
+ var TOKEN_PAUSE_WINDOW_MS = z5.number().int().positive().max(MAX_TIMER_MS);
1187
+ function createBudget(policy, now, sleepFor) {
1188
+ const { rate, fallback } = policy;
1189
+ const pauses = createOutboundPauses(policy, now, () => queue.wake());
1190
+ const queue = createOutboundQueue(1000 / rate, now, sleepFor, pauses);
1191
+ const pauseAfter = (error, options) => {
1072
1192
  const failure = classifyTelegramSendFailure2(error);
1073
1193
  if (failure.reason !== "rate-limited")
1074
1194
  return;
1075
- pauseFor(failure.retryAfterSeconds === undefined ? fallback : failure.retryAfterSeconds * 1000);
1195
+ const milliseconds = failure.retryAfterSeconds === undefined ? fallback : failure.retryAfterSeconds * 1000;
1196
+ if (options?.chatId === undefined)
1197
+ pauses.pauseToken(milliseconds);
1198
+ else
1199
+ pauses.pauseChat(String(options.chatId), milliseconds);
1076
1200
  };
1077
- const waitUntilResumed = async (signal) => {
1201
+ const waitUntilResumed = async (signal, options) => {
1202
+ const chat = options?.chatId === undefined ? undefined : String(options.chatId);
1078
1203
  while (true) {
1079
1204
  if (signal?.aborted)
1080
1205
  throw signal.reason;
1081
- const remaining = pausedUntil - now();
1206
+ const remaining = pauses.resumesAt(chat) - now();
1082
1207
  if (remaining <= 0)
1083
1208
  return;
1084
- await wait(Math.min(remaining, MAX_TIMER_MS), signal);
1209
+ await queue.wait(Math.min(remaining, MAX_TIMER_MS), signal);
1085
1210
  }
1086
1211
  };
1087
- const run = async (method, payload, signal, send) => {
1212
+ const run = async (method, payload, signal, send, options) => {
1088
1213
  if (signal?.aborted)
1089
1214
  throw signal.reason;
1090
1215
  const cost = sendCost(method, payload);
1216
+ const chatId = options?.chatId ?? chatOf(payload);
1217
+ const call = chatId === undefined ? {} : { chatId };
1091
1218
  if (cost > 0)
1092
- await acquire(cost, signal);
1219
+ await queue.acquire(cost, signal, { ...options, ...call });
1093
1220
  if (signal?.aborted)
1094
1221
  throw signal.reason;
1222
+ const pauseOnRefusal = (refusal) => {
1223
+ if (cost > 0 && options?.refusal !== "call")
1224
+ pauseAfter(refusal, call);
1225
+ };
1095
1226
  let result;
1096
1227
  try {
1097
1228
  result = await send();
1098
1229
  } catch (error) {
1099
- pauseAfter(error);
1230
+ pauseOnRefusal(error);
1100
1231
  throw error;
1101
1232
  }
1102
1233
  if (isRecord(result) && result.ok === false)
1103
- pauseAfter(result);
1234
+ pauseOnRefusal(result);
1104
1235
  return result;
1105
1236
  };
1106
- const budget = {
1237
+ return {
1107
1238
  run,
1108
- acquire,
1109
- pause: pauseFor,
1239
+ acquire: queue.acquire,
1240
+ pause: pauses.pauseToken,
1110
1241
  pauseAfter,
1111
1242
  waitUntilResumed,
1112
1243
  stats: () => ({
1113
- granted,
1114
- messages,
1115
- queued: queue.length,
1244
+ ...queue.counts(),
1116
1245
  messagesPerSecond: rate,
1117
- pausedForMs: Math.max(0, pausedUntil - now())
1246
+ pausedForMs: pauses.pausedForMs(),
1247
+ pausedChats: pauses.pausedChats()
1118
1248
  })
1119
1249
  };
1120
- return budget;
1121
1250
  }
1122
1251
  var budgets = new Map;
1123
1252
  function keyOf(token) {
@@ -1127,20 +1256,24 @@ function telegramOutboundBudget(config) {
1127
1256
  const token = TOKEN.parse(config.token);
1128
1257
  const rate = MESSAGES_PER_SECOND.parse(config.messagesPerSecond ?? DEFAULT_MESSAGES_PER_SECOND);
1129
1258
  const fallback = RETRY_AFTER_FALLBACK_MS.parse(config.retryAfterFallbackMs ?? DEFAULT_RETRY_AFTER_FALLBACK_MS);
1259
+ const tokenPauseChats = TOKEN_PAUSE_CHATS.parse(config.tokenPauseChats ?? DEFAULT_TOKEN_PAUSE_CHATS);
1260
+ const tokenPauseWindowMs = TOKEN_PAUSE_WINDOW_MS.parse(config.tokenPauseWindowMs ?? DEFAULT_TOKEN_PAUSE_WINDOW_MS);
1130
1261
  const key = keyOf(token);
1131
1262
  const existing = budgets.get(key);
1132
1263
  if (existing) {
1133
- const same = existing.rate === rate && existing.fallback === fallback && existing.nowOption === config.now && existing.sleepOption === config.sleep;
1264
+ const same = existing.rate === rate && existing.fallback === fallback && existing.tokenPauseChats === tokenPauseChats && existing.tokenPauseWindowMs === tokenPauseWindowMs && existing.nowOption === config.now && existing.sleepOption === config.sleep;
1134
1265
  if (!same) {
1135
1266
  throw new Error("a Telegram outbound budget already exists for this token with different options");
1136
1267
  }
1137
1268
  return existing.budget;
1138
1269
  }
1139
- const budget = createBudget(rate, fallback, config.now ?? (() => performance.now()), config.sleep ?? sleep);
1270
+ const budget = createBudget({ rate, fallback, tokenPauseChats, tokenPauseWindowMs }, config.now ?? (() => performance.now()), config.sleep ?? sleep);
1140
1271
  budgets.set(key, {
1141
1272
  budget,
1142
1273
  rate,
1143
1274
  fallback,
1275
+ tokenPauseChats,
1276
+ tokenPauseWindowMs,
1144
1277
  nowOption: config.now,
1145
1278
  sleepOption: config.sleep
1146
1279
  });
@@ -74,7 +74,7 @@ import {
74
74
  WaitTimeoutError,
75
75
  runWaitOperation,
76
76
  createCli2
77
- } from "../index-3qqdqwxe.js";
77
+ } from "../index-w7jq3fz9.js";
78
78
  import"../index-2kbysk9s.js";
79
79
  import {
80
80
  ManagedFilePathSchema2,
@@ -94,8 +94,8 @@ import {
94
94
  import {
95
95
  createToolInvoker2
96
96
  } from "../index-vwhrnp67.js";
97
- import"../index-j8kf56wx.js";
98
- import"../index-x0ddbj85.js";
97
+ import"../index-q968hy6y.js";
98
+ import"../index-arjmb6de.js";
99
99
  import {
100
100
  collectToolSurface
101
101
  } from "../index-86nbfysb.js";
@@ -0,0 +1,11 @@
1
+ /**
2
+ * `stitchkit/tracking/contract` — the wire shapes of tracking: the contract
3
+ * factory, the event and visit schemas, and the performance sample with its
4
+ * trust threshold. It brings `zod`, so it belongs to the server and to a shared
5
+ * contract, and browser code that sends events does not import it. → ADR 0280.
6
+ */
7
+ export { createTrackingContract, type TrackingContractConfig, type TrackingContractEndpoints, } from '../../tracking/contract.js';
8
+ export { type PerformanceSample, PerformanceSampleSchema, type PerformanceVitals, PerformanceVitalsSchema, } from '../../tracking/performance/schema.js';
9
+ export { type PerformanceMetricSummary, summarizePerformanceMetric, } from '../../tracking/performance/summary.js';
10
+ export { type AttributionData, AttributionDataSchema, createTrackingSchemas, DEFAULT_BUILD_ID_PATTERN, type TrackEventsRequest, type TrackEventsResponse, TrackEventsResponseSchema, type TrackingDisposition, TrackingDispositionSchema, type TrackingDispositionStatus, TrackingDispositionStatusSchema, type TrackingEventEnvelope, type TrackingEventShape, type TrackingOutboxState, TrackingOutboxStateSchema, type TrackingSchemas, type TrackingSchemasConfig, type UtmData, UtmDataSchema, type VisitBootstrapResponse, VisitBootstrapResponseSchema, type VisitEntryContext, type VisitEntryContextShape, } from '../../tracking/schemas.js';
11
+ //# sourceMappingURL=contract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../../../src/entrypoints/tracking/contract.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EACL,sBAAsB,EACtB,KAAK,sBAAsB,EAC3B,KAAK,yBAAyB,GAC/B,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,KAAK,iBAAiB,EACtB,uBAAuB,EACvB,KAAK,iBAAiB,EACtB,uBAAuB,GACxB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EACL,KAAK,wBAAwB,EAC7B,0BAA0B,GAC3B,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EACL,KAAK,eAAe,EACpB,qBAAqB,EACrB,qBAAqB,EACrB,wBAAwB,EACxB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,yBAAyB,EACzB,KAAK,mBAAmB,EACxB,yBAAyB,EACzB,KAAK,yBAAyB,EAC9B,+BAA+B,EAC/B,KAAK,qBAAqB,EAC1B,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,yBAAyB,EACzB,KAAK,eAAe,EACpB,KAAK,qBAAqB,EAC1B,KAAK,OAAO,EACZ,aAAa,EACb,KAAK,sBAAsB,EAC3B,4BAA4B,EAC5B,KAAK,iBAAiB,EACtB,KAAK,sBAAsB,GAC5B,MAAM,wBAAwB,CAAC"}
@@ -0,0 +1,162 @@
1
+ import"../../index-m38ej7e5.js";
2
+ import {
3
+ defineContract2
4
+ } from "../../index-d9hq291n.js";
5
+
6
+ // src/tracking/schemas.ts
7
+ import { z } from "zod";
8
+ var UtmDataSchema = z.object({
9
+ source: z.string().max(200).optional(),
10
+ medium: z.string().max(200).optional(),
11
+ campaign: z.string().max(200).optional(),
12
+ content: z.string().max(200).optional(),
13
+ term: z.string().max(200).optional()
14
+ });
15
+ var AttributionDataSchema = z.object({
16
+ utm: UtmDataSchema.optional(),
17
+ referrer: z.string().optional(),
18
+ landingPage: z.string().optional()
19
+ });
20
+ var DEFAULT_BUILD_ID_PATTERN = /^(dev|[a-f0-9]{7,40})$/;
21
+ var TrackingDispositionStatusSchema = z.enum([
22
+ "accepted",
23
+ "duplicate",
24
+ "identity-invalid",
25
+ "excluded-bot"
26
+ ]);
27
+ var TrackingDispositionSchema = z.object({
28
+ eventId: z.uuid(),
29
+ status: TrackingDispositionStatusSchema
30
+ });
31
+ var TrackEventsResponseSchema = z.object({
32
+ accepted: z.number().int().nonnegative(),
33
+ dispositions: z.array(TrackingDispositionSchema)
34
+ });
35
+ var VisitBootstrapResponseSchema = z.object({
36
+ visitId: z.uuid(),
37
+ expiresAt: z.iso.datetime()
38
+ });
39
+ var TrackingOutboxStateSchema = z.enum(["available", "unavailable"]);
40
+ function trackingEventShape(eventTypes) {
41
+ return {
42
+ eventId: z.uuid(),
43
+ visitId: z.uuid(),
44
+ browserStreamId: z.uuid(),
45
+ browserSequence: z.number().int().nonnegative(),
46
+ type: z.enum(eventTypes),
47
+ page: z.string().max(2000),
48
+ metadata: z.record(z.string(), z.unknown()).optional(),
49
+ clientTimestamp: z.number()
50
+ };
51
+ }
52
+ function visitEntryContextShape(buildIdPattern) {
53
+ return {
54
+ browserStreamId: z.uuid(),
55
+ previousVisitId: z.uuid().optional(),
56
+ origin: z.url().max(500),
57
+ landingPath: z.string().startsWith("/").max(300).regex(/^[^?#]*$/),
58
+ referrer: z.url().max(2000).optional(),
59
+ utm: UtmDataSchema.optional(),
60
+ displayMode: z.enum(["browser", "standalone"]),
61
+ screenWidth: z.number().int().positive().max(20000),
62
+ screenHeight: z.number().int().positive().max(20000),
63
+ buildId: z.string().regex(buildIdPattern),
64
+ outboxState: TrackingOutboxStateSchema,
65
+ outboxQueued: z.number().int().nonnegative().max(1000),
66
+ outboxDropped: z.number().int().nonnegative()
67
+ };
68
+ }
69
+ function createTrackingSchemas(config) {
70
+ const base = z.object(trackingEventShape(config.eventTypes));
71
+ const event = config.eventExtras ? base.extend(config.eventExtras.shape) : base;
72
+ const buildIdPattern = config.buildIdPattern ?? DEFAULT_BUILD_ID_PATTERN;
73
+ const maxEvents = config.maxEventsPerBatch ?? 50;
74
+ return {
75
+ event,
76
+ request: z.object({
77
+ buildId: z.string().regex(buildIdPattern),
78
+ events: z.array(event).min(1).max(maxEvents),
79
+ utm: UtmDataSchema.optional()
80
+ }),
81
+ response: TrackEventsResponseSchema,
82
+ disposition: TrackingDispositionSchema,
83
+ entry: z.object(visitEntryContextShape(buildIdPattern)),
84
+ bootstrap: VisitBootstrapResponseSchema
85
+ };
86
+ }
87
+
88
+ // src/tracking/contract.ts
89
+ function createTrackingContract(config) {
90
+ const schemas = createTrackingSchemas(config);
91
+ return defineContract2({ prefix: config.prefix ?? "tracking", scope: config.scope }, {
92
+ bootstrap: {
93
+ method: "POST",
94
+ path: "/visit",
95
+ expose: ["HTTP"],
96
+ desc: "Issue or renew a browser visit lease",
97
+ input: schemas.entry,
98
+ output: schemas.bootstrap
99
+ },
100
+ track: {
101
+ method: "POST",
102
+ path: "/events",
103
+ expose: ["HTTP"],
104
+ desc: "Batch track browser events",
105
+ input: schemas.request,
106
+ output: schemas.response,
107
+ safelistedBody: true,
108
+ maxJsonBodyBytes: config.maxJsonBodyBytes ?? 256 * 1024
109
+ }
110
+ });
111
+ }
112
+ // src/tracking/performance/schema.ts
113
+ import { z as z2 } from "zod";
114
+ var BoundedMsSchema = z2.number().nonnegative().max(3600000).nullable();
115
+ var PerformanceVitalsSchema = z2.object({
116
+ lcpMs: BoundedMsSchema,
117
+ inpMs: BoundedMsSchema,
118
+ fcpMs: BoundedMsSchema,
119
+ ttfbMs: BoundedMsSchema,
120
+ cls: z2.number().nonnegative().max(100).nullable()
121
+ });
122
+ var PerformanceSampleSchema = z2.object({
123
+ schemaVersion: z2.literal(1),
124
+ id: z2.string().min(1).max(64),
125
+ kind: z2.enum(["early", "final"]),
126
+ route: z2.string().min(1).max(2000),
127
+ buildId: z2.string().min(1).max(64),
128
+ visitId: z2.string().min(1).max(128).nullable(),
129
+ deviceClass: z2.enum(["mobile", "desktop"]),
130
+ navigationType: z2.string().min(1).max(32).nullable(),
131
+ vitals: PerformanceVitalsSchema,
132
+ capturedAt: z2.number().int().nonnegative()
133
+ });
134
+ // src/tracking/performance/summary.ts
135
+ var DEFAULT_MIN_SAMPLES = 100;
136
+ function percentile(ascending, rank) {
137
+ return ascending[Math.ceil(ascending.length * rank) - 1] ?? null;
138
+ }
139
+ function summarizePerformanceMetric(values, options = {}) {
140
+ const present = values.filter((value) => value !== null).sort((left, right) => left - right);
141
+ const trusted = present.length >= (options.minSamples ?? DEFAULT_MIN_SAMPLES);
142
+ return {
143
+ samples: present.length,
144
+ p75: percentile(present, 0.75),
145
+ p95: trusted ? percentile(present, 0.95) : null
146
+ };
147
+ }
148
+ export {
149
+ AttributionDataSchema,
150
+ DEFAULT_BUILD_ID_PATTERN,
151
+ PerformanceSampleSchema,
152
+ PerformanceVitalsSchema,
153
+ TrackEventsResponseSchema,
154
+ TrackingDispositionSchema,
155
+ TrackingDispositionStatusSchema,
156
+ TrackingOutboxStateSchema,
157
+ UtmDataSchema,
158
+ VisitBootstrapResponseSchema,
159
+ createTrackingContract,
160
+ createTrackingSchemas,
161
+ summarizePerformanceMetric
162
+ };
@@ -0,0 +1,8 @@
1
+ /**
2
+ * `stitchkit/tracking/performance` — Web Vitals from real visitors, collected
3
+ * after the page has loaded. The sample's schema and the report's trust threshold
4
+ * are `stitchkit/tracking/contract`; tables and reports stay the application's.
5
+ * Importing this needs the optional peer `web-vitals`. → ADR 0280.
6
+ */
7
+ export { createPerformanceCollector, isLabUserAgent, type PerformanceCollector, type PerformanceCollectorConfig, type PerformanceHost, type VitalsLibrary, type VitalsMetric, type VitalsObserver, } from '../../tracking/performance/collector.js';
8
+ //# sourceMappingURL=performance.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"performance.d.ts","sourceRoot":"","sources":["../../../src/entrypoints/tracking/performance.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EACL,0BAA0B,EAC1B,cAAc,EACd,KAAK,oBAAoB,EACzB,KAAK,0BAA0B,EAC/B,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,cAAc,GACpB,MAAM,sCAAsC,CAAC"}