agent-comms 9.0.8 → 9.0.9

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 (36) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/dist/bridges/user/web/dist/bundle.js +1 -1
  4. package/dist/bridges/user/web/dist/sw.js +1 -1
  5. package/dist/bridges/user/web/frontend/project-tree.d.ts.map +1 -1
  6. package/dist/bridges/user/web/frontend/project-tree.js +2 -1
  7. package/dist/bridges/user/web/frontend/project-tree.js.map +1 -1
  8. package/dist/core/machine-list-groups.d.ts.map +1 -1
  9. package/dist/core/machine-list-groups.js +2 -1
  10. package/dist/core/machine-list-groups.js.map +1 -1
  11. package/dist/core/ordering.d.ts +14 -0
  12. package/dist/core/ordering.d.ts.map +1 -0
  13. package/dist/core/ordering.js +27 -0
  14. package/dist/core/ordering.js.map +1 -0
  15. package/dist/core/relay-selection.d.ts.map +1 -1
  16. package/dist/core/relay-selection.js +2 -1
  17. package/dist/core/relay-selection.js.map +1 -1
  18. package/dist/core/store.d.ts.map +1 -1
  19. package/dist/core/store.js +5 -2
  20. package/dist/core/store.js.map +1 -1
  21. package/dist/test/bridge-mesh-data-directory.integration.test.js +2 -1
  22. package/dist/test/bridge-mesh-data-directory.integration.test.js.map +1 -1
  23. package/dist/test/conformance-functions.helper.d.ts.map +1 -1
  24. package/dist/test/conformance-functions.helper.js +3 -0
  25. package/dist/test/conformance-functions.helper.js.map +1 -1
  26. package/dist/test/conformance-kept-tests.unit.test.js +1 -0
  27. package/dist/test/conformance-kept-tests.unit.test.js.map +1 -1
  28. package/dist/test/filestore.unit.test.js +40 -0
  29. package/dist/test/filestore.unit.test.js.map +1 -1
  30. package/package.json +1 -1
  31. package/src/bridges/user/web/dist/bundle.js +1 -1
  32. package/src/bridges/user/web/dist/sw.js +1 -1
  33. package/src/core/machine-list-groups.ts +2 -1
  34. package/src/core/ordering.ts +27 -0
  35. package/src/core/relay-selection.ts +2 -1
  36. package/src/core/store.ts +5 -2
@@ -1 +1 @@
1
- (function(){var e=`agent-comms-v1`,t=[{"revision":"41c6737126547228482ecc2b359801e6","url":"mesh-worker.js"},{"revision":"f1ee3afb8c9e8f0d7315fd93d6457316","url":"index.html"},{"revision":"3c68c960e2ae7cf0918c97b921c287da","url":"bundle.js"}].map(e=>e.url);self.addEventListener(`install`,n=>{n.waitUntil(self.caches.open(e).then(async e=>e.addAll(t)).then(async()=>self.skipWaiting()))}),self.addEventListener(`activate`,t=>{t.waitUntil(self.caches.keys().then(async t=>Promise.all(t.filter(t=>t!==e).map(async e=>self.caches.delete(e)))))});var n=200,r=300;function i(e,t){return e.method===`GET`&&t.status>=n&&t.status<r}self.addEventListener(`fetch`,t=>{if(new URL(t.request.url).pathname.startsWith(`/api/`)){t.respondWith(fetch(t.request).then(n=>{if(i(t.request,n)){let r=n.clone();self.caches.open(e).then(async e=>e.put(t.request,r))}return n}).catch(async()=>self.caches.open(e).then(async e=>e.match(t.request)).then(e=>e??new Response(`Offline`,{status:503}))));return}t.respondWith(self.caches.open(e).then(async e=>e.match(t.request)).then(async n=>n||fetch(t.request).then(n=>{if(i(t.request,n)){let r=n.clone();self.caches.open(e).then(async e=>e.put(t.request,r))}return n})))})})();
1
+ (function(){var e=`agent-comms-v1`,t=[{"revision":"41c6737126547228482ecc2b359801e6","url":"mesh-worker.js"},{"revision":"f1ee3afb8c9e8f0d7315fd93d6457316","url":"index.html"},{"revision":"0bc3d9d0beacf516bee36c2e9be3835f","url":"bundle.js"}].map(e=>e.url);self.addEventListener(`install`,n=>{n.waitUntil(self.caches.open(e).then(async e=>e.addAll(t)).then(async()=>self.skipWaiting()))}),self.addEventListener(`activate`,t=>{t.waitUntil(self.caches.keys().then(async t=>Promise.all(t.filter(t=>t!==e).map(async e=>self.caches.delete(e)))))});var n=200,r=300;function i(e,t){return e.method===`GET`&&t.status>=n&&t.status<r}self.addEventListener(`fetch`,t=>{if(new URL(t.request.url).pathname.startsWith(`/api/`)){t.respondWith(fetch(t.request).then(n=>{if(i(t.request,n)){let r=n.clone();self.caches.open(e).then(async e=>e.put(t.request,r))}return n}).catch(async()=>self.caches.open(e).then(async e=>e.match(t.request)).then(e=>e??new Response(`Offline`,{status:503}))));return}t.respondWith(self.caches.open(e).then(async e=>e.match(t.request)).then(async n=>n||fetch(t.request).then(n=>{if(i(t.request,n)){let r=n.clone();self.caches.open(e).then(async e=>e.put(t.request,r))}return n})))})})();
@@ -4,6 +4,7 @@
4
4
 
