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,547 @@
1
+ """MetaLearner - orchestrates cognitive performance tracking and self-improvement."""
2
+ from __future__ import annotations
3
+
4
+ import json
5
+ from dataclasses import dataclass
6
+ from typing import Callable, List, Optional, Dict, Any, TYPE_CHECKING
7
+ from datetime import datetime, timezone
8
+ import time
9
+
10
+ from .types import GapSeverity
11
+
12
+ if TYPE_CHECKING:
13
+ from apsimo.vector.embedder import EmbeddingPipeline
14
+ from apsimo.vector.store import VectorStore
15
+
16
+
17
+ @dataclass
18
+ class CycleResult:
19
+ """Result of a complete cognition cycle."""
20
+ cpi: Optional["CognitivePerformanceIndex"]
21
+ gaps: List["Gap"]
22
+ adjustments: List["Adjustment"]
23
+ duration_ms: float
24
+ errors: List[str]
25
+
26
+ @property
27
+ def has_critical_gaps(self) -> bool:
28
+ return any(g.severity == GapSeverity.CRITICAL for g in self.gaps) if self.gaps else False
29
+
30
+
31
+ @dataclass
32
+ class MetaLearnerConfig:
33
+ """Configuration for MetaLearner behavior."""
34
+ metrics_lookback_days: int = 7
35
+ min_cpi_threshold: float = 50.0
36
+ max_adjustments_per_cycle: int = 3
37
+ persist_to_graph: bool = True
38
+
39
+
40
+ logger = __import__('logging').getLogger(__name__)
41
+
42
+
43
+ class MetaLearner:
44
+ """Orchestrates cognitive performance tracking and self-improvement."""
45
+
46
+ def __init__(self, graph: "ColonyGraph", config: Optional[MetaLearnerConfig] = None):
47
+ self.graph = graph
48
+ self.config = config or MetaLearnerConfig()
49
+
50
+ # Sub-components (loaded lazily)
51
+ self._metrics_collector = None
52
+ self._performance_index = None
53
+ self._gap_detector = None
54
+ self._strategy_adjuster = None
55
+
56
+ # Optional FeedbackStore for consuming user corrections
57
+ self._feedback_store: Optional[Any] = None
58
+
59
+ # Vector store + embedder for cognition pattern matching (§6.2)
60
+ self._vector_store: Optional["VectorStore"] = None
61
+ self._embedder: Optional["EmbeddingPipeline"] = None
62
+
63
+ # Hooks for external metric recording
64
+ self._metric_hooks: List[Callable[..., None]] = []
65
+
66
+ # Counter incremented on each persistence failure for observability
67
+ self._persist_errors: int = 0
68
+
69
+ # Dedup / throttle state
70
+ self._last_persisted_cpi: Optional[float] = None # overall score of last persisted CPI
71
+ self._cycle_count: int = 0 # total run_cycle() calls; persist summary every 10th
72
+ self._open_gap_types: set = set() # gap_type values with an open Gap node in graph
73
+
74
+ @property
75
+ def is_fully_wired(self) -> bool:
76
+ """Check if all sub-components are available."""
77
+ return all([
78
+ self._performance_index is not None,
79
+ self._gap_detector is not None,
80
+ self._strategy_adjuster is not None,
81
+ ])
82
+
83
+ @property
84
+ def available_components(self) -> List[str]:
85
+ """List which sub-components are available."""
86
+ components = []
87
+ if self._metrics_collector:
88
+ components.append("metrics_collector")
89
+ if self._performance_index:
90
+ components.append("performance_index")
91
+ if self._gap_detector:
92
+ components.append("gap_detector")
93
+ if self._strategy_adjuster:
94
+ components.append("strategy_adjuster")
95
+ return components
96
+
97
+ # Component setters for dependency injection
98
+ def set_metrics_collector(self, collector: "MetricsCollector") -> None:
99
+ self._metrics_collector = collector
100
+
101
+ def set_performance_index(self, computer: "PerformanceIndexComputer") -> None:
102
+ self._performance_index = computer
103
+
104
+ def set_gap_detector(self, detector: "GapDetector") -> None:
105
+ self._gap_detector = detector
106
+
107
+ def set_strategy_adjuster(self, adjuster: "StrategyAdjuster") -> None:
108
+ self._strategy_adjuster = adjuster
109
+
110
+ def set_feedback_store(self, store: Any) -> None:
111
+ """Wire a FeedbackStore so MetaLearner consumes user corrections each cycle."""
112
+ self._feedback_store = store
113
+
114
+ def set_vector_store(self, store: "VectorStore", embedder: "EmbeddingPipeline") -> None:
115
+ """Wire vector store + embedder for cognition pattern matching."""
116
+ self._vector_store = store
117
+ self._embedder = embedder
118
+
119
+ def register_metric_hook(self, hook: Callable[..., None]) -> None:
120
+ """Register a callback to receive metrics."""
121
+ self._metric_hooks.append(hook)
122
+
123
+ async def record_metric(self, metric_type: str, value: float, domain: str = "general") -> None:
124
+ """Record a metric value."""
125
+ if self._metrics_collector:
126
+ await self._metrics_collector.record(metric_type, value, domain)
127
+
128
+ # Also notify hooks
129
+ for hook in self._metric_hooks:
130
+ try:
131
+ hook(metric_type, value, domain)
132
+ except Exception as exc:
133
+ logger.warning(
134
+ "MetaLearner: metric hook %r raised (hook will be skipped): %s",
135
+ getattr(hook, "__name__", hook),
136
+ exc,
137
+ )
138
+
139
+ async def evaluate(self) -> "CognitivePerformanceIndex":
140
+ """Compute current Cognitive Performance Index."""
141
+ if not self._performance_index:
142
+ logger.warning("MetaLearner.evaluate: PerformanceIndexComputer not wired — CPI unavailable")
143
+ from apsimo.intelligence.cognition.performance_index import CognitivePerformanceIndex, CPIComponent
144
+ now = datetime.now(timezone.utc)
145
+ default_comp = CPIComponent(
146
+ name="unavailable", score=0.0, trend="unavailable",
147
+ metrics={"available": False, "deprecated": True},
148
+ available=False)
149
+ return CognitivePerformanceIndex(
150
+ retrieval=default_comp, prediction=default_comp,
151
+ goal_progress=default_comp, tool_efficiency=default_comp,
152
+ initiative=default_comp, response_quality=default_comp,
153
+ overall=0.0, computed_at=now, deprecated=True,
154
+ available_components=[],
155
+ )
156
+
157
+ cpi = await self._performance_index.compute(self._metrics_collector)
158
+
159
+ # Persist to graph
160
+ if self.config.persist_to_graph:
161
+ await self._persist_cpi(cpi)
162
+
163
+ return cpi
164
+
165
+ async def detect_gaps(self, cpi: Optional["CognitivePerformanceIndex"] = None) -> List["Gap"]:
166
+ """Detect performance gaps from CPI."""
167
+ if not self._gap_detector:
168
+ raise RuntimeError("GapDetector not wired")
169
+
170
+ if cpi is None:
171
+ cpi = await self.evaluate()
172
+
173
+ gaps = self._gap_detector.detect(cpi)
174
+
175
+ # Sort by severity
176
+ severity_order = {GapSeverity.CRITICAL: 0, GapSeverity.WARNING: 1, GapSeverity.INFO: 2}
177
+ gaps.sort(key=lambda g: severity_order.get(g.severity, 99))
178
+
179
+ # Persist gaps to graph
180
+ if self.config.persist_to_graph:
181
+ for gap in gaps:
182
+ await self._persist_gap(gap)
183
+
184
+ return gaps
185
+
186
+ async def adjust_strategy(self, gap: "Gap") -> "Adjustment":
187
+ """Generate and apply strategy adjustment for a gap."""
188
+ if not self._strategy_adjuster:
189
+ raise RuntimeError("StrategyAdjuster not wired")
190
+
191
+ adjustment = await self._strategy_adjuster.generate(gap)
192
+
193
+ # Apply the adjustment
194
+ await self._strategy_adjuster.apply(adjustment)
195
+
196
+ # Persist to graph — pass the source gap for relationship creation
197
+ if self.config.persist_to_graph:
198
+ await self._persist_adjustment(adjustment, gap)
199
+
200
+ return adjustment
201
+
202
+ async def run_cycle(self) -> CycleResult:
203
+ """Execute full cognition cycle: evaluate → detect → adjust."""
204
+ self._cycle_count += 1
205
+ start_time = time.time()
206
+ cpi = None
207
+ gaps = []
208
+ adjustments = []
209
+ errors = []
210
+
211
+ try:
212
+ # 1. Evaluate current performance
213
+ cpi = await self.evaluate()
214
+ except Exception as e:
215
+ msg = f"Evaluation failed: {e}"
216
+ logger.warning("MetaLearner.run_cycle: %s", msg)
217
+ errors.append(msg)
218
+
219
+ try:
220
+ # 2. Detect gaps
221
+ if cpi:
222
+ gaps = await self.detect_gaps(cpi)
223
+ except Exception as e:
224
+ msg = f"Gap detection failed: {e}"
225
+ logger.warning("MetaLearner.run_cycle: %s", msg)
226
+ errors.append(msg)
227
+
228
+ try:
229
+ # 2b. Process user corrections from FeedbackStore
230
+ if self._feedback_store is not None:
231
+ await self._process_feedback_corrections()
232
+ except Exception as e:
233
+ msg = f"Feedback processing failed: {e}"
234
+ logger.warning("MetaLearner.run_cycle: %s", msg)
235
+ errors.append(msg)
236
+
237
+ try:
238
+ # 3. Adjust strategy for each gap (up to max)
239
+ if gaps and self._strategy_adjuster:
240
+ for gap in gaps[:self.config.max_adjustments_per_cycle]:
241
+ try:
242
+ adjustment = await self.adjust_strategy(gap)
243
+ adjustments.append(adjustment)
244
+ except Exception as e:
245
+ msg = f"Adjustment failed for {gap.gap_type}: {e}"
246
+ logger.warning("MetaLearner.run_cycle: %s", msg)
247
+ errors.append(msg)
248
+ except Exception as e:
249
+ msg = f"Strategy adjustment phase failed: {e}"
250
+ logger.warning("MetaLearner.run_cycle: %s", msg)
251
+ errors.append(msg)
252
+
253
+ # Persist cycle summary
254
+ if self.config.persist_to_graph:
255
+ await self._persist_cycle_summary(cpi, gaps, adjustments, errors)
256
+
257
+ # Store cycle in vector store for pattern matching (§6.2)
258
+ await self._store_cognition_vector(cpi, gaps, adjustments)
259
+
260
+ duration_ms = (time.time() - start_time) * 1000
261
+ return CycleResult(
262
+ cpi=cpi,
263
+ gaps=gaps,
264
+ adjustments=adjustments,
265
+ duration_ms=duration_ms,
266
+ errors=errors,
267
+ )
268
+
269
+ async def _process_feedback_corrections(self) -> None:
270
+ """Pull unapplied corrections from FeedbackStore and record as metrics."""
271
+ if self._feedback_store is None:
272
+ return
273
+ corrections = self._feedback_store.get_unapplied(limit=50)
274
+ if not corrections:
275
+ return
276
+ metric_map = {
277
+ "factual": ("factual_error_rate", 1.0),
278
+ "tone": ("tone_error_rate", 1.0),
279
+ "action": ("action_error_rate", 1.0),
280
+ "preference": ("preference_mismatch_rate", 1.0),
281
+ }
282
+ applied_ids = []
283
+ for correction in corrections:
284
+ metric_type, value = metric_map.get(
285
+ correction.correction_type, ("correction_rate", 1.0)
286
+ )
287
+ await self.record_metric(metric_type, value, domain="feedback")
288
+ applied_ids.append(correction.correction_id)
289
+ self._feedback_store.mark_applied(applied_ids)
290
+
291
+ async def _persist_cpi(self, cpi: "CognitivePerformanceIndex") -> None:
292
+ """Persist CPI components as CognitiveMetric nodes (MERGE, only on >1.0 change)."""
293
+ overall = getattr(cpi, "overall", 0.0)
294
+ # Skip if change from last persisted value is ≤1.0 (avoids 53K identical nodes)
295
+ if self._last_persisted_cpi is not None and abs(overall - self._last_persisted_cpi) <= 1.0:
296
+ return
297
+ self._last_persisted_cpi = overall
298
+ try:
299
+ async with self.graph.driver.session(database=self.graph.database) as session:
300
+ today = datetime.now().strftime("%Y-%m-%d")
301
+ for component_name in ["retrieval", "prediction", "goal_progress", "tool_efficiency", "initiative", "response_quality"]:
302
+ component = getattr(cpi, component_name, None)
303
+ if component and getattr(component, "available", True):
304
+ await session.run("""
305
+ MERGE (m:CognitiveMetric {metric_type: $component_name, day: $day})
306
+ SET m.value = $score,
307
+ m.domain = 'cpi',
308
+ m.recorded_at = datetime()
309
+ """, component_name=component_name, day=today, score=component.score)
310
+ except Exception as exc:
311
+ self._persist_errors += 1
312
+ logger.warning("MetaLearner: failed to persist CPI to graph: %s", exc)
313
+
314
+ async def _persist_gap(self, gap: "Gap") -> None:
315
+ """Persist gap to graph (MERGE on gap_type+status='open', skip if already open)."""
316
+ gap_type_val = gap.gap_type.value if hasattr(gap.gap_type, "value") else str(gap.gap_type)
317
+ # Skip if we already have an open node for this gap type in this session
318
+ if gap_type_val in self._open_gap_types:
319
+ return
320
+ try:
321
+ async with self.graph.driver.session(database=self.graph.database) as session:
322
+ # Check for existing open gap of same type before creating
323
+ result = await session.run(
324
+ "MATCH (g:Gap {gap_type: $gap_type, status: 'open'}) RETURN count(g) AS cnt",
325
+ gap_type=gap_type_val,
326
+ )
327
+ record = await result.single()
328
+ if record and record["cnt"] > 0:
329
+ self._open_gap_types.add(gap_type_val)
330
+ return
331
+ await session.run("""
332
+ MERGE (g:Gap {gap_type: $gap_type, status: 'open'})
333
+ ON CREATE SET
334
+ g.id = randomUUID(),
335
+ g.severity = $severity,
336
+ g.component = $component,
337
+ g.description = $description,
338
+ g.diagnosed_at = datetime()
339
+ ON MATCH SET
340
+ g.severity = $severity,
341
+ g.updated_at = datetime()
342
+ """, gap_type=gap_type_val, severity=gap.severity.value,
343
+ component=gap.component, description=gap.description)
344
+ self._open_gap_types.add(gap_type_val)
345
+ except Exception as exc:
346
+ self._persist_errors += 1
347
+ logger.warning(
348
+ "MetaLearner: failed to persist gap %r to graph: %s",
349
+ gap_type_val, exc,
350
+ )
351
+
352
+ async def _persist_adjustment(self, adjustment: "Adjustment", gap: Optional["Gap"] = None) -> None:
353
+ """Persist adjustment to graph and link to its source gap."""
354
+ gap_type_val = None
355
+ if gap is not None:
356
+ gap_type_val = gap.gap_type.value if hasattr(gap.gap_type, "value") else str(gap.gap_type)
357
+ try:
358
+ async with self.graph.driver.session(database=self.graph.database) as session:
359
+ if gap_type_val:
360
+ # Only create adjustment + link if the gap has no resolver yet
361
+ await session.run("""
362
+ MATCH (g:Gap {gap_type: $gap_type, status: 'open'})
363
+ WHERE NOT exists((g)-[:RESOLVED_BY]->(:Adjustment))
364
+ CREATE (a:Adjustment {
365
+ id: randomUUID(),
366
+ adjustment_type: $adj_type,
367
+ hypothesis: $hypothesis,
368
+ status: 'applied',
369
+ applied_at: datetime()
370
+ })
371
+ CREATE (g)-[:RESOLVED_BY]->(a)
372
+ """, gap_type=gap_type_val, adj_type=adjustment.adjustment_type,
373
+ hypothesis=adjustment.hypothesis)
374
+ else:
375
+ await session.run("""
376
+ CREATE (a:Adjustment {
377
+ id: randomUUID(),
378
+ adjustment_type: $adj_type,
379
+ hypothesis: $hypothesis,
380
+ status: 'applied',
381
+ applied_at: datetime()
382
+ })
383
+ """, adj_type=adjustment.adjustment_type, hypothesis=adjustment.hypothesis)
384
+ except Exception as exc:
385
+ self._persist_errors += 1
386
+ logger.warning(
387
+ "MetaLearner: failed to persist adjustment %r to graph: %s",
388
+ adjustment.adjustment_type, exc,
389
+ )
390
+
391
+ async def _persist_cycle_summary(
392
+ self,
393
+ cpi: Optional["CognitivePerformanceIndex"],
394
+ gaps: List["Gap"],
395
+ adjustments: List["Adjustment"],
396
+ errors: List[str]
397
+ ) -> None:
398
+ """Persist cycle summary every 10th cycle to reduce graph churn."""
399
+ if self._cycle_count % 10 != 0:
400
+ return
401
+ try:
402
+ overall_score = cpi.overall if cpi else 0.0
403
+ gap_types = [
404
+ g.gap_type.value if hasattr(g.gap_type, "value") else str(g.gap_type)
405
+ for g in gaps
406
+ ]
407
+ async with self.graph.driver.session(database=self.graph.database) as session:
408
+ result = await session.run("""
409
+ CREATE (c:CognitionCycle {
410
+ id: randomUUID(),
411
+ cycle_number: $cycle_num,
412
+ overall_cpi: $overall,
413
+ gap_count: $gap_count,
414
+ adjustment_count: $adj_count,
415
+ error_count: $error_count,
416
+ completed_at: datetime()
417
+ })
418
+ RETURN c.id AS cycle_id
419
+ """, cycle_num=self._cycle_count, overall=overall_score,
420
+ gap_count=len(gaps), adj_count=len(adjustments),
421
+ error_count=len(errors))
422
+ record = await result.single()
423
+ cycle_id = record["cycle_id"] if record else None
424
+
425
+ # Link to detected gaps
426
+ if cycle_id and gap_types:
427
+ for gap_type_val in gap_types:
428
+ await session.run("""
429
+ MATCH (c:CognitionCycle {id: $cycle_id})
430
+ MATCH (g:Gap {gap_type: $gap_type})
431
+ MERGE (c)-[:DETECTED]->(g)
432
+ """, cycle_id=cycle_id, gap_type=gap_type_val)
433
+ except Exception as exc:
434
+ self._persist_errors += 1
435
+ logger.warning("MetaLearner: failed to persist cycle summary to graph: %s", exc)
436
+
437
+ async def _cleanup_old_metrics(self) -> None:
438
+ """Delete CognitiveMetric nodes older than 7 days (TTL cleanup)."""
439
+ try:
440
+ async with self.graph.driver.session(database=self.graph.database) as session:
441
+ result = await session.run("""
442
+ MATCH (m:CognitiveMetric)
443
+ WHERE m.recorded_at < datetime() - duration({days: 7})
444
+ WITH m LIMIT 1000
445
+ DETACH DELETE m
446
+ RETURN count(m) AS deleted
447
+ """)
448
+ record = await result.single()
449
+ deleted = record["deleted"] if record else 0
450
+ if deleted:
451
+ logger.info("MetaLearner TTL: deleted %d old CognitiveMetric nodes", deleted)
452
+ except Exception as exc:
453
+ logger.debug("MetaLearner TTL cleanup error (non-fatal): %s", exc)
454
+
455
+ # ------------------------------------------------------------------
456
+ # Cognition pattern matching via vector store (§6.2)
457
+ # ------------------------------------------------------------------
458
+
459
+ def _build_cycle_summary(
460
+ self,
461
+ cpi: Optional["CognitivePerformanceIndex"],
462
+ gaps: List["Gap"],
463
+ adjustments: List["Adjustment"],
464
+ ) -> str:
465
+ """Generate a text summary of a cognition cycle for embedding."""
466
+ parts = []
467
+ if cpi:
468
+ parts.append(f"CPI overall={getattr(cpi, 'overall', 0.0):.1f}")
469
+ if gaps:
470
+ gap_strs = [
471
+ f"{g.gap_type.value if hasattr(g.gap_type, 'value') else g.gap_type}({g.severity.value})"
472
+ for g in gaps
473
+ ]
474
+ parts.append(f"Gaps: {', '.join(gap_strs)}")
475
+ if adjustments:
476
+ adj_strs = [a.adjustment_type for a in adjustments]
477
+ parts.append(f"Adjustments: {', '.join(adj_strs)}")
478
+ return "; ".join(parts) if parts else "Empty cognition cycle"
479
+
480
+ async def _store_cognition_vector(
481
+ self,
482
+ cpi: Optional["CognitivePerformanceIndex"],
483
+ gaps: List["Gap"],
484
+ adjustments: List["Adjustment"],
485
+ ) -> None:
486
+ """Embed and store a cognition cycle summary in the COGNITION collection."""
487
+ if self._vector_store is None or self._embedder is None:
488
+ return
489
+ try:
490
+ from apsimo.vector.collections import Collection
491
+ import uuid
492
+
493
+ summary = self._build_cycle_summary(cpi, gaps, adjustments)
494
+ vector = await self._embedder.embed(summary)
495
+ cycle_id = str(uuid.uuid4())
496
+
497
+ await self._vector_store.add(
498
+ collection=Collection.COGNITION,
499
+ id=cycle_id,
500
+ text=summary,
501
+ vector=vector,
502
+ metadata={
503
+ "cycle_id": cycle_id,
504
+ "cpi_score": getattr(cpi, "overall", 0.0) if cpi else 0.0,
505
+ "gaps": [
506
+ g.gap_type.value if hasattr(g.gap_type, "value") else str(g.gap_type)
507
+ for g in gaps
508
+ ],
509
+ "adjustments": [a.adjustment_type for a in adjustments],
510
+ "cycle_at": time.time(),
511
+ },
512
+ )
513
+ except Exception as exc:
514
+ logger.warning("MetaLearner: failed to store cognition vector: %s", exc)
515
+
516
+ async def find_similar_cycles(self, limit: int = 5) -> List[Dict[str, Any]]:
517
+ """Query the COGNITION collection for similar past cycles.
518
+
519
+ Called at the start of a new cycle to provide pattern context to
520
+ StrategyAdjuster. Returns empty list if vector store is not
521
+ configured.
522
+ """
523
+ if self._vector_store is None or self._embedder is None:
524
+ return []
525
+ try:
526
+ from apsimo.vector.collections import Collection
527
+
528
+ # Use the most recent cycle summary as the query
529
+ count = await self._vector_store.count(Collection.COGNITION)
530
+ if count == 0:
531
+ return []
532
+
533
+ # Build a query from current state (lightweight — just embed a status string)
534
+ query_text = "recent cognition cycle performance gaps and adjustments"
535
+ query_vector = await self._embedder.embed(query_text)
536
+ results = await self._vector_store.search(
537
+ collection=Collection.COGNITION,
538
+ query_vector=query_vector,
539
+ limit=limit,
540
+ )
541
+ return [
542
+ {"text": r.text, "score": r.score, "metadata": r.metadata}
543
+ for r in results
544
+ ]
545
+ except Exception as exc:
546
+ logger.warning("MetaLearner: failed to query similar cycles: %s", exc)
547
+ return []