@gemwright/ai 0.0.0 → 0.8.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 (59) hide show
  1. package/Dockerfile +12 -0
  2. package/README.md +82 -1
  3. package/catalogs/aggressive-bodies.json +1 -0
  4. package/catalogs/crafts/alchemy.json +22 -0
  5. package/catalogs/crafts/blacksmith.json +110 -0
  6. package/catalogs/crafts/bowcraft.json +13 -0
  7. package/catalogs/crafts/carpentry.json +105 -0
  8. package/catalogs/crafts/inscription.json +67 -0
  9. package/catalogs/crafts/tailoring.json +72 -0
  10. package/catalogs/crafts/tinkering.json +61 -0
  11. package/catalogs/dungeons/covetous.json +1 -0
  12. package/catalogs/dungeons/deceit.json +1 -0
  13. package/catalogs/dungeons/despise.json +1 -0
  14. package/catalogs/dungeons/destard.json +1 -0
  15. package/catalogs/dungeons/fire.json +1 -0
  16. package/catalogs/dungeons/hythloth.json +1 -0
  17. package/catalogs/dungeons/ice.json +1 -0
  18. package/catalogs/dungeons/khaldun.json +1 -0
  19. package/catalogs/dungeons/misc-dungeons.json +1 -0
  20. package/catalogs/dungeons/orc-cave.json +1 -0
  21. package/catalogs/dungeons/shame.json +1 -0
  22. package/catalogs/dungeons/terathan-keep.json +1 -0
  23. package/catalogs/dungeons/wrong.json +1 -0
  24. package/catalogs/house-lots.json +86 -0
  25. package/catalogs/hunting-grounds.json +10071 -0
  26. package/catalogs/magery-spells.json +697 -0
  27. package/catalogs/monsters.json +1 -0
  28. package/catalogs/prices.json +466 -0
  29. package/catalogs/tamables.json +864 -0
  30. package/catalogs/taming-grounds.json +2198 -0
  31. package/dist/index.js +55289 -0
  32. package/dist/life-journal-worker.js +469 -0
  33. package/dist/load-test.js +55098 -0
  34. package/dist/path-worker.js +1384 -0
  35. package/dist/persistence-worker.js +16503 -0
  36. package/dist/progress-report.js +182 -0
  37. package/dist/report.js +163 -0
  38. package/dist/serve.js +62078 -0
  39. package/locations/ankhs.json +91 -0
  40. package/locations/areas.json +760 -0
  41. package/locations/cobblers.json +148 -0
  42. package/locations/escorts.json +115 -0
  43. package/locations/forges.json +18 -0
  44. package/locations/locations.json +1501 -0
  45. package/locations/new-haven.json +522 -0
  46. package/locations/route-corridors.json +1 -0
  47. package/locations/services-guide.json +1329 -0
  48. package/locations/services.json +144 -0
  49. package/locations/teleporters.json +38 -0
  50. package/locations/vendors.json +1503 -0
  51. package/package.json +39 -3
  52. package/railway.json +6 -0
  53. package/start.sh +36 -0
  54. package/templates/archetypes-new-haven.json +38 -0
  55. package/templates/archetypes.json +274 -0
  56. package/templates/population.example.json +123 -0
  57. package/tuning.json +17 -0
  58. package/world/names.json +4055 -0
  59. package/world/world.json +743 -0
