@bitmagic/world-forger 0.1.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 (105) hide show
  1. package/dist/artifact-types.d.ts +295 -0
  2. package/dist/artifact-types.d.ts.map +1 -0
  3. package/dist/artifact-types.js +17 -0
  4. package/dist/artifact-types.js.map +1 -0
  5. package/dist/forge-constants.d.ts +106 -0
  6. package/dist/forge-constants.d.ts.map +1 -0
  7. package/dist/forge-constants.js +111 -0
  8. package/dist/forge-constants.js.map +1 -0
  9. package/dist/index.d.ts +28 -0
  10. package/dist/index.d.ts.map +1 -0
  11. package/dist/index.js +28 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/log.d.ts +26 -0
  14. package/dist/log.d.ts.map +1 -0
  15. package/dist/log.js +14 -0
  16. package/dist/log.js.map +1 -0
  17. package/dist/pipeline/archetype-skips.d.ts +57 -0
  18. package/dist/pipeline/archetype-skips.d.ts.map +1 -0
  19. package/dist/pipeline/archetype-skips.js +51 -0
  20. package/dist/pipeline/archetype-skips.js.map +1 -0
  21. package/dist/pipeline/artifact-store.d.ts +4 -0
  22. package/dist/pipeline/artifact-store.d.ts.map +1 -0
  23. package/dist/pipeline/artifact-store.js +10 -0
  24. package/dist/pipeline/artifact-store.js.map +1 -0
  25. package/dist/pipeline/asset-catalog.d.ts +27 -0
  26. package/dist/pipeline/asset-catalog.d.ts.map +1 -0
  27. package/dist/pipeline/asset-catalog.js +47 -0
  28. package/dist/pipeline/asset-catalog.js.map +1 -0
  29. package/dist/pipeline/create-level-archetypes.d.ts +45 -0
  30. package/dist/pipeline/create-level-archetypes.d.ts.map +1 -0
  31. package/dist/pipeline/create-level-archetypes.js +197 -0
  32. package/dist/pipeline/create-level-archetypes.js.map +1 -0
  33. package/dist/pipeline/deps.d.ts +72 -0
  34. package/dist/pipeline/deps.d.ts.map +1 -0
  35. package/dist/pipeline/deps.js +2 -0
  36. package/dist/pipeline/deps.js.map +1 -0
  37. package/dist/pipeline/errors.d.ts +10 -0
  38. package/dist/pipeline/errors.d.ts.map +1 -0
  39. package/dist/pipeline/errors.js +13 -0
  40. package/dist/pipeline/errors.js.map +1 -0
  41. package/dist/pipeline/forged-work-store.d.ts +46 -0
  42. package/dist/pipeline/forged-work-store.d.ts.map +1 -0
  43. package/dist/pipeline/forged-work-store.js +69 -0
  44. package/dist/pipeline/forged-work-store.js.map +1 -0
  45. package/dist/pipeline/index.d.ts +36 -0
  46. package/dist/pipeline/index.d.ts.map +1 -0
  47. package/dist/pipeline/index.js +36 -0
  48. package/dist/pipeline/index.js.map +1 -0
  49. package/dist/pipeline/job-types.d.ts +145 -0
  50. package/dist/pipeline/job-types.d.ts.map +1 -0
  51. package/dist/pipeline/job-types.js +2 -0
  52. package/dist/pipeline/job-types.js.map +1 -0
  53. package/dist/pipeline/level-registry.d.ts +57 -0
  54. package/dist/pipeline/level-registry.d.ts.map +1 -0
  55. package/dist/pipeline/level-registry.js +108 -0
  56. package/dist/pipeline/level-registry.js.map +1 -0
  57. package/dist/pipeline/model-types.d.ts +51 -0
  58. package/dist/pipeline/model-types.d.ts.map +1 -0
  59. package/dist/pipeline/model-types.js +2 -0
  60. package/dist/pipeline/model-types.js.map +1 -0
  61. package/dist/pipeline/persist-level.d.ts +82 -0
  62. package/dist/pipeline/persist-level.d.ts.map +1 -0
  63. package/dist/pipeline/persist-level.js +201 -0
  64. package/dist/pipeline/persist-level.js.map +1 -0
  65. package/dist/pipeline/place-and-persist-level.d.ts +83 -0
  66. package/dist/pipeline/place-and-persist-level.d.ts.map +1 -0
  67. package/dist/pipeline/place-and-persist-level.js +405 -0
  68. package/dist/pipeline/place-and-persist-level.js.map +1 -0
  69. package/dist/pipeline/request-id.d.ts +7 -0
  70. package/dist/pipeline/request-id.d.ts.map +1 -0
  71. package/dist/pipeline/request-id.js +10 -0
  72. package/dist/pipeline/request-id.js.map +1 -0
  73. package/dist/pipeline/spawn-points.d.ts +47 -0
  74. package/dist/pipeline/spawn-points.d.ts.map +1 -0
  75. package/dist/pipeline/spawn-points.js +100 -0
  76. package/dist/pipeline/spawn-points.js.map +1 -0
  77. package/dist/pipeline/starter-scatter.d.ts +34 -0
  78. package/dist/pipeline/starter-scatter.d.ts.map +1 -0
  79. package/dist/pipeline/starter-scatter.js +49 -0
  80. package/dist/pipeline/starter-scatter.js.map +1 -0
  81. package/dist/pipeline/storage-types.d.ts +48 -0
  82. package/dist/pipeline/storage-types.d.ts.map +1 -0
  83. package/dist/pipeline/storage-types.js +19 -0
  84. package/dist/pipeline/storage-types.js.map +1 -0
  85. package/dist/pipeline/transport-types.d.ts +35 -0
  86. package/dist/pipeline/transport-types.d.ts.map +1 -0
  87. package/dist/pipeline/transport-types.js +2 -0
  88. package/dist/pipeline/transport-types.js.map +1 -0
  89. package/dist/pipeline/voxelize-level.d.ts +24 -0
  90. package/dist/pipeline/voxelize-level.d.ts.map +1 -0
  91. package/dist/pipeline/voxelize-level.js +63 -0
  92. package/dist/pipeline/voxelize-level.js.map +1 -0
  93. package/dist/pipeline/world-json-source.d.ts +78 -0
  94. package/dist/pipeline/world-json-source.d.ts.map +1 -0
  95. package/dist/pipeline/world-json-source.js +2 -0
  96. package/dist/pipeline/world-json-source.js.map +1 -0
  97. package/dist/pipeline/world-json-types.d.ts +104 -0
  98. package/dist/pipeline/world-json-types.d.ts.map +1 -0
  99. package/dist/pipeline/world-json-types.js +15 -0
  100. package/dist/pipeline/world-json-types.js.map +1 -0
  101. package/dist/terrain-height-grid.d.ts +45 -0
  102. package/dist/terrain-height-grid.d.ts.map +1 -0
  103. package/dist/terrain-height-grid.js +50 -0
  104. package/dist/terrain-height-grid.js.map +1 -0
  105. package/package.json +72 -0
