@its-not-rocket-science/ananke 0.1.68 → 0.5.1

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 (165) hide show
  1. package/CHANGELOG.md +274 -0
  2. package/README.md +150 -431
  3. package/STABLE_API.md +113 -490
  4. package/conformance/README.md +46 -44
  5. package/conformance/lockstep-sequence.json +395 -395
  6. package/conformance/phase-order.json +21 -0
  7. package/conformance/replay-parity.json +65 -65
  8. package/conformance/state-hash-regression.json +39 -0
  9. package/conformance/state-hash.json +23 -22
  10. package/dist/src/conformance.d.ts +8 -2
  11. package/dist/src/content/index.d.ts +3 -0
  12. package/dist/src/content/index.js +2 -0
  13. package/dist/src/content/injector.d.ts +19 -0
  14. package/dist/src/content/injector.js +61 -0
  15. package/dist/src/content/loader.d.ts +3 -0
  16. package/dist/src/content/loader.js +170 -0
  17. package/dist/src/content/types.d.ts +56 -0
  18. package/dist/src/content/types.js +1 -0
  19. package/dist/src/content/validator.d.ts +6 -0
  20. package/dist/src/content/validator.js +14 -0
  21. package/dist/src/content-pack.d.ts +3 -2
  22. package/dist/src/content-pack.js +205 -10
  23. package/dist/src/data-governance.d.ts +73 -0
  24. package/dist/src/data-governance.js +188 -0
  25. package/dist/src/debug.d.ts +6 -0
  26. package/dist/src/debug.js +9 -0
  27. package/dist/src/determinism.d.ts +12 -0
  28. package/dist/src/determinism.js +93 -0
  29. package/dist/src/dialogue.d.ts +41 -0
  30. package/dist/src/dialogue.js +113 -2
  31. package/dist/src/economy.d.ts +97 -3
  32. package/dist/src/economy.js +191 -37
  33. package/dist/src/history/autosave.d.ts +19 -0
  34. package/dist/src/history/autosave.js +98 -0
  35. package/dist/src/history/timetravel.d.ts +28 -0
  36. package/dist/src/history/timetravel.js +61 -0
  37. package/dist/src/index.d.ts +19 -31
  38. package/dist/src/index.js +20 -38
  39. package/dist/src/model3d.js +63 -2
  40. package/dist/src/narrative/combat-logger.d.ts +25 -0
  41. package/dist/src/narrative/combat-logger.js +97 -0
  42. package/dist/src/narrative/plausibility.d.ts +30 -0
  43. package/dist/src/narrative/plausibility.js +70 -0
  44. package/dist/src/narrative-layer.d.ts +2 -0
  45. package/dist/src/narrative-layer.js +2 -0
  46. package/dist/src/navigation/causal-chain.d.ts +26 -0
  47. package/dist/src/navigation/causal-chain.js +74 -0
  48. package/dist/src/netcode.d.ts +1 -1
  49. package/dist/src/netcode.js +1 -1
  50. package/dist/src/performance/adaptive-tick.d.ts +23 -0
  51. package/dist/src/performance/adaptive-tick.js +50 -0
  52. package/dist/src/plugins/loader.d.ts +23 -0
  53. package/dist/src/plugins/loader.js +86 -0
  54. package/dist/src/plugins/registry.d.ts +11 -0
  55. package/dist/src/plugins/registry.js +28 -0
  56. package/dist/src/plugins/types.d.ts +31 -0
  57. package/dist/src/plugins/types.js +7 -0
  58. package/dist/src/replay.js +5 -2
  59. package/dist/src/scenario.js +2 -1
  60. package/dist/src/serialization/binary.d.ts +5 -0
  61. package/dist/src/serialization/binary.js +193 -0
  62. package/dist/src/session.d.ts +115 -0
  63. package/dist/src/session.js +228 -0
  64. package/dist/src/sim/ai/decide.js +1 -1
  65. package/dist/src/sim/ai/personality.js +2 -2
  66. package/dist/src/sim/bodyplan.d.ts +30 -0
  67. package/dist/src/sim/context.d.ts +2 -0
  68. package/dist/src/sim/kernel.js +80 -1294
  69. package/dist/src/sim/normalization.d.ts +12 -0
  70. package/dist/src/sim/normalization.js +92 -0
  71. package/dist/src/sim/resolvers/attack-resolver.d.ts +30 -0
  72. package/dist/src/sim/resolvers/attack-resolver.js +310 -0
  73. package/dist/src/sim/resolvers/capability-resolver.d.ts +15 -0
  74. package/dist/src/sim/resolvers/capability-resolver.js +270 -0
  75. package/dist/src/sim/resolvers/grapple-resolver.d.ts +28 -0
  76. package/dist/src/sim/resolvers/grapple-resolver.js +70 -0
  77. package/dist/src/sim/resolvers/shoot-resolver.d.ts +27 -0
  78. package/dist/src/sim/resolvers/shoot-resolver.js +212 -0
  79. package/dist/src/sim/resolvers/treat-resolver.d.ts +16 -0
  80. package/dist/src/sim/resolvers/treat-resolver.js +77 -0
  81. package/dist/src/sim/step/apply/intents.d.ts +6 -0
  82. package/dist/src/sim/step/apply/intents.js +83 -0
  83. package/dist/src/sim/step/kernel-pipeline.d.ts +9 -0
  84. package/dist/src/sim/step/kernel-pipeline.js +15 -0
  85. package/dist/src/sim/step/phases/capability-phase.d.ts +8 -0
  86. package/dist/src/sim/step/phases/capability-phase.js +65 -0
  87. package/dist/src/sim/step/phases/cooldowns-phase.d.ts +3 -0
  88. package/dist/src/sim/step/phases/cooldowns-phase.js +37 -0
  89. package/dist/src/sim/step/phases/prepare-phase.d.ts +10 -0
  90. package/dist/src/sim/step/phases/prepare-phase.js +33 -0
  91. package/dist/src/sim/step/pipeline.d.ts +2 -0
  92. package/dist/src/sim/step/pipeline.js +15 -0
  93. package/dist/src/sim/step/resolvers/impact-resolver.d.ts +9 -0
  94. package/dist/src/sim/step/resolvers/impact-resolver.js +55 -0
  95. package/dist/src/sim/step/world-phases.d.ts +7 -0
  96. package/dist/src/sim/step/world-phases.js +15 -0
  97. package/dist/src/sim/step/world-step-context.d.ts +23 -0
  98. package/dist/src/sim/step/world-step-context.js +1 -0
  99. package/dist/src/sim/systemic-toxicology.js +1 -1
  100. package/dist/src/sim/team.js +2 -2
  101. package/dist/src/sim/testing.js +4 -3
  102. package/dist/src/sim/toxicology.d.ts +1 -1
  103. package/dist/src/sim/toxicology.js +1 -1
  104. package/dist/src/sim/world.d.ts +17 -24
  105. package/dist/src/snapshot.js +3 -6
  106. package/dist/src/tier2.d.ts +19 -0
  107. package/dist/src/tier2.js +21 -0
  108. package/dist/src/tier3.d.ts +10 -0
  109. package/dist/src/tier3.js +12 -0
  110. package/dist/src/version.d.ts +6 -0
  111. package/dist/src/version.js +8 -0
  112. package/dist/src/wasm/bridge.d.ts +14 -0
  113. package/dist/src/wasm/bridge.js +84 -0
  114. package/dist/src/wasm-kernel.d.ts +2 -0
  115. package/dist/src/wasm-kernel.js +1 -0
  116. package/dist/src/world-evolution-backend/engine.d.ts +4 -0
  117. package/dist/src/world-evolution-backend/engine.js +347 -0
  118. package/dist/src/world-evolution-backend/host-schema.d.ts +145 -0
  119. package/dist/src/world-evolution-backend/host-schema.js +375 -0
  120. package/dist/src/world-evolution-backend/index.d.ts +1 -0
  121. package/dist/src/world-evolution-backend/index.js +1 -0
  122. package/dist/src/world-evolution-backend/open-world-host-adapter.d.ts +96 -0
  123. package/dist/src/world-evolution-backend/open-world-host-adapter.js +208 -0
  124. package/dist/src/world-evolution-backend/profiles.d.ts +4 -0
  125. package/dist/src/world-evolution-backend/profiles.js +136 -0
  126. package/dist/src/world-evolution-backend/public.d.ts +11 -0
  127. package/dist/src/world-evolution-backend/public.js +7 -0
  128. package/dist/src/world-evolution-backend/reproducibility.d.ts +8 -0
  129. package/dist/src/world-evolution-backend/reproducibility.js +35 -0
  130. package/dist/src/world-evolution-backend/timeline.d.ts +6 -0
  131. package/dist/src/world-evolution-backend/timeline.js +337 -0
  132. package/dist/src/world-evolution-backend/types.d.ts +138 -0
  133. package/dist/src/world-evolution-backend/types.js +1 -0
  134. package/dist/src/world-evolution-host-backend.d.ts +51 -0
  135. package/dist/src/world-evolution-host-backend.js +97 -0
  136. package/dist/src/world-evolution.d.ts +201 -0
  137. package/dist/src/world-evolution.js +543 -0
  138. package/dist/src/world-factory.js +2 -1
  139. package/dist/tools/pack-cli.js +55 -2
  140. package/docs/bridge-contract.md +140 -332
  141. package/docs/completion-evidence-dashboard.md +63 -0
  142. package/docs/host-contract.md +86 -310
  143. package/docs/host-world-evolution-schema.md +155 -0
  144. package/docs/integration-primer.md +29 -22
  145. package/docs/migration-monolith-to-modular.md +102 -120
  146. package/docs/package-architecture.md +116 -323
  147. package/docs/plugins/README.md +42 -0
  148. package/docs/plugins/hooks.md +22 -0
  149. package/docs/plugins/publishing.md +29 -0
  150. package/docs/plugins/security-model.md +273 -0
  151. package/docs/project-overview.md +30 -30
  152. package/docs/release-dashboard.md +47 -0
  153. package/docs/session-api.md +127 -0
  154. package/docs/stable-api-manifest.json +52 -0
  155. package/docs/trust-dashboard.md +37 -0
  156. package/docs/versioning.md +256 -245
  157. package/docs/wire-protocol.md +140 -209
  158. package/docs/world-evolution-backend.md +200 -0
  159. package/docs/world-evolution-engine.md +150 -0
  160. package/docs/world-evolution-host-backend.md +70 -0
  161. package/docs/world-evolution-orchestration.md +248 -0
  162. package/package.json +125 -6
  163. package/schema/content-pack.schema.json +85 -0
  164. package/schema/plugin.schema.json +57 -0
  165. package/schema/world-evolution-input.schema.json +196 -0
