kirby-api 0.2.0__tar.gz → 0.2.2__tar.gz

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 (258) hide show
  1. {kirby_api-0.2.0/kirby_api.egg-info → kirby_api-0.2.2}/PKG-INFO +2 -2
  2. kirby_api-0.2.2/kirby/_migrations/versions/20261005_0001_knowledge_indexes.py +41 -0
  3. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/builds/category_extraction.py +4 -19
  4. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/builds/combatant_loader.py +7 -80
  5. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/builds/power_library_service.py +0 -37
  6. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/campaigns/services/extract.py +3 -35
  7. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/characters/routes.py +22 -22
  8. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/codex/services/villain_extractor.py +3 -10
  9. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/combat/__init__.py +7 -9
  10. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/comic_canon/__init__.py +4 -3
  11. {kirby_api-0.2.0 → kirby_api-0.2.2/kirby_api.egg-info}/PKG-INFO +2 -2
  12. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby_api.egg-info/SOURCES.txt +2 -6
  13. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby_api.egg-info/requires.txt +1 -1
  14. {kirby_api-0.2.0 → kirby_api-0.2.2}/pyproject.toml +3 -5
  15. kirby_api-0.2.2/tests/test_knowledge_indexes_migration.py +28 -0
  16. kirby_api-0.2.0/kirby/builds/concept_to_build.py +0 -505
  17. kirby_api-0.2.0/kirby/builds/hdc_exporter.py +0 -107
  18. kirby_api-0.2.0/kirby/characters/services/entity_service.py +0 -430
  19. kirby_api-0.2.0/kirby/comic_canon/services/comic_vine.py +0 -215
  20. kirby_api-0.2.0/kirby/comic_canon/services/prewarm.py +0 -455
  21. kirby_api-0.2.0/kirby/comic_canon/services/text_clean.py +0 -28
  22. {kirby_api-0.2.0 → kirby_api-0.2.2}/LICENSE +0 -0
  23. {kirby_api-0.2.0 → kirby_api-0.2.2}/README.md +0 -0
  24. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/__init__.py +0 -0
  25. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/README +0 -0
  26. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/alembic.ini +0 -0
  27. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/env.py +0 -0
  28. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/script.py.mako +0 -0
  29. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/1b7affd8668e_initial_schema_users_tenants_characters_.py +0 -0
  30. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260424_0001_combat_tables.py +0 -0
  31. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260429_0001_add_combatant_jsonb.py +0 -0
  32. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260501_0001_codex_targeted_pages.py +0 -0
  33. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260501_0002_hd_relational_schema.py +0 -0
  34. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260501_0003_definitions_codex_chunk_id.py +0 -0
  35. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260503_0001_per_template_base_values.py +0 -0
  36. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260504_0001_pr1_relational_combat_schema.py +0 -0
  37. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260504_0002_seed_tactics_and_profiles.py +0 -0
  38. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260504_0003_backfill_combat_relational.py +0 -0
  39. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260504_0004_drop_combat_jsonb_columns.py +0 -0
  40. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260504_0005_pr1_index_audit_fills.py +0 -0
  41. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260504_0006_pending_phase_modifiers.py +0 -0
  42. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260504_0007_combat_session_scene_id.py +0 -0
  43. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260504_0008_morale_expires_at_seq.py +0 -0
  44. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260504_0009_is_stunned.py +0 -0
  45. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260504_0010_tactic_catalog_expansion.py +0 -0
  46. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260504_0011_slam_velocity_tactic.py +0 -0
  47. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260504_0012_mind_control_state.py +0 -0
  48. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260504_0013_mental_illusion_state.py +0 -0
  49. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260504_0014_mental_entangle_state.py +0 -0
  50. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260504_0015_was_surprised.py +0 -0
  51. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260504_0016_catalog_expansion_for_new_actions.py +0 -0
  52. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260504_0017_combat_started.py +0 -0
  53. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260504_0018_physical_entangle.py +0 -0
  54. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260504_0019_stat_drains.py +0 -0
  55. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260504_0020_continuous_effects.py +0 -0
  56. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260504_0021_lockout_state.py +0 -0
  57. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260504_0022_dispelled_powers.py +0 -0
  58. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260506_0001_campaign_management_v1.py +0 -0
  59. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260506_0002_extend_characters_for_pc.py +0 -0
  60. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260506_0003_combatant_control_mode.py +0 -0
  61. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260506_0004_brain_embeddings.py +0 -0
  62. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260507_0001_knowledge_tier.py +0 -0
  63. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260507_0002_normalize_jsonb.py +0 -0
  64. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260507_0003_drop_legacy_jsonb.py +0 -0
  65. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260507_0004_canon_embeddings.py +0 -0
  66. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260508_0001_comic_canon_init.py +0 -0
  67. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260511_0001_power_library_init.py +0 -0
  68. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260514_0001_combat_session_ai_provider.py +0 -0
  69. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260528_0001_coordinate_raw_tables.py +0 -0
  70. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260528_0002_coordination_initiator.py +0 -0
  71. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260529_0001_character_def_rename.py +0 -0
  72. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260529_0002_character_instance.py +0 -0
  73. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260529_0003_team_def.py +0 -0
  74. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260529_0004_team_member_def.py +0 -0
  75. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260529_0005_team_instance.py +0 -0
  76. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260529_0006_team_def_real_history.py +0 -0
  77. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260529_0007_team_page.py +0 -0
  78. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260529_0008_team_narrative.py +0 -0
  79. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260529_0009_migrate_org_journal_entries_to_team.py +0 -0
  80. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260530_0002_templates_catalog_columns.py +0 -0
  81. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260530_0003_reconcile_template_category_vocab.py +0 -0
  82. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260531_0001_align_catalog_to_engine_schema.py +0 -0
  83. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260601_0001_build_doc_fields.py +0 -0
  84. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260605_0001_proposals.py +0 -0
  85. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260605_0002_membership_roster.py +0 -0
  86. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260605_0003_xp_award.py +0 -0
  87. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260606_0001_base_drift_guard.py +0 -0
  88. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260607_0001_maneuver_details.py +0 -0
  89. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260609_0001_scene_wall_def_value.py +0 -0
  90. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260609_0002_combat_session_constructs.py +0 -0
  91. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260611_0001_arena_hazard_kind_status_tokens.py +0 -0
  92. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260611_0002_tactic_entry_role.py +0 -0
  93. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260611_0003_seed_archetype_profiles.py +0 -0
  94. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260612_0001_combatant_held_construct.py +0 -0
  95. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260613_0001_combatant_is_hidden.py +0 -0
  96. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260613_0002_combatant_flashed_sense.py +0 -0
  97. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260614_0001_construct_sense_group.py +0 -0
  98. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260614_0002_image_decoy.py +0 -0
  99. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260618_0001_character_extraction_tables.py +0 -0
  100. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260621_0001_combat_session_combatant_slot.py +0 -0
  101. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260621_0002_vpp_power.py +0 -0
  102. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260623_0001_power_categories.py +0 -0
  103. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260726_0001_combat_intent.py +0 -0
  104. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260727_0001_intent_checks_since_form.py +0 -0
  105. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260728_0001_stunned_at_sequence.py +0 -0
  106. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260729_0001_intent_budget_turns.py +0 -0
  107. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260730_0001_standable_structures.py +0 -0
  108. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260731_0001_climb_difficulty.py +0 -0
  109. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260731_0002_widen_character_object_alias.py +0 -0
  110. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260802_0001_identity_by_id_not_xmlid.py +0 -0
  111. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260803_0001_combatant_opening_state.py +0 -0
  112. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260804_0001_combat_random_fight.py +0 -0
  113. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260804_0002_presence_attack_effects.py +0 -0
  114. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260816_0001_template_level_power.py +0 -0
  115. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260831_0001_construct_no_teleport.py +0 -0
  116. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260831_0002_physical_entangle_state.py +0 -0
  117. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260831_0003_character_object_text.py +0 -0
  118. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260902_0001_combatant_in_hero_id.py +0 -0
  119. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260902_0002_object_affects_flags.py +0 -0
  120. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260917_0001_session_names_a_seat.py +0 -0
  121. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260918_0000_drop_combat_tables.py +0 -0
  122. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260918_0001_harness_tables.py +0 -0
  123. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/20260918_0002_narration_speaker_id.py +0 -0
  124. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/21980c836860_add_asset_id_refs.py +0 -0
  125. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/_migrations/versions/ba28e71c955d_codex_init.py +0 -0
  126. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/admin/__init__.py +0 -0
  127. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/auth/__init__.py +0 -0
  128. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/auth/authz.py +0 -0
  129. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/auth/dependencies.py +0 -0
  130. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/auth/jwt.py +0 -0
  131. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/auth/models.py +0 -0
  132. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/auth/routes.py +0 -0
  133. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/auth/users.py +0 -0
  134. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/builds/__init__.py +0 -0
  135. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/builds/build_doc_store.py +0 -0
  136. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/builds/cli.py +0 -0
  137. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/builds/codex_linker.py +0 -0
  138. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/builds/importer.py +0 -0
  139. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/builds/json_exporter.py +0 -0
  140. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/builds/json_importer.py +0 -0
  141. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/builds/models.py +0 -0
  142. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/builds/power_categories.py +0 -0
  143. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/builds/power_library_importer.py +0 -0
  144. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/builds/recompute.py +0 -0
  145. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/builds/routes.py +0 -0
  146. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/builds/template_provider.py +0 -0
  147. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/campaigns/__init__.py +0 -0
  148. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/campaigns/_shared.py +0 -0
  149. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/campaigns/membership_service.py +0 -0
  150. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/campaigns/models.py +0 -0
  151. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/campaigns/routes.py +0 -0
  152. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/campaigns/routes_acl.py +0 -0
  153. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/campaigns/routes_brain.py +0 -0
  154. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/campaigns/routes_documents.py +0 -0
  155. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/campaigns/routes_knowledge.py +0 -0
  156. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/campaigns/routes_membership.py +0 -0
  157. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/campaigns/routes_relationships.py +0 -0
  158. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/campaigns/routes_roster.py +0 -0
  159. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/campaigns/routes_sessions_events.py +0 -0
  160. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/campaigns/routes_xp.py +0 -0
  161. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/campaigns/services/__init__.py +0 -0
  162. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/campaigns/services/brain.py +0 -0
  163. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/campaigns/services/conflicts.py +0 -0
  164. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/campaigns/services/embeddings.py +0 -0
  165. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/campaigns/services/generation.py +0 -0
  166. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/campaigns/services/legacy_translation.py +0 -0
  167. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/campaigns/services/permissions.py +0 -0
  168. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/campaigns/services/recap.py +0 -0
  169. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/characters/__init__.py +0 -0
  170. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/characters/_shared.py +0 -0
  171. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/characters/build_routes.py +0 -0
  172. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/characters/combat_seed.py +0 -0
  173. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/characters/corpus_copy.py +0 -0
  174. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/characters/models.py +0 -0
  175. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/characters/proposals/__init__.py +0 -0
  176. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/characters/proposals/routes.py +0 -0
  177. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/characters/proposals/service.py +0 -0
  178. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/characters/services/__init__.py +0 -0
  179. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/characters/services/character_info_backfill.py +0 -0
  180. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/characters/services/effective.py +0 -0
  181. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/characters/services/writeup_persistence.py +0 -0
  182. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/characters/variants_service.py +0 -0
  183. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/characters/xp_service.py +0 -0
  184. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/cli.py +0 -0
  185. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/codex/__init__.py +0 -0
  186. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/codex/cli.py +0 -0
  187. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/codex/deps.py +0 -0
  188. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/codex/models.py +0 -0
  189. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/codex/repositories.py +0 -0
  190. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/codex/routes.py +0 -0
  191. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/codex/schemas.py +0 -0
  192. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/codex/services/__init__.py +0 -0
  193. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/codex/services/embed.py +0 -0
  194. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/codex/services/extract.py +0 -0
  195. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/codex/services/index.py +0 -0
  196. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/codex/services/orchestrator.py +0 -0
  197. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/codex/services/search.py +0 -0
  198. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/codex/services/villain_writeup_linker.py +0 -0
  199. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/codex/workers.py +0 -0
  200. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/combat/choosers.py +0 -0
  201. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/combat/create.py +0 -0
  202. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/combat/events.py +0 -0
  203. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/combat/harness.py +0 -0
  204. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/combat/models.py +0 -0
  205. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/combat/random_fight/__init__.py +0 -0
  206. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/combat/random_fight/models.py +0 -0
  207. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/combat/random_fight/outcome.py +0 -0
  208. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/combat/random_fight/roster.py +0 -0
  209. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/combat/random_fight/routes.py +0 -0
  210. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/combat/random_fight/runner.py +0 -0
  211. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/combat/random_fight/schemas.py +0 -0
  212. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/combat/random_fight/session.py +0 -0
  213. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/combat/random_fight/worker.py +0 -0
  214. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/combat/routes.py +0 -0
  215. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/combat/schemas.py +0 -0
  216. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/combat/services/__init__.py +0 -0
  217. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/combat/services/connection_hub.py +0 -0
  218. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/combat/services/publish.py +0 -0
  219. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/combat/services/subscriber.py +0 -0
  220. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/comic_canon/models.py +0 -0
  221. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/comic_canon/routes.py +0 -0
  222. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/comic_canon/services/__init__.py +0 -0
  223. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/comic_canon/services/archetypes.py +0 -0
  224. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/comic_canon/services/embeddings.py +0 -0
  225. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/engines/__init__.py +0 -0
  226. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/engines/cost/__init__.py +0 -0
  227. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/engines/cost/hdc.py +0 -0
  228. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/library/__init__.py +0 -0
  229. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/main.py +0 -0
  230. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/media/__init__.py +0 -0
  231. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/media/asset_uses.py +0 -0
  232. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/media/client.py +0 -0
  233. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/media/routes.py +0 -0
  234. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/media/schemas.py +0 -0
  235. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/media/taxonomy.py +0 -0
  236. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/settings.py +0 -0
  237. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/shared/__init__.py +0 -0
  238. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/shared/db.py +0 -0
  239. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/teams/__init__.py +0 -0
  240. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/teams/models.py +0 -0
  241. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/teams/routes.py +0 -0
  242. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/teams/schemas.py +0 -0
  243. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/teams/services/__init__.py +0 -0
  244. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/teams/services/effective.py +0 -0
  245. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/templates/__init__.py +0 -0
  246. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/tenants/__init__.py +0 -0
  247. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/tenants/models.py +0 -0
  248. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/tenants/routes.py +0 -0
  249. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/users/__init__.py +0 -0
  250. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/users/routes.py +0 -0
  251. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby/version.py +0 -0
  252. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby_api.egg-info/dependency_links.txt +0 -0
  253. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby_api.egg-info/entry_points.txt +0 -0
  254. {kirby_api-0.2.0 → kirby_api-0.2.2}/kirby_api.egg-info/top_level.txt +0 -0
  255. {kirby_api-0.2.0 → kirby_api-0.2.2}/setup.cfg +0 -0
  256. {kirby_api-0.2.0 → kirby_api-0.2.2}/tests/test_public_vocabulary.py +0 -0
  257. {kirby_api-0.2.0 → kirby_api-0.2.2}/tests/test_teardown.py +0 -0
  258. {kirby_api-0.2.0 → kirby_api-0.2.2}/tests/test_wheel_contents.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kirby-api
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary: Kirby — HERO System VTT backend API (FastAPI mono)
5
5
  Author: PeterB
