@xynogen/pix-runtime 0.8.4 → 0.9.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xynogen/pix-runtime",
3
- "version": "0.8.4",
3
+ "version": "0.9.0",
4
4
  "description": "Pix shared runtime — versioned pix.json config, atomic persistence, typed change events",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -27,6 +27,7 @@
27
27
  "./collapse": "./src/collapse.ts",
28
28
  "./icon-catalog": "./src/icon-catalog.ts",
29
29
  "./io": "./src/io.ts",
30
+ "./lfid": "./src/lfid.ts",
30
31
  "./once": "./src/once.ts",
31
32
  "./testing": "./src/testing.ts"
32
33
  },
package/src/index.ts CHANGED
@@ -24,6 +24,14 @@ export {
24
24
  withAgentBlock,
25
25
  } from "./herdr-state.ts";
26
26
  export { ioTimeoutMs, ioTimeoutSignal } from "./io.ts";
27
+ export {
28
+ generateLfid,
29
+ isLfid,
30
+ LFID_RE,
31
+ type LfidOptions,
32
+ parseLfid,
33
+ uniqueLfid,
34
+ } from "./lfid.ts";
27
35
  export {
28
36
  config,
29
37
  createRuntime,
package/src/lfid.ts ADDED
@@ -0,0 +1,86 @@
1
+ /**
2
+ * LFID — LLM-friendly IDs (`agent-happy-walrus-42`).
3
+ *
4
+ * Raw UUID fragments (`3f2a1b9c-4d5e`, `abc123`) waste parent-model tokens and
5
+ * invite typos when the model must type them back (steer/stop/resume). An LFID
6
+ * is `[prefix]-[adjective]-[noun]-[NN]`: pronounceable, copyable, and
7
+ * self-evidently an ID in a transcript. Two random words + two digits give
8
+ * 100×100×100 = 1M combinations per prefix — plenty for a live agent set, and
9
+ * the owning registry (e.g. pix-subagent's AgentManager) maps LFID → record.
10
+ *
11
+ * `ponytail:` 2-digit suffix caps the namespace at 1M per prefix. If live sets
12
+ * ever approach that, widen the suffix or add a third word here.
13
+ */
14
+
15
+ import { randomInt } from "node:crypto";
16
+
17
+ /** `agent-<adj>-<noun>-<NN>` shape. Prefix is caller-owned (`agent`, `job`, …). */
18
+ export const LFID_RE = /^[a-z][a-z0-9]*-[a-z]+-[a-z]+-\d{2}$/;
19
+
20
+ // Small curated lists: common words, no ambiguous pairs, no profanity.
21
+ // Sized at 100 × 100 so two digits complete a 1M namespace per prefix.
22
+ const ADJECTIVES = (
23
+ "agate amber azure birch bold brave bright brisk calm cedar cinder clever cloudy coral crisp dawn drift dusk eager elm" +
24
+ "ember fair fern fleet flint fresh frost garnet glad golden grand green grove happy hazy heath iris ivory jade juniper" +
25
+ "keen kind lagoon larch light linden lively lotus lucid lunar maple meadow merry misty moss nectar nimble nimbus noble north" +
26
+ "ocean olive opal pearl pine plain plum proud quartz quick quiet rapid reef ridge river rocky round royal rusty sable" +
27
+ "sage sandy sedge sharp shiny silent silver sleek small smart smooth solid sorrel spring steady stone sunny swift tango teal"
28
+ ).split(" ");
29
+
30
+ const NOUNS = (
31
+ "adder albatross anchovy badger beagle bear beaver boar bobcat cobra condor cougar coyote crane cricket deer dove drake duck eagle" +
32
+ "falcon ferret finch fisher fox frog gannet gazelle gecko goose gopher grouse gull hamster hare hawk heron hyena ibis impala" +
33
+ "indigo jackal jaguar jay kelp kestrel koala kudu lark lemur llama lynx magpie manatee marmot marten meerkat mink mole mongoose" +
34
+ "moose narwhal newt numbats ocelot okapi oriole osprey otter owl ox oyster panda pangolin parrot pelican penguin pigeon pika platypus" +
35
+ "porpoise possum puffin python quail quokka rabbit raven rhea robin sable salmon saola seal shark shrew skunk sloth snail sparrow"
36
+ ).split(" ");
37
+
38
+ export interface LfidOptions {
39
+ /** Namespace prefix, e.g. `"agent"`. Defaults to `"agent"`. */
40
+ prefix?: string;
41
+ /** RNG override for tests/determinism. Must return an int in [0, max). */
42
+ rand?: (max: number) => number;
43
+ }
44
+
45
+ /** Generate one LFID, e.g. `agent-happy-walrus-42`. */
46
+ export function generateLfid(opts: LfidOptions = {}): string {
47
+ const prefix = opts.prefix ?? "agent";
48
+ const rand = opts.rand ?? randomInt;
49
+ const pick = (xs: string[]): string => xs[rand(xs.length)] as string;
50
+ const n = String(rand(100)).padStart(2, "0");
51
+ return `${prefix}-${pick(ADJECTIVES)}-${pick(NOUNS)}-${n}`;
52
+ }
53
+
54
+ /** Loose check: is this string LFID-shaped (any prefix)? */
55
+ export function isLfid(s: string): boolean {
56
+ return LFID_RE.test(s);
57
+ }
58
+
59
+ /** Parse an LFID into its parts; undefined when the shape doesn't match. */
60
+ export function parseLfid(
61
+ s: string,
62
+ ): { prefix: string; adjective: string; noun: string; num: string } | undefined {
63
+ const m = LFID_RE.exec(s);
64
+ if (!m) return undefined;
65
+ const [prefix, adjective, noun, num] = s.split("-");
66
+ return {
67
+ prefix: prefix as string,
68
+ adjective: adjective as string,
69
+ noun: noun as string,
70
+ num: num as string,
71
+ };
72
+ }
73
+
74
+ /**
75
+ * Generate an LFID not already present in `taken`. Retries a bounded number of
76
+ * times, then throws — a full or near-full namespace is a caller bug, not a
77
+ * loop-candidate. Collisions across live sets are essentially impossible long
78
+ * before this bound (1M combinations), so 64 tries is generous.
79
+ */
80
+ export function uniqueLfid(taken: (id: string) => boolean, opts: LfidOptions = {}): string {
81
+ for (let i = 0; i < 64; i++) {
82
+ const id = generateLfid(opts);
83
+ if (!taken(id)) return id;
84
+ }
85
+ throw new Error("lfid: namespace exhausted (too many live IDs for prefix)");
86
+ }