@@ -0,0 +1,145 @@
1
+ import type { TreatyType } from "../diplomacy.js";
2
+ import type { GovernanceType } from "../governance.js";
3
+ import type { TransmissionRoute } from "../sim/disease.js";
4
+ import { type TechEra } from "../sim/tech.js";
5
+ import { type WorldEvolutionRulesetId, type WorldEvolutionRunRequest, type WorldEvolutionRunResult, type WorldEvolutionSnapshot } from "./types.js";
6
+ export declare const HOST_WORLD_EVOLUTION_SCHEMA_VERSION: "ananke.host-world-evolution-input.v1";
7
+ export interface HostRuleOverrides {
8
+ polityDayEnabled?: boolean;
9
+ governanceEnabled?: boolean;
10
+ diplomacyEnabled?: boolean;
11
+ tradeEnabled?: boolean;
12
+ migrationEnabled?: boolean;
13
+ epidemicEnabled?: boolean;
14
+ climateEnabled?: boolean;
15
+ governanceStabilityDaysPerStep?: number;
16
+ treatyStrengthBoost_Q?: number;
17
+ routeEfficiencyBoost_Q?: number;
18
+ epidemicHealthBuffer_Q?: number;
19
+ }
20
+ export interface HostPolity {
21
+ kind: "polity";
22
+ id: string;
23
+ name: string;
24
+ factionId?: string;
25
+ controlledRegionIds?: string[];
26
+ controlledSettlementIds?: string[];
27
+ population?: number;
28
+ treasury_cu?: number;
29
+ stabilityQ?: number;
30
+ moraleQ?: number;
31
+ techEra?: TechEra;
32
+ governanceType?: GovernanceType;
33
+ activeLawIds?: string[];
34
+ metadata?: Record<string, unknown>;
35
+ }
36
+ export interface HostSettlement {
37
+ kind: "settlement";
38
+ id: string;
39
+ name: string;
40
+ regionId?: string;
41
+ polityId?: string;
42
+ population?: number;
43
+ metadata?: Record<string, unknown>;
44
+ }
45
+ export interface HostRegion {
46
+ kind: "region";
47
+ id: string;
48
+ name: string;
49
+ biome?: string;
50
+ polityId?: string;
51
+ population?: number;
52
+ metadata?: Record<string, unknown>;
53
+ }
54
+ export type HostWorldEntity = HostPolity | HostSettlement | HostRegion;
55
+ export interface HostRelationship {
56
+ id: string;
57
+ kind: "border" | "trade_route" | "treaty" | "war";
58
+ sourceId: string;
59
+ targetId: string;
60
+ sharedBorderCount?: number;
61
+ routeQualityQ?: number;
62
+ baseVolume_cu?: number;
63
+ treatyType?: TreatyType;
64
+ treatyStrength_Q?: number;
65
+ signedTick?: number;
66
+ expiryTick?: number;
67
+ metadata?: Record<string, unknown>;
68
+ }
69
+ export interface HostResourceNode {
70
+ id: string;
71
+ polityId?: string;
72
+ regionId?: string;
73
+ settlementId?: string;
74
+ resourceType: string;
75
+ stock?: number;
76
+ metadata?: Record<string, unknown>;
77
+ }
78
+ export interface HostDiseaseProfile {
79
+ id: string;
80
+ name: string;
81
+ transmissionRoute: TransmissionRoute;
82
+ baseTransmissionRate_Q: number;
83
+ incubationPeriod_s: number;
84
+ symptomaticDuration_s: number;
85
+ mortalityRate_Q: number;
86
+ symptomSeverity_Q: number;
87
+ airborneRange_Sm: number;
88
+ immunityDuration_s: number;
89
+ }
90
+ export interface HostEpidemicState {
91
+ polityId: string;
92
+ diseaseId: string;
93
+ prevalence_Q: number;
94
+ }
95
+ export interface WorldEvolutionInput {
96
+ schemaVersion?: string;
97
+ worldSeed: number;
98
+ tick?: number;
99
+ entities: HostWorldEntity[];
100
+ relationships?: HostRelationship[];
101
+ resources?: HostResourceNode[];
102
+ diseases?: HostDiseaseProfile[];
103
+ epidemics?: HostEpidemicState[];
104
+ profileId?: WorldEvolutionRulesetId;
105
+ ruleOverrides?: HostRuleOverrides;
106
+ hostMetadata?: Record<string, unknown>;
107
+ simulationState?: {
108
+ climateByPolity?: WorldEvolutionSnapshot["climateByPolity"];
109
+ governanceLawRegistry?: WorldEvolutionSnapshot["governanceLawRegistry"];
110
+ };
111
+ }
112
+ export interface ValidationError {
113
+ code: string;
114
+ path: string;
115
+ message: string;
116
+ }
117
+ export interface NormalizedWorldEvolutionInput extends WorldEvolutionInput {
118
+ schemaVersion: typeof HOST_WORLD_EVOLUTION_SCHEMA_VERSION;
119
+ tick: number;
120
+ relationships: HostRelationship[];
121
+ resources: HostResourceNode[];
122
+ diseases: HostDiseaseProfile[];
123
+ epidemics: HostEpidemicState[];
124
+ entities: HostWorldEntity[];
125
+ }
126
+ export interface HostAdapterContext {
127
+ hostMetadata?: Record<string, unknown>;
128
+ resourceNodes: HostResourceNode[];
129
+ relationships: HostRelationship[];
130
+ passthrough: {
131
+ polityMetadataById: Record<string, Record<string, unknown>>;
132
+ regionMetadataById: Record<string, Record<string, unknown>>;
133
+ settlementMetadataById: Record<string, Record<string, unknown>>;
134
+ };
135
+ }
136
+ export interface AnankeEvolutionAdapterState {
137
+ snapshot: WorldEvolutionSnapshot;
138
+ context: HostAdapterContext;
139
+ }
140
+ export declare function validateWorldEvolutionInput(input: WorldEvolutionInput): ValidationError[];
141
+ export declare function normalizeHostWorldInput(input: WorldEvolutionInput): NormalizedWorldEvolutionInput;
142
+ export declare function toAnankeEvolutionState(input: WorldEvolutionInput): AnankeEvolutionAdapterState;
143
+ export declare function fromAnankeEvolutionState(snapshot: WorldEvolutionSnapshot, context?: HostAdapterContext): WorldEvolutionInput;
144
+ export declare function toWorldEvolutionRunRequest(input: WorldEvolutionInput, steps: number, options?: Pick<WorldEvolutionRunRequest, "includeDeltas" | "checkpointInterval">): WorldEvolutionRunRequest;
145
+ export declare function fromWorldEvolutionRunResult(result: WorldEvolutionRunResult, context?: HostAdapterContext): WorldEvolutionInput;
@@ -0,0 +1,375 @@
1
+ import { PRESET_LAW_CODES } from "../governance.js";
2
+ import { TechEra as TechEraCode } from "../sim/tech.js";
3
+ import { SCALE } from "../units.js";
4
+ import { WORLD_EVOLUTION_BACKEND_SCHEMA_VERSION, } from "./types.js";
5
+ import { mergeWorldEvolutionProfileWithOverrides } from "./profiles.js";
6
+ export const HOST_WORLD_EVOLUTION_SCHEMA_VERSION = "ananke.host-world-evolution-input.v1";
7
+ export function validateWorldEvolutionInput(input) {
8
+ const errors = [];
9
+ if (!Number.isInteger(input.worldSeed)) {
10
+ errors.push({
11
+ code: "invalid_world_seed",
12
+ path: "$.worldSeed",
13
+ message: "worldSeed must be an integer",
14
+ });
15
+ }
16
+ if (input.tick != null && (!Number.isInteger(input.tick) || input.tick < 0)) {
17
+ errors.push({
18
+ code: "invalid_tick",
19
+ path: "$.tick",
20
+ message: "tick must be an integer >= 0",
21
+ });
22
+ }
23
+ if (!Array.isArray(input.entities) || input.entities.length === 0) {
24
+ errors.push({
25
+ code: "missing_entities",
26
+ path: "$.entities",
27
+ message: "entities must be a non-empty array",
28
+ });
29
+ }
30
+ const entityIds = new Set();
31
+ (input.entities ?? []).forEach((entity, index) => {
32
+ const basePath = `$.entities[${index}]`;
33
+ if (!entity.id || entity.id.trim().length === 0) {
34
+ errors.push({ code: "missing_entity_id", path: `${basePath}.id`, message: "entity id is required" });
35
+ return;
36
+ }
37
+ if (entityIds.has(entity.id)) {
38
+ errors.push({ code: "duplicate_entity_id", path: `${basePath}.id`, message: `duplicate entity id '${entity.id}'` });
39
+ }
40
+ entityIds.add(entity.id);
41
+ if (entity.kind === "polity") {
42
+ if (!entity.name)
43
+ errors.push({ code: "missing_polity_name", path: `${basePath}.name`, message: "polity name is required" });
44
+ if (entity.population != null && entity.population < 0) {
45
+ errors.push({ code: "invalid_polity_population", path: `${basePath}.population`, message: "population must be >= 0" });
46
+ }
47
+ }
48
+ if (entity.kind === "settlement" || entity.kind === "region") {
49
+ if (!entity.name)
50
+ errors.push({ code: "missing_entity_name", path: `${basePath}.name`, message: "name is required" });
51
+ if (entity.population != null && entity.population < 0) {
52
+ errors.push({ code: "invalid_entity_population", path: `${basePath}.population`, message: "population must be >= 0" });
53
+ }
54
+ }
55
+ });
56
+ (input.relationships ?? []).forEach((rel, index) => {
57
+ const basePath = `$.relationships[${index}]`;
58
+ if (!rel.id)
59
+ errors.push({ code: "missing_relationship_id", path: `${basePath}.id`, message: "relationship id is required" });
60
+ if (!entityIds.has(rel.sourceId)) {
61
+ errors.push({ code: "unknown_relationship_source", path: `${basePath}.sourceId`, message: `unknown entity '${rel.sourceId}'` });
62
+ }
63
+ if (!entityIds.has(rel.targetId)) {
64
+ errors.push({ code: "unknown_relationship_target", path: `${basePath}.targetId`, message: `unknown entity '${rel.targetId}'` });
65
+ }
66
+ if (rel.kind === "border") {
67
+ if (rel.sharedBorderCount != null && rel.sharedBorderCount < 0) {
68
+ errors.push({ code: "invalid_shared_border", path: `${basePath}.sharedBorderCount`, message: "sharedBorderCount must be >= 0" });
69
+ }
70
+ }
71
+ if (rel.kind === "trade_route") {
72
+ if (rel.baseVolume_cu == null || rel.baseVolume_cu < 0) {
73
+ errors.push({ code: "invalid_trade_base_volume", path: `${basePath}.baseVolume_cu`, message: "trade_route requires baseVolume_cu >= 0" });
74
+ }
75
+ }
76
+ if (rel.kind === "treaty" && rel.treatyType == null) {
77
+ errors.push({ code: "missing_treaty_type", path: `${basePath}.treatyType`, message: "treaty relationship requires treatyType" });
78
+ }
79
+ });
80
+ (input.resources ?? []).forEach((resource, index) => {
81
+ const basePath = `$.resources[${index}]`;
82
+ if (!resource.id)
83
+ errors.push({ code: "missing_resource_id", path: `${basePath}.id`, message: "resource id is required" });
84
+ if (!resource.resourceType)
85
+ errors.push({ code: "missing_resource_type", path: `${basePath}.resourceType`, message: "resourceType is required" });
86
+ if (resource.stock != null && resource.stock < 0) {
87
+ errors.push({ code: "invalid_resource_stock", path: `${basePath}.stock`, message: "stock must be >= 0" });
88
+ }
89
+ if (resource.polityId != null && !entityIds.has(resource.polityId)) {
90
+ errors.push({ code: "unknown_resource_polity", path: `${basePath}.polityId`, message: `unknown entity '${resource.polityId}'` });
91
+ }
92
+ });
93
+ const diseaseIds = new Set((input.diseases ?? []).map((d) => d.id));
94
+ (input.epidemics ?? []).forEach((epidemic, index) => {
95
+ const basePath = `$.epidemics[${index}]`;
96
+ if (!entityIds.has(epidemic.polityId)) {
97
+ errors.push({ code: "unknown_epidemic_polity", path: `${basePath}.polityId`, message: `unknown entity '${epidemic.polityId}'` });
98
+ }
99
+ if (!diseaseIds.has(epidemic.diseaseId)) {
100
+ errors.push({ code: "unknown_epidemic_disease", path: `${basePath}.diseaseId`, message: `unknown disease '${epidemic.diseaseId}'` });
101
+ }
102
+ if (epidemic.prevalence_Q < 0) {
103
+ errors.push({ code: "invalid_epidemic_prevalence", path: `${basePath}.prevalence_Q`, message: "prevalence_Q must be >= 0" });
104
+ }
105
+ });
106
+ return errors.sort((a, b) => a.path.localeCompare(b.path) || a.code.localeCompare(b.code));
107
+ }
108
+ export function normalizeHostWorldInput(input) {
109
+ const errors = validateWorldEvolutionInput(input);
110
+ if (errors.length > 0) {
111
+ const detail = errors.map((e) => `${e.path} [${e.code}] ${e.message}`).join("; ");
112
+ throw new Error(`Invalid WorldEvolutionInput: ${detail}`);
113
+ }
114
+ return {
115
+ ...input,
116
+ schemaVersion: HOST_WORLD_EVOLUTION_SCHEMA_VERSION,
117
+ tick: input.tick ?? 0,
118
+ relationships: [...(input.relationships ?? [])]
119
+ .map((r) => ({ ...r }))
120
+ .sort((a, b) => `${a.kind}:${a.sourceId}:${a.targetId}:${a.id}`.localeCompare(`${b.kind}:${b.sourceId}:${b.targetId}:${b.id}`)),
121
+ resources: [...(input.resources ?? [])]
122
+ .map((r) => ({ ...r }))
123
+ .sort((a, b) => a.id.localeCompare(b.id)),
124
+ diseases: [...(input.diseases ?? [])]
125
+ .map((d) => ({ ...d }))
126
+ .sort((a, b) => a.id.localeCompare(b.id)),
127
+ epidemics: [...(input.epidemics ?? [])]
128
+ .map((e) => ({ ...e }))
129
+ .sort((a, b) => `${a.polityId}:${a.diseaseId}`.localeCompare(`${b.polityId}:${b.diseaseId}`)),
130
+ entities: [...input.entities]
131
+ .map((entity) => ({
132
+ ...entity,
133
+ ...(entity.kind === "polity" ? {
134
+ controlledRegionIds: [...(entity.controlledRegionIds ?? [])].sort(),
135
+ controlledSettlementIds: [...(entity.controlledSettlementIds ?? [])].sort(),
136
+ activeLawIds: [...(entity.activeLawIds ?? [])].sort(),
137
+ } : {}),
138
+ }))
139
+ .sort((a, b) => `${a.kind}:${a.id}`.localeCompare(`${b.kind}:${b.id}`)),
140
+ };
141
+ }
142
+ export function toAnankeEvolutionState(input) {
143
+ const normalized = normalizeHostWorldInput(input);
144
+ const polityEntities = normalized.entities.filter((e) => e.kind === "polity");
145
+ const polityIds = new Set(polityEntities.map((p) => p.id));
146
+ const derivedPopulationByPolity = new Map();
147
+ for (const entity of normalized.entities) {
148
+ if (entity.kind !== "settlement" && entity.kind !== "region")
149
+ continue;
150
+ if (entity.polityId == null || !polityIds.has(entity.polityId))
151
+ continue;
152
+ derivedPopulationByPolity.set(entity.polityId, (derivedPopulationByPolity.get(entity.polityId) ?? 0) + (entity.population ?? 0));
153
+ }
154
+ const pairs = normalized.relationships
155
+ .filter((r) => r.kind === "border" && polityIds.has(r.sourceId) && polityIds.has(r.targetId))
156
+ .map((r) => ({
157
+ polityAId: r.sourceId,
158
+ polityBId: r.targetId,
159
+ sharedLocations: r.sharedBorderCount ?? 1,
160
+ routeQuality_Q: clampQField(r.routeQualityQ ?? SCALE.Q),
161
+ }));
162
+ const activeWars = normalized.relationships
163
+ .filter((r) => r.kind === "war" && polityIds.has(r.sourceId) && polityIds.has(r.targetId))
164
+ .map((r) => [r.sourceId, r.targetId]);
165
+ const treaties = normalized.relationships
166
+ .filter((r) => r.kind === "treaty" && polityIds.has(r.sourceId) && polityIds.has(r.targetId) && r.treatyType != null)
167
+ .map((r) => ({
168
+ treatyId: r.id,
169
+ polityAId: r.sourceId,
170
+ polityBId: r.targetId,
171
+ type: r.treatyType,
172
+ strength_Q: clampQField(r.treatyStrength_Q ?? SCALE.Q),
173
+ signedTick: r.signedTick ?? normalized.tick,
174
+ expiryTick: r.expiryTick ?? -1,
175
+ tributeFromA_Q: 0,
176
+ tributeFromB_Q: 0,
177
+ }));
178
+ const tradeRoutes = normalized.relationships
179
+ .filter((r) => r.kind === "trade_route" && polityIds.has(r.sourceId) && polityIds.has(r.targetId) && r.baseVolume_cu != null)
180
+ .map((r) => ({
181
+ routeId: r.id,
182
+ polityAId: r.sourceId,
183
+ polityBId: r.targetId,
184
+ baseVolume_cu: r.baseVolume_cu,
185
+ efficiency_Q: clampQField(r.routeQualityQ ?? SCALE.Q),
186
+ establishedTick: normalized.tick,
187
+ }));
188
+ const governanceLawRegistry = normalized.simulationState?.governanceLawRegistry ?? PRESET_LAW_CODES;
189
+ const snapshot = {
190
+ schemaVersion: WORLD_EVOLUTION_BACKEND_SCHEMA_VERSION,
191
+ worldSeed: normalized.worldSeed,
192
+ tick: normalized.tick,
193
+ polities: polityEntities.map((p) => ({
194
+ id: p.id,
195
+ name: p.name,
196
+ factionId: p.factionId ?? `f.${p.id}`,
197
+ locationIds: [...(p.controlledRegionIds ?? []), ...(p.controlledSettlementIds ?? [])],
198
+ population: p.population ?? Math.max(0, derivedPopulationByPolity.get(p.id) ?? 0),
199
+ treasury_cu: p.treasury_cu ?? 0,
200
+ techEra: p.techEra ?? TechEraCode.Medieval,
201
+ militaryStrength_Q: 0,
202
+ stabilityQ: clampQField(p.stabilityQ ?? Math.floor(SCALE.Q * 0.70)),
203
+ moraleQ: clampQField(p.moraleQ ?? Math.floor(SCALE.Q * 0.65)),
204
+ })),
205
+ pairs,
206
+ activeWars,
207
+ treaties,
208
+ tradeRoutes,
209
+ governanceStates: polityEntities.map((p) => ({
210
+ polityId: p.id,
211
+ governanceType: p.governanceType ?? "monarchy",
212
+ activeLawIds: [...(p.activeLawIds ?? [])],
213
+ changeCooldown: 0,
214
+ })),
215
+ governanceLawRegistry: governanceLawRegistry.map((law) => ({ ...law })),
216
+ epidemics: normalized.epidemics.map((e) => ({ ...e })),
217
+ diseases: normalized.diseases.map((d) => ({ ...d })),
218
+ climateByPolity: normalized.simulationState?.climateByPolity?.map((entry) => ({
219
+ polityId: entry.polityId,
220
+ active: entry.active.map((active) => ({
221
+ event: { ...active.event },
222
+ remainingDays: active.remainingDays,
223
+ elapsedDays: active.elapsedDays,
224
+ })),
225
+ })) ?? [],
226
+ };
227
+ const context = {
228
+ resourceNodes: normalized.resources,
229
+ relationships: normalized.relationships,
230
+ passthrough: {
231
+ polityMetadataById: Object.fromEntries(normalized.entities
232
+ .filter((e) => e.kind === "polity" && e.metadata != null)
233
+ .map((p) => [p.id, p.metadata])),
234
+ regionMetadataById: Object.fromEntries(normalized.entities
235
+ .filter((e) => e.kind === "region" && e.metadata != null)
236
+ .map((r) => [r.id, r.metadata])),
237
+ settlementMetadataById: Object.fromEntries(normalized.entities
238
+ .filter((e) => e.kind === "settlement" && e.metadata != null)
239
+ .map((s) => [s.id, s.metadata])),
240
+ },
241
+ };
242
+ if (normalized.hostMetadata != null) {
243
+ context.hostMetadata = normalized.hostMetadata;
244
+ }
245
+ return { snapshot, context };
246
+ }
247
+ export function fromAnankeEvolutionState(snapshot, context) {
248
+ const entities = snapshot.polities.map((p) => {
249
+ const governance = snapshot.governanceStates.find((g) => g.polityId === p.id);
250
+ const polity = {
251
+ kind: "polity",
252
+ id: p.id,
253
+ name: p.name,
254
+ factionId: p.factionId,
255
+ population: p.population,
256
+ treasury_cu: p.treasury_cu,
257
+ techEra: p.techEra,
258
+ stabilityQ: p.stabilityQ,
259
+ moraleQ: p.moraleQ,
260
+ controlledRegionIds: [...p.locationIds],
261
+ activeLawIds: [...(governance?.activeLawIds ?? [])],
262
+ };
263
+ if (governance != null)
264
+ polity.governanceType = governance.governanceType;
265
+ const metadata = context?.passthrough.polityMetadataById[p.id];
266
+ if (metadata != null)
267
+ polity.metadata = metadata;
268
+ return polity;
269
+ });
270
+ const relationships = [
271
+ ...snapshot.pairs.map((pair, idx) => ({
272
+ id: `border:${idx}:${pair.polityAId}:${pair.polityBId}`,
273
+ kind: "border",
274
+ sourceId: pair.polityAId,
275
+ targetId: pair.polityBId,
276
+ sharedBorderCount: pair.sharedLocations,
277
+ routeQualityQ: pair.routeQuality_Q,
278
+ })),
279
+ ...snapshot.tradeRoutes.map((route) => ({
280
+ id: route.routeId,
281
+ kind: "trade_route",
282
+ sourceId: route.polityAId,
283
+ targetId: route.polityBId,
284
+ baseVolume_cu: route.baseVolume_cu,
285
+ routeQualityQ: route.efficiency_Q,
286
+ })),
287
+ ...snapshot.treaties.map((treaty) => ({
288
+ id: treaty.treatyId,
289
+ kind: "treaty",
290
+ sourceId: treaty.polityAId,
291
+ targetId: treaty.polityBId,
292
+ treatyType: treaty.type,
293
+ treatyStrength_Q: treaty.strength_Q,
294
+ signedTick: treaty.signedTick,
295
+ expiryTick: treaty.expiryTick,
296
+ })),
297
+ ...snapshot.activeWars.map((pair, idx) => ({
298
+ id: `war:${idx}:${pair[0]}:${pair[1]}`,
299
+ kind: "war",
300
+ sourceId: pair[0],
301
+ targetId: pair[1],
302
+ })),
303
+ ];
304
+ return normalizeHostWorldInput({
305
+ schemaVersion: HOST_WORLD_EVOLUTION_SCHEMA_VERSION,
306
+ worldSeed: snapshot.worldSeed,
307
+ tick: snapshot.tick,
308
+ entities,
309
+ relationships,
310
+ resources: context?.resourceNodes ?? [],
311
+ diseases: snapshot.diseases.map((disease) => ({ ...disease })),
312
+ epidemics: snapshot.epidemics.map((epidemic) => ({ ...epidemic })),
313
+ simulationState: {
314
+ climateByPolity: snapshot.climateByPolity.map((entry) => ({
315
+ polityId: entry.polityId,
316
+ active: entry.active.map((active) => ({
317
+ event: { ...active.event },
318
+ remainingDays: active.remainingDays,
319
+ elapsedDays: active.elapsedDays,
320
+ })),
321
+ })),
322
+ governanceLawRegistry: snapshot.governanceLawRegistry.map((law) => ({ ...law })),
323
+ },
324
+ ...(context?.hostMetadata != null ? { hostMetadata: context.hostMetadata } : {}),
325
+ });
326
+ }
327
+ export function toWorldEvolutionRunRequest(input, steps, options = {}) {
328
+ const adapterState = toAnankeEvolutionState(input);
329
+ const request = {
330
+ snapshot: adapterState.snapshot,
331
+ steps,
332
+ ...options,
333
+ };
334
+ if (input.profileId != null) {
335
+ request.profileId = input.profileId;
336
+ }
337
+ if (input.ruleOverrides != null || input.profileId != null) {
338
+ request.profile = toInlineRuleset(input.profileId, input.ruleOverrides);
339
+ delete request.profileId;
340
+ }
341
+ return request;
342
+ }
343
+ export function fromWorldEvolutionRunResult(result, context) {
344
+ return fromAnankeEvolutionState(result.finalSnapshot, context);
345
+ }
346
+ function toInlineRuleset(profileId, overrides) {
347
+ const compiledOverrides = {};
348
+ if (overrides?.polityDayEnabled != null)
349
+ compiledOverrides.polityDayEnabled = overrides.polityDayEnabled;
350
+ if (overrides?.governanceEnabled != null)
351
+ compiledOverrides.governanceEnabled = overrides.governanceEnabled;
352
+ if (overrides?.diplomacyEnabled != null)
353
+ compiledOverrides.diplomacyEnabled = overrides.diplomacyEnabled;
354
+ if (overrides?.tradeEnabled != null)
355
+ compiledOverrides.tradeEnabled = overrides.tradeEnabled;
356
+ if (overrides?.migrationEnabled != null)
357
+ compiledOverrides.migrationEnabled = overrides.migrationEnabled;
358
+ if (overrides?.epidemicEnabled != null)
359
+ compiledOverrides.epidemicEnabled = overrides.epidemicEnabled;
360
+ if (overrides?.climateEnabled != null)
361
+ compiledOverrides.climateEnabled = overrides.climateEnabled;
362
+ if (overrides?.governanceStabilityDaysPerStep != null) {
363
+ compiledOverrides.governanceStabilityDaysPerStep = overrides.governanceStabilityDaysPerStep;
364
+ }
365
+ if (overrides?.treatyStrengthBoost_Q != null)
366
+ compiledOverrides.treatyStrengthBoost_Q = clampQField(overrides.treatyStrengthBoost_Q);
367
+ if (overrides?.routeEfficiencyBoost_Q != null)
368
+ compiledOverrides.routeEfficiencyBoost_Q = clampQField(overrides.routeEfficiencyBoost_Q);
369
+ if (overrides?.epidemicHealthBuffer_Q != null)
370
+ compiledOverrides.epidemicHealthBuffer_Q = clampQField(overrides.epidemicHealthBuffer_Q);
371
+ return mergeWorldEvolutionProfileWithOverrides(profileId, Object.keys(compiledOverrides).length > 0 ? compiledOverrides : undefined);
372
+ }
373
+ function clampQField(value) {
374
+ return Math.max(0, Math.min(SCALE.Q, Math.floor(value)));
375
+ }
@@ -0,0 +1 @@
1
+ export * from "./public.js";
@@ -0,0 +1 @@
1
+ export * from "./public.js";
@@ -0,0 +1,96 @@
1
+ import type { TreatyType } from "../diplomacy.js";
2
+ import { type AnankeEvolutionAdapterState, type WorldEvolutionInput } from "./host-schema.js";
3
+ export declare const OPEN_WORLD_HOST_ADAPTER_SCHEMA_VERSION: "ananke.open-world-host-adapter.v1";
4
+ export interface MetadataBuckets {
5
+ simulation?: Record<string, unknown>;
6
+ descriptive?: Record<string, unknown>;
7
+ opaque?: Record<string, unknown>;
8
+ }
9
+ export interface OpenWorldRegion {
10
+ id: string;
11
+ name: string;
12
+ tileCount?: number;
13
+ climateTag?: string;
14
+ polityId?: string;
15
+ population?: number;
16
+ metadata?: MetadataBuckets;
17
+ }
18
+ export interface OpenWorldSettlement {
19
+ id: string;
20
+ name: string;
21
+ regionId: string;
22
+ polityId?: string;
23
+ population?: number;
24
+ metadata?: MetadataBuckets;
25
+ }
26
+ export interface OpenWorldFaction {
27
+ id: string;
28
+ name: string;
29
+ polityId?: string;
30
+ treasury_cu?: number;
31
+ population?: number;
32
+ stabilityQ?: number;
33
+ moraleQ?: number;
34
+ metadata?: MetadataBuckets;
35
+ }
36
+ export interface OpenWorldResource {
37
+ id: string;
38
+ resourceType: string;
39
+ regionId?: string;
40
+ settlementId?: string;
41
+ factionId?: string;
42
+ stock?: number;
43
+ metadata?: MetadataBuckets;
44
+ }
45
+ export interface OpenWorldTradeLink {
46
+ id: string;
47
+ sourceFactionId: string;
48
+ targetFactionId: string;
49
+ baseVolume_cu?: number;
50
+ routeQualityQ?: number;
51
+ sharedBorderCount?: number;
52
+ treatyType?: TreatyType;
53
+ treatyStrength_Q?: number;
54
+ war?: boolean;
55
+ metadata?: MetadataBuckets;
56
+ }
57
+ export interface OpenWorldEnvironment {
58
+ climateByRegionId?: Record<string, string>;
59
+ climateByPolity?: NonNullable<WorldEvolutionInput["simulationState"]>["climateByPolity"];
60
+ metadata?: MetadataBuckets;
61
+ }
62
+ export interface OpenWorldLoreMetadata {
63
+ summary?: string;
64
+ tags?: string[];
65
+ metadata?: MetadataBuckets;
66
+ }
67
+ export interface OpenWorldHostInput {
68
+ schemaVersion?: string;
69
+ worldSeed: number;
70
+ tick?: number;
71
+ regions: OpenWorldRegion[];
72
+ settlements: OpenWorldSettlement[];
73
+ factions: OpenWorldFaction[];
74
+ resources?: OpenWorldResource[];
75
+ tradeLinks?: OpenWorldTradeLink[];
76
+ environment?: OpenWorldEnvironment;
77
+ lore?: OpenWorldLoreMetadata;
78
+ metadata?: MetadataBuckets;
79
+ }
80
+ export interface OpenWorldHostAdapterOptions {
81
+ entityMetadataTransform?: (metadata: MetadataBuckets | undefined) => Record<string, unknown> | undefined;
82
+ relationshipMetadataTransform?: (metadata: MetadataBuckets | undefined) => Record<string, unknown> | undefined;
83
+ topLevelOpaqueMerge?: Record<string, unknown>;
84
+ }
85
+ export interface OpenWorldAdapterContext {
86
+ schemaVersion: typeof OPEN_WORLD_HOST_ADAPTER_SCHEMA_VERSION;
87
+ metadataPassthrough: Record<string, unknown>;
88
+ factionByPolityId: Record<string, string>;
89
+ }
90
+ export interface OpenWorldMappedState {
91
+ input: WorldEvolutionInput;
92
+ context: OpenWorldAdapterContext;
93
+ }
94
+ export declare function canonicalizeOpenWorldInput(input: OpenWorldHostInput): OpenWorldHostInput;
95
+ export declare function mapOpenWorldHostToEvolutionInput(input: OpenWorldHostInput, options?: OpenWorldHostAdapterOptions): OpenWorldMappedState;
96
+ export declare function toAnankeEvolutionStateFromOpenWorld(input: OpenWorldHostInput, options?: OpenWorldHostAdapterOptions): AnankeEvolutionAdapterState;