apsimo 1.3.0__py3-none-any.whl

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 (614) hide show
  1. apsimo/__init__.py +38 -0
  2. apsimo/__main__.py +6 -0
  3. apsimo/agent/__init__.py +6 -0
  4. apsimo/agent/client.py +276 -0
  5. apsimo/agent/models.py +46 -0
  6. apsimo/agents/__init__.py +20 -0
  7. apsimo/agents/models.py +264 -0
  8. apsimo/agents/store.py +861 -0
  9. apsimo/agents/websocket.py +522 -0
  10. apsimo/api/__init__.py +1 -0
  11. apsimo/api/auth_telemetry.py +287 -0
  12. apsimo/api/authority.py +1203 -0
  13. apsimo/api/contact_grants.py +347 -0
  14. apsimo/api/middleware.py +483 -0
  15. apsimo/api/routers/__init__.py +1 -0
  16. apsimo/api/routers/commitment_work.py +265 -0
  17. apsimo/api/routers/context_gate.py +123 -0
  18. apsimo/api/routers/executions.py +140 -0
  19. apsimo/api/routers/followup_plans.py +147 -0
  20. apsimo/api/routers/governed_actions.py +162 -0
  21. apsimo/api/routers/host.py +14473 -0
  22. apsimo/api/routers/initiative_work.py +115 -0
  23. apsimo/api/routers/mining.py +104 -0
  24. apsimo/api/routers/observations.py +110 -0
  25. apsimo/api/routers/social_state.py +225 -0
  26. apsimo/api/routers/task_queue.py +2715 -0
  27. apsimo/api/routers/temporal_followups.py +251 -0
  28. apsimo/api/routers/transport.py +110 -0
  29. apsimo/api/routers/transport_ingress_api.py +240 -0
  30. apsimo/api/schemas/__init__.py +1 -0
  31. apsimo/api/schemas/host.py +1949 -0
  32. apsimo/autonomy/cli.py +110 -0
  33. apsimo/autonomy/condition_worker.py +437 -0
  34. apsimo/autonomy/config.py +424 -0
  35. apsimo/autonomy/loop.py +4316 -0
  36. apsimo/autonomy/registry.py +339 -0
  37. apsimo/autonomy/scheduler.py +1822 -0
  38. apsimo/autonomy/synthesis.py +449 -0
  39. apsimo/backup.py +962 -0
  40. apsimo/beliefs/__init__.py +23 -0
  41. apsimo/beliefs/contradictions.py +109 -0
  42. apsimo/beliefs/decay.py +61 -0
  43. apsimo/beliefs/engine.py +479 -0
  44. apsimo/beliefs/models.py +67 -0
  45. apsimo/beliefs/promotion.py +41 -0
  46. apsimo/beliefs/resolve.py +58 -0
  47. apsimo/beliefs/source_claims.py +690 -0
  48. apsimo/beliefs/source_projection.py +883 -0
  49. apsimo/beliefs/source_time.py +208 -0
  50. apsimo/beliefs/store.py +133 -0
  51. apsimo/briefings/aggregators.py +824 -0
  52. apsimo/briefings/composer.py +420 -0
  53. apsimo/briefings/config.py +55 -0
  54. apsimo/briefings/delivery.py +439 -0
  55. apsimo/briefings/engagement.py +97 -0
  56. apsimo/briefings/engine.py +274 -0
  57. apsimo/briefings/enhancer.py +99 -0
  58. apsimo/briefings/models.py +183 -0
  59. apsimo/briefings/scheduler.py +382 -0
  60. apsimo/briefings/store.py +435 -0
  61. apsimo/chain/__init__.py +48 -0
  62. apsimo/chain/block.py +100 -0
  63. apsimo/chain/cli.py +704 -0
  64. apsimo/chain/genesis.py +443 -0
  65. apsimo/chain/identity.py +416 -0
  66. apsimo/chain/keys.py +1025 -0
  67. apsimo/chain/local_keys.py +187 -0
  68. apsimo/chain/manager.py +290 -0
  69. apsimo/chain/node.py +163 -0
  70. apsimo/chain/plugin_transactions.py +371 -0
  71. apsimo/chain/protocol.py +220 -0
  72. apsimo/chain/state_machine.py +676 -0
  73. apsimo/chain/storage.py +503 -0
  74. apsimo/chain/transactions.py +250 -0
  75. apsimo/chain/validation.py +397 -0
  76. apsimo/channels/__init__.py +1 -0
  77. apsimo/channels/manifest.py +31 -0
  78. apsimo/channels/migrations/001_channels_schema.sql +12 -0
  79. apsimo/channels/phone_gateways.py +42 -0
  80. apsimo/channels/presence.py +188 -0
  81. apsimo/channels/router.py +235 -0
  82. apsimo/channels/store.py +231 -0
  83. apsimo/cli.py +2688 -0
  84. apsimo/cognition/__init__.py +11 -0
  85. apsimo/cognition/charter.py +398 -0
  86. apsimo/cognition/drive_governance.py +3530 -0
  87. apsimo/cognition/evidence_pipeline.py +1627 -0
  88. apsimo/cognition/external_events.py +932 -0
  89. apsimo/cognition/goal_spine.py +3488 -0
  90. apsimo/cognition/introspection.py +214 -0
  91. apsimo/cognition/prompt.py +150 -0
  92. apsimo/cognition/runtime.py +108 -0
  93. apsimo/cognition/trigger.py +154 -0
  94. apsimo/commitments/__init__.py +18 -0
  95. apsimo/commitments/local_work.py +355 -0
  96. apsimo/commitments/store.py +1052 -0
  97. apsimo/commitments/work.py +91 -0
  98. apsimo/compat.py +53 -0
  99. apsimo/compression/__init__.py +467 -0
  100. apsimo/connectors/__init__.py +21 -0
  101. apsimo/connectors/base.py +152 -0
  102. apsimo/connectors/caldav_calendar.py +125 -0
  103. apsimo/connectors/fs_documents.py +85 -0
  104. apsimo/connectors/imap_email.py +138 -0
  105. apsimo/connectors/manager.py +218 -0
  106. apsimo/connectors/webhook_pull.py +88 -0
  107. apsimo/contacts/__init__.py +33 -0
  108. apsimo/contacts/comms.py +357 -0
  109. apsimo/contacts/config.py +79 -0
  110. apsimo/contacts/exporters/__init__.py +1 -0
  111. apsimo/contacts/exporters/vcard.py +71 -0
  112. apsimo/contacts/identity_links.py +251 -0
  113. apsimo/contacts/importer.py +280 -0
  114. apsimo/contacts/importers/__init__.py +1 -0
  115. apsimo/contacts/importers/batch.py +43 -0
  116. apsimo/contacts/importers/macos_contacts.py +101 -0
  117. apsimo/contacts/migrations/001_contacts_schema.sql +141 -0
  118. apsimo/contacts/migrations/002_trust_scopes.sql +36 -0
  119. apsimo/contacts/migrations/003_open_gateway_enum.sql +32 -0
  120. apsimo/contacts/migrations/004_contact_provision_operations.sql +18 -0
  121. apsimo/contacts/migrations/005_identity_links.sql +27 -0
  122. apsimo/contacts/models.py +308 -0
  123. apsimo/contacts/scoring.py +16 -0
  124. apsimo/contacts/store.py +1623 -0
  125. apsimo/contacts/transport_ingress.py +252 -0
  126. apsimo/contacts/world_bridge.py +314 -0
  127. apsimo/contextgate/__init__.py +69 -0
  128. apsimo/contextgate/chunker.py +169 -0
  129. apsimo/contextgate/estimate.py +54 -0
  130. apsimo/contextgate/gate.py +313 -0
  131. apsimo/contextgate/retrieve.py +115 -0
  132. apsimo/delivery/__init__.py +16 -0
  133. apsimo/delivery/bridge.py +1260 -0
  134. apsimo/delivery/channels.py +526 -0
  135. apsimo/delivery/classification.py +50 -0
  136. apsimo/delivery/rate_limiter.py +268 -0
  137. apsimo/delivery/reachout_policy.py +206 -0
  138. apsimo/directed/__init__.py +22 -0
  139. apsimo/directed/audit.py +167 -0
  140. apsimo/directed/intake.py +95 -0
  141. apsimo/directed/models.py +191 -0
  142. apsimo/directed/service.py +509 -0
  143. apsimo/directives/__init__.py +25 -0
  144. apsimo/directives/evidence.py +87 -0
  145. apsimo/directives/extractor.py +188 -0
  146. apsimo/directives/guard.py +364 -0
  147. apsimo/directives/models.py +206 -0
  148. apsimo/directives/service.py +372 -0
  149. apsimo/directives/store.py +167 -0
  150. apsimo/doctor.py +2173 -0
  151. apsimo/environment.py +43 -0
  152. apsimo/events/__init__.py +33 -0
  153. apsimo/events/broadcaster.py +98 -0
  154. apsimo/events/bus.py +217 -0
  155. apsimo/events/journal.py +863 -0
  156. apsimo/events/stream.py +131 -0
  157. apsimo/events/types.py +150 -0
  158. apsimo/execution_results.py +357 -0
  159. apsimo/feedback/__init__.py +5 -0
  160. apsimo/feedback/store.py +76 -0
  161. apsimo/feeds/__init__.py +19 -0
  162. apsimo/feeds/cli.py +84 -0
  163. apsimo/feeds/engine.py +437 -0
  164. apsimo/feeds/example-feed.yaml +77 -0
  165. apsimo/feeds/hermes_cron.py +126 -0
  166. apsimo/feeds/manager.py +235 -0
  167. apsimo/feeds/spec.py +250 -0
  168. apsimo/feeds/template.py +202 -0
  169. apsimo/gate/__init__.py +18 -0
  170. apsimo/gate/audit.py +61 -0
  171. apsimo/gate/communication_policy.py +166 -0
  172. apsimo/gate/config.py +72 -0
  173. apsimo/gate/context_provenance.py +170 -0
  174. apsimo/gate/env_risk.py +226 -0
  175. apsimo/gate/guard_audit.py +353 -0
  176. apsimo/gate/layers/__init__.py +1 -0
  177. apsimo/gate/layers/base.py +15 -0
  178. apsimo/gate/layers/l1_recipient.py +66 -0
  179. apsimo/gate/layers/l2_pii.py +134 -0
  180. apsimo/gate/layers/l3_cross_context.py +50 -0
  181. apsimo/gate/layers/l4_trust_tier.py +78 -0
  182. apsimo/gate/layers/l5_injection.py +199 -0
  183. apsimo/gate/layers/l6_review.py +86 -0
  184. apsimo/gate/layers/l7_delay.py +100 -0
  185. apsimo/gate/layers/tom2_epistemic.py +185 -0
  186. apsimo/gate/models.py +64 -0
  187. apsimo/gate/pending_dispatch.py +5 -0
  188. apsimo/gate/pipeline.py +206 -0
  189. apsimo/gate/rejection.py +259 -0
  190. apsimo/gate/response_guard.py +700 -0
  191. apsimo/gate/rulesets/injection_v1.yaml +51 -0
  192. apsimo/gate/surface_policy.py +189 -0
  193. apsimo/gate/taint.py +226 -0
  194. apsimo/genesis.json +9 -0
  195. apsimo/goals/__init__.py +100 -0
  196. apsimo/goals/config.py +38 -0
  197. apsimo/goals/decomposer.py +421 -0
  198. apsimo/goals/engine.py +617 -0
  199. apsimo/goals/inference.py +354 -0
  200. apsimo/goals/models.py +302 -0
  201. apsimo/goals/priority.py +270 -0
  202. apsimo/goals/queue_bridge.py +149 -0
  203. apsimo/goals/replan.py +450 -0
  204. apsimo/goals/schema.sql +89 -0
  205. apsimo/goals/store.py +692 -0
  206. apsimo/governed_actions.py +1708 -0
  207. apsimo/harness_integration/__init__.py +45 -0
  208. apsimo/harness_integration/context.py +41 -0
  209. apsimo/harness_integration/skills.py +231 -0
  210. apsimo/identity/__init__.py +26 -0
  211. apsimo/identity/participants.py +181 -0
  212. apsimo/identity/resolver.py +329 -0
  213. apsimo/identity_bootstrap/__init__.py +5 -0
  214. apsimo/identity_bootstrap/builder.py +208 -0
  215. apsimo/identity_bootstrap/corpus.py +443 -0
  216. apsimo/identity_bootstrap/models.py +54 -0
  217. apsimo/identity_bootstrap/runner.py +353 -0
  218. apsimo/identity_bootstrap/seeders/__init__.py +25 -0
  219. apsimo/identity_bootstrap/seeders/briefings.py +109 -0
  220. apsimo/identity_bootstrap/seeders/chain.py +57 -0
  221. apsimo/identity_bootstrap/seeders/goals.py +128 -0
  222. apsimo/identity_bootstrap/seeders/memory.py +191 -0
  223. apsimo/identity_bootstrap/seeders/neo4j_cognition.py +79 -0
  224. apsimo/identity_bootstrap/seeders/relationship.py +152 -0
  225. apsimo/identity_bootstrap/seeders/sessions.py +67 -0
  226. apsimo/identity_bootstrap/seeders/skills.py +92 -0
  227. apsimo/identity_bootstrap/seeders/task_queue.py +72 -0
  228. apsimo/identity_bootstrap/seeders/world_model.py +143 -0
  229. apsimo/identity_bootstrap/self_query.py +92 -0
  230. apsimo/identity_bootstrap/self_reflection.py +155 -0
  231. apsimo/identity_bootstrap/skill.py +37 -0
  232. apsimo/identity_bootstrap/verifier.py +436 -0
  233. apsimo/initiatives/__init__.py +20 -0
  234. apsimo/initiatives/action_registry.py +454 -0
  235. apsimo/initiatives/approval_authority.py +2105 -0
  236. apsimo/initiatives/approval_policy.py +123 -0
  237. apsimo/initiatives/assignment.py +263 -0
  238. apsimo/initiatives/backup_evidence.py +100 -0
  239. apsimo/initiatives/context_freshness.py +103 -0
  240. apsimo/initiatives/models.py +318 -0
  241. apsimo/initiatives/native_work.py +270 -0
  242. apsimo/initiatives/standing_approvals.py +232 -0
  243. apsimo/initiatives/store.py +1081 -0
  244. apsimo/initiatives/temporal_followup.py +410 -0
  245. apsimo/intelligence/__init__.py +1 -0
  246. apsimo/intelligence/cognition/__init__.py +24 -0
  247. apsimo/intelligence/cognition/gap_detector.py +148 -0
  248. apsimo/intelligence/cognition/metalearner.py +547 -0
  249. apsimo/intelligence/cognition/metrics_collector.py +217 -0
  250. apsimo/intelligence/cognition/performance_index.py +299 -0
  251. apsimo/intelligence/cognition/registry.py +192 -0
  252. apsimo/intelligence/cognition/strategy_adjuster.py +222 -0
  253. apsimo/intelligence/cognition/types.py +16 -0
  254. apsimo/intelligence/components/__init__.py +66 -0
  255. apsimo/intelligence/components/anomaly_detector.py +413 -0
  256. apsimo/intelligence/components/initiative_engine.py +2643 -0
  257. apsimo/intelligence/components/preference_learner.py +521 -0
  258. apsimo/intelligence/components/research_orchestrator.py +358 -0
  259. apsimo/intelligence/components/self_directed_thinker.py +221 -0
  260. apsimo/intelligence/components/self_reflector.py +252 -0
  261. apsimo/intelligence/components/session_continuity.py +154 -0
  262. apsimo/intelligence/components/task_planner.py +320 -0
  263. apsimo/intelligence/components/tool_learner.py +217 -0
  264. apsimo/intelligence/graph/__init__.py +79 -0
  265. apsimo/intelligence/graph/client.py +2483 -0
  266. apsimo/intelligence/graph/consolidator.py +405 -0
  267. apsimo/intelligence/graph/distiller.py +312 -0
  268. apsimo/intelligence/graph/migrations.py +129 -0
  269. apsimo/intelligence/graph/queries.py +248 -0
  270. apsimo/intelligence/graph/recall.py +281 -0
  271. apsimo/intelligence/graph/reconciler.py +144 -0
  272. apsimo/intelligence/graph/schema.py +337 -0
  273. apsimo/intelligence/graph/selection.py +252 -0
  274. apsimo/intelligence/learning/__init__.py +17 -0
  275. apsimo/intelligence/learning/continuous_learner.py +245 -0
  276. apsimo/intelligence/learning/feedback_store.py +321 -0
  277. apsimo/intelligence/mind_model/__init__.py +1 -0
  278. apsimo/intelligence/mind_model/graph_baseline.py +136 -0
  279. apsimo/intelligence/mind_model/signal_collector.py +361 -0
  280. apsimo/intelligence/relationships/__init__.py +11 -0
  281. apsimo/intelligence/relationships/profiler.py +389 -0
  282. apsimo/intelligence/relationships/scorer.py +560 -0
  283. apsimo/intelligence/relationships/signal_floor.py +66 -0
  284. apsimo/intelligence/relationships/trust_tiers.py +300 -0
  285. apsimo/intelligence/synthesis/__init__.py +40 -0
  286. apsimo/intelligence/synthesis/connection_discoverer.py +379 -0
  287. apsimo/intelligence/synthesis/cross_domain_analyzer.py +287 -0
  288. apsimo/intelligence/synthesis/insight_deliverer.py +171 -0
  289. apsimo/intelligence/synthesis/insight_store.py +79 -0
  290. apsimo/intelligence/synthesis/insight_validator.py +183 -0
  291. apsimo/intelligence/synthesis/novelty_scorer.py +267 -0
  292. apsimo/intelligence/turn_middleware/__init__.py +15 -0
  293. apsimo/intelligence/turn_middleware/memory_sync.py +119 -0
  294. apsimo/mcp/__init__.py +41 -0
  295. apsimo/mcp/__main__.py +6 -0
  296. apsimo/mcp/config.py +287 -0
  297. apsimo/mcp/server.py +501 -0
  298. apsimo/migrations.py +187 -0
  299. apsimo/mining/__init__.py +27 -0
  300. apsimo/mining/corpus.py +239 -0
  301. apsimo/mining/escalations.py +289 -0
  302. apsimo/mining/models.py +169 -0
  303. apsimo/mining/store.py +210 -0
  304. apsimo/models/__init__.py +30 -0
  305. apsimo/models/memory.py +80 -0
  306. apsimo/models/mesh.py +72 -0
  307. apsimo/models/person.py +104 -0
  308. apsimo/models/signal.py +108 -0
  309. apsimo/observations/__init__.py +15 -0
  310. apsimo/observations/store.py +277 -0
  311. apsimo/patterns/__init__.py +6 -0
  312. apsimo/patterns/extract.py +187 -0
  313. apsimo/patterns/store.py +227 -0
  314. apsimo/persona/__init__.py +1 -0
  315. apsimo/persona/engine.py +611 -0
  316. apsimo/persona/manifest.py +140 -0
  317. apsimo/projects/__init__.py +28 -0
  318. apsimo/projects/engine.py +1681 -0
  319. apsimo/projects/event_outbox.py +188 -0
  320. apsimo/projects/models.py +216 -0
  321. apsimo/projects/planner.py +181 -0
  322. apsimo/projects/store.py +1446 -0
  323. apsimo/proposals/__init__.py +12 -0
  324. apsimo/proposals/engine.py +114 -0
  325. apsimo/proposals/models.py +207 -0
  326. apsimo/qualification/__init__.py +1 -0
  327. apsimo/qualification/cases.py +75 -0
  328. apsimo/qualification/cli.py +51 -0
  329. apsimo/qualification/memory_cases.py +209 -0
  330. apsimo/qualification/records.py +92 -0
  331. apsimo/qualification/report.py +87 -0
  332. apsimo/qualification/runner.py +311 -0
  333. apsimo/qualification/structured_cases.py +131 -0
  334. apsimo/reasoning/__init__.py +13 -0
  335. apsimo/reasoning/executor.py +506 -0
  336. apsimo/reasoning/loop.py +373 -0
  337. apsimo/reasoning/native_tools/__init__.py +16 -0
  338. apsimo/reasoning/native_tools/calculate.py +141 -0
  339. apsimo/reasoning/native_tools/file_ops.py +150 -0
  340. apsimo/reasoning/native_tools/web_search.py +49 -0
  341. apsimo/reasoning/tool_policy.py +182 -0
  342. apsimo/redact/__init__.py +176 -0
  343. apsimo/repos/__init__.py +5 -0
  344. apsimo/repos/mirrors.py +204 -0
  345. apsimo/research/__init__.py +41 -0
  346. apsimo/research/artifact.py +482 -0
  347. apsimo/research/gatherer.py +387 -0
  348. apsimo/research/pipeline.py +513 -0
  349. apsimo/research/search/__init__.py +7 -0
  350. apsimo/research/search/base.py +41 -0
  351. apsimo/research/search/brave.py +59 -0
  352. apsimo/research/search/cache.py +51 -0
  353. apsimo/research/search/duckduckgo.py +103 -0
  354. apsimo/research/search/orchestrator.py +119 -0
  355. apsimo/research/search/serpapi.py +59 -0
  356. apsimo/research/search/tavily.py +59 -0
  357. apsimo/research/synthesizer.py +309 -0
  358. apsimo/router/__init__.py +30 -0
  359. apsimo/router/complexity_scorer.py +148 -0
  360. apsimo/router/endpoints.py +153 -0
  361. apsimo/router/fallback.py +58 -0
  362. apsimo/router/functions.py +243 -0
  363. apsimo/router/native_policy.py +52 -0
  364. apsimo/router/router.py +762 -0
  365. apsimo/router/self_learning.py +174 -0
  366. apsimo/router/tiers.py +677 -0
  367. apsimo/sandbox/__init__.py +21 -0
  368. apsimo/sandbox/backend.py +195 -0
  369. apsimo/sandbox/manager.py +173 -0
  370. apsimo/scope_bounds.py +7 -0
  371. apsimo/secrets/__init__.py +6 -0
  372. apsimo/secrets/backends/__init__.py +8 -0
  373. apsimo/secrets/backends/base.py +42 -0
  374. apsimo/secrets/backends/env.py +110 -0
  375. apsimo/secrets/backends/keyring.py +72 -0
  376. apsimo/secrets/backends/onepassword.py +232 -0
  377. apsimo/secrets/cli.py +191 -0
  378. apsimo/secrets/manager.py +160 -0
  379. apsimo/secrets/migration.py +101 -0
  380. apsimo/secrets/types.py +98 -0
  381. apsimo/seed.py +41 -0
  382. apsimo/self_model/__init__.py +37 -0
  383. apsimo/self_model/appraisals.py +673 -0
  384. apsimo/self_model/benchmark.py +1314 -0
  385. apsimo/self_model/brief.py +40 -0
  386. apsimo/self_model/event_concerns.py +1128 -0
  387. apsimo/self_model/execution_forecasts.py +353 -0
  388. apsimo/self_model/expectations.py +1595 -0
  389. apsimo/self_model/experiments.py +1150 -0
  390. apsimo/self_model/journal.py +148 -0
  391. apsimo/self_model/judgments.py +705 -0
  392. apsimo/self_model/native_outcomes.py +55 -0
  393. apsimo/self_model/params.py +220 -0
  394. apsimo/self_model/perspective.py +246 -0
  395. apsimo/self_model/reconcile.py +183 -0
  396. apsimo/self_model/reply_forecasts.py +381 -0
  397. apsimo/self_model/runtime_forecasts.py +296 -0
  398. apsimo/self_model/runtime_models.py +67 -0
  399. apsimo/self_model/settlement.py +207 -0
  400. apsimo/self_model/situation.py +1731 -0
  401. apsimo/self_model/store.py +883 -0
  402. apsimo/self_model/supervised.py +137 -0
  403. apsimo/self_model/thinker.py +99 -0
  404. apsimo/self_model/trust.py +388 -0
  405. apsimo/self_model/workspace.py +2388 -0
  406. apsimo/server.py +4197 -0
  407. apsimo/services/__init__.py +1 -0
  408. apsimo/services/agent_bridge.py +474 -0
  409. apsimo/services/initiative_executor.py +914 -0
  410. apsimo/services/instance.py +297 -0
  411. apsimo/sessions/__init__.py +22 -0
  412. apsimo/sessions/config.py +13 -0
  413. apsimo/sessions/context_loader.py +88 -0
  414. apsimo/sessions/federation_session.py +75 -0
  415. apsimo/sessions/isolated_session.py +98 -0
  416. apsimo/sessions/reports.py +84 -0
  417. apsimo/sessions/store.py +148 -0
  418. apsimo/setup.py +2818 -0
  419. apsimo/setup_hermes.py +879 -0
  420. apsimo/setup_local_work.py +218 -0
  421. apsimo/setup_native_goals.py +134 -0
  422. apsimo/setup_native_reviews.py +115 -0
  423. apsimo/skills/__init__.py +10 -0
  424. apsimo/skills/base.py +108 -0
  425. apsimo/skills/budget.py +28 -0
  426. apsimo/skills/executor.py +493 -0
  427. apsimo/skills/executors/__init__.py +1 -0
  428. apsimo/skills/executors/behavioral_correction.py +75 -0
  429. apsimo/skills/executors/capability_gap.py +38 -0
  430. apsimo/skills/executors/data_quality.py +163 -0
  431. apsimo/skills/executors/knowledge_acquisition.py +41 -0
  432. apsimo/skills/executors/operational_hygiene.py +185 -0
  433. apsimo/skills/executors/subsystem_health.py +169 -0
  434. apsimo/skills/hermes_export.py +431 -0
  435. apsimo/skills/index.py +123 -0
  436. apsimo/skills/learning/__init__.py +21 -0
  437. apsimo/skills/learning/novelty_detector.py +206 -0
  438. apsimo/skills/learning/pattern_extractor.py +199 -0
  439. apsimo/skills/learning/triggers.py +159 -0
  440. apsimo/skills/loader.py +246 -0
  441. apsimo/skills/migrations/002_progressive_loading.sql +6 -0
  442. apsimo/skills/migrations/backfill_triggers.py +20 -0
  443. apsimo/skills/models.py +202 -0
  444. apsimo/skills/packager.py +128 -0
  445. apsimo/skills/protocols.py +70 -0
  446. apsimo/skills/registry.py +191 -0
  447. apsimo/skills/runtime.py +58 -0
  448. apsimo/skills/sandbox_runner.py +229 -0
  449. apsimo/skills/scheduler.py +129 -0
  450. apsimo/skills/schema.py +79 -0
  451. apsimo/skills/security/__init__.py +12 -0
  452. apsimo/skills/security/guards.py +53 -0
  453. apsimo/skills/security/scanner.py +223 -0
  454. apsimo/skills_memory/__init__.py +26 -0
  455. apsimo/skills_memory/distill.py +159 -0
  456. apsimo/skills_memory/models.py +85 -0
  457. apsimo/skills_memory/retrieve.py +62 -0
  458. apsimo/skills_memory/store.py +172 -0
  459. apsimo/surprise/__init__.py +6 -0
  460. apsimo/surprise/accumulation.py +57 -0
  461. apsimo/surprise/scorer.py +102 -0
  462. apsimo/surprise/store.py +203 -0
  463. apsimo/task_queue/__init__.py +69 -0
  464. apsimo/task_queue/action_receipts.py +148 -0
  465. apsimo/task_queue/approval_relay_canary.py +108 -0
  466. apsimo/task_queue/config.py +85 -0
  467. apsimo/task_queue/contract.py +361 -0
  468. apsimo/task_queue/events.py +130 -0
  469. apsimo/task_queue/governor.py +1031 -0
  470. apsimo/task_queue/handlers/__init__.py +16 -0
  471. apsimo/task_queue/handlers/base.py +37 -0
  472. apsimo/task_queue/handlers/inference.py +640 -0
  473. apsimo/task_queue/handlers/monitoring.py +116 -0
  474. apsimo/task_queue/handlers/registry.py +75 -0
  475. apsimo/task_queue/handlers/subtask_handler.py +173 -0
  476. apsimo/task_queue/handlers/system_maintenance.py +147 -0
  477. apsimo/task_queue/mesh_integration.py +111 -0
  478. apsimo/task_queue/models.py +317 -0
  479. apsimo/task_queue/queue_manager.py +8286 -0
  480. apsimo/task_queue/routing.py +287 -0
  481. apsimo/task_queue/scheduler.py +252 -0
  482. apsimo/task_queue/schema.sql +197 -0
  483. apsimo/task_queue/work_control.py +342 -0
  484. apsimo/task_queue/worker.py +993 -0
  485. apsimo/telemetry.py +145 -0
  486. apsimo/tom/__init__.py +6 -0
  487. apsimo/tom/affect.py +387 -0
  488. apsimo/tom/approvals.py +171 -0
  489. apsimo/tom/arcs.py +896 -0
  490. apsimo/tom/asymmetry.py +131 -0
  491. apsimo/tom/eligibility.py +248 -0
  492. apsimo/tom/engagement.py +214 -0
  493. apsimo/tom/exposure.py +214 -0
  494. apsimo/tom/extractor.py +306 -0
  495. apsimo/tom/fact_adapters.py +144 -0
  496. apsimo/tom/facts.py +326 -0
  497. apsimo/tom/integration.py +592 -0
  498. apsimo/tom/leveled.py +118 -0
  499. apsimo/tom/levels.py +247 -0
  500. apsimo/tom/recipient_audit.py +995 -0
  501. apsimo/tom/recipient_simulator.py +593 -0
  502. apsimo/tom/source_lineage.py +93 -0
  503. apsimo/tom/tom2.py +277 -0
  504. apsimo/tom/visibility.py +559 -0
  505. apsimo/tom/visibility_store.py +414 -0
  506. apsimo/tools/__init__.py +0 -0
  507. apsimo/tools/definitions.py +740 -0
  508. apsimo/tools/handlers.py +943 -0
  509. apsimo/toolsmith/__init__.py +26 -0
  510. apsimo/toolsmith/authority.py +166 -0
  511. apsimo/toolsmith/engine.py +559 -0
  512. apsimo/toolsmith/integrity.py +100 -0
  513. apsimo/toolsmith/miner.py +145 -0
  514. apsimo/toolsmith/policy.py +110 -0
  515. apsimo/toolsmith/registry.py +635 -0
  516. apsimo/turns/__init__.py +17 -0
  517. apsimo/turns/audio.py +134 -0
  518. apsimo/turns/documents.py +235 -0
  519. apsimo/turns/executions.py +486 -0
  520. apsimo/turns/hermes_history.py +245 -0
  521. apsimo/turns/hermes_kanban.py +268 -0
  522. apsimo/turns/hermes_work.py +96 -0
  523. apsimo/turns/idempotency.py +752 -0
  524. apsimo/turns/local_work.py +115 -0
  525. apsimo/turns/media.py +581 -0
  526. apsimo/turns/reported_workers.py +196 -0
  527. apsimo/turns/source_annotations.py +283 -0
  528. apsimo/turns/source_attribution.py +154 -0
  529. apsimo/turns/source_read.py +351 -0
  530. apsimo/turns/source_vectors.py +263 -0
  531. apsimo/turns/video.py +210 -0
  532. apsimo/util/autonomy_preset.py +220 -0
  533. apsimo/util/instance.py +92 -0
  534. apsimo/util/model_output.py +25 -0
  535. apsimo/util/quiet_hours.py +27 -0
  536. apsimo/util/session_safety.py +37 -0
  537. apsimo/util/temporal.py +343 -0
  538. apsimo/vector/__init__.py +75 -0
  539. apsimo/vector/backfill.py +171 -0
  540. apsimo/vector/caption.py +114 -0
  541. apsimo/vector/collections.py +51 -0
  542. apsimo/vector/config.py +102 -0
  543. apsimo/vector/embedder.py +670 -0
  544. apsimo/vector/image_preprocess.py +406 -0
  545. apsimo/vector/image_store.py +296 -0
  546. apsimo/vector/indexes.py +162 -0
  547. apsimo/vector/migrate.py +334 -0
  548. apsimo/vector/multimodal_provider.py +417 -0
  549. apsimo/vector/multimodal_types.py +87 -0
  550. apsimo/vector/openai_provider.py +119 -0
  551. apsimo/vector/query.py +49 -0
  552. apsimo/vector/reranker.py +565 -0
  553. apsimo/vector/safety_image.py +159 -0
  554. apsimo/vector/scanner.py +197 -0
  555. apsimo/vector/setup.py +289 -0
  556. apsimo/vector/store.py +533 -0
  557. apsimo/vector/tiers.py +263 -0
  558. apsimo/work_orders.py +925 -0
  559. apsimo/workers/__init__.py +21 -0
  560. apsimo/workers/agent_bridge.py +640 -0
  561. apsimo/workers/colony_worker.py +382 -0
  562. apsimo/workers/queue_worker.py +441 -0
  563. apsimo/workers/skills_sync.py +152 -0
  564. apsimo/world_model/__init__.py +71 -0
  565. apsimo/world_model/causal_maintenance.py +131 -0
  566. apsimo/world_model/causal_policy.py +43 -0
  567. apsimo/world_model/causal_query.py +125 -0
  568. apsimo/world_model/confidence.py +54 -0
  569. apsimo/world_model/config.py +64 -0
  570. apsimo/world_model/constants.py +97 -0
  571. apsimo/world_model/entities.py +145 -0
  572. apsimo/world_model/expectation_resolvers.py +177 -0
  573. apsimo/world_model/extraction/__init__.py +7 -0
  574. apsimo/world_model/extraction/base.py +62 -0
  575. apsimo/world_model/extraction/conversation_extractor.py +262 -0
  576. apsimo/world_model/extraction/detector.py +74 -0
  577. apsimo/world_model/extraction/document_extractor.py +78 -0
  578. apsimo/world_model/extraction/formats/__init__.py +24 -0
  579. apsimo/world_model/extraction/formats/csv_fmt.py +68 -0
  580. apsimo/world_model/extraction/formats/html_fmt.py +72 -0
  581. apsimo/world_model/extraction/formats/json_fmt.py +68 -0
  582. apsimo/world_model/extraction/formats/pdf.py +43 -0
  583. apsimo/world_model/extraction/formats/text.py +27 -0
  584. apsimo/world_model/extraction/llm_extractor.py +164 -0
  585. apsimo/world_model/extraction/pipeline.py +73 -0
  586. apsimo/world_model/integrations/__init__.py +5 -0
  587. apsimo/world_model/integrations/mind_model_bridge.py +115 -0
  588. apsimo/world_model/integrations/social_intel_bridge.py +120 -0
  589. apsimo/world_model/jobs/__init__.py +4 -0
  590. apsimo/world_model/jobs/extraction_job.py +168 -0
  591. apsimo/world_model/llm_extract.py +572 -0
  592. apsimo/world_model/neo4j/__init__.py +5 -0
  593. apsimo/world_model/neo4j/backend.py +654 -0
  594. apsimo/world_model/observations.py +155 -0
  595. apsimo/world_model/populator.py +307 -0
  596. apsimo/world_model/postgres/__init__.py +1 -0
  597. apsimo/world_model/postgres/backend.py +683 -0
  598. apsimo/world_model/relationships.py +25 -0
  599. apsimo/world_model/resolution/__init__.py +13 -0
  600. apsimo/world_model/resolution/entity_resolver.py +232 -0
  601. apsimo/world_model/resolution/merge_audit.py +16 -0
  602. apsimo/world_model/resolution/merge_workflow.py +117 -0
  603. apsimo/world_model/source_reports.py +121 -0
  604. apsimo/world_model/sqlite/__init__.py +4 -0
  605. apsimo/world_model/sqlite/backend.py +855 -0
  606. apsimo/world_model/sqlite/schema.sql +132 -0
  607. apsimo/world_model/store.py +545 -0
  608. apsimo-1.3.0.dist-info/METADATA +78 -0
  609. apsimo-1.3.0.dist-info/RECORD +614 -0
  610. apsimo-1.3.0.dist-info/WHEEL +5 -0
  611. apsimo-1.3.0.dist-info/entry_points.txt +11 -0
  612. apsimo-1.3.0.dist-info/licenses/LICENSE +21 -0
  613. apsimo-1.3.0.dist-info/top_level.txt +2 -0
  614. colony_sidecar/__init__.py +4 -0
