@threadbase-sh/streamer 1.55.4 → 1.57.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.cjs CHANGED
@@ -6249,6 +6249,12 @@ var init_feature_flags = __esm({
6249
6249
  default: false,
6250
6250
  env: "THREADBASE_FEATURE_LIVE_ACTIVITY_PUSH"
6251
6251
  },
6252
+ {
6253
+ id: "e2ee",
6254
+ description: "Application-layer encryption between a paired device and this server, independent of TLS, so a tunnel or a LAN observer on the path carries ciphertext. Off by default: it is negotiated per device, never forced, because released mobile builds cannot be force-updated and a server that demanded it would break every one of them.",
6255
+ default: false,
6256
+ env: "THREADBASE_FEATURE_E2EE"
6257
+ },
6252
6258
  {
6253
6259
  id: "ptyHost",
6254
6260
  description: "Keep live PTYs in a separate host process so a streamer restart can reconnect without restarting the agents. Off by default until cross-platform behavior is qualified.",
@@ -135775,6 +135781,25 @@ function describePushCapability(liveActivityEnabled, env = process.env) {
135775
135781
  liveActivityReason: describeMissingApnsCredentials(env) ?? "APNs credentials are set but the push token store is unavailable, so Live Activity push is disabled."
135776
135782
  };
135777
135783
  }
135784
+ var E2EE_PROTOCOL_VERSION = 1;
135785
+ var E2EE_SUPPORTED = false;
135786
+ function describeE2eeCapability(flagEnabled) {
135787
+ const enabled = E2EE_SUPPORTED && flagEnabled;
135788
+ const base = {
135789
+ supported: E2EE_SUPPORTED,
135790
+ enabled,
135791
+ version: E2EE_PROTOCOL_VERSION,
135792
+ required: false
135793
+ };
135794
+ if (enabled) return base;
135795
+ return {
135796
+ ...base,
135797
+ // Always says why, including when `supported` is false. An operator who set
135798
+ // the flag and saw nothing happen has exactly one question, and a field that
135799
+ // goes absent in the case they hit is the field not answering it.
135800
+ reason: E2EE_SUPPORTED ? "disabled by the e2ee feature flag \u2014 set THREADBASE_FEATURE_E2EE=1, --feature e2ee=true, or feature_flags: in server.yaml" : "this build carries the capability negotiation but not yet the handshake it gates"
135801
+ };
135802
+ }
135778
135803
  var identityKeyFailureLogged = false;
135779
135804
  function describeServerIdentityKey() {
135780
135805
  try {
@@ -135829,7 +135854,11 @@ var createMiscRoutes = (deps) => {
135829
135854
  // learn it without re-scanning. Additive: absent means a server with no
135830
135855
  // readable identity key, which a client must read as "cannot verify this
135831
135856
  // server" — never as a reason to fail the rest of this response.
135832
- serverIdentityKey: describeServerIdentityKey()
135857
+ serverIdentityKey: describeServerIdentityKey(),
135858
+ // Whether to encrypt to this server. Additive, same contract as `push`:
135859
+ // absent means an older server, which a client must read as "unknown" and
135860
+ // resolve as today's plaintext path — never as a reason to fail.
135861
+ e2ee: describeE2eeCapability(deps.featureFlagsConfig().values.e2ee)
135833
135862
  });
135834
135863
  });
135835
135864
  app.get("/api/profiles", (c) => c.json([]));
@@ -145630,6 +145659,7 @@ function readBody2(req) {
145630
145659
  // src/api/handlers/conversations.handlers.ts
145631
145660
  var SEARCH_OVERFETCH = 4;
145632
145661
  var SEARCH_MAX_SCAN = 1e3;
145662
+ var MAX_BYTES_CEILING = 32 * 1024 * 1024;
145633
145663
  var ConversationHandlers = class {
145634
145664
  constructor(deps) {
145635
145665
  this.deps = deps;
@@ -146035,7 +146065,7 @@ var ConversationHandlers = class {
146035
146065
  const total = filtered.length;
146036
146066
  const hasAnchor = url2.searchParams.has("anchor_index");
146037
146067
  const hasAfter = url2.searchParams.has("after_index");
146038
- const usePaging = url2.searchParams.has("msg_limit") || url2.searchParams.has("before_index") || hasAnchor || hasAfter;
146068
+ const usePaging = url2.searchParams.has("msg_limit") || url2.searchParams.has("before_index") || url2.searchParams.has("max_bytes") || hasAnchor || hasAfter;
146039
146069
  let slice = filtered;
146040
146070
  let fromIdx = 0;
146041
146071
  let messagePagination;
@@ -146157,6 +146187,28 @@ var ConversationHandlers = class {
146157
146187
  content
146158
146188
  };
146159
146189
  });
146190
+ const requestedMaxBytes = intParam(url2, "max_bytes", 0);
146191
+ if (requestedMaxBytes > 0 && messagesPayload.length > 0) {
146192
+ const budget = Math.min(requestedMaxBytes, MAX_BYTES_CEILING);
146193
+ let used = 0;
146194
+ let firstKept = messagesPayload.length - 1;
146195
+ for (let i = messagesPayload.length - 1; i >= 0; i--) {
146196
+ const size = Buffer.byteLength(JSON.stringify(messagesPayload[i]));
146197
+ if (i < messagesPayload.length - 1 && used + size > budget) break;
146198
+ used += size;
146199
+ firstKept = i;
146200
+ }
146201
+ if (firstKept > 0) {
146202
+ messagesPayload.splice(0, firstKept);
146203
+ if (messagePagination) {
146204
+ const newFrom = fromIdx + firstKept;
146205
+ messagePagination.from_index = newFrom;
146206
+ messagePagination.has_more_older = newFrom > 0;
146207
+ messagePagination.next_before_index = newFrom > 0 ? newFrom : null;
146208
+ }
146209
+ }
146210
+ if (messagePagination) messagePagination.served_bytes = used;
146211
+ }
146160
146212
  const conv = conversation;
146161
146213
  const cachedConvMeta = this.cache?.getMetaById(id);
146162
146214
  const convProvider = coerceProviderForRunner(conv.provider ?? cachedConvMeta?.provider);