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,435 @@
1
+ """Colony Briefing System — SQLite persistence store.
2
+
3
+ Persists Briefing objects, schedule state, and section engagement records.
4
+ Thread-safe via connection-per-thread (check_same_thread=False + threading.Lock).
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import json
10
+ import logging
11
+ import os
12
+ import sqlite3
13
+ import threading
14
+ from datetime import datetime, timezone
15
+ from pathlib import Path
16
+ from typing import List, Optional
17
+
18
+ from apsimo import get_state_dir
19
+
20
+ from .models import (
21
+ Briefing,
22
+ BriefingPriority,
23
+ BriefingSection,
24
+ BriefingStatus,
25
+ BriefingType,
26
+ ScheduleEntry,
27
+ SectionEngagementRecord,
28
+ )
29
+
30
+ logger = logging.getLogger(__name__)
31
+
32
+ _briefing_store_instance: Optional["BriefingStore"] = None
33
+
34
+ _SCHEMA = """
35
+ CREATE TABLE IF NOT EXISTS briefings (
36
+ briefing_id TEXT PRIMARY KEY,
37
+ briefing_type TEXT NOT NULL,
38
+ status TEXT NOT NULL DEFAULT 'draft',
39
+ sections_json TEXT NOT NULL DEFAULT '[]',
40
+ priority TEXT NOT NULL DEFAULT 'normal',
41
+ triggered_by TEXT,
42
+ gateway TEXT,
43
+ created_at TEXT NOT NULL,
44
+ delivered_at TEXT,
45
+ read_at TEXT
46
+ );
47
+
48
+ CREATE INDEX IF NOT EXISTS idx_briefings_status ON briefings(status);
49
+ CREATE INDEX IF NOT EXISTS idx_briefings_type ON briefings(briefing_type);
50
+ CREATE INDEX IF NOT EXISTS idx_briefings_created ON briefings(created_at DESC);
51
+
52
+ CREATE TABLE IF NOT EXISTS briefing_schedule (
53
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
54
+ type TEXT NOT NULL UNIQUE,
55
+ last_run TEXT,
56
+ next_run TEXT
57
+ );
58
+
59
+ INSERT OR IGNORE INTO briefing_schedule (type) VALUES ('daily');
60
+ INSERT OR IGNORE INTO briefing_schedule (type) VALUES ('weekly');
61
+
62
+ CREATE TABLE IF NOT EXISTS section_engagement (
63
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
64
+ section_name TEXT NOT NULL,
65
+ briefing_id TEXT NOT NULL,
66
+ signal TEXT NOT NULL,
67
+ recorded_at TEXT NOT NULL,
68
+ context TEXT,
69
+ FOREIGN KEY (briefing_id) REFERENCES briefings(briefing_id)
70
+ );
71
+
72
+ CREATE INDEX IF NOT EXISTS idx_engagement_section ON section_engagement(section_name);
73
+ CREATE INDEX IF NOT EXISTS idx_engagement_briefing ON section_engagement(briefing_id);
74
+ CREATE INDEX IF NOT EXISTS idx_engagement_time ON section_engagement(recorded_at DESC);
75
+
76
+ CREATE TABLE IF NOT EXISTS suppressed_sections (
77
+ section_name TEXT PRIMARY KEY,
78
+ suppressed_at TEXT NOT NULL,
79
+ reason TEXT
80
+ );
81
+ """
82
+
83
+
84
+ def _dt_to_str(dt: Optional[datetime]) -> Optional[str]:
85
+ if dt is None:
86
+ return None
87
+ return dt.isoformat()
88
+
89
+
90
+ def _str_to_dt(s: Optional[str]) -> Optional[datetime]:
91
+ if not s:
92
+ return None
93
+ dt = datetime.fromisoformat(s)
94
+ if dt.tzinfo is None:
95
+ dt = dt.replace(tzinfo=timezone.utc)
96
+ return dt
97
+
98
+
99
+ def _briefing_to_row(b: Briefing) -> dict:
100
+ sections = [
101
+ {
102
+ "section_id": s.section_id,
103
+ "name": s.name,
104
+ "content": s.content,
105
+ "narrative": s.narrative,
106
+ "priority": s.priority,
107
+ "suppressed": s.suppressed,
108
+ "engagement": s.engagement,
109
+ }
110
+ for s in b.sections
111
+ ]
112
+ return {
113
+ "briefing_id": b.briefing_id,
114
+ "briefing_type": b.briefing_type.value,
115
+ "status": b.status.value,
116
+ "sections_json": json.dumps(sections),
117
+ "priority": b.priority.value,
118
+ "triggered_by": b.triggered_by,
119
+ "gateway": b.gateway,
120
+ "created_at": _dt_to_str(b.created_at),
121
+ "delivered_at": _dt_to_str(b.delivered_at),
122
+ "read_at": _dt_to_str(b.read_at),
123
+ }
124
+
125
+
126
+ def _row_to_briefing(row: sqlite3.Row) -> Briefing:
127
+ raw_sections = json.loads(row["sections_json"] or "[]")
128
+ sections = [
129
+ BriefingSection(
130
+ section_id=s.get("section_id", ""),
131
+ name=s.get("name", ""),
132
+ content=s.get("content", {}),
133
+ narrative=s.get("narrative", ""),
134
+ priority=s.get("priority", 50),
135
+ suppressed=s.get("suppressed", False),
136
+ engagement=s.get("engagement"),
137
+ )
138
+ for s in raw_sections
139
+ ]
140
+ return Briefing(
141
+ briefing_id=row["briefing_id"],
142
+ briefing_type=BriefingType(row["briefing_type"]),
143
+ status=BriefingStatus(row["status"]),
144
+ sections=sections,
145
+ priority=BriefingPriority(row["priority"]),
146
+ triggered_by=row["triggered_by"],
147
+ gateway=row["gateway"],
148
+ created_at=_str_to_dt(row["created_at"]) or datetime.now(timezone.utc),
149
+ delivered_at=_str_to_dt(row["delivered_at"]),
150
+ read_at=_str_to_dt(row["read_at"]),
151
+ )
152
+
153
+
154
+ class BriefingStore:
155
+ """Thread-safe SQLite-backed store for briefings and schedule state."""
156
+
157
+ def __init__(self, db_path: str = ":memory:") -> None:
158
+ self._db_path = db_path
159
+ self._lock = threading.Lock()
160
+ self._conn = sqlite3.connect(db_path, check_same_thread=False)
161
+ self._conn.row_factory = sqlite3.Row
162
+ self._init_schema()
163
+
164
+ def _init_schema(self) -> None:
165
+ with self._lock:
166
+ self._conn.executescript(_SCHEMA)
167
+ self._conn.commit()
168
+
169
+ def close(self) -> None:
170
+ self._conn.close()
171
+
172
+ @classmethod
173
+ def get_instance(cls) -> "BriefingStore":
174
+ """Return the process-wide singleton backed by ~/.colony/data/briefings.db."""
175
+ global _briefing_store_instance
176
+ if _briefing_store_instance is None:
177
+ colony_home = get_state_dir()
178
+ _briefing_store_instance = cls(str(colony_home / "briefings.db"))
179
+ return _briefing_store_instance
180
+
181
+ def list(self, limit: int = 50, cursor: Optional[str] = None) -> dict:
182
+ """Return a paginated dict of briefings for the API router."""
183
+ offset = 0
184
+ if cursor:
185
+ try:
186
+ offset = int(cursor)
187
+ except ValueError:
188
+ pass
189
+ briefings = self.list_recent(limit=limit + 1)
190
+ has_more = len(briefings) > limit
191
+ if has_more:
192
+ briefings = briefings[:limit]
193
+ items = [
194
+ {
195
+ "briefing_id": b.briefing_id,
196
+ "briefing_type": b.briefing_type.value,
197
+ "status": b.status.value,
198
+ "priority": b.priority.value,
199
+ "triggered_by": b.triggered_by,
200
+ "gateway": b.gateway,
201
+ "created_at": b.created_at.isoformat(),
202
+ "delivered_at": b.delivered_at.isoformat() if b.delivered_at else None,
203
+ "section_count": len(b.sections),
204
+ }
205
+ for b in briefings
206
+ ]
207
+ return {
208
+ "data": items,
209
+ "meta": {
210
+ "total": len(items),
211
+ "page_size": limit,
212
+ "has_more": has_more,
213
+ "cursor": str(offset + limit) if has_more else None,
214
+ },
215
+ }
216
+
217
+ # ------------------------------------------------------------------
218
+ # Briefing CRUD
219
+ # ------------------------------------------------------------------
220
+
221
+ def save(self, briefing: Briefing) -> Briefing:
222
+ row = _briefing_to_row(briefing)
223
+ with self._lock:
224
+ self._conn.execute(
225
+ """
226
+ INSERT INTO briefings
227
+ (briefing_id, briefing_type, status, sections_json, priority,
228
+ triggered_by, gateway, created_at, delivered_at, read_at)
229
+ VALUES
230
+ (:briefing_id, :briefing_type, :status, :sections_json, :priority,
231
+ :triggered_by, :gateway, :created_at, :delivered_at, :read_at)
232
+ ON CONFLICT(briefing_id) DO UPDATE SET
233
+ status = excluded.status,
234
+ sections_json = excluded.sections_json,
235
+ priority = excluded.priority,
236
+ triggered_by = excluded.triggered_by,
237
+ gateway = excluded.gateway,
238
+ delivered_at = excluded.delivered_at,
239
+ read_at = excluded.read_at
240
+ """,
241
+ row,
242
+ )
243
+ self._conn.commit()
244
+ try:
245
+ from apsimo.events.broadcaster import emit as _emit
246
+ _emit("briefing", {
247
+ "briefing_id": briefing.briefing_id,
248
+ "status": getattr(briefing.status, "value", str(briefing.status)),
249
+ "priority": briefing.priority,
250
+ })
251
+ except Exception:
252
+ pass
253
+ return briefing
254
+
255
+ def get(self, briefing_id: str) -> Optional[Briefing]:
256
+ with self._lock:
257
+ cur = self._conn.execute(
258
+ "SELECT * FROM briefings WHERE briefing_id = ?", (briefing_id,)
259
+ )
260
+ row = cur.fetchone()
261
+ return _row_to_briefing(row) if row else None
262
+
263
+ def list_by_status(self, status: BriefingStatus, limit: int = 50) -> List[Briefing]:
264
+ with self._lock:
265
+ cur = self._conn.execute(
266
+ "SELECT * FROM briefings WHERE status = ? ORDER BY created_at DESC LIMIT ?",
267
+ (status.value, limit),
268
+ )
269
+ rows = cur.fetchall()
270
+ return [_row_to_briefing(r) for r in rows]
271
+
272
+ def list_recent(self, limit: int = 10) -> List[Briefing]:
273
+ with self._lock:
274
+ cur = self._conn.execute(
275
+ "SELECT * FROM briefings ORDER BY created_at DESC LIMIT ?",
276
+ (limit,),
277
+ )
278
+ rows = cur.fetchall()
279
+ return [_row_to_briefing(r) for r in rows]
280
+
281
+ def mark_queued(self, briefing_id: str) -> None:
282
+ with self._lock:
283
+ self._conn.execute(
284
+ "UPDATE briefings SET status = 'queued' WHERE briefing_id = ?",
285
+ (briefing_id,),
286
+ )
287
+ self._conn.commit()
288
+
289
+ def mark_delivering(self, briefing_id: str) -> None:
290
+ with self._lock:
291
+ self._conn.execute(
292
+ "UPDATE briefings SET status = 'delivering' WHERE briefing_id = ?",
293
+ (briefing_id,),
294
+ )
295
+ self._conn.commit()
296
+
297
+ def mark_delivered(self, briefing_id: str, gateway: str) -> None:
298
+ now = _dt_to_str(datetime.now(timezone.utc))
299
+ with self._lock:
300
+ self._conn.execute(
301
+ "UPDATE briefings SET status = 'delivered', gateway = ?, delivered_at = ? WHERE briefing_id = ?",
302
+ (gateway, now, briefing_id),
303
+ )
304
+ self._conn.commit()
305
+
306
+ def mark_read(self, briefing_id: str) -> None:
307
+ now = _dt_to_str(datetime.now(timezone.utc))
308
+ with self._lock:
309
+ self._conn.execute(
310
+ "UPDATE briefings SET status = 'read', read_at = ? WHERE briefing_id = ?",
311
+ (now, briefing_id),
312
+ )
313
+ self._conn.commit()
314
+
315
+ def count_today(self) -> int:
316
+ today = datetime.now(timezone.utc).date().isoformat()
317
+ with self._lock:
318
+ cur = self._conn.execute(
319
+ "SELECT COUNT(*) FROM briefings WHERE created_at >= ?",
320
+ (today,),
321
+ )
322
+ return cur.fetchone()[0]
323
+
324
+ # ------------------------------------------------------------------
325
+ # Schedule
326
+ # ------------------------------------------------------------------
327
+
328
+ def get_schedule(self) -> List[ScheduleEntry]:
329
+ with self._lock:
330
+ cur = self._conn.execute("SELECT type, last_run, next_run FROM briefing_schedule")
331
+ rows = cur.fetchall()
332
+ return [
333
+ ScheduleEntry(
334
+ type=r["type"],
335
+ last_run=_str_to_dt(r["last_run"]),
336
+ next_run=_str_to_dt(r["next_run"]),
337
+ )
338
+ for r in rows
339
+ ]
340
+
341
+ def get_schedule_entry(self, briefing_type: BriefingType) -> Optional[ScheduleEntry]:
342
+ with self._lock:
343
+ cur = self._conn.execute(
344
+ "SELECT type, last_run, next_run FROM briefing_schedule WHERE type = ?",
345
+ (briefing_type.value,),
346
+ )
347
+ row = cur.fetchone()
348
+ if not row:
349
+ return None
350
+ return ScheduleEntry(
351
+ type=row["type"],
352
+ last_run=_str_to_dt(row["last_run"]),
353
+ next_run=_str_to_dt(row["next_run"]),
354
+ )
355
+
356
+ def update_schedule_last_run(self, briefing_type: BriefingType) -> None:
357
+ now = _dt_to_str(datetime.now(timezone.utc))
358
+ with self._lock:
359
+ self._conn.execute(
360
+ "UPDATE briefing_schedule SET last_run = ? WHERE type = ?",
361
+ (now, briefing_type.value),
362
+ )
363
+ self._conn.commit()
364
+
365
+ # ------------------------------------------------------------------
366
+ # Engagement
367
+ # ------------------------------------------------------------------
368
+
369
+ def record_engagement(self, record: SectionEngagementRecord) -> None:
370
+ with self._lock:
371
+ self._conn.execute(
372
+ """
373
+ INSERT INTO section_engagement
374
+ (section_name, briefing_id, signal, recorded_at, context)
375
+ VALUES (?, ?, ?, ?, ?)
376
+ """,
377
+ (
378
+ record.section_name,
379
+ record.briefing_id,
380
+ record.signal,
381
+ _dt_to_str(record.recorded_at),
382
+ record.context,
383
+ ),
384
+ )
385
+ self._conn.commit()
386
+
387
+ def get_engagement_records(
388
+ self,
389
+ section_name: Optional[str] = None,
390
+ limit: int = 500,
391
+ ) -> List[SectionEngagementRecord]:
392
+ with self._lock:
393
+ if section_name:
394
+ cur = self._conn.execute(
395
+ "SELECT * FROM section_engagement WHERE section_name = ? ORDER BY recorded_at DESC LIMIT ?",
396
+ (section_name, limit),
397
+ )
398
+ else:
399
+ cur = self._conn.execute(
400
+ "SELECT * FROM section_engagement ORDER BY recorded_at DESC LIMIT ?",
401
+ (limit,),
402
+ )
403
+ rows = cur.fetchall()
404
+ return [
405
+ SectionEngagementRecord(
406
+ section_name=r["section_name"],
407
+ briefing_id=r["briefing_id"],
408
+ signal=r["signal"],
409
+ recorded_at=_str_to_dt(r["recorded_at"]) or datetime.now(timezone.utc),
410
+ context=r["context"],
411
+ )
412
+ for r in rows
413
+ ]
414
+
415
+ def get_suppressed_sections(self) -> List[str]:
416
+ with self._lock:
417
+ cur = self._conn.execute("SELECT section_name FROM suppressed_sections")
418
+ return [r["section_name"] for r in cur.fetchall()]
419
+
420
+ def suppress_section(self, section_name: str, reason: Optional[str] = None) -> None:
421
+ now = _dt_to_str(datetime.now(timezone.utc))
422
+ with self._lock:
423
+ self._conn.execute(
424
+ "INSERT OR REPLACE INTO suppressed_sections (section_name, suppressed_at, reason) VALUES (?, ?, ?)",
425
+ (section_name, now, reason),
426
+ )
427
+ self._conn.commit()
428
+
429
+ def unsuppress_section(self, section_name: str) -> None:
430
+ with self._lock:
431
+ self._conn.execute(
432
+ "DELETE FROM suppressed_sections WHERE section_name = ?",
433
+ (section_name,),
434
+ )
435
+ self._conn.commit()
@@ -0,0 +1,48 @@
1
+ """ColonyChain — lightweight append-only identity and trust ledger.
2
+
3
+ Provides:
4
+ - Block model with SHA-256 hash chain and Ed25519 signatures
5
+ - Transaction types for colony lifecycle and governance
6
+ - SQLite-backed persistent chain storage
7
+ - Genesis admin capabilities
8
+ - Chain sync protocol messages
9
+ """
10
+
11
+ from .block import Block, build_merkle_root, compute_block_hash
12
+ from .transactions import (
13
+ TxType,
14
+ Transaction,
15
+ ChainState,
16
+ ColonyRecord,
17
+ KeyHistoryEntry,
18
+ TrustEdge,
19
+ SentinelRecord,
20
+ ProtocolConfig,
21
+ UntrustEvent,
22
+ )
23
+ from .storage import ChainStore
24
+ from .validation import TransactionValidator, BlockValidator, ValidationResult
25
+ from .genesis import create_genesis_block, GenesisConfig
26
+ from .manager import ChainManager
27
+
28
+ __all__ = [
29
+ "Block",
30
+ "build_merkle_root",
31
+ "compute_block_hash",
32
+ "TxType",
33
+ "Transaction",
34
+ "ChainState",
35
+ "ColonyRecord",
36
+ "KeyHistoryEntry",
37
+ "TrustEdge",
38
+ "SentinelRecord",
39
+ "ProtocolConfig",
40
+ "UntrustEvent",
41
+ "ChainStore",
42
+ "TransactionValidator",
43
+ "BlockValidator",
44
+ "ValidationResult",
45
+ "create_genesis_block",
46
+ "GenesisConfig",
47
+ "ChainManager",
48
+ ]
apsimo/chain/block.py ADDED
@@ -0,0 +1,100 @@
1
+ """Block model for ColonyChain.
2
+
3
+ SHA-256 hash chain with Ed25519 validator signatures and Merkle root
4
+ computation over transactions.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import hashlib
10
+ import json
11
+ from dataclasses import dataclass, field
12
+ from typing import TYPE_CHECKING, Any
13
+
14
+ if TYPE_CHECKING:
15
+ from .transactions import Transaction
16
+
17
+
18
+ def build_merkle_root(tx_ids: list[str]) -> str:
19
+ """Compute SHA-256 Merkle root over a list of transaction IDs."""
20
+ if not tx_ids:
21
+ return hashlib.sha256(b"").hexdigest()
22
+ level: list[str] = [hashlib.sha256(tid.encode()).hexdigest() for tid in tx_ids]
23
+ while len(level) > 1:
24
+ if len(level) % 2 == 1:
25
+ level.append(level[-1])
26
+ level = [
27
+ hashlib.sha256(
28
+ bytes.fromhex(level[i]) + bytes.fromhex(level[i + 1])
29
+ ).hexdigest()
30
+ for i in range(0, len(level), 2)
31
+ ]
32
+ return level[0]
33
+
34
+
35
+ def compute_block_hash(block_dict: dict[str, Any]) -> str:
36
+ """Compute SHA-256 hash of a block dict (excluding 'signature' field)."""
37
+ fields = {k: v for k, v in block_dict.items() if k != "signature"}
38
+ canonical = json.dumps(fields, sort_keys=True, separators=(",", ":"))
39
+ return hashlib.sha256(canonical.encode()).hexdigest()
40
+
41
+
42
+ @dataclass
43
+ class Block:
44
+ """A single block in the ColonyChain."""
45
+
46
+ index: int
47
+ previous_hash: str
48
+ timestamp: str
49
+ transactions: list["Transaction"]
50
+ merkle_root: str
51
+ producer_id: str
52
+ signature: str
53
+ metadata: dict[str, Any] = field(default_factory=dict)
54
+
55
+ # Computed and cached after creation
56
+ _hash: str = field(default="", repr=False, compare=False)
57
+
58
+ def to_dict(self) -> dict[str, Any]:
59
+ return {
60
+ "index": self.index,
61
+ "previous_hash": self.previous_hash,
62
+ "timestamp": self.timestamp,
63
+ "transactions": [t.to_dict() for t in self.transactions],
64
+ "merkle_root": self.merkle_root,
65
+ "producer_id": self.producer_id,
66
+ "signature": self.signature,
67
+ "metadata": self.metadata,
68
+ }
69
+
70
+ def compute_hash(self) -> str:
71
+ """Compute SHA-256 hash of this block (excluding signature)."""
72
+ d = self.to_dict()
73
+ return compute_block_hash(d)
74
+
75
+ @property
76
+ def block_hash(self) -> str:
77
+ if not self._hash:
78
+ self._hash = self.compute_hash()
79
+ return self._hash
80
+
81
+ @classmethod
82
+ def from_dict(cls, d: dict[str, Any]) -> "Block":
83
+ from .transactions import Transaction # avoid circular at module load
84
+
85
+ txs = [Transaction.from_dict(t) for t in d.get("transactions", [])]
86
+ block = cls(
87
+ index=d["index"],
88
+ previous_hash=d["previous_hash"],
89
+ timestamp=d["timestamp"],
90
+ transactions=txs,
91
+ merkle_root=d["merkle_root"],
92
+ producer_id=d["producer_id"],
93
+ signature=d.get("signature", ""),
94
+ metadata=d.get("metadata", {}),
95
+ )
96
+ return block
97
+
98
+ def __post_init__(self) -> None:
99
+ # Reset cached hash if fields change
100
+ self._hash = ""