@@ -0,0 +1,572 @@
1
+ """LLM-assisted world-model extraction (batch, journaled).
2
+
3
+ Complements the rule-based WorldModelPopulator: on a daily batch cadence
4
+ (never per-turn), recent conversation memories are handed to a fast
5
+ classifier LLM which proposes structured entities + relationships as STRICT
6
+ JSON. Code validates everything (known types, name quality, confidence
7
+ floor, boundary check, resolver dedup) before any write, and every write is
8
+ recorded in the unified action journal so the owner can review exactly what
9
+ was added and why.
10
+
11
+ Endpoint: env-driven OpenAI-compatible chat endpoint
12
+ (COLONY_WORLD_LLM_BASE_URL / _MODEL / _API_KEY, falling back to the
13
+ COLONY_INTROSPECT_* fast-classifier endpoint convention).
14
+
15
+ Modes (COLONY_WORLD_LLM_EXTRACT): off (default) | shadow (log the would-be
16
+ writes) | live (write + journal).
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import json
22
+ import logging
23
+ import os
24
+ import re
25
+ from typing import Any, Dict, List, Optional
26
+
27
+ from apsimo.world_model.constants import CAUSAL_RELATIONSHIP_TYPES
28
+ from apsimo.world_model.populator import _is_low_quality, _looks_like_fragment
29
+
30
+ logger = logging.getLogger(__name__)
31
+
32
+ _ENTITY_TYPES = {"person", "company", "project", "product", "location",
33
+ "event", "concept"}
34
+ _REL_TYPES = {"WM_WORKS_AT", "WM_KNOWS", "WM_PART_OF", "WM_RELATED_TO",
35
+ "WM_LOCATED_IN", "WM_BUILDS"}
36
+ _MIN_CONF = 0.5
37
+
38
+ # Legacy causal extraction is a hypothesis with a conservative creation
39
+ # ceiling. Repeat batches are not independent predictive evidence.
40
+ _CAUSAL_CREATE_CEILING = 0.5
41
+
42
+ _SYSTEM_PROMPT = """\
43
+ You extract structured world knowledge from conversation excerpts for a
44
+ personal intelligence system. Extract only REAL named entities the owner's
45
+ world contains (people, companies, projects, products, locations, events,
46
+ concepts) and clearly stated relationships between them.
47
+
48
+ Rules:
49
+ - Only entities explicitly named in the text. Never invent, never guess.
50
+ - Skip generic words, greetings, dates, the assistant itself, and anything
51
+ that is not a proper name.
52
+ - confidence reflects how clearly the text establishes the entity (0-1).
53
+ - Weight owner relevance: entities recurring in the excerpt or tied to the
54
+ owner's work, projects, or relationships score higher; incidental one-off
55
+ names score low (<=0.4).
56
+
57
+ Respond with ONLY a JSON object (no prose, no markdown fences):
58
+ {"entities": [{"name": str, "type": one of ["person","company","project",
59
+ "product","location","event","concept"], "confidence": float}],
60
+ "relationships": [{"source": str, "rel": one of ["WM_WORKS_AT","WM_KNOWS",
61
+ "WM_PART_OF","WM_RELATED_TO","WM_LOCATED_IN","WM_BUILDS"], "target": str,
62
+ "confidence": float}],
63
+ "observations": [{"entity": str, "property": "lowercase_property_key", "value": str, "temporal_status": "current",
64
+ "evidence": "exact verbatim sentence from excerpt containing both entity and value"}]}
65
+ Observations are reported claims only, never proof or direct sensor observations.
66
+ Only unqualified present-tense states belong in observations. Omit future, past,
67
+ dated, conditional or uncertain assertions; receiving a report today does not
68
+ make its described state current. Never clip a date/tense qualifier off a quote.
69
+ Do not infer personality, authority or trust. Omit vague, negated or uncertain claims.
70
+ Never turn a question, example, instruction or an assistant's paraphrase into a fact."""
71
+
72
+ _CAUSAL_PROMPT = """
73
+
74
+ Additionally extract clearly stated CAUSAL claims between those same
75
+ entities as "causal": [{"source": str, "rel": one of ["WM_CAUSES",
76
+ "WM_ENABLES","WM_BLOCKS","WM_INHIBITS"], "target": str, "evidence": str,
77
+ "confidence": float}].
78
+ Causal rules:
79
+ - Only when the text itself asserts the causal link. Never infer one.
80
+ - "evidence" MUST be a short verbatim quote copied character-for-character
81
+ from the excerpt; a claim without its exact quote will be discarded."""
82
+
83
+
84
+ def llm_extract_mode() -> str:
85
+ from apsimo.util.autonomy_preset import resolve
86
+ return resolve("COLONY_WORLD_LLM_EXTRACT",
87
+ ("off", "shadow", "live"), "off")
88
+
89
+
90
+ def world_supervised_max_writes() -> int:
91
+ """Per-run cap on supervised world-model writes (H1.5, default 25)."""
92
+ try:
93
+ v = int(os.environ.get("COLONY_WORLD_SUPERVISED_MAX_WRITES", "25"))
94
+ return v if v > 0 else 25
95
+ except (TypeError, ValueError):
96
+ return 25
97
+
98
+
99
+ def causal_extract_mode() -> str:
100
+ """Effective causal-extraction mode: min(COLONY_CAUSAL_EXTRACT,
101
+ COLONY_WORLD_LLM_EXTRACT) over off < shadow < live — causal extraction
102
+ can never be MORE live than the extractor that carries it."""
103
+ from apsimo.util.autonomy_preset import resolve
104
+ order = ("off", "shadow", "live")
105
+ causal = resolve("COLONY_CAUSAL_EXTRACT", order, "off")
106
+ return order[min(order.index(causal), order.index(llm_extract_mode()))]
107
+
108
+
109
+ def _endpoint() -> Dict[str, str]:
110
+ base = (os.environ.get("COLONY_WORLD_LLM_BASE_URL", "")
111
+ or os.environ.get("COLONY_INTROSPECT_BASE_URL", "")).rstrip("/")
112
+ model = (os.environ.get("COLONY_WORLD_LLM_MODEL", "")
113
+ or os.environ.get("COLONY_INTROSPECT_MODEL", ""))
114
+ key = (os.environ.get("COLONY_WORLD_LLM_API_KEY", "")
115
+ or os.environ.get("COLONY_INTROSPECT_API_KEY", ""))
116
+ return {"base": base, "model": model, "key": key}
117
+
118
+
119
+ def _parse_obj(content: str) -> Optional[dict]:
120
+ text = (content or "").strip()
121
+ if "```" in text:
122
+ m = re.search(r"```(?:json)?\s*(.*?)```", text, re.DOTALL)
123
+ if m:
124
+ text = m.group(1).strip()
125
+ if not text.startswith("{"):
126
+ m = re.search(r"\{.*\}", text, re.DOTALL)
127
+ if not m:
128
+ return None
129
+ text = m.group(0)
130
+ try:
131
+ data = json.loads(text)
132
+ except json.JSONDecodeError:
133
+ return None
134
+ return data if isinstance(data, dict) else None
135
+
136
+
137
+ class WorldLLMExtractor:
138
+ def __init__(self, store: Any, *, graph: Any = None,
139
+ directive_manager: Any = None, journal: Any = None,
140
+ self_model: Any = None, source_ledger: Any = None, router_provider: Any = None) -> None:
141
+ self._store = store
142
+ self._graph = graph
143
+ self._directives = directive_manager
144
+ self._journal = journal
145
+ self._self_model = self_model
146
+ self._source_ledger = source_ledger
147
+ self._router_provider = router_provider
148
+ self._resolver = None
149
+ if store is not None:
150
+ try:
151
+ from apsimo.world_model.resolution.entity_resolver import (
152
+ EntityResolver,
153
+ )
154
+ self._resolver = EntityResolver(store)
155
+ except Exception:
156
+ pass
157
+ self.last_report: Dict[str, Any] = {}
158
+
159
+ # -- source text -------------------------------------------------------
160
+
161
+ _RECENT_MEMORY_QUERY = (
162
+ """MATCH (m:Memory)
163
+ WHERE m.created_at >= datetime() - duration({hours: $hours})
164
+ AND m.type IN ['episodic', 'semantic']
165
+ AND m.superseded_by IS NULL
166
+ RETURN m.content AS content
167
+ ORDER BY m.created_at DESC LIMIT $limit""")
168
+
169
+ async def _recent_memory_texts(self, hours: float = 24.0,
170
+ limit: int = 30) -> List[str]:
171
+ if self._graph is None or not hasattr(self._graph, "run_query"):
172
+ return []
173
+ try:
174
+ # Register this exact parameterized read query with the graph
175
+ # client's Cypher allowlist (single-sourced here).
176
+ if hasattr(type(self._graph), "register_allowed_cypher"):
177
+ type(self._graph).register_allowed_cypher(
178
+ self._RECENT_MEMORY_QUERY)
179
+ rows = await self._graph.run_query(
180
+ self._RECENT_MEMORY_QUERY,
181
+ {"hours": int(hours), "limit": int(limit)})
182
+ return [str(r.get("content") or "") for r in rows or []
183
+ if r.get("content")]
184
+ except Exception as exc:
185
+ logger.info("world llm-extract memory query failed: %s", exc)
186
+ return []
187
+
188
+ # -- one LLM batch -------------------------------------------------------
189
+ @staticmethod
190
+ def _excerpt(texts: List[str]) -> str:
191
+ """The exact excerpt sent to the LLM — also the anti-fabrication
192
+ reference that causal evidence quotes are pinned against."""
193
+ return "\n---\n".join(t[:600] for t in texts)[:8000]
194
+
195
+ async def _llm_batch(self, texts: List[str]) -> Optional[dict]:
196
+ if self._router_provider is not None:
197
+ # Production uses the same discoverable extraction role as source
198
+ # learning. Re-read the router each batch so rebinding needs no restart.
199
+ import asyncio
200
+ from apsimo.beliefs.source_claims import final_text, extraction_timeout_seconds
201
+ router = self._router_provider()
202
+ if getattr(router, 'supports_function_routing', False) is not True:
203
+ return None
204
+ prompt = _SYSTEM_PROMPT + (_CAUSAL_PROMPT if causal_extract_mode() != 'off' else '')
205
+ response = await asyncio.wait_for(router.complete(messages=[
206
+ {'role': 'system', 'content': prompt}, {'role': 'user', 'content': self._excerpt(texts)}],
207
+ context={'function_role': 'extraction', 'task': 'world_source_reports',
208
+ 'max_output_tokens': 900, 'allow_fallback': True}),
209
+ timeout=extraction_timeout_seconds(router))
210
+ self.last_report['processor'] = {'function_role': 'extraction',
211
+ 'model_id': getattr(response, 'model_id', None),
212
+ 'model_revision': getattr(response, 'model_revision', None),
213
+ 'config_revision': getattr(response, 'config_revision', None)}
214
+ return _parse_obj(final_text(response))
215
+ ep = _endpoint()
216
+ if not ep["base"] or not ep["model"]:
217
+ return None
218
+ try:
219
+ import aiohttp
220
+ except ImportError:
221
+ return None
222
+ excerpt = self._excerpt(texts)
223
+ system_prompt = _SYSTEM_PROMPT
224
+ if causal_extract_mode() != "off":
225
+ system_prompt = _SYSTEM_PROMPT + _CAUSAL_PROMPT
226
+ payload = {
227
+ "model": ep["model"], "temperature": 0, "max_tokens": 900,
228
+ "messages": [{"role": "system", "content": system_prompt},
229
+ {"role": "user", "content": excerpt}],
230
+ }
231
+ headers = {"Content-Type": "application/json"}
232
+ if ep["key"]:
233
+ headers["Authorization"] = f"Bearer {ep['key']}"
234
+ try:
235
+ # Keep the per-request timeout WELL under the autonomy tick
236
+ # budget: when the tick's wait_for cancels mid-request, aiohttp's
237
+ # session unwind gets interrupted and leaks ("Unclosed client
238
+ # session" spam). A clean inner timeout aborts and closes.
239
+ timeout = aiohttp.ClientTimeout(total=float(
240
+ os.environ.get("COLONY_WORLD_LLM_TIMEOUT", "40")))
241
+ async with aiohttp.ClientSession() as s:
242
+ async with s.post(ep["base"] + "/chat/completions",
243
+ json=payload, headers=headers,
244
+ timeout=timeout) as resp:
245
+ if resp.status != 200:
246
+ return None
247
+ data = await resp.json()
248
+ return _parse_obj(data["choices"][0]["message"]["content"])
249
+ except Exception as exc:
250
+ logger.debug("world llm-extract call failed: %s", exc)
251
+ return None
252
+
253
+ def _boundary_ok(self, name: str) -> bool:
254
+ if self._directives is None:
255
+ # No directive manager configured: nothing to check against
256
+ # (distinct from "checked and allowed" — see log line).
257
+ logger.debug("world llm-extract boundary UNCHECKED for %r "
258
+ "(no directive manager)", name)
259
+ return True
260
+ try:
261
+ from apsimo.directives import Action
262
+ return self._directives.check(
263
+ Action(kind="populate", text=name, target=name)).allowed
264
+ except Exception:
265
+ # Fail closed, matching directives.guard.boundary_fail_closed():
266
+ # an error while evaluating an owner boundary must refuse.
267
+ logger.warning("world llm-extract boundary check errored for %r "
268
+ "— failing closed", name, exc_info=True)
269
+ return False
270
+
271
+ # -- supervised rung (H1.5) -----------------------------------------------
272
+
273
+ def _effective_mode(self) -> str:
274
+ """Env mode, graduated by the trust engine ONLY when the world_model
275
+ domain is explicitly enrolled in COLONY_SUPERVISED_LIVE_DOMAINS —
276
+ otherwise the env mode is returned untouched (regression lock: a
277
+ deployment that never enrolls the domain sees zero behavior change,
278
+ whatever the trust stage says)."""
279
+ env = llm_extract_mode()
280
+ try:
281
+ from apsimo.self_model.supervised import (
282
+ effective_mode, supervised_domains,
283
+ )
284
+ if "world_model" not in supervised_domains():
285
+ return env
286
+ return effective_mode(
287
+ "world_model", env,
288
+ getattr(self._self_model, "trust", None))
289
+ except Exception:
290
+ return env
291
+
292
+ def _may_write(self, mode: str, op: str, report: Dict[str, Any]) -> bool:
293
+ """live => yes. supervised => only ops pinned reversible for the
294
+ world_model domain, and only under the per-run write cap. Anything
295
+ else => no."""
296
+ if mode == "live":
297
+ return True
298
+ if mode != "supervised":
299
+ return False
300
+ from apsimo.self_model.supervised import reversible
301
+ if not reversible("world_model", op):
302
+ return False
303
+ if report.get("writes", 0) >= world_supervised_max_writes():
304
+ report["supervised_capped"] = report.get("supervised_capped", 0) + 1
305
+ return False
306
+ return True
307
+
308
+ def _record_outcome(self, mode: str, report: Dict[str, Any]) -> None:
309
+ """Real trust outcomes for the world_model domain — but only when
310
+ the domain is enrolled on the rung, and only when the run actually
311
+ wrote something (a no-op run earns nothing; see H1.3)."""
312
+ if self._self_model is None:
313
+ return
314
+ try:
315
+ from apsimo.self_model.supervised import supervised_domains
316
+ if "world_model" not in supervised_domains():
317
+ return
318
+ if mode not in ("live", "supervised"):
319
+ return
320
+ if report.get("writes", 0) > 0:
321
+ self._self_model.record("world_model", "success", shadow=False)
322
+ except Exception:
323
+ pass
324
+
325
+ # -- main run -------------------------------------------------------------
326
+ async def run(self, texts: Optional[List[str]] = None) -> Dict[str, Any]:
327
+ mode = self._effective_mode()
328
+ cmode = causal_extract_mode()
329
+ report: Dict[str, Any] = {"mode": mode, "batches": 0, "created": [],
330
+ "merged": [], "relationships": [],
331
+ "skipped": 0, "causal_mode": cmode,
332
+ "causal": [], "causal_corroborated": [],
333
+ "causal_skipped": 0,
334
+ "writes": 0, "supervised_capped": 0}
335
+ self.last_report = report
336
+ if mode == "off" or self._store is None:
337
+ return report
338
+ self._seen_rels: set = set()
339
+ source_batches = None
340
+ if texts is None and self._source_ledger is not None:
341
+ from .source_reports import recent_batches
342
+ source_batches = recent_batches(self._source_ledger)
343
+ texts = [source['content'] for batch in source_batches for source in batch]
344
+ elif texts is None:
345
+ texts = await self._recent_memory_texts()
346
+ report["texts"] = len(texts or [])
347
+ if not texts:
348
+ logger.info("world-llm-extract[%s]: no recent memory texts to "
349
+ "process", mode)
350
+ return report
351
+
352
+ batches = [([source['content'] for source in batch], batch) for batch in source_batches] if source_batches is not None else [
353
+ (texts[i:i + 10], []) for i in range(0, len(texts), 10)]
354
+ for batch, sources in batches:
355
+ name_to_id: Dict[str, str] = {}
356
+ data = await self._llm_batch(batch)
357
+ if not data:
358
+ continue
359
+ report["batches"] += 1
360
+ for e in (data.get("entities") or []):
361
+ if not isinstance(e, dict):
362
+ continue
363
+ name = str(e.get("name", "")).strip()[:120]
364
+ etype = str(e.get("type", "")).strip().lower()
365
+ try:
366
+ conf = float(e.get("confidence", 0))
367
+ except (TypeError, ValueError):
368
+ conf = 0.0
369
+ if (not name or etype not in _ENTITY_TYPES
370
+ or conf < _MIN_CONF
371
+ or _looks_like_fragment(name)
372
+ or _is_low_quality(name, etype)):
373
+ report["skipped"] += 1
374
+ continue
375
+ if not self._boundary_ok(name):
376
+ report["skipped"] += 1
377
+ continue
378
+ eid = await self._upsert(name, etype, conf, mode, report)
379
+ if eid:
380
+ name_to_id[name.lower()] = eid
381
+ from .source_reports import record_reports
382
+ await record_reports(self, data.get('observations'), sources, name_to_id, mode, report)
383
+ for r in (data.get("relationships") or []):
384
+ if not isinstance(r, dict):
385
+ continue
386
+ rel = str(r.get("rel", "")).strip().upper()
387
+ src = str(r.get("source", "")).strip().lower()
388
+ tgt = str(r.get("target", "")).strip().lower()
389
+ if (rel not in _REL_TYPES or src not in name_to_id
390
+ or tgt not in name_to_id or src == tgt):
391
+ continue
392
+ await self._upsert_rel(name_to_id[src], rel,
393
+ name_to_id[tgt],
394
+ float(r.get("confidence", 0.5) or 0.5),
395
+ mode, report)
396
+ if cmode != "off":
397
+ excerpt_lower = self._excerpt(batch).lower()
398
+ for c in (data.get("causal") or []):
399
+ if not isinstance(c, dict):
400
+ continue
401
+ rel = str(c.get("rel", "")).strip().upper()
402
+ src = str(c.get("source", "")).strip().lower()
403
+ tgt = str(c.get("target", "")).strip().lower()
404
+ evidence = str(c.get("evidence", "")).strip()
405
+ try:
406
+ conf = float(c.get("confidence", 0.5) or 0.5)
407
+ except (TypeError, ValueError):
408
+ conf = 0.5
409
+ if (rel not in CAUSAL_RELATIONSHIP_TYPES
410
+ or src not in name_to_id or tgt not in name_to_id
411
+ or src == tgt):
412
+ report["causal_skipped"] += 1
413
+ continue
414
+ # Anti-fabrication pin: the evidence quote must actually
415
+ # occur in the source excerpt (case-insensitive). A causal
416
+ # claim the model cannot quote is a fabrication and is
417
+ # discarded, whatever its confidence.
418
+ if not evidence or evidence.lower() not in excerpt_lower:
419
+ report["causal_skipped"] += 1
420
+ continue
421
+ await self._upsert_causal(
422
+ name_to_id[src], rel, name_to_id[tgt],
423
+ evidence, conf, cmode, report)
424
+ logger.info(
425
+ "world-llm-extract[%s]: %d batch(es), created=%d merged=%d "
426
+ "rel=%d skipped=%d writes=%d", mode, report["batches"],
427
+ len(report["created"]), len(report["merged"]),
428
+ len(report["relationships"]), report["skipped"],
429
+ report["writes"])
430
+ self._record_outcome(mode, report)
431
+ return report
432
+
433
+ async def _upsert(self, name: str, etype: str, conf: float, mode: str,
434
+ report: Dict[str, Any]) -> Optional[str]:
435
+ action, matched = "create", None
436
+ if self._resolver is not None:
437
+ try:
438
+ from apsimo.world_model.extraction.conversation_extractor import (
439
+ ExtractionCandidate,
440
+ )
441
+ cand = ExtractionCandidate(text=name, entity_type=etype,
442
+ start_char=0, end_char=len(name),
443
+ confidence=conf,
444
+ context_window=name)
445
+ res = await self._resolver.resolve(cand, etype)
446
+ action = getattr(res.action, "value", str(res.action))
447
+ matched = res.matched_entity_id
448
+ except Exception:
449
+ pass
450
+ if action == "merge" and matched:
451
+ report["merged"].append({"name": name, "into": matched})
452
+ if self._may_write(mode, "alias_merge", report):
453
+ try:
454
+ await self._store.add_entity_alias(matched, name)
455
+ report["writes"] = report.get("writes", 0) + 1
456
+ except Exception:
457
+ pass
458
+ self._journal_write(f"merged alias {name!r} into {matched}",
459
+ conf, matched)
460
+ return matched
461
+ if action == "propose":
462
+ report["skipped"] += 1
463
+ return matched
464
+ report["created"].append({"name": name, "type": etype,
465
+ "confidence": round(conf, 2)})
466
+ if not self._may_write(mode, "entity_upsert", report):
467
+ return None
468
+ try:
469
+ from apsimo.world_model.entities import ENTITY_CLASS_MAP, BaseEntity
470
+ from apsimo.world_model.sqlite.backend import _generate_id
471
+ cls = ENTITY_CLASS_MAP.get(etype, BaseEntity)
472
+ ent = cls(id=_generate_id("we"), name=name, entity_type=etype,
473
+ confidence=conf)
474
+ await self._store.upsert_entity(ent)
475
+ report["writes"] = report.get("writes", 0) + 1
476
+ self._journal_write(f"created {etype} entity {name!r}", conf,
477
+ ent.id)
478
+ return ent.id
479
+ except Exception as exc:
480
+ logger.debug("llm-extract upsert failed for %r: %s", name, exc)
481
+ return None
482
+
483
+ async def _upsert_rel(self, src_id: str, rel: str, tgt_id: str,
484
+ conf: float, mode: str,
485
+ report: Dict[str, Any]) -> None:
486
+ key = (src_id, rel, tgt_id)
487
+ if key in self._seen_rels:
488
+ return
489
+ self._seen_rels.add(key)
490
+ report["relationships"].append(
491
+ {"source": src_id, "rel": rel, "target": tgt_id})
492
+ if mode not in ("live", "supervised"):
493
+ return
494
+ try:
495
+ # Repeated batches do not create another edge or improve its
496
+ # confidence. Qualified observations own new source evidence.
497
+ try:
498
+ existing = await self._store.query_relationships(
499
+ source_id=src_id, target_id=tgt_id,
500
+ relationship_type=rel, min_confidence=0.0, limit=1)
501
+ except Exception:
502
+ existing = []
503
+ if existing:
504
+ return
505
+ # Creation remains an explicit live-mode operation.
506
+ if not self._may_write(mode, "edge_create", report):
507
+ return
508
+ from apsimo.world_model.relationships import WorldRelationship
509
+ await self._store.upsert_relationship(WorldRelationship(
510
+ id="", source_id=src_id, target_id=tgt_id,
511
+ relationship_type=rel, confidence=min(0.7, conf)))
512
+ report["writes"] = report.get("writes", 0) + 1
513
+ self._journal_write(f"linked {src_id} -{rel}-> {tgt_id}", conf,
514
+ src_id)
515
+ except Exception:
516
+ logger.debug("llm-extract relationship upsert failed",
517
+ exc_info=True)
518
+
519
+ async def _upsert_causal(self, src_id: str, rel: str, tgt_id: str,
520
+ evidence: str, conf: float, mode: str,
521
+ report: Dict[str, Any]) -> None:
522
+ """Retain a reported causal hypothesis without repeat-confidence boosts.
523
+
524
+ Repeat extraction does not refresh its support clock. Shadow writes no
525
+ graph state; independent outcomes belong to the forecast lifecycle.
526
+ """
527
+ key = (src_id, rel, tgt_id)
528
+ if key in self._seen_rels:
529
+ return
530
+ self._seen_rels.add(key)
531
+ report["causal"].append({"source": src_id, "rel": rel,
532
+ "target": tgt_id,
533
+ "evidence": evidence[:200],
534
+ "confidence": round(conf, 2)})
535
+ if mode != "live":
536
+ return
537
+ try:
538
+ try:
539
+ existing = await self._store.query_relationships(
540
+ source_id=src_id, target_id=tgt_id,
541
+ relationship_type=rel, min_confidence=0.0, limit=1)
542
+ except Exception:
543
+ existing = []
544
+ from datetime import datetime, timezone
545
+ now_iso = datetime.now(timezone.utc).isoformat()
546
+ if existing:
547
+ report.setdefault("causal_repeated", []).append(existing[0].id)
548
+ return
549
+ from apsimo.world_model.relationships import WorldRelationship
550
+ create_conf = min(_CAUSAL_CREATE_CEILING, conf)
551
+ created = await self._store.upsert_relationship(WorldRelationship(
552
+ id="", source_id=src_id, target_id=tgt_id,
553
+ relationship_type=rel, confidence=create_conf,
554
+ properties={"evidence": evidence[:300],
555
+ "extraction": "llm_causal",
556
+ "last_support_at": now_iso}))
557
+ self._journal_write(
558
+ f"causal {src_id} -{rel}-> {tgt_id}", create_conf, src_id)
559
+ except Exception:
560
+ logger.debug("llm-extract causal upsert failed", exc_info=True)
561
+
562
+ def _journal_write(self, description: str, conf: float, ref: str) -> None:
563
+ if self._journal is None:
564
+ return
565
+ try:
566
+ self._journal.record(
567
+ "world_model", description,
568
+ reasoning="LLM extraction from recent conversation memories",
569
+ confidence=conf, reversibility="reversible",
570
+ decision="acted", ref=str(ref))
571
+ except Exception:
572
+ pass
@@ -0,0 +1,5 @@
1
+ """Neo4j schema definitions and backend for the Colony World Model."""
2
+
3
+ from apsimo.world_model.neo4j.backend import Neo4jBackend
4
+
5
+ __all__ = ["Neo4jBackend"]