5
5
  import type { AgentIdentity } from "./types.js";
6
6
  import type { Namer } from "./naming.js";
7
+ import { compareCodeUnits } from "./ordering.js";
7
8
 
8
9
  /** One run of listed agents and the heading above it; no heading when nothing is grouped at all. */
9
10
  export interface AgentGroup {
@@ -40,7 +41,7 @@ export function groupAgentsByMachine(
40
41
  .sort(([a], [b]) => {
41
42
  if (a === ownMachine) return -1;
42
43
  if (b === ownMachine) return 1;
43
- return a.localeCompare(b);
44
+ return compareCodeUnits(a, b);
44
45
  })
45
46
  .map(([machine, members]) => ({
46
47
  heading: `Machine ${namer(machine)}${machine === ownMachine ? " (this machine)" : ""}:`,
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Orderings that depend on neither a locale nor a runtime's collation data, so every implementation sorts the same names the same way. Strings compare by UTF-16 code unit, the order of JavaScript's `<`.
3
+ */
4
+
5
+ /**
6
+ * Compares two strings by UTF-16 code unit.
7
+ * @returns A negative number when `a` sorts first, a positive one when `b` does, 0 when they are equal.
8
+ */
9
+ export function compareCodeUnits(a: string, b: string): number {
10
+ if (a < b) return -1;
11
+ if (a > b) return 1;
12
+ return 0;
13
+ }
14
+
15
+ /** Folds the ASCII letters A to Z to lower case and leaves every other character as it is, so no Unicode case table is involved. */
16
+ function foldAsciiCase(name: string): string {
17
+ return name.replace(/[A-Z]/gu, (letter) => letter.toLowerCase());
18
+ }
19
+
20
+ /**
21
+ * Compares two names a person reads: ASCII letters are folded to lower case before comparing by UTF-16 code unit, and names that fold equal are ordered by the names as written, so the order is total and stable.
22
+ * @returns A negative number when `a` sorts first, a positive one when `b` does, 0 when they are equal.
23
+ */
24
+ export function compareDisplayNames(a: string, b: string): number {
25
+ const folded = compareCodeUnits(foldAsciiCase(a), foldAsciiCase(b));
26
+ return folded === 0 ? compareCodeUnits(a, b) : folded;
27
+ }
@@ -7,6 +7,7 @@
7
7
  */
8
8
 
9
9
  import { BlockList, isIPv4, isIPv6 } from "node:net";
10
+ import { compareCodeUnits } from "./ordering.js";
10
11
 
11
12
  /** A relay offer this store has heard, with what it knows about the device making it. */
12
13
  export interface RelayOffer {
@@ -104,7 +105,7 @@ export function selectRelay(
104
105
  ): SelectedRelay {
105
106
  const eligible = input.offers
106
107
  .filter((offer) => offer.deviceHex !== input.ownDeviceHex)
107
- .toSorted((a, b) => a.deviceHex.localeCompare(b.deviceHex));
108
+ .toSorted((a, b) => compareCodeUnits(a.deviceHex, b.deviceHex));
108
109
  for (const tier of OFFER_TIERS) {
109
110
  for (const offer of eligible) {
110
111
  const admitted =
package/src/core/store.ts CHANGED
@@ -23,6 +23,7 @@ import {
23
23
  RoomMessageSchema,
24
24
  RoomSchema,
25
25
  } from "./types.js";
26
+ import { compareCodeUnits } from "./ordering.js";
26
27
  import type {
27
28
  AgentIdentity,
28
29
  DeliveryEvent,
@@ -584,7 +585,8 @@ export class FileStore implements CommsStore {
584
585
  messages.push(msg);
585
586
  }
586
587
  }
587
- messages.sort((a, b) => a.timestamp.localeCompare(b.timestamp));
588
+ // Every timestamp this store writes is `new Date().toISOString()`: fixed width, UTC, millisecond precision, so UTF-16 order is chronological order (held by a test at the producer).
589
+ messages.sort((a, b) => compareCodeUnits(a.timestamp, b.timestamp));
588
590
  return messages;
589
591
  } catch {
590
592
  return [];
@@ -653,6 +655,7 @@ export class FileStore implements CommsStore {
653
655
  events.push(event);
654
656
  await fs.unlink(path.join(dir, file));
655
657
  }
658
+ // Same fixed timestamp form as readRoomMessages sorts on.
656
659
  events.sort((a, b) => {
657
660
  const ta =
658
661
  a.type === "room_message" || a.type === "dm"
@@ -662,7 +665,7 @@ export class FileStore implements CommsStore {
662
665
  b.type === "room_message" || b.type === "dm"
663
666
  ? b.message.timestamp
664
667
  : "";
665
- return ta.localeCompare(tb);
668
+ return compareCodeUnits(ta, tb);
666
669
  });
667
670
  return events;
668
671
  } catch {