@@ -0,0 +1,469 @@
1
+ // @bun
2
+ var __create = Object.create;
3
+ var __getProtoOf = Object.getPrototypeOf;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ function __accessProp(key) {
8
+ return this[key];
9
+ }
10
+ var __toESMCache_node;
11
+ var __toESMCache_esm;
12
+ var __toESM = (mod, isNodeMode, target) => {
13
+ var canCache = mod != null && typeof mod === "object";
14
+ if (canCache) {
15
+ var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
16
+ var cached = cache.get(mod);
17
+ if (cached)
18
+ return cached;
19
+ }
20
+ target = mod != null ? __create(__getProtoOf(mod)) : {};
21
+ const to = isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
22
+ if (mod && typeof mod === "object" || typeof mod === "function") {
23
+ for (let key of __getOwnPropNames(mod))
24
+ if (!__hasOwnProp.call(to, key))
25
+ __defProp(to, key, {
26
+ get: __accessProp.bind(mod, key),
27
+ enumerable: true
28
+ });
29
+ }
30
+ if (canCache)
31
+ cache.set(mod, to);
32
+ return to;
33
+ };
34
+ var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
35
+ var __returnValue = (v) => v;
36
+ function __exportSetter(name, newValue) {
37
+ this[name] = __returnValue.bind(null, newValue);
38
+ }
39
+ var __export = (target, all) => {
40
+ for (var name in all)
41
+ __defProp(target, name, {
42
+ get: all[name],
43
+ enumerable: true,
44
+ configurable: true,
45
+ set: __exportSetter.bind(all, name)
46
+ });
47
+ };
48
+ var __esm = (fn, res, err) => () => {
49
+ if (fn)
50
+ try {
51
+ res = fn(fn = 0);
52
+ } catch (e) {
53
+ err = [e];
54
+ }
55
+ if (err)
56
+ throw err[0];
57
+ return res;
58
+ };
59
+
60
+ // src/telemetry-metrics.ts
61
+ class RuntimeMetrics {
62
+ counters = new Map;
63
+ histograms = new Map;
64
+ starts = new Map;
65
+ increment(name, amount = 1) {
66
+ const key = this.counters.has(name) || this.counters.size < 512 ? name : "other";
67
+ this.counters.set(key, (this.counters.get(key) ?? 0) + amount);
68
+ }
69
+ duration(name, ms) {
70
+ if (!Number.isFinite(ms) || ms < 0)
71
+ return;
72
+ const key = this.histograms.has(name) || this.histograms.size < 64 ? name : "other";
73
+ const histogram = this.histograms.get(key) ?? { count: 0, sum: 0, max: 0, buckets: bounds.map(() => 0) };
74
+ histogram.count++;
75
+ histogram.sum += ms;
76
+ histogram.max = Math.max(histogram.max, ms);
77
+ const index = bounds.findIndex((bound) => ms <= bound);
78
+ histogram.buckets[index] = (histogram.buckets[index] ?? 0) + 1;
79
+ this.histograms.set(key, histogram);
80
+ }
81
+ observe(event) {
82
+ this.increment(`${event.component}.${event.type}.${event.result}`);
83
+ if (event.type !== "action_lifecycle" || !event.actionId)
84
+ return;
85
+ if (["proposed", "authorized", "dispatched", "started"].includes(event.result)) {
86
+ if (!this.starts.has(event.actionId))
87
+ this.starts.set(event.actionId, Date.parse(event.at));
88
+ if (this.starts.size > 4096)
89
+ this.starts.delete(this.starts.keys().next().value ?? "");
90
+ } else if (["completed", "failed", "interrupted", "timed-out", "unconfirmed"].includes(event.result)) {
91
+ const start = this.starts.get(event.actionId);
92
+ if (start !== undefined)
93
+ this.duration("action_ms", Date.parse(event.at) - start);
94
+ this.starts.delete(event.actionId);
95
+ }
96
+ }
97
+ snapshot() {
98
+ return {
99
+ counters: Object.fromEntries(this.counters),
100
+ histograms: Object.fromEntries([...this.histograms].map(([name, h]) => {
101
+ const percentile = (fraction) => {
102
+ let count = 0;
103
+ for (let i = 0;i < bounds.length; i++) {
104
+ count += h.buckets[i] ?? 0;
105
+ if (count >= h.count * fraction)
106
+ return bounds[i] === Infinity ? h.max : bounds[i];
107
+ }
108
+ return h.max;
109
+ };
110
+ return [
111
+ name,
112
+ {
113
+ count: h.count,
114
+ mean: h.sum / Math.max(1, h.count),
115
+ max: h.max,
116
+ p50: percentile(0.5),
117
+ p95: percentile(0.95),
118
+ p99: percentile(0.99)
119
+ }
120
+ ];
121
+ }))
122
+ };
123
+ }
124
+ }
125
+ function markThreat(session, active, at) {
126
+ if (!active) {
127
+ threats.delete(session);
128
+ return;
129
+ }
130
+ if (!threats.has(session))
131
+ threats.set(session, { at, recorded: new Set });
132
+ }
133
+ function observeInterruption(session, event) {
134
+ const threat = threats.get(session);
135
+ const name = event.stage === "interrupted" ? "work_interrupt_ms" : event.kind === "escape" && event.stage === "authorized" && !event.command ? "safety_authorize_ms" : undefined;
136
+ if (!threat || !name || threat.recorded.has(name))
137
+ return;
138
+ runtimeMetrics.duration(name, event.at - threat.at);
139
+ threat.recorded.add(name);
140
+ }
141
+ var bounds, runtimeMetrics, threats;
142
+ var init_telemetry_metrics = __esm(() => {
143
+ bounds = [1, 5, 10, 25, 50, 100, 250, 500, 1000, 5000, 30000, Infinity];
144
+ runtimeMetrics = new RuntimeMetrics;
145
+ threats = new WeakMap;
146
+ });
147
+
148
+ // src/telemetry-schema.ts
149
+ function redact(value, depth = 0, seen = new WeakSet) {
150
+ if (depth > 12)
151
+ return "[depth-limit]";
152
+ if (value instanceof Error)
153
+ return { name: value.name, message: String(redact(value.message, depth + 1, seen)) };
154
+ if (typeof value === "string")
155
+ return value.replace(/\b(Bearer\s+)\S+/gi, "$1[redacted]").replace(/\b(password|api[_-]?key|secret|token)=([^\s&]+)/gi, "$1=[redacted]");
156
+ if (typeof value === "bigint")
157
+ return value.toString();
158
+ if (value === null || typeof value !== "object")
159
+ return value;
160
+ if (seen.has(value))
161
+ return "[circular]";
162
+ seen.add(value);
163
+ try {
164
+ if (Array.isArray(value))
165
+ return value.map((item) => redact(item, depth + 1, seen));
166
+ return Object.fromEntries(Object.entries(value).map(([key, item]) => [
167
+ key,
168
+ sensitive.test(key) ? "[redacted]" : redact(item, depth + 1, seen)
169
+ ]));
170
+ } finally {
171
+ seen.delete(value);
172
+ }
173
+ }
174
+ var sensitive;
175
+ var init_telemetry_schema = __esm(() => {
176
+ sensitive = /^(?:password|passwd|secret|authorization|cookie|token|api[_-]?key|credentials|accountRef|username)$/i;
177
+ });
178
+
179
+ // src/telemetry.ts
180
+ import { AsyncLocalStorage } from "async_hooks";
181
+ function updateTrace(fields) {
182
+ const current = context.getStore();
183
+ if (current)
184
+ Object.assign(current, fields);
185
+ }
186
+ function registerActor(session, residentId, name) {
187
+ const actor = { residentId, name, sessionId: crypto.randomUUID() };
188
+ actors.set(session, actor);
189
+ return actor;
190
+ }
191
+ function actorFor(session) {
192
+ return {
193
+ ...actors.get(session) ?? { residentId: null, name: null, sessionId: null },
194
+ position: session.world.position ? { map: session.world.map, ...session.world.position } : null
195
+ };
196
+ }
197
+ function configureTelemetry(options) {
198
+ const previous = { sink, output };
199
+ sink = options.sink ?? sink;
200
+ output = options.output ?? output;
201
+ return () => {
202
+ sink = previous.sink;
203
+ output = previous.output;
204
+ };
205
+ }
206
+ function makeEvent(component, type, result, details, fields = {}) {
207
+ const current = context.getStore();
208
+ const actor = current?.actor?.();
209
+ return {
210
+ schemaVersion: 2,
211
+ id: crypto.randomUUID(),
212
+ at: new Date().toISOString(),
213
+ runId,
214
+ deploymentId: process.env.RAILWAY_DEPLOYMENT_ID ?? null,
215
+ sequence: ++sequence,
216
+ component,
217
+ message: String(redact(typeof details.message === "string" ? details.message : `${component}: ${type} ${result}`)),
218
+ level: result === "failed" ? "error" : "info",
219
+ type,
220
+ result,
221
+ residentId: actor?.residentId ?? null,
222
+ name: actor?.name ?? null,
223
+ sessionId: actor?.sessionId ?? null,
224
+ traceId: current?.traceId ?? current?.intentId ?? current?.actionId ?? actor?.sessionId ?? runId,
225
+ intentId: current?.intentId,
226
+ decisionId: current?.decisionId,
227
+ actionId: current?.actionId,
228
+ parentId: current?.parentId,
229
+ position: actor?.position ?? null,
230
+ ...Object.fromEntries(Object.entries(fields).filter(([, value]) => value !== undefined)),
231
+ details: redact(details)
232
+ };
233
+ }
234
+ function emitEvent(event, options = {}) {
235
+ runtimeMetrics.observe(event);
236
+ if (options.journal !== false)
237
+ sink?.(event);
238
+ if (options.stdout)
239
+ output?.(JSON.stringify(event), event.level);
240
+ }
241
+ function operational(component, level, message, details = {}) {
242
+ const event = makeEvent(component, "operational_log", level === "error" ? "failed" : "observed", { message, ...details }, { level });
243
+ emitEvent(event, { stdout: true });
244
+ return event;
245
+ }
246
+ function checkpointEvent(checkpoint, component, type, result, details, fields = {}) {
247
+ const session = checkpointSession(checkpoint);
248
+ emitEvent(makeEvent(component, type, result, details, { ...session ? actorFor(session) : {}, ...fields }));
249
+ }
250
+ var context, runId, sequence = 0, sink, output, actors, checkpoints, traceContext = () => context.getStore(), inTrace = (fields, run) => context.run({ ...context.getStore(), ...fields }, run), inSession = (session, run) => inTrace({ actor: () => actorFor(session) }, run), bindCheckpoint = (checkpoint, session) => checkpoints.set(checkpoint, session), checkpointSession = (checkpoint) => checkpoints.get(checkpoint);
251
+ var init_telemetry = __esm(() => {
252
+ init_telemetry_metrics();
253
+ init_telemetry_schema();
254
+ context = new AsyncLocalStorage;
255
+ runId = crypto.randomUUID();
256
+ actors = new WeakMap;
257
+ checkpoints = new WeakMap;
258
+ });
259
+
260
+ // src/life-journal-worker.ts
261
+ import { parentPort, workerData } from "worker_threads";
262
+
263
+ // src/journal-files.ts
264
+ init_telemetry();
265
+ import { mkdir, open, readdir, stat, statfs, unlink } from "fs/promises";
266
+ import { join } from "path";
267
+ import { promisify } from "util";
268
+ import { gzip } from "zlib";
269
+ var compress = promisify(gzip);
270
+ var traceTypes = new Set([
271
+ "perception_snapshot",
272
+ "perception_delta",
273
+ "action_lifecycle",
274
+ "activity",
275
+ "operational_log",
276
+ "runtime_metrics",
277
+ "runtime_health",
278
+ "resident_health"
279
+ ]);
280
+ var tracePattern = /^\d{4}-\d{2}-\d{2}\.trace\.\d{8}\.jsonl\.gz$/;
281
+ var dayPattern = /^(\d{4}-\d{2}-\d{2})(?:\.\d{8})?\.jsonl\.gz$/;
282
+
283
+ class JournalFiles {
284
+ directory;
285
+ traces = [];
286
+ history = [];
287
+ traceBytes = 0;
288
+ historyBytes = 0;
289
+ evictedBytes = 0;
290
+ evictedFiles = 0;
291
+ diskCheckedAt = 0;
292
+ options;
293
+ constructor(directory, options = {}) {
294
+ this.directory = directory;
295
+ this.options = {
296
+ traceBudgetBytes: 512 * 1024 * 1024,
297
+ segmentBytes: 16 * 1024 * 1024,
298
+ reserveBytes: 128 * 1024 * 1024,
299
+ historyBudgetBytes: 1024 * 1024 * 1024,
300
+ historySegmentBytes: 64 * 1024 * 1024,
301
+ historyMaxDays: 2,
302
+ ...options
303
+ };
304
+ }
305
+ async initialize() {
306
+ await mkdir(this.directory, { recursive: true, mode: 448 });
307
+ for (const name of (await readdir(this.directory)).sort()) {
308
+ if (!name.endsWith(".jsonl.gz"))
309
+ continue;
310
+ const file = await stat(join(this.directory, name));
311
+ if (!file.isFile())
312
+ continue;
313
+ if (tracePattern.test(name)) {
314
+ this.traces.push({ name, bytes: file.size });
315
+ this.traceBytes += file.size;
316
+ } else if (dayPattern.test(name)) {
317
+ this.history.push({ name, bytes: file.size });
318
+ this.historyBytes += file.size;
319
+ }
320
+ }
321
+ }
322
+ statistics() {
323
+ return {
324
+ historyBytes: this.historyBytes,
325
+ traceBytes: this.traceBytes,
326
+ historyBudgetBytes: this.options.historyBudgetBytes,
327
+ traceBudgetBytes: this.options.traceBudgetBytes,
328
+ evictedBytes: this.evictedBytes,
329
+ evictedFiles: this.evictedFiles
330
+ };
331
+ }
332
+ async append(name, events, level) {
333
+ const bytes = await compress(`${events.map((event) => JSON.stringify(event)).join(`
334
+ `)}
335
+ `, { level });
336
+ const file = await open(join(this.directory, name), "a", 384);
337
+ try {
338
+ await file.writeFile(bytes);
339
+ await file.sync();
340
+ } finally {
341
+ await file.close();
342
+ }
343
+ return bytes.length;
344
+ }
345
+ async evict(name) {
346
+ const entry = this.history.find((file) => file.name === name);
347
+ if (!entry)
348
+ return 0;
349
+ await unlink(join(this.directory, name));
350
+ this.history = this.history.filter((file) => file.name !== name);
351
+ this.historyBytes -= entry.bytes;
352
+ this.evictedBytes += entry.bytes;
353
+ this.evictedFiles++;
354
+ return entry.bytes;
355
+ }
356
+ async trim(incoming = 0, keep) {
357
+ const removable = () => this.history.filter((file) => file.name !== keep);
358
+ const today = new Date().toISOString().slice(0, 10);
359
+ const oldestDay = new Date(Date.now() - this.options.historyMaxDays * 86400000).toISOString().slice(0, 10);
360
+ for (const file of removable()) {
361
+ const day = dayPattern.exec(file.name)?.[1] ?? today;
362
+ if (day < oldestDay)
363
+ await this.evict(file.name);
364
+ }
365
+ while (this.historyBytes + incoming > this.options.historyBudgetBytes) {
366
+ const oldest = removable().sort((a, b) => a.name.localeCompare(b.name))[0];
367
+ if (!oldest)
368
+ break;
369
+ await this.evict(oldest.name);
370
+ }
371
+ const remove = [];
372
+ let remaining = this.traceBytes;
373
+ for (const trace of this.traces) {
374
+ if (remaining + incoming <= this.options.traceBudgetBytes)
375
+ break;
376
+ remove.push(trace);
377
+ remaining -= trace.bytes;
378
+ }
379
+ if (!remove.length)
380
+ return;
381
+ const event = makeEvent("journal", "journal_retention", "observed", {
382
+ reason: "Bounded diagnostic trace window; life-event history is retained",
383
+ files: remove.map((file) => file.name),
384
+ bytes: remove.reduce((sum, file) => sum + file.bytes, 0),
385
+ replayComplete: false
386
+ }, { level: "warn" });
387
+ const segment = this.segment(event.at.slice(0, 10));
388
+ const written = await this.append(segment.name, [event], 6);
389
+ segment.bytes += written;
390
+ this.historyBytes += written;
391
+ for (const trace of remove) {
392
+ await unlink(join(this.directory, trace.name));
393
+ this.traceBytes -= trace.bytes;
394
+ this.evictedBytes += trace.bytes;
395
+ this.evictedFiles++;
396
+ this.traces = this.traces.filter((file) => file.name !== trace.name);
397
+ }
398
+ }
399
+ segment(day) {
400
+ const files = this.history.filter((file) => dayPattern.exec(file.name)?.[1] === day).sort((a, b) => a.name.localeCompare(b.name));
401
+ const latest = files.at(-1);
402
+ if (latest && latest.bytes < this.options.historySegmentBytes && latest.name !== `${day}.jsonl.gz`)
403
+ return latest;
404
+ const index = files.filter((file) => /\.\d{8}\.jsonl\.gz$/.test(file.name)).length;
405
+ const fresh = { name: `${day}.${String(index).padStart(8, "0")}.jsonl.gz`, bytes: 0 };
406
+ this.history.push(fresh);
407
+ this.history.sort((a, b) => a.name.localeCompare(b.name));
408
+ return fresh;
409
+ }
410
+ async write(events) {
411
+ await this.trim();
412
+ if (Date.now() - this.diskCheckedAt > 5000) {
413
+ const disk = await statfs(this.directory);
414
+ const free = Number(disk.bavail) * Number(disk.bsize);
415
+ if (free < this.options.reserveBytes)
416
+ throw new Error("Journal disk reserve reached; archive life-event history before continuing");
417
+ this.diskCheckedAt = Date.now();
418
+ }
419
+ const groups = new Map;
420
+ for (const event of events) {
421
+ const day = event.at.slice(0, 10);
422
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(day))
423
+ throw new Error("Invalid life-event timestamp");
424
+ const trace = traceTypes.has(event.type) && event.level !== "error" && event.level !== "warn";
425
+ const key = day + (trace ? ".trace" : "");
426
+ const group = groups.get(key) ?? [];
427
+ group.push(event);
428
+ groups.set(key, group);
429
+ }
430
+ for (const [key, group] of groups) {
431
+ if (!key.endsWith(".trace")) {
432
+ const day = key;
433
+ const segment = this.segment(day);
434
+ const bytes = await this.append(segment.name, group, 6);
435
+ segment.bytes += bytes;
436
+ this.historyBytes += bytes;
437
+ await this.trim(0, segment.name);
438
+ continue;
439
+ }
440
+ const latest = this.traces.filter((file) => file.name.startsWith(`${key}.`)).at(-1);
441
+ const index = latest ? Number(latest.name.split(".")[2]) + (latest.bytes >= this.options.segmentBytes ? 1 : 0) : 0;
442
+ const name = `${key}.${String(index).padStart(8, "0")}.jsonl.gz`;
443
+ const bytes = await this.append(name, group, 3);
444
+ const file = this.traces.find((file) => file.name === name);
445
+ if (file)
446
+ file.bytes += bytes;
447
+ else {
448
+ this.traces.push({ name, bytes });
449
+ this.traces.sort((a, b) => a.name.localeCompare(b.name));
450
+ }
451
+ this.traceBytes += bytes;
452
+ await this.trim(0, name);
453
+ }
454
+ }
455
+ }
456
+
457
+ // src/life-journal-worker.ts
458
+ var files = new JournalFiles(String(workerData.directory));
459
+ await files.initialize();
460
+ var chain = Promise.resolve();
461
+ parentPort?.on("message", (message) => {
462
+ chain = chain.then(async () => {
463
+ await files.write(message.events);
464
+ parentPort?.postMessage({ through: message.through, storage: files.statistics() });
465
+ }).catch((error) => parentPort?.postMessage({
466
+ problem: `Life-event journal could not be saved: ${error instanceof Error ? error.message : String(error)}`
467
+ }));
468
+ });
469
+ parentPort?.postMessage({ ready: true });