6
6
  License: PolyForm Noncommercial License 1.0.0
@@ -32,7 +32,7 @@ Requires-Dist: pgvector>=0.3
32
32
  Requires-Dist: pypdfium2>=4.30
33
33
  Requires-Dist: kirby-combat==0.18.5
34
34
  Requires-Dist: kirby-dice>=0.1.0
35
- Requires-Dist: kirby-ai>=0.6.0
35
+ Requires-Dist: kirby-ai>=0.7.0
36
36
  Requires-Dist: prometheus-client>=0.21
37
37
  Requires-Dist: prometheus-fastapi-instrumentator>=7.0
38
38
  Requires-Dist: tenacity>=8.0
@@ -0,0 +1,41 @@
1
+ """the three knowledge indexes the models declared and no migration made
2
+
3
+ A schema-drift check (models vs a migrated database) found three
4
+ ``index=True`` columns that no migration ever created, in production as
5
+ well as in a fresh ``alembic upgrade head``:
6
+
7
+ * ``knowledge_link.campaign_id`` -> ``ix_knowledge_link_campaign_id``
8
+ * ``knowledge_link.tenant_id`` -> ``ix_knowledge_link_tenant_id``
9
+ * ``knowledge_unlock.tenant_id`` -> ``ix_knowledge_unlock_tenant_id``
10
+
11
+ Purely additive and idempotent (``if_not_exists``): safe on a database
12
+ where someone already created them by hand. The fourth drift item,
13
+ ``codex_chunk.idx_codex_chunk_embedding``, is deliberately NOT here —
14
+ codex is moving to kirby-codex.
15
+
16
+ Revision ID: 20261005_0001
17
+ Revises: 20260918_0002
18
+ Create Date: 2026-10-05
19
+ """
20
+ from alembic import op
21
+
22
+ revision = "20261005_0001"
23
+ down_revision = "20260918_0002"
24
+ branch_labels = None
25
+ depends_on = None
26
+
27
+ _INDEXES = (
28
+ ("ix_knowledge_link_campaign_id", "knowledge_link", "campaign_id"),
29
+ ("ix_knowledge_link_tenant_id", "knowledge_link", "tenant_id"),
30
+ ("ix_knowledge_unlock_tenant_id", "knowledge_unlock", "tenant_id"),
31
+ )
32
+
33
+
34
+ def upgrade() -> None:
35
+ for name, table, column in _INDEXES:
36
+ op.create_index(name, table, [column], if_not_exists=True)
37
+
38
+
39
+ def downgrade() -> None:
40
+ for name, table, _column in _INDEXES:
41
+ op.drop_index(name, table_name=table, if_exists=True)
@@ -13,7 +13,7 @@ from __future__ import annotations
13
13
  import json
