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,25 @@
1
+ """World Model relationship dataclass."""
2
+
3
+ from dataclasses import dataclass, field
4
+ from typing import Any, Dict, Optional
5
+
6
+
7
+ @dataclass
8
+ class WorldRelationship:
9
+ """A directed relationship between two world model entities."""
10
+ id: str # wr-<timestamp>-<random7>
11
+ source_id: str
12
+ target_id: str
13
+ relationship_type: str # WM_ prefixed type from constants
14
+ confidence: float = 0.5 # 0.0–1.0
15
+ valid_from: Optional[str] = None # ISO8601; when relationship began
16
+ valid_to: Optional[str] = None # ISO8601; None = currently active
17
+ properties: Dict[str, Any] = field(default_factory=dict)
18
+ source_observation_id: Optional[str] = None # extraction provenance
19
+ created_at: Optional[str] = None
20
+ updated_at: Optional[str] = None
21
+
22
+ @property
23
+ def is_active(self) -> bool:
24
+ """True if the relationship has no end date (currently active)."""
25
+ return self.valid_to is None
@@ -0,0 +1,13 @@
1
+ """Entity resolution and merge workflow."""
2
+ from .entity_resolver import EntityResolver, ResolutionAction, ResolutionResult
3
+ from .merge_workflow import MergeWorkflow, MergeProposal
4
+ from .merge_audit import MergeAuditRecord
5
+
6
+ __all__ = [
7
+ "EntityResolver",
8
+ "ResolutionAction",
9
+ "ResolutionResult",
10
+ "MergeWorkflow",
11
+ "MergeProposal",
12
+ "MergeAuditRecord",
13
+ ]
@@ -0,0 +1,232 @@
1
+ """Entity resolver: resolves extraction candidates against the world model.
2
+
3
+ Resolution priority order (first match wins):
4
+ 1. Exact external ID match → MERGE (confidence ≥ 0.98)
5
+ 2. Exact canonical name match (case-insensitive) → MERGE (confidence ≥ 0.90)
6
+ 3. Alias list match → MERGE (confidence ≥ 0.85)
7
+ 4. High string similarity (≥ 0.92) + same type → PROPOSE MERGE
8
+ 5. Property match (domain, email, ticker) → MERGE (confidence ≥ 0.90)
9
+ 6. Moderate similarity (0.75–0.92) + same type → FLAG
10
+ 7. No match → CREATE NEW ENTITY
11
+ """
12
+
13
+ from dataclasses import dataclass
14
+ from enum import Enum
15
+ from typing import List, Optional, TYPE_CHECKING
16
+
17
+ if TYPE_CHECKING:
18
+ from ..store import WorldModelStore
19
+ from ..extraction.conversation_extractor import ExtractionCandidate
20
+ from ..entities import BaseEntity
21
+
22
+
23
+ def _jaro_winkler(s1: str, s2: str) -> float:
24
+ """Compute Jaro-Winkler similarity between two strings.
25
+
26
+ Returns a value in [0.0, 1.0].
27
+ Pure Python implementation; no external dependencies.
28
+ """
29
+ if s1 == s2:
30
+ return 1.0
31
+ if not s1 or not s2:
32
+ return 0.0
33
+
34
+ s1 = s1.lower()
35
+ s2 = s2.lower()
36
+ len1, len2 = len(s1), len(s2)
37
+
38
+ match_distance = max(len1, len2) // 2 - 1
39
+ match_distance = max(match_distance, 0)
40
+
41
+ s1_matches = [False] * len1
42
+ s2_matches = [False] * len2
43
+
44
+ matches = 0
45
+ transpositions = 0
46
+
47
+ for i in range(len1):
48
+ start = max(0, i - match_distance)
49
+ end = min(i + match_distance + 1, len2)
50
+ for j in range(start, end):
51
+ if s2_matches[j] or s1[i] != s2[j]:
52
+ continue
53
+ s1_matches[i] = True
54
+ s2_matches[j] = True
55
+ matches += 1
56
+ break
57
+
58
+ if matches == 0:
59
+ return 0.0
60
+
61
+ k = 0
62
+ for i in range(len1):
63
+ if not s1_matches[i]:
64
+ continue
65
+ while not s2_matches[k]:
66
+ k += 1
67
+ if s1[i] != s2[k]:
68
+ transpositions += 1
69
+ k += 1
70
+
71
+ jaro = (
72
+ matches / len1 + matches / len2 + (matches - transpositions / 2) / matches
73
+ ) / 3
74
+
75
+ # Winkler prefix bonus (up to 4 chars)
76
+ prefix = 0
77
+ for i in range(min(4, len1, len2)):
78
+ if s1[i] == s2[i]:
79
+ prefix += 1
80
+ else:
81
+ break
82
+
83
+ return jaro + prefix * 0.1 * (1 - jaro)
84
+
85
+
86
+ class ResolutionAction(str, Enum):
87
+ CREATE = "create" # no match found; create new entity
88
+ MERGE = "merge" # confident match; merge immediately
89
+ PROPOSE_MERGE = "propose" # uncertain match; queue for review
90
+ FLAG = "flag" # possible match; log and continue as new
91
+
92
+
93
+ @dataclass
94
+ class ResolutionResult:
95
+ action: ResolutionAction
96
+ matched_entity_id: Optional[str] # existing entity ID if matched
97
+ match_confidence: float
98
+ match_reason: str # human-readable explanation
99
+ candidate_id: Optional[str] = None # new entity ID if CREATE
100
+
101
+
102
+ class EntityResolver:
103
+ """Resolves extraction candidates against existing world model entities."""
104
+
105
+ def __init__(
106
+ self,
107
+ store: "WorldModelStore",
108
+ auto_merge_threshold: float = 0.85,
109
+ propose_merge_threshold: float = 0.70,
110
+ similarity_auto_merge: float = 0.92,
111
+ similarity_propose: float = 0.75,
112
+ ) -> None:
113
+ self._store = store
114
+ self._auto_merge_threshold = auto_merge_threshold
115
+ self._propose_threshold = propose_merge_threshold
116
+ self._sim_auto = similarity_auto_merge
117
+ self._sim_propose = similarity_propose
118
+
119
+ async def resolve(
120
+ self,
121
+ candidate: ExtractionCandidate,
122
+ entity_type: str,
123
+ ) -> ResolutionResult:
124
+ """Attempt to resolve a candidate to an existing entity.
125
+
126
+ Runs the priority-ordered resolution algorithm.
127
+ """
128
+ text = candidate.text.strip()
129
+
130
+ # ── Step 1: External ID match ─────────────────────────────────────
131
+ if "@" in text:
132
+ existing = await self._store.get_entity_by_external_id("email", text)
133
+ if existing:
134
+ return ResolutionResult(
135
+ action=ResolutionAction.MERGE,
136
+ matched_entity_id=existing.id,
137
+ match_confidence=0.98,
138
+ match_reason="Exact external ID match (email)",
139
+ )
140
+
141
+ # ── Step 2: Exact canonical name match ────────────────────────────
142
+ candidates = await self._store.find_entities(
143
+ query=text, entity_type=entity_type, min_confidence=0.0, limit=50
144
+ )
145
+ for entity in candidates:
146
+ if entity.name.lower() == text.lower():
147
+ return ResolutionResult(
148
+ action=ResolutionAction.MERGE,
149
+ matched_entity_id=entity.id,
150
+ match_confidence=0.90,
151
+ match_reason="Exact canonical name match",
152
+ )
153
+
154
+ # ── Step 3: Alias list match ──────────────────────────────────────
155
+ for entity in candidates:
156
+ for alias in entity.aliases:
157
+ if alias.lower() == text.lower():
158
+ return ResolutionResult(
159
+ action=ResolutionAction.MERGE,
160
+ matched_entity_id=entity.id,
161
+ match_confidence=0.85,
162
+ match_reason=f"Alias match: '{alias}'",
163
+ )
164
+
165
+ # ── Steps 4 & 6: String similarity ───────────────────────────────
166
+ best_sim = 0.0
167
+ best_entity: Optional[BaseEntity] = None
168
+ for entity in candidates:
169
+ sim = await self.compute_string_similarity(text, entity.name)
170
+ if sim > best_sim:
171
+ best_sim = sim
172
+ best_entity = entity
173
+
174
+ if best_entity and best_sim >= self._sim_auto:
175
+ return ResolutionResult(
176
+ action=ResolutionAction.PROPOSE_MERGE,
177
+ matched_entity_id=best_entity.id,
178
+ match_confidence=best_sim,
179
+ match_reason=f"High string similarity ({best_sim:.2f})",
180
+ )
181
+
182
+ if best_entity and best_sim >= self._sim_propose:
183
+ return ResolutionResult(
184
+ action=ResolutionAction.FLAG,
185
+ matched_entity_id=best_entity.id,
186
+ match_confidence=best_sim,
187
+ match_reason=f"Moderate string similarity ({best_sim:.2f})",
188
+ )
189
+
190
+ # ── Step 7: No match — create new ────────────────────────────────
191
+ return ResolutionResult(
192
+ action=ResolutionAction.CREATE,
193
+ matched_entity_id=None,
194
+ match_confidence=0.0,
195
+ match_reason="No match found",
196
+ )
197
+
198
+ async def compute_string_similarity(self, a: str, b: str) -> float:
199
+ """Jaro-Winkler similarity between two name strings."""
200
+ return _jaro_winkler(a, b)
201
+
202
+ async def resolve_entity_against_store(
203
+ self,
204
+ entity: BaseEntity,
205
+ ) -> ResolutionResult:
206
+ """Resolve a full BaseEntity against the store.
207
+
208
+ More thorough than ExtractionCandidate resolution: checks external IDs,
209
+ domain, ticker, etc.
210
+ """
211
+ # External ID matches
212
+ for key, value in entity.external_ids.items():
213
+ existing = await self._store.get_entity_by_external_id(key, value)
214
+ if existing and existing.id != entity.id:
215
+ return ResolutionResult(
216
+ action=ResolutionAction.MERGE,
217
+ matched_entity_id=existing.id,
218
+ match_confidence=0.98,
219
+ match_reason=f"Exact external ID match ({key}={value})",
220
+ )
221
+
222
+ # Name-based resolution
223
+ from ..extraction.conversation_extractor import ExtractionCandidate
224
+ candidate = ExtractionCandidate(
225
+ text=entity.name,
226
+ entity_type=entity.entity_type,
227
+ start_char=0,
228
+ end_char=len(entity.name),
229
+ confidence=entity.confidence,
230
+ context_window="",
231
+ )
232
+ return await self.resolve(candidate, entity.entity_type)
@@ -0,0 +1,16 @@
1
+ """Merge audit record dataclass."""
2
+
3
+ from dataclasses import dataclass
4
+ from typing import Optional
5
+
6
+
7
+ @dataclass
8
+ class MergeAuditRecord:
9
+ id: str # ma-<timestamp>-<random7>
10
+ surviving_id: str # entity that survived
11
+ retired_id: str # entity that was merged away
12
+ relationships_repointed: int
13
+ properties_updated: int
14
+ executed_by: str # "auto" | "owner_approved"
15
+ merge_proposal_id: Optional[str]
16
+ created_at: str
@@ -0,0 +1,117 @@
1
+ """Merge workflow: automatic and proposed entity merges."""
2
+
3
+ from dataclasses import dataclass, field
4
+ from typing import Any, Dict, Optional, TYPE_CHECKING
5
+
6
+ if TYPE_CHECKING:
7
+ from ..store import WorldModelStore
8
+
9
+
10
+ @dataclass
11
+ class MergeProposal:
12
+ id: str # mp-<timestamp>-<random7>
13
+ candidate_id: str # the incoming entity
14
+ existing_id: str # the entity to merge into
15
+ match_confidence: float
16
+ match_reason: str
17
+ evidence: Dict[str, Any] = field(default_factory=dict)
18
+ status: str = "pending" # "pending" | "approved" | "rejected" | "expired"
19
+ created_at: Optional[str] = None
20
+ resolved_at: Optional[str] = None
21
+
22
+
23
+ class MergeWorkflow:
24
+ """Manages proposed and automatic entity merges."""
25
+
26
+ def __init__(self, store: "WorldModelStore") -> None:
27
+ self._store = store
28
+
29
+ async def propose_merge(
30
+ self,
31
+ candidate_id: str,
32
+ existing_id: str,
33
+ confidence: float,
34
+ reason: str,
35
+ evidence: Optional[Dict[str, Any]] = None,
36
+ ) -> MergeProposal:
37
+ """Create a merge proposal and enqueue for owner review."""
38
+ proposal_id = await self._store._backend.create_merge_proposal(
39
+ candidate_id=candidate_id,
40
+ existing_id=existing_id,
41
+ confidence=confidence,
42
+ reason=reason,
43
+ evidence=evidence,
44
+ )
45
+ return MergeProposal(
46
+ id=proposal_id,
47
+ candidate_id=candidate_id,
48
+ existing_id=existing_id,
49
+ match_confidence=confidence,
50
+ match_reason=reason,
51
+ evidence=evidence or {},
52
+ )
53
+
54
+ async def execute_merge(self, proposal_id: str) -> None:
55
+ """Execute an approved merge. The candidate entity is retired.
56
+
57
+ All relationships pointing to candidate_id are repointed to
58
+ existing_id. A merge audit record is created.
59
+ """
60
+ proposal = await self._store._backend.get_merge_proposal(proposal_id)
61
+ if not proposal:
62
+ raise ValueError(f"Merge proposal not found: {proposal_id}")
63
+ if proposal["status"] not in ("pending", "approved"):
64
+ raise ValueError(
65
+ f"Cannot execute merge in status '{proposal['status']}'"
66
+ )
67
+
68
+ await self._store._backend.execute_merge(
69
+ surviving_id=proposal["existing_id"],
70
+ retired_id=proposal["candidate_id"],
71
+ executed_by="owner_approved",
72
+ proposal_id=proposal_id,
73
+ )
74
+ await self._store._backend.update_merge_proposal_status(
75
+ proposal_id, "approved"
76
+ )
77
+
78
+ async def execute_auto_merge(
79
+ self,
80
+ surviving_id: str,
81
+ retired_id: str,
82
+ ) -> str:
83
+ """Execute an automatic high-confidence merge without owner review.
84
+
85
+ Returns the merge audit record ID.
86
+ """
87
+ return await self._store._backend.execute_merge(
88
+ surviving_id=surviving_id,
89
+ retired_id=retired_id,
90
+ executed_by="auto",
91
+ proposal_id=None,
92
+ )
93
+
94
+ async def reject_merge(self, proposal_id: str) -> None:
95
+ """Reject a merge proposal.
96
+
97
+ Both entities remain separate. The candidate's aliases list is
98
+ updated to avoid re-proposing the same merge.
99
+ """
100
+ proposal = await self._store._backend.get_merge_proposal(proposal_id)
101
+ if not proposal:
102
+ raise ValueError(f"Merge proposal not found: {proposal_id}")
103
+
104
+ await self._store._backend.update_merge_proposal_status(
105
+ proposal_id, "rejected"
106
+ )
107
+
108
+ # Add candidate's name as alias on existing to avoid re-proposing
109
+ candidate = await self._store.get_entity(proposal["candidate_id"])
110
+ if candidate:
111
+ await self._store.add_entity_alias(
112
+ proposal["existing_id"], candidate.name
113
+ )
114
+
115
+ async def get_pending_proposals(self) -> list:
116
+ """Return all pending merge proposals."""
117
+ return await self._store._backend.get_pending_merge_proposals()
@@ -0,0 +1,121 @@
1
+ """Existing world batch extraction driven by attributed canonical user sources."""
2
+ from contextlib import closing
3
+ from datetime import datetime, timedelta, timezone
4
+ import hashlib
5
+ import json
6
+ import re
7
+
8
+ from apsimo.turns.idempotency import canonical_turn_digest, source_message_hash
9
+ from apsimo.turns.source_attribution import is_invalidated
10
+ from .observations import canonical
11
+
12
+
13
+ # This first current-state producer abstains on dated/modally qualified
14
+ # messages. The canonical memory retains them for its existing valid-time
15
+ # parser; receipt time must never turn a historical/future claim into now.
16
+ _NONCURRENT = re.compile(r"\b(will|would|was|were|had|used to|tomorrow|yesterday|last|next|previously|formerly|"
17
+ r"soon|later|eventually|before|after|until|since|starting|ended|stopped|might|may|could|should|"
18
+ r"if|unless|not|never|isn't|aren't|doesn't|didn't|weren't|wasn't|"
19
+ r"monday|tuesday|wednesday|thursday|friday|saturday|sunday|"
20
+ r"january|february|march|april|june|july|august|september|october|november|december)\b|"
21
+ r"\b\d{4}-\d{2}-\d{2}\b|\?", re.I)
22
+ _PRESENT = re.compile(r"\b(is|are|runs|uses|has|needs|depends on)\b", re.I)
23
+
24
+
25
+ def current_source(ledger, source):
26
+ with closing(ledger._connect()) as conn:
27
+ row = conn.execute("SELECT contact_id,scope,messages_json FROM turn_sources WHERE turn_id=?",
28
+ (source['source_id'],)).fetchone()
29
+ return bool(row and row['contact_id'] == source['contact_id'] and row['scope'] == 'person'
30
+ and not is_invalidated(conn, source['source_id'])
31
+ and canonical_turn_digest(json.loads(row['messages_json'])) == source['source_version'])
32
+
33
+
34
+ def recent_batches(ledger, *, hours=24, limit=30):
35
+ """Same bounded batch job, with one participant per batch and exact lineage.
36
+
37
+ Only ordinary learning-admitted user sources are eligible. Historical
38
+ source-only imports and unattributed checkpoints remain source-only.
39
+ """
40
+ cutoff = (datetime.now(timezone.utc) - timedelta(hours=hours)).isoformat()
41
+ with closing(ledger._connect()) as conn:
42
+ rows = conn.execute('''SELECT s.* FROM turn_sources s JOIN source_claim_jobs j ON j.turn_id=s.turn_id
43
+ WHERE s.scope='person' AND julianday(s.ingested_at)>=julianday(?)
44
+ ORDER BY s.ingested_at DESC,s.turn_id LIMIT ?''', (cutoff, max(1, min(limit, 100)))).fetchall()
45
+ grouped = {}
46
+ for row in rows:
47
+ if is_invalidated(conn, row['turn_id']):
48
+ continue
49
+ messages = json.loads(row['messages_json'])
50
+ version = canonical_turn_digest(messages)
51
+ for message in messages:
52
+ if message.get('role') != 'user':
53
+ continue
54
+ content = message.get('content', '')
55
+ if isinstance(content, list):
56
+ content = '\n'.join(b.get('text', '') for b in content if isinstance(b, dict)
57
+ and b.get('type') in {'text', 'input_text'})
58
+ if not isinstance(content, str) or not content.strip():
59
+ continue
60
+ grouped.setdefault(row['contact_id'], []).append(dict(content=content[:600],
61
+ current_time_eligible=not bool(_NONCURRENT.search(content)),
62
+ source_id=row['turn_id'], source_version=version, contact_id=row['contact_id'],
63
+ message_hash=source_message_hash(row['session_id'], message),
64
+ observed_at=row['occurred_at'] or row['ingested_at'],
65
+ time_basis='occurred_at' if row['occurred_at'] else 'received_at'))
66
+ return [items[start:start+10] for items in grouped.values() for start in range(0, len(items), 10)]
67
+
68
+
69
+ async def record_reports(extractor, proposals, sources, name_to_id, mode, report):
70
+ """Quote-qualified reports, never sensor observations or proven properties."""
71
+ if not sources or extractor._source_ledger is None or mode != 'live':
72
+ return
73
+ report.setdefault('property_observations', [])
74
+ report.setdefault('property_skipped', 0)
75
+ for item in proposals[:25] if isinstance(proposals, list) else []:
76
+ if not isinstance(item, dict):
77
+ continue
78
+ name, key, quote = str(item.get('entity', '')).strip(), str(item.get('property', '')).strip(), item.get('evidence')
79
+ value = item.get('value')
80
+ eid = name_to_id.get(name.lower())
81
+ matches = [s for s in sources if isinstance(quote, str) and len(quote) >= 8 and quote in s['content']
82
+ and s['current_time_eligible'] and item.get('temporal_status') == 'current'
83
+ and _PRESENT.search(quote) and name.lower() in quote.lower()
84
+ and isinstance(value, str) and value.lower() in quote.lower()]
85
+ # Ambiguous identical quotations across messages need explicit source
86
+ # selection in a later extraction, rather than an invented attribution.
87
+ if not eid or not key or len(matches) != 1 or not current_source(extractor._source_ledger, matches[0]):
88
+ report['property_skipped'] += 1
89
+ continue
90
+ source = matches[0]
91
+ oid = 'wo-report-' + hashlib.sha256(canonical([source['source_id'], source['source_version'], source['contact_id'],
92
+ source['message_hash'], eid, key, quote]).encode()).hexdigest()
93
+ at = datetime.fromisoformat(source['observed_at'].replace('Z', '+00:00'))
94
+ try:
95
+ result = await extractor._store.record_property_observation(observation_id=oid, entity_id=eid,
96
+ property_key=key, value=value, kind='reported', producer=f"contact:{source['contact_id']}",
97
+ evidence_refs=[f"source:{source['source_id']}", f"message:{source['message_hash']}"],
98
+ source_refs=[dict(source_id=source['source_id'], source_version=source['source_version'])],
99
+ observed_at=at.isoformat(), fresh_until=(at + timedelta(days=1)).isoformat(),
100
+ subject_person_id=source['contact_id'], viewer_scope=f"person:{source['contact_id']}",
101
+ shareability='subject_private')
102
+ if not current_source(extractor._source_ledger, source):
103
+ await extractor._store.erase_property_evidence([f"source:{source['source_id']}"],
104
+ subject_person_id=source['contact_id'])
105
+ report['property_skipped'] += 1
106
+ continue
107
+ report['property_observations'].append(dict(observation_id=result['observation_id'],
108
+ entity_id=eid, property_key=key, kind='reported', time_basis=source['time_basis']))
109
+ report['writes'] += 1
110
+ except (ValueError, NotImplementedError):
111
+ report['property_skipped'] += 1
112
+
113
+
114
+ def validate_reports(records, ledger):
115
+ """Hydration checks close the cross-store correction/erasure race."""
116
+ result = []
117
+ for row in records:
118
+ refs = row.get('source_refs', [])
119
+ valid = all(current_source(ledger, ref | {'contact_id': row['subject_person_id']}) for ref in refs) if ledger else not refs
120
+ result.append(row if valid else row | {'invalidated': True, 'value': None})
121
+ return result
@@ -0,0 +1,4 @@
1
+ """SQLite backend for the Colony World Model."""
2
+ from .backend import SQLiteBackend
3
+
4
+ __all__ = ["SQLiteBackend"]