@@ -0,0 +1,34 @@
1
+ /**
2
+ * The starter template's scattered trees/rocks a forged level orphans, and the
3
+ * plan for stripping them.
4
+ *
5
+ * Moved here from game-play-agent's `utils/voxel-default-assets.ts` — which
6
+ * MINTS these ids and now re-exports this planner — because the world-forger's
7
+ * persist step is its only caller and that step no longer lives in
8
+ * game-play-agent. The generator and the matcher still share one definition of
9
+ * the id shapes; they are just imported across the seam instead of adjacent.
10
+ */
11
+ /** Ids of the starter scatter to strip from a world.json. */
12
+ export interface StarterScatterRemoval {
13
+ /** `assets[].id` values to drop. */
14
+ assetIds: string[];
15
+ /** `environmentObjects[].id` values to drop. */
16
+ objectIds: string[];
17
+ }
18
+ /**
19
+ * Work out which starter trees/rocks a world.json still carries from its template.
20
+ *
21
+ * Every voxel starter template ships the six default tree/rock assets, and two of them
22
+ * (standard-3d, first-person) also ship 23 scattered instances — that's the scene in the
23
+ * template screenshot. When the World-Forger bakes a real level it *replaces* the template's
24
+ * terrain but only upserts its own assets/instances, so the starter scatter survives: the
25
+ * trees get re-snapped onto the forged terrain (they're `placeOnTerrain`) somewhere off in
26
+ * the origin corner, and the six assets stay in the library where the creator's Remove button
27
+ * is hidden for anything still referenced by an instance.
28
+ *
29
+ * Only exact id matches on BOTH the instance id and its assetId count as starter scatter, so
30
+ * anything the AI later placed from these assets is left alone; an asset is dropped only once
31
+ * nothing else references it.
32
+ */
33
+ export declare function planStarterScatterRemoval(worldData: Record<string, unknown> | null | undefined): StarterScatterRemoval;
34
+ //# sourceMappingURL=starter-scatter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"starter-scatter.d.ts","sourceRoot":"","sources":["../../src/pipeline/starter-scatter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAOH,6DAA6D;AAC7D,MAAM,WAAW,qBAAqB;IACpC,oCAAoC;IACpC,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,gDAAgD;IAChD,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,yBAAyB,CACvC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,SAAS,GACpD,qBAAqB,CA0BvB"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * The starter template's scattered trees/rocks a forged level orphans, and the
3
+ * plan for stripping them.
4
+ *
5
+ * Moved here from game-play-agent's `utils/voxel-default-assets.ts` — which
6
+ * MINTS these ids and now re-exports this planner — because the world-forger's
7
+ * persist step is its only caller and that step no longer lives in
8
+ * game-play-agent. The generator and the matcher still share one definition of
9
+ * the id shapes; they are just imported across the seam instead of adjacent.
10
+ */
11
+ /** Asset ids minted by the default-asset generator (and hardcoded in the starter templates' world.json). */
12
+ const STARTER_ASSET_ID_RE = /^default-(tree|rock)-\d+$/;
13
+ /** Instance ids minted by `generateObjectId` (and hardcoded in the starter templates). */
14
+ const STARTER_OBJECT_ID_RE = /^default_(tree|rock)_\d+$/;
15
+ /**
16
+ * Work out which starter trees/rocks a world.json still carries from its template.
17
+ *
18
+ * Every voxel starter template ships the six default tree/rock assets, and two of them
19
+ * (standard-3d, first-person) also ship 23 scattered instances — that's the scene in the
20
+ * template screenshot. When the World-Forger bakes a real level it *replaces* the template's
21
+ * terrain but only upserts its own assets/instances, so the starter scatter survives: the
22
+ * trees get re-snapped onto the forged terrain (they're `placeOnTerrain`) somewhere off in
23
+ * the origin corner, and the six assets stay in the library where the creator's Remove button
24
+ * is hidden for anything still referenced by an instance.
25
+ *
26
+ * Only exact id matches on BOTH the instance id and its assetId count as starter scatter, so
27
+ * anything the AI later placed from these assets is left alone; an asset is dropped only once
28
+ * nothing else references it.
29
+ */
30
+ export function planStarterScatterRemoval(worldData) {
31
+ const empty = { assetIds: [], objectIds: [] };
32
+ if (!worldData)
33
+ return empty;
34
+ const assets = Array.isArray(worldData.assets) ? worldData.assets : [];
35
+ const environmentObjects = Array.isArray(worldData.environmentObjects) ? worldData.environmentObjects : [];
36
+ const idOf = (item, key) => String(item?.[key] ?? '');
37
+ const objectIds = environmentObjects
38
+ .filter((obj) => STARTER_OBJECT_ID_RE.test(idOf(obj, 'id')) && STARTER_ASSET_ID_RE.test(idOf(obj, 'assetId')))
39
+ .map((obj) => idOf(obj, 'id'));
40
+ const removedObjectIds = new Set(objectIds);
41
+ const stillReferenced = new Set(environmentObjects
42
+ .filter((obj) => !removedObjectIds.has(idOf(obj, 'id')))
43
+ .map((obj) => idOf(obj, 'assetId')));
44
+ const assetIds = assets
45
+ .map((asset) => idOf(asset, 'id'))
46
+ .filter((id) => STARTER_ASSET_ID_RE.test(id) && !stillReferenced.has(id));
47
+ return { assetIds, objectIds };
48
+ }
49
+ //# sourceMappingURL=starter-scatter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"starter-scatter.js","sourceRoot":"","sources":["../../src/pipeline/starter-scatter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,4GAA4G;AAC5G,MAAM,mBAAmB,GAAG,2BAA2B,CAAC;AACxD,0FAA0F;AAC1F,MAAM,oBAAoB,GAAG,2BAA2B,CAAC;AAUzD;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,yBAAyB,CACvC,SAAqD;IAErD,MAAM,KAAK,GAA0B,EAAE,QAAQ,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC;IACrE,IAAI,CAAC,SAAS;QAAE,OAAO,KAAK,CAAC;IAE7B,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IACvE,MAAM,kBAAkB,GAAG,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAAC,CAAC,EAAE,CAAC;IAE3G,MAAM,IAAI,GAAG,CAAC,IAAa,EAAE,GAAqB,EAAU,EAAE,CAC5D,MAAM,CAAE,IAAuC,EAAE,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;IAEhE,MAAM,SAAS,GAAG,kBAAkB;SACjC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,IAAI,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC,CAAC;SAC7G,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC;IAEjC,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC;IAC5C,MAAM,eAAe,GAAG,IAAI,GAAG,CAC7B,kBAAkB;SACf,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC;SACvD,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC,CACtC,CAAC;IAEF,MAAM,QAAQ,GAAG,MAAM;SACpB,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;SACjC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,mBAAmB,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IAE5E,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;AACjC,CAAC"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * The storage seam — deliberately a NARROW structural subset of the host's
3
+ * real `StorageClient`/`StorageEnv` (game-play-agent's
4
+ * `src/mastra/storage/StorageClient.ts` / `storage-env.ts`), field-for-field
5
+ * on the methods this package actually calls. Because TypeScript interfaces
6
+ * are structural, the host's real S3/GCS/local client satisfies this without
7
+ * any adapter — `getStorageEnv()` can be passed straight through as
8
+ * `ForgeDeps.storage`.
9
+ *
10
+ * Bytes are `Uint8Array`, not node's `Buffer`. `Buffer` would be correct for
11
+ * every host today, but it is a NODE type: putting it in this published
12
+ * package's type surface forces every consumer to install `@types/node` or
13
+ * type-check with `skipLibCheck`, and a consumer without either gets
14
+ * `Cannot find name 'Buffer'` from a .d.ts they never wrote. `Buffer` extends
15
+ * `Uint8Array`, so a host whose real client takes and returns Buffers still
16
+ * satisfies this unchanged.
17
+ */
18
+ export interface ForgePutObjectParams {
19
+ bucket: string;
20
+ key: string;
21
+ body: string | Uint8Array;
22
+ contentType: string;
23
+ cacheControl: string;
24
+ contentEncoding?: string;
25
+ }
26
+ export interface ForgeHeadObjectParams {
27
+ bucket: string;
28
+ key: string;
29
+ }
30
+ export interface ForgeGetObjectParams {
31
+ bucket: string;
32
+ key: string;
33
+ }
34
+ export interface ForgeStorageClient {
35
+ /** Returns the number of bytes actually written (the host may compress). */
36
+ putObject(params: ForgePutObjectParams): Promise<number>;
37
+ headObject(params: ForgeHeadObjectParams): Promise<boolean>;
38
+ getObject(params: ForgeGetObjectParams): Promise<Uint8Array | null>;
39
+ }
40
+ /** `createForgedWorkStore(jobId, env)` already takes this shape as a parameter
41
+ * (see forged-work-store.ts) — this is that SAME seam, not a new one. */
42
+ export interface ForgeStorageEnv {
43
+ storageClient: ForgeStorageClient;
44
+ bucket: string;
45
+ /** Public URL prefix (CDN, or file://) the uploaded key is appended to. */
46
+ publicUrl: string;
47
+ }
48
+ //# sourceMappingURL=storage-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage-types.d.ts","sourceRoot":"","sources":["../../src/pipeline/storage-types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,GAAG,UAAU,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,kBAAkB;IACjC,4EAA4E;IAC5E,SAAS,CAAC,MAAM,EAAE,oBAAoB,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACzD,UAAU,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5D,SAAS,CAAC,MAAM,EAAE,oBAAoB,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC;CACrE;AAED;0EAC0E;AAC1E,MAAM,WAAW,eAAe;IAC9B,aAAa,EAAE,kBAAkB,CAAC;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,2EAA2E;IAC3E,SAAS,EAAE,MAAM,CAAC;CACnB"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * The storage seam — deliberately a NARROW structural subset of the host's
3
+ * real `StorageClient`/`StorageEnv` (game-play-agent's
4
+ * `src/mastra/storage/StorageClient.ts` / `storage-env.ts`), field-for-field
5
+ * on the methods this package actually calls. Because TypeScript interfaces
6
+ * are structural, the host's real S3/GCS/local client satisfies this without
7
+ * any adapter — `getStorageEnv()` can be passed straight through as
8
+ * `ForgeDeps.storage`.
9
+ *
10
+ * Bytes are `Uint8Array`, not node's `Buffer`. `Buffer` would be correct for
11
+ * every host today, but it is a NODE type: putting it in this published
12
+ * package's type surface forces every consumer to install `@types/node` or
13
+ * type-check with `skipLibCheck`, and a consumer without either gets
14
+ * `Cannot find name 'Buffer'` from a .d.ts they never wrote. `Buffer` extends
15
+ * `Uint8Array`, so a host whose real client takes and returns Buffers still
16
+ * satisfies this unchanged.
17
+ */
18
+ export {};
19
+ //# sourceMappingURL=storage-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage-types.js","sourceRoot":"","sources":["../../src/pipeline/storage-types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * The transport seam — replaces game-play-agent's `sendAndWaitForResult`
3
+ * (`utils/voxel-ws.ts`). One method: send a command, await a typed result.
4
+ *
5
+ * Not read by steps 1-2 (design/forge+upload), which is why api-server's host
6
+ * passes a throwing stub. Its three consumers are the browser-driven steps in
7
+ * this package: `create-level-archetypes.ts`, `voxelize-level.ts` and
8
+ * `place-and-persist-level.ts`. The web lane wires a real WebSocket
9
+ * implementation (`game-play-agent`'s `transport-client.ts`); the CLI lane
10
+ * wires one over a headless-Chrome page bridge (`cli/src/forge/transport.ts`).
11
+ */
12
+ export interface ForgeTransportOptions {
13
+ /** Default 3 (matches the web lane's WS retry policy). Pass 1 for
14
+ * expensive, non-idempotent work (a level bake) where a timed-out attempt
15
+ * must not be silently re-fired. */
16
+ maxRetries?: number;
17
+ /**
18
+ * When set, `timeoutMs` becomes an IDLE timeout: a message of this type
19
+ * (matching the command's requestId, when present) RE-ARMS the timer
20
+ * instead of counting toward it, so a long-but-active job (a chunk-by-chunk
21
+ * level bake that can legitimately run many minutes) is not killed
22
+ * mid-flight — only genuine silence for `timeoutMs` fails the attempt.
23
+ */
24
+ progressType?: string;
25
+ }
26
+ export interface ForgeTransport {
27
+ /**
28
+ * Send `command` and await the first message of type `resultType` (matched
29
+ * on `requestId` when the command carries one). Resolves `null` on timeout
30
+ * or transport failure — never rejects for that case, so callers can tell
31
+ * "no answer" apart from "answered with an error".
32
+ */
33
+ sendAndWait(command: Record<string, unknown>, resultType: string, timeoutMs: number, options?: ForgeTransportOptions): Promise<Record<string, unknown> | null>;
34
+ }
35
+ //# sourceMappingURL=transport-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transport-types.d.ts","sourceRoot":"","sources":["../../src/pipeline/transport-types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,WAAW,qBAAqB;IACpC;;yCAEqC;IACrC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,cAAc;IAC7B;;;;;OAKG;IACH,WAAW,CACT,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,qBAAqB,GAC9B,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;CAC5C"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=transport-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transport-types.js","sourceRoot":"","sources":["../../src/pipeline/transport-types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Step 4 — bake the level in the browser through the host's transport seam.
3
+ *
4
+ * Moved from game-play-agent's `pipeline.ts` verbatim, with the one
5
+ * `sendAndWaitForResult` call site turned into `deps.transport.sendAndWait`
6
+ * (same timeout, same `{ maxRetries: 1, progressType }` policy) and the logger
7
+ * turned into `deps.logger`.
8
+ */
9
+ import type { ForgedWorkStore } from './forged-work-store.js';
10
+ import type { ForgeDeps } from './deps.js';
11
+ /**
12
+ * Idle timeout for the level bake: it runs chunk-by-chunk in the browser and
13
+ * emits VOXELIZE_GLB_AS_LEVEL_PROGRESS per chunk, each of which re-arms this
14
+ * timer (see the transport seam's `progressType`). So a slow-but-active bake is
15
+ * never killed mid-flight — only ~20 min of true silence (browser closed, tab
16
+ * frozen) fails the attempt.
17
+ */
18
+ export declare const VOXELIZE_TIMEOUT_MS: number;
19
+ /**
20
+ * Bake the level. Tagged instance groups are skipped (objectAssetInstances).
21
+ * Single attempt: a timed-out bake must not be silently re-run.
22
+ */
23
+ export declare function voxelizeLevel(store: ForgedWorkStore, deps: ForgeDeps): Promise<void>;
24
+ //# sourceMappingURL=voxelize-level.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"voxelize-level.d.ts","sourceRoot":"","sources":["../../src/pipeline/voxelize-level.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAM3C;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,QAAiB,CAAC;AAIlD;;;GAGG;AACH,wBAAsB,aAAa,CAAC,KAAK,EAAE,eAAe,EAAE,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAyD1F"}
@@ -0,0 +1,63 @@
1
+ import { requireArtifact } from './artifact-store.js';
2
+ import { ForgeStepError } from './errors.js';
3
+ import { forgeRequestId } from './request-id.js';
4
+ /**
5
+ * Idle timeout for the level bake: it runs chunk-by-chunk in the browser and
6
+ * emits VOXELIZE_GLB_AS_LEVEL_PROGRESS per chunk, each of which re-arms this
7
+ * timer (see the transport seam's `progressType`). So a slow-but-active bake is
8
+ * never killed mid-flight — only ~20 min of true silence (browser closed, tab
9
+ * frozen) fails the attempt.
10
+ */
11
+ export const VOXELIZE_TIMEOUT_MS = 20 * 60 * 1000;
12
+ /** Progress message the bake emits per chunk; resets the idle timeout above. */
13
+ const VOXELIZE_PROGRESS_TYPE = 'VOXELIZE_GLB_AS_LEVEL_PROGRESS';
14
+ /**
15
+ * Bake the level. Tagged instance groups are skipped (objectAssetInstances).
16
+ * Single attempt: a timed-out bake must not be silently re-run.
17
+ */
18
+ export async function voxelizeLevel(store, deps) {
19
+ const input = await requireArtifact(store, 'input');
20
+ const forge = await requireArtifact(store, 'forge');
21
+ const requestId = forgeRequestId();
22
+ const result = await deps.transport.sendAndWait({
23
+ type: 'VOXELIZE_GLB_AS_LEVEL',
24
+ requestId,
25
+ glbUrl: forge.glbUrl,
26
+ levelName: input.name,
27
+ // Carries the dungeon's `emissiveByColor` map (deriveVoxelizeOptions put
28
+ // it there): the bake matches those authored colors against the palette
29
+ // it actually produces and writes the level's emissive block.
30
+ options: forge.voxelizeOptions,
31
+ }, 'VOXELIZE_GLB_AS_LEVEL_RESULT', VOXELIZE_TIMEOUT_MS,
32
+ // Single attempt (a timed-out bake must not be silently re-fired), but the
33
+ // timeout is now idle-based: per-chunk progress re-arms it.
34
+ { maxRetries: 1, progressType: VOXELIZE_PROGRESS_TYPE });
35
+ if (!result) {
36
+ throw new ForgeStepError('Level voxelization timed out. The editor (browser) must be open for baking. The designed GLB was uploaded and can be voxelized manually from the Assets tab.', forge.glbUrl);
37
+ }
38
+ if (!result.success) {
39
+ throw new ForgeStepError(`Level voxelization failed: ${result.error || result.message || 'Unknown error'}`, forge.glbUrl);
40
+ }
41
+ // Authored emissive colors the bake's palette never produced: an advisory
42
+ // (the glow simply doesn't appear), never a failure.
43
+ const unmatched = result.emissiveUnmatched ?? [];
44
+ const notes = unmatched.length > 0
45
+ ? [`Emissive colors that matched no baked voxel: ${unmatched.join(', ')} — those surfaces do not glow`]
46
+ : [];
47
+ const bake = {
48
+ vwldUrl: result.vwldUrl,
49
+ bakedRecord: {
50
+ ...result.assetRecord,
51
+ // The navmesh sidecar was compiled against THIS bake's level transform,
52
+ // so it rides on the level asset the engine installs at level load.
53
+ ...(forge.navUrl ? { navUrl: forge.navUrl } : {}),
54
+ },
55
+ nonEmptyChunkCount: result.nonEmptyChunkCount,
56
+ totalLod0Leaves: result.totalLod0Leaves,
57
+ totalTrimeshTriangles: result.totalTrimeshTriangles,
58
+ ...(notes.length > 0 ? { notes } : {}),
59
+ };
60
+ await store.put('level-bake', bake);
61
+ deps.logger.info({ jobId: store.jobId, vwldUrl: bake.vwldUrl, chunks: bake.nonEmptyChunkCount, navUrl: forge.navUrl, unmatched }, 'Level baked');
62
+ }
63
+ //# sourceMappingURL=voxelize-level.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"voxelize-level.js","sourceRoot":"","sources":["../../src/pipeline/voxelize-level.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * The world.json READ seam — the persist step (step 5) plans against the
3
+ * game's current world.json, but this package never opens a file. The host
4
+ * supplies the content; the host also applies the modifications the step
5
+ * returns (see `place-and-persist-level.ts`).
6
+ *
7
+ * ── Why TWO reads, and what each one actually wants ──────────────────────────
8
+ *
9
+ * game-play-agent's step 5 read TWO DIFFERENT files, and the difference is not
10
+ * cosmetic — so this seam preserves it rather than quietly picking one:
11
+ *
12
+ * - the starter-scatter plan read the SESSION copy
13
+ * (`readWorldData(runtimeContext)` → `tempGenrePath/world.json`, the file
14
+ * the agent's writer also writes),
15
+ * - the persist plan read the monorepo WORK copy
16
+ * (`readWorkWorldJson()` → `game/src/work/world.json`, the copy the
17
+ * creator/browser edits and that the session copy is copied wholesale OVER
18
+ * at cleanup — a `copyFileSync` behind one "did temp go completely empty"
19
+ * guard, not a merge, so whichever file a plan was computed against is the
20
+ * one whose content ends up surviving).
21
+ *
22
+ * What each SHOULD want is the same thing: the file the returned modifications
23
+ * will be applied to. Both plans are differential — the scatter plan emits
24
+ * `removeRoot` predicates over ids it saw, the persist plan emits a whole-array
25
+ * `setRoot ['environmentObjects']` computed from the instances it saw plus a
26
+ * purge over the assets it saw — so planning against a file OTHER than the
27
+ * write target either drops work that is only in the target or writes back
28
+ * content the target had already removed.
29
+ *
30
+ * That is an argument about what the code should do, not a licence to change
31
+ * what it does: in game-play-agent's web lane the two files genuinely differ
32
+ * mid-session (the creator writes assets into the work copy while the agent
33
+ * writes the session copy), so collapsing them here would silently change which
34
+ * content survives a forge — outside the scope of this extraction, and exactly
35
+ * the kind of change that would only show up in a shipped level. So the seam
36
+ * keeps both reads, named for what they FEED rather than for which file they
37
+ * happen to hit, and the host decides:
38
+ *
39
+ * - the web lane wires each to the exact file it reads today (no behaviour
40
+ * change),
41
+ * - a CLI project has exactly ONE world.json, so its adapter returns that
42
+ * same file from both — which is what "plan against the write target"
43
+ * means when there is only one target.
44
+ *
45
+ * Both reads DEGRADE deliberately and that degradation is part of the contract
46
+ * (see each method): a game with no world.json, or an unparseable one, must
47
+ * still be able to forge a level — the persist plan then simply plans against
48
+ * an empty world, and the scatter plan finds nothing to strip. Neither is a
49
+ * failure the user could act on, and both cost a paid 4-8 minute forge.
50
+ *
51
+ * Asynchronous on purpose: a host that keeps world.json in object storage (the
52
+ * CLI lane's api-server) must not be forced to pretend the read is local.
53
+ */
54
+ import type { WorldJsonShape } from './world-json-types.js';
55
+ export interface ForgeWorldJsonSource {
56
+ /**
57
+ * The world.json the persist plan is computed against — the level registry,
58
+ * the existing instances the level-scoped replace preserves, and the assets
59
+ * the placeholder purge considers.
60
+ *
61
+ * Returns `{}` when the game has no world.json or it cannot be parsed: every
62
+ * consumer downstream has a sane empty-world path (no levels registry, no
63
+ * instances to keep, nothing to purge), so an unreadable file costs the user
64
+ * nothing but a fresh start.
65
+ */
66
+ readForPersistPlan(): Promise<WorldJsonShape>;
67
+ /**
68
+ * The world.json the starter-scatter removal is planned against — the
69
+ * template's `default_tree_N` / `default-tree-N` instances and assets the
70
+ * forged level orphans.
71
+ *
72
+ * Returns `null` when unavailable: no read means no ids, which means no
73
+ * removals. Stripping nothing is always safe; the leftovers are cosmetic and
74
+ * removable in the creator afterwards.
75
+ */
76
+ readForStarterScatterPlan(): Promise<Record<string, unknown> | null>;
77
+ }
78
+ //# sourceMappingURL=world-json-source.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"world-json-source.d.ts","sourceRoot":"","sources":["../../src/pipeline/world-json-source.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoDG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,MAAM,WAAW,oBAAoB;IACnC;;;;;;;;;OASG;IACH,kBAAkB,IAAI,OAAO,CAAC,cAAc,CAAC,CAAC;IAE9C;;;;;;;;OAQG;IACH,yBAAyB,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;CACtE"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=world-json-source.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"world-json-source.js","sourceRoot":"","sources":["../../src/pipeline/world-json-source.ts"],"names":[],"mappings":""}
@@ -0,0 +1,104 @@
1
+ /**
2
+ * The world.json shapes the persist step speaks — the MODIFICATION format it
3
+ * returns, and the minimal read shape it plans against.
4
+ *
5
+ * Types only, no runtime: this package never reads or writes world.json. The
6
+ * host owns the file (game-play-agent's `utils/world-json-editor.ts` applies
7
+ * these against the session's copy; a CLI lane applies them against the one
8
+ * file its project has) and re-exports {@link WorldJsonModification} from here
9
+ * so there is exactly one definition of the format on both sides of the seam.
10
+ *
11
+ * Moved verbatim from game-play-agent's `utils/world-json-editor.ts` (the
12
+ * modification union) and `utils/level-registry.ts` (the read shape).
13
+ */
14
+ /** Base modification type for `worldProfileData` paths. */
15
+ export interface BaseModification {
16
+ /** Path within worldProfileData to modify (e.g. `['skyboxUrl']` or `['animations']`). */
17
+ path: string[];
18
+ }
19
+ /** Set a single property value within worldProfileData. */
20
+ export interface SetModification extends BaseModification {
21
+ type: 'set';
22
+ value: unknown;
23
+ }
24
+ /** Push a value to an array within worldProfileData (creates the array if absent). */
25
+ export interface PushModification extends BaseModification {
26
+ type: 'push';
27
+ value: unknown;
28
+ }
29
+ /** Update an existing array item within worldProfileData. */
30
+ export interface UpdateModification extends BaseModification {
31
+ type: 'update';
32
+ /** Predicate to find the item to update. */
33
+ predicate: (item: unknown) => boolean;
34
+ /** New value to replace the found item, or an updater function. */
35
+ value: unknown | ((item: unknown) => unknown);
36
+ }
37
+ /** Remove items from an array within worldProfileData. */
38
+ export interface RemoveModification extends BaseModification {
39
+ type: 'remove';
40
+ /** Items matching this predicate are removed. */
41
+ predicate: (item: unknown) => boolean;
42
+ }
43
+ /** Upsert (update or insert) an array item within worldProfileData. */
44
+ export interface UpsertModification extends BaseModification {
45
+ type: 'upsert';
46
+ predicate: (item: unknown) => boolean;
47
+ value: unknown;
48
+ }
49
+ /** Base modification type for root-level paths (not within worldProfileData). */
50
+ export interface BaseRootModification {
51
+ /** Path from the JSON root (e.g. `['assets']`). */
52
+ path: string[];
53
+ }
54
+ /** Set a property at root level. */
55
+ export interface SetRootModification extends BaseRootModification {
56
+ type: 'setRoot';
57
+ value: unknown;
58
+ }
59
+ /** Push to an array at root level. */
60
+ export interface PushRootModification extends BaseRootModification {
61
+ type: 'pushRoot';
62
+ value: unknown;
63
+ }
64
+ /** Update an array item at root level. */
65
+ export interface UpdateRootModification extends BaseRootModification {
66
+ type: 'updateRoot';
67
+ predicate: (item: unknown) => boolean;
68
+ value: unknown | ((item: unknown) => unknown);
69
+ }
70
+ /** Remove items from an array at root level. */
71
+ export interface RemoveRootModification extends BaseRootModification {
72
+ type: 'removeRoot';
73
+ predicate: (item: unknown) => boolean;
74
+ }
75
+ /** Upsert (update or insert) an array item at root level. */
76
+ export interface UpsertRootModification extends BaseRootModification {
77
+ type: 'upsertRoot';
78
+ predicate: (item: unknown) => boolean;
79
+ value: unknown;
80
+ }
81
+ export type WorldJsonModification = SetModification | PushModification | UpdateModification | RemoveModification | UpsertModification | SetRootModification | PushRootModification | UpdateRootModification | RemoveRootModification | UpsertRootModification;
82
+ /** One entry of the multi-level registry (`worldProfileData.levels`). */
83
+ export interface LevelEntry {
84
+ id: string;
85
+ name: string;
86
+ vwldAssetId: string;
87
+ spawnPoints?: Array<Record<string, unknown>>;
88
+ overrides?: Record<string, unknown>;
89
+ }
90
+ /** The parts of world.json the persist plan reads. Everything else passes through untouched. */
91
+ export interface WorldJsonShape {
92
+ worldProfileData?: {
93
+ levels?: LevelEntry[];
94
+ startLevelId?: string;
95
+ voxelUrl?: string | null;
96
+ spawnPoints?: Array<Record<string, unknown>>;
97
+ playerSpawnPosition?: Record<string, unknown>;
98
+ [key: string]: unknown;
99
+ };
100
+ assets?: Array<Record<string, unknown>>;
101
+ environmentObjects?: Array<Record<string, unknown>>;
102
+ [key: string]: unknown;
103
+ }
104
+ //# sourceMappingURL=world-json-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"world-json-types.d.ts","sourceRoot":"","sources":["../../src/pipeline/world-json-types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * The world.json shapes the persist step speaks — the MODIFICATION format it
3
+ * returns, and the minimal read shape it plans against.
4
+ *
5
+ * Types only, no runtime: this package never reads or writes world.json. The
6
+ * host owns the file (game-play-agent's `utils/world-json-editor.ts` applies
7
+ * these against the session's copy; a CLI lane applies them against the one
8
+ * file its project has) and re-exports {@link WorldJsonModification} from here
9
+ * so there is exactly one definition of the format on both sides of the seam.
10
+ *
11
+ * Moved verbatim from game-play-agent's `utils/world-json-editor.ts` (the
12
+ * modification union) and `utils/level-registry.ts` (the read shape).
13
+ */
14
+ export {};
15
+ //# sourceMappingURL=world-json-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"world-json-types.js","sourceRoot":"","sources":["../../src/pipeline/world-json-types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * World-space terrain height grid — the bridge that lets objects placed AFTER
3
+ * a forge (e.g. collectibles the coding/asset agent scatters via the voxel CLI)
4
+ * sit on the forged surface instead of the world floor.
5
+ *
6
+ * The forge knows each prop's exact ground height because it samples its own
7
+ * GLB-space `Heightfield` and maps the result to world space with the level
8
+ * transform (see `forge-world.ts`). Agent-driven placements happen later, in a
9
+ * separate process, with no access to that in-memory heightfield — and a
10
+ * placement raycast can't help because the freshly-forged terrain only loads
11
+ * after the session reload. So the forge persists THIS grid (a sidecar next to
12
+ * world.json); the CLI samples it to resolve `placeOnTerrain` Y the same way
13
+ * the forge resolved its props.
14
+ *
15
+ * The grid is the carved heightfield expressed in WORLD coordinates. Because the
16
+ * level transform is affine and axis-aligned (uniform scale + translate, no
17
+ * rotation — see `marker-transform.ts`), a GLB-regular grid maps to a
18
+ * world-regular grid, so the conversion is a per-vertex scale/translate.
19
+ *
20
+ * The GRID and its SAMPLER live on the publishable side, because the host that
21
+ * writes the sidecar and the tools that read it are CLIENTS of the forge, not
22
+ * part of it. The producer — `heightfieldToWorldGrid`, which needs the forge's
23
+ * own heightfield and level transform — stays with the geometry, in the private
24
+ * package's module of the same name.
25
+ */
26
+ /** Sidecar filename written next to world.json. The CLI hardcodes the same name. */
27
+ export declare const FORGED_HEIGHTFIELD_FILE = "forged-heightfield.json";
28
+ export interface ForgedHeightGrid {
29
+ /** World coordinate of vertex (0, 0). */
30
+ minX: number;
31
+ minZ: number;
32
+ /** World meters per cell. */
33
+ cellSize: number;
34
+ /** Cells along X / Z. Vertex counts are nx+1 / nz+1. */
35
+ nx: number;
36
+ nz: number;
37
+ /** Row-major world-space heights, index = j * (nx + 1) + i, length (nx+1)*(nz+1). */
38
+ heights: number[];
39
+ }
40
+ /**
41
+ * Bilinear height sample in world space, clamped to the grid edge. Mirrors
42
+ * `heightfield.ts` `heightAt` so the CLI's sampler and this one agree.
43
+ */
44
+ export declare function sampleWorldHeight(g: ForgedHeightGrid, x: number, z: number): number;
45
+ //# sourceMappingURL=terrain-height-grid.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terrain-height-grid.d.ts","sourceRoot":"","sources":["../src/terrain-height-grid.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,oFAAoF;AACpF,eAAO,MAAM,uBAAuB,4BAA4B,CAAC;AAEjE,MAAM,WAAW,gBAAgB;IAC/B,yCAAyC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,6BAA6B;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,wDAAwD;IACxD,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,qFAAqF;IACrF,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,gBAAgB,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAiBnF"}