14
14
  import logging
15
15
 
16
- from kirby_ai import GenerationClient, strip_code_fences
16
+ from kirby_ai import GenerationClient, extract_json_array
17
17
 
18
18
  from kirby.settings import settings
19
19
 
@@ -27,26 +27,11 @@ not the game mechanic. Emit ONLY the JSON array, no prose."""
27
27
 
28
28
 
29
29
  def _extract_json_array(raw: str) -> list:
30
- """Parse a JSON array out of model output.
31
-
32
- The sibling ``kirby_ai.extract_json_object`` handles objects; this is its
33
- array counterpart — kirby-ai has no array extractor. Falls back to
34
- slicing the outermost brackets so a model that wraps the array in prose
35
- still parses.
36
- """
37
- text = strip_code_fences(raw)
30
+ """The model's slug array, or ``[]`` when there is none to find."""
38
31
  try:
39
- v = json.loads(text)
32
+ return extract_json_array(raw)
40
33
  except json.JSONDecodeError:
41
- s, e = text.find("["), text.rfind("]")
42
- if s != -1 and e != -1 and e > s:
43
- try:
44
- v = json.loads(text[s:e + 1])
45
- except json.JSONDecodeError:
46
- return []
47
- else:
48
- return []
49
- return v if isinstance(v, list) else []
34
+ return []
50
35
 
51
36
 
52
37
  def extract_category_slugs(
@@ -1,4 +1,4 @@
1
- """``HeroCombatant.from_db()`` and the to-legacy bridge.
1
+ """``HeroCombatant.from_db()`` — a stored build becomes a combatant.
2
2
 
3
3
  These helpers attach to (rather than monkey-patch into)
4
4
  ``kirby_combat.hero_view``. The reason: hero_view.py lives in the
@@ -18,12 +18,7 @@ from uuid import UUID
18
18
 
19
19
  from sqlalchemy.orm import Session
20
20
 
21
- from kirby_combat.hero_view import (
22
- HeroCombatant,
23
- HeroCombatState,
24
- _compute_stats_from_hero,
25
- )
26
- from kirby_combat.models import Combatant
21
+ from kirby_combat.hero_view import HeroCombatant
27
22
 
28
23
  from kirby.builds.build_doc_store import build_doc_from_rows
29
24
  from kirby.characters.models import CharacterDef
@@ -82,17 +77,11 @@ def hero_combatant_from_build_doc(
82
77
  if id is None:
83
78
  id = (hero.name.lower().replace(" ", "_") if hero.name
84
79
  else str(fallback_id))
85
- stats = _compute_stats_from_hero(hero)
86
- return HeroCombatant(
87
- id=id,
88
- hero=hero,
89
- state=HeroCombatState(
90
- current_stun=stats.max_stun,
91
- current_body=stats.max_body,
92
- current_end=stats.max_end,
93
- ),
94
- knockback_resistance=0,
95
- )
80
+ # The engine's own door, not a copy of it: this used to construct the
81
+ # HeroCombatant here from the engine's PRIVATE `_compute_stats_from_hero`,
82
+ # agreeing with `from_build` only because the two happen to call the same
83
+ # function today.
84
+ return HeroCombatant.from_build(hero, id=id)
96
85
 
97
86
 
98
87
  def hero_combatant_from_db(
@@ -116,65 +105,3 @@ def hero_combatant_from_db(
116
105
  source=f"character {character_id}",
117
106
  )
118
107
 
119
-
120
- def to_legacy_combatant(hc: HeroCombatant) -> Combatant:
121
- """Convert a HeroCombatant to the legacy flat ``Combatant`` shape.
122
-
123
- DEPRECATED as of 2026-05-02 (combatant-redesign step 4 complete).
124
- The engine now accepts HeroCombatant directly via the uniform
125
- ``combat_stats()`` / flat-property interface added by step 6.
126
- Pass ``HeroCombatant`` straight into ``AttackInput``; the engine
127
- handles either shape.
128
-
129
- Kept for backward compatibility with callers that haven't migrated.
130
- Will be removed in a future kirby-api release.
131
- """
132
- s = hc.combat_stats()
133
- defs = hc.defense_view()
134
- attacks = []
135
- attack_xmlids = {"ENERGYBLAST", "RKA", "HKA", "EGOATTACK",
136
- "MENTALBLAST", "KILLINGATTACKRANGED",
137
- "KILLINGATTACKHTH"}
138
- seen_xmlids: set[str] = set()
139
-
140
- def _collect_attacks(power_list) -> None:
141
- """Walk top-level + nested (multipower/VPP slot) powers."""
142
- for p in power_list or []:
143
- x = (getattr(p, "xmlid", None) or "").upper()
144
- if x in attack_xmlids and x not in seen_xmlids:
145
- # attack_view picks the first match by xmlid; if a
146
- # combatant has multiple ENERGYBLASTs we still get
147
- # only the first slot. That's fine for the v1 demo;
148
- # step 3 wires slot_xmlid for proper Multipower
149
- # selection.
150
- try:
151
- attacks.append(hc.attack_view(p.xmlid))
152
- seen_xmlids.add(x)
153
- except ValueError:
154
- pass
155
- # Recurse into sub_powers — Multipower / VPP slots live here.
156
- sub = getattr(p, "sub_powers", None)
157
- if sub:
158
- _collect_attacks(sub)
159
-
160
- _collect_attacks(hc.hero.powers) # type: ignore[union-attr]
161
- name = (
162
- getattr(hc.hero, "name", None) or hc.id
163
- )
164
- return Combatant(
165
- id=hc.id,
166
- name=name,
167
- ocv=s.ocv, dcv=s.dcv, omcv=s.omcv, dmcv=s.dmcv,
168
- spd=s.spd,
169
- dex=s.dex, ego=s.ego, str_=s.str_, con=s.con, pre=s.pre, rec=s.rec,
170
- pd=s.pd, ed=s.ed,
171
- rpd=s.rpd, red=s.red, md=s.md,
172
- power_defense=s.power_defense, flash_defense=s.flash_defense,
173
- max_stun=s.max_stun, max_body=s.max_body, max_end=s.max_end,
174
- current_stun=hc.state.current_stun,
175
- current_body=hc.state.current_body,
176
- current_end=hc.state.current_end,
177
- attacks=attacks,
178
- defenses=defs,
179
- knockback_resistance=hc.knockback_resistance,
180
- )
@@ -219,40 +219,3 @@ def get_power_tree(db: Session, power_root_object_id: int) -> dict[str, Any] | N
219
219
 
220
220
 
221
221
  # ── convenience: render a hit for prompt context ────────────────────────────
222
-
223
-
224
- def render_hit_for_prompt(
225
- db: Session,
226
- hit: PowerLibraryHit,
227
- *,
228
- include_tree: bool = False,
229
- ) -> str:
230
- """Format one hit as a prompt-ready block. Pass 1 typically uses
231
- ``include_tree=False`` (prose + tags only); Pass 2 uses
232
- ``include_tree=True`` to give the structured-output model the exact
233
- JSON shape it should produce.
234
- """
235
- parts: list[str] = []
236
- parts.append(f"## {hit.name}")
237
- if hit.base_name and hit.base_name != hit.name:
238
- parts.append(f" (variant of {hit.base_name})")
239
- tags = ", ".join(hit.sfx_tags + hit.mechanical_tags)
240
- parts.append(f" Tags: {tags}")
241
- ap = f"{hit.active_cost}" if hit.active_cost is not None else "?"
242
- rc = f"{hit.real_cost}" if hit.real_cost is not None else "?"
243
- parts.append(f" Active: {ap} Real: {rc} Kind: {hit.kind}")
244
- if hit.description:
245
- snippet = hit.description.strip()
246
- if len(snippet) > 1200:
247
- snippet = snippet[:1200].rsplit(" ", 1)[0] + "…"
248
- parts.append("")
249
- parts.append(snippet)
250
-
251
- if include_tree:
252
- import json
253
- tree = get_power_tree(db, hit.power_root_object_id)
254
- parts.append("")
255
- parts.append("```json")
256
- parts.append(json.dumps(tree, indent=2, default=str))
257
- parts.append("```")
258
- return "\n".join(parts)
@@ -28,12 +28,11 @@ section/paragraph they came from.
28
28
  from __future__ import annotations
29
29
 
30
30
  import json
31
- import re
32
31
  from dataclasses import asdict, dataclass, field
33
32
  from typing import Any, Optional
34
33
  from uuid import UUID
35
34
 
36
- from kirby_ai import GenerationClient
35
+ from kirby_ai import GenerationClient, extract_json_array
37
36
  from sqlalchemy.orm import Session
38
37
 
39
38
  from kirby.settings import settings
@@ -123,48 +122,17 @@ def parse_drafts_json(raw: str) -> list[ExtractedDraft]:
123
122
 
124
123
  Tolerant of:
125
124
  - Markdown code fences around the JSON (strip them)
126
- - Trailing prose after the array (find the first complete `[...]`)
125
+ - Prose on either side of the array (kirby_ai.extract_json_array)
127
126
  - Individual draft items missing optional fields
128
127
 
129
128
  Returns the drafts that parsed cleanly. Skips malformed entries
130
129
  rather than failing the whole import.
131
130
  """
132
- cleaned = raw.strip()
133
- # Strip markdown fences
134
- if cleaned.startswith("```"):
135
- cleaned = re.sub(r"^```(?:json)?\s*", "", cleaned)
136
- cleaned = re.sub(r"\s*```\s*$", "", cleaned)
137
- cleaned = cleaned.strip()
138
-
139
- # Find the first balanced [...] in the string. The LLM occasionally
140
- # emits trailing chatter; bracket-counting locates the canonical array.
141
- start = cleaned.find("[")
142
- if start < 0:
143
- return []
144
-
145
- depth = 0
146
- end = -1
147
- for i in range(start, len(cleaned)):
148
- c = cleaned[i]
149
- if c == "[":
150
- depth += 1
151
- elif c == "]":
152
- depth -= 1
153
- if depth == 0:
154
- end = i + 1
155
- break
156
- if end < 0:
157
- return []
158
-
159
- json_str = cleaned[start:end]
160
131
  try:
161
- parsed = json.loads(json_str)
132
+ parsed = extract_json_array(raw)
162
133
  except json.JSONDecodeError:
163
134
  return []
164
135
 
165
- if not isinstance(parsed, list):
166
- return []
167
-
168
136
  drafts: list[ExtractedDraft] = []
169
137
  for item in parsed:
170
138
  if not isinstance(item, dict):
@@ -462,28 +462,27 @@ async def import_hdc(
462
462
  content = await file.read()
463
463
  filename = file.filename or "unknown.hdc"
464
464
 
465
- # Parse the HDC via EntityService (uses kirby.engines.cost — the
466
- # canonical kirby-cost loader). The relational
467
- # ``character_objects`` rows are the source of truth for combat
468
- # state; PR-4 dropped the legacy ``combatant_jsonb`` projection.
469
- # We still grab the parsed Combatant dataclass to seed the
470
- # transient ``current_*`` columns.
471
- from kirby.characters.services.entity_service import EntityService
465
+ # The canonical import: the engine loads and costs the file, the build is
466
+ # persisted relationally (so combat can load it), and the opening vitals
467
+ # are the engine's own (`HeroCombatant.from_build`, the same door a fight
468
+ # uses). This route used to run a private translator that built its own
469
+ # combatant -- base + CHARACTERISTICS levels only, so a character with
470
+ # Resistant Protection got rPD 0 -- kept only STUN/BODY/END from it, and
471
+ # never persisted the build at all, so combat refused the character.
472
+ from kirby.builds.build_doc_store import persist_loaded
473
+ from kirby.engines.cost.hdc import load_hdc
474
+ from kirby_combat.hero_view import HeroCombatant
472
475
 
473
476
  try:
474
- imported = EntityService().import_hdc(content)
475
- except (ValueError, Exception) as exc:
477
+ imported = load_hdc(content)
478
+ except Exception as exc:
476
479
  raise HTTPException(
477
480
  status_code=400,
478
481
  detail=f"Failed to parse HDC: {exc}",
479
482
  )
480
-
481
- # Bases (kind=='base') don't produce a Combatant — they're scene
482
- # fragments. Allow the import (so the file can be referenced) but
483
- # the combatant-derived transient state stays None and the row
484
- # can't be added to a session.
485
- name = imported.name or filename.replace(".hdc", "").replace("_", " ")
486
- cb = imported.combatant
483
+ hero = imported.hero
484
+ vitals = HeroCombatant.from_build(hero).state
485
+ name = (hero.name or "").strip() or filename.replace(".hdc", "").replace("_", " ")
487
486
 
488
487
  char = CharacterDef(
489
488
  tenant_id=tid,
@@ -491,14 +490,15 @@ async def import_hdc(
491
490
  name=name,
492
491
  hdc_filename=filename,
493
492
  source="hdc_import",
494
- is_npc=(imported.kind == "npc"),
495
- # Seed transient state from the combatant's max values so the
496
- # character is ready to take a hit on first session start.
497
- current_stun=cb.current_stun if cb is not None else None,
498
- current_body=cb.current_body if cb is not None else None,
499
- current_end=cb.current_end if cb is not None else None,
493
+ # A file upload is a character; the GM marks NPCs as with any field.
494
+ is_npc=False,
495
+ current_stun=vitals.current_stun,
496
+ current_body=vitals.current_body,
497
+ current_end=vitals.current_end,
500
498
  )
501
499
  db.add(char)
500
+ db.flush()
501
+ persist_loaded(db, char, hero)
502
502
  db.commit()
503
503
  db.refresh(char)
504
504
  return char
@@ -7,10 +7,9 @@ from __future__ import annotations
7
7
 
8
8
  import json
9
9
  import logging
10
- import re
11
10
  from dataclasses import dataclass, field
12
11
 
13
- from kirby_ai import GenerationClient
12
+ from kirby_ai import GenerationClient, extract_json_object
14
13
 
15
14
  from kirby.settings import settings
16
15
 
@@ -78,16 +77,10 @@ minions", "Avoid melee with bricks") tied to a source_snippet quoted from the te
78
77
 
79
78
  def _extract_json(raw: str) -> dict:
80
79
  """Pull the first JSON object out of model output (tolerates fences/prose)."""
81
- fenced = re.search(r"```(?:json)?\s*(\{.*?\})\s*```", raw, re.DOTALL)
82
- candidate = fenced.group(1) if fenced else raw
83
- start = candidate.find("{")
84
- end = candidate.rfind("}")
85
- if start == -1 or end == -1 or end < start:
86
- raise ExtractionError(f"no JSON object in model output: {raw[:200]!r}")
87
80
  try:
88
- return json.loads(candidate[start:end + 1])
81
+ return extract_json_object(raw)
89
82
  except json.JSONDecodeError as exc:
90
- raise ExtractionError(f"invalid JSON: {exc}; raw={raw[:200]!r}") from exc
83
+ raise ExtractionError(f"no JSON object in model output: {raw[:200]!r}") from exc
91
84
 
92
85
 
93
86
  def parse_villain_writeup(raw: str) -> VillainWriteup:
@@ -18,15 +18,13 @@ tables, its routes and its tests. What stands today:
18
18
  runner's turn and step caps; its seeding and its
19
19
  worker await the harness (plan C5)
20
20
 
21
- ``services/entity_service`` IS NOT HERE ANY MORE. HDC import is a
22
- characters/builds concern and its only caller is
23
- ``kirby/characters/routes.py``; it lived here until the 2026-09-17 final
24
- review found it was a parser, a classifier and a dozen substituted 6E
25
- numbers inside the one module that claims to hold no rule. It is
26
- ``kirby/characters/services/entity_service.py`` now, and
27
- ``tests/combat/test_no_rules_here.py`` grew a fourth property — the
28
- engine's rule vocabulary, turned on this SOURCE tree — so the next one
29
- is caught rather than certified.
21
+ ``services/entity_service`` IS GONE. It moved from here to
22
+ ``kirby/characters/`` after the 2026-09-17 review found a parser, a classifier
23
+ and substituted 6E numbers inside it, and was deleted 2026-10-04: the HDC
24
+ upload now takes the canonical import (``load_hdc`` -> ``persist_loaded``) and
25
+ the engine's own ``HeroCombatant.from_build`` for its opening vitals.
26
+ ``tests/combat/test_no_rules_here.py`` scans the WHOLE ``kirby/`` source tree
27
+ for rule numbers now, so moving a file out of ``combat/`` hides nothing.
30
28
 
31
29
  ``async_db`` IS NOT HERE ANY MORE EITHER. It was the deleted
32
30
  ``websocket.py``'s dependency, it had no consumer left anywhere in
@@ -7,15 +7,16 @@ Design mirrors codex:
7
7
  comic_character, comic_storyarc, comic_chunk,
8
8
  comic_archetype, comic_book_tenant)
9
9
  services/
10
- comic_vine.py — async Comic Vine API client (port from
11
- comic-mcp standalone)
12
10
  archetypes.py — YAML loader → DB sync, tenant-scoped
13
- prewarm.py — pre-warm pipeline; tenant + group aware
14
11
  search.py — cosine search over comic_chunk + retrieval
15
12
  helpers used by brain.py
16
13
  routes.py — FastAPI routes for archetype CRUD + cache stats
17
14
  schemas.py — Pydantic schemas
18
15
 
16
+ The Comic Vine client and pre-warm pipeline were removed 2026-10-04: nothing
17
+ imported them. Their live home is the standalone comic-mcp
18
+ (`comic_mcp/{comic_vine,prewarm,text_clean}.py`).
19
+
19
20
  Tenant pattern: comic_book_tenant junction grants tenant access to
20
21
  specific Comic Vine resources, paralleling codex_book_tenant. Tenants
21
22
  inherit a universal-baseline archetype set and can author their own
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kirby-api
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary: Kirby — HERO System VTT backend API (FastAPI mono)
5
5
  Author: PeterB
6
6
  License: PolyForm Noncommercial License 1.0.0
@@ -32,7 +32,7 @@ Requires-Dist: pgvector>=0.3
32
32
  Requires-Dist: pypdfium2>=4.30
33
33
  Requires-Dist: kirby-combat==0.18.5
34
34
  Requires-Dist: kirby-dice>=0.1.0
35
- Requires-Dist: kirby-ai>=0.6.0
35
+ Requires-Dist: kirby-ai>=0.7.0
36
36
  Requires-Dist: prometheus-client>=0.21
37
37
  Requires-Dist: prometheus-fastapi-instrumentator>=7.0
38
38
  Requires-Dist: tenacity>=8.0
@@ -105,6 +105,7 @@ kirby/_migrations/versions/20260917_0001_session_names_a_seat.py
105
105
  kirby/_migrations/versions/20260918_0000_drop_combat_tables.py
106
106
  kirby/_migrations/versions/20260918_0001_harness_tables.py
107
107
  kirby/_migrations/versions/20260918_0002_narration_speaker_id.py
108
+ kirby/_migrations/versions/20261005_0001_knowledge_indexes.py
108
109
  kirby/_migrations/versions/21980c836860_add_asset_id_refs.py
109
110
  kirby/_migrations/versions/ba28e71c955d_codex_init.py
110
111
  kirby/admin/__init__.py
@@ -121,8 +122,6 @@ kirby/builds/category_extraction.py
121
122
  kirby/builds/cli.py
122
123
  kirby/builds/codex_linker.py
123
124
  kirby/builds/combatant_loader.py
124
- kirby/builds/concept_to_build.py
125
- kirby/builds/hdc_exporter.py
126
125
  kirby/builds/importer.py
127
126
  kirby/builds/json_exporter.py
128
127
  kirby/builds/json_importer.py
@@ -171,7 +170,6 @@ kirby/characters/proposals/service.py
171
170
  kirby/characters/services/__init__.py
172
171
  kirby/characters/services/character_info_backfill.py
173
172
  kirby/characters/services/effective.py
174
- kirby/characters/services/entity_service.py
175
173
  kirby/characters/services/writeup_persistence.py
176
174
  kirby/codex/__init__.py
177
175
  kirby/codex/cli.py
@@ -215,10 +213,7 @@ kirby/comic_canon/models.py
215
213
  kirby/comic_canon/routes.py
216
214
  kirby/comic_canon/services/__init__.py
217
215
  kirby/comic_canon/services/archetypes.py
218
- kirby/comic_canon/services/comic_vine.py
219
216
  kirby/comic_canon/services/embeddings.py
220
- kirby/comic_canon/services/prewarm.py
221
- kirby/comic_canon/services/text_clean.py
222
217
  kirby/engines/__init__.py
223
218
  kirby/engines/cost/__init__.py
224
219
  kirby/engines/cost/hdc.py
@@ -249,6 +244,7 @@ kirby_api.egg-info/dependency_links.txt
249
244
  kirby_api.egg-info/entry_points.txt
250
245
  kirby_api.egg-info/requires.txt
251
246
  kirby_api.egg-info/top_level.txt
247
+ tests/test_knowledge_indexes_migration.py
252
248
  tests/test_public_vocabulary.py
253
249
  tests/test_teardown.py
254
250
  tests/test_wheel_contents.py
@@ -23,7 +23,7 @@ pgvector>=0.3
23
23
  pypdfium2>=4.30
24
24
  kirby-combat==0.18.5
25
25
  kirby-dice>=0.1.0
26
- kirby-ai>=0.6.0
26
+ kirby-ai>=0.7.0
27
27
  prometheus-client>=0.21
28
28
  prometheus-fastapi-instrumentator>=7.0
29
29
  tenacity>=8.0
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "kirby-api"
3
- version = "0.2.0"
3
+ version = "0.2.2"
4
4
  description = "Kirby — HERO System VTT backend API (FastAPI mono)"
5
5
  # Published to public PyPI. Every runtime dependency below resolves from
6
6
  # public PyPI too — kirby-cost, kirby-combat, kirby-dice and kirby-ai all
@@ -196,9 +196,7 @@ dependencies = [
196
196
  # better a public `EVENT_KINDS` mapping the engine owns and both
197
197
  # repos read (it already builds one to key its own serializer).
198
198
  # When that lands, `events.py` reads the public name and this goes
199
- # back to `>=`. `kirby/builds/hdc_exporter.py:32` has the same shape
200
- # against kirby-cost (`from kirby_cost.io.build_json import
201
- # _root_from_doc`) and wants the same ask.
199
+ # back to `>=`.
202
200
  "kirby-combat==0.18.5",
203
201
 
204
202
  # The dice roller left kirby-combat at 0.10.0 for its own package, so
@@ -217,7 +215,7 @@ dependencies = [
217
215
  # `AttributeError` inside a fight — and every earlier version silently
218
216
  # substitutes `FirstLegalChooser`, which is a model playing on without
219
217
  # having answered. `IntentChooser` also arrives in 0.3.0.
220
- "kirby-ai>=0.6.0",
218
+ "kirby-ai>=0.7.0",
221
219
  "prometheus-client>=0.21",
222
220
  "prometheus-fastapi-instrumentator>=7.0",
223
221
 
@@ -0,0 +1,28 @@
1
+ """The knowledge indexes the models declare must exist after migrating."""
2
+ import pytest
3
+ from sqlalchemy import text
4
+
5
+ from kirby.shared.db import SessionLocal
6
+
7
+ EXPECTED = {
8
+ ("knowledge_link", "ix_knowledge_link_campaign_id"),
9
+ ("knowledge_link", "ix_knowledge_link_tenant_id"),
10
+ ("knowledge_unlock", "ix_knowledge_unlock_tenant_id"),
11
+ }
12
+
13
+
14
+ def test_knowledge_indexes_exist():
15
+ db = SessionLocal()
16
+ try:
17
+ if db.get_bind().dialect.name != "postgresql":
18
+ pytest.skip("pg_indexes needs Postgres")
19
+ rows = db.execute(
20
+ text(
21
+ "SELECT tablename, indexname FROM pg_indexes "
22
+ "WHERE indexname = ANY(:names)"
23
+ ),
24
+ {"names": [n for _, n in EXPECTED]},
25
+ ).all()
26
+ assert {tuple(r) for r in rows} == EXPECTED
27
+ finally:
28
+ db.close()