colonymind 1.0.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 (566) hide show
  1. colonymind/__init__.py +40 -0
  2. colonymind/__main__.py +6 -0
  3. colonymind/agent_mailbox.py +411 -0
  4. colonymind/api/__init__.py +1 -0
  5. colonymind/api/archive_queue.py +30 -0
  6. colonymind/api/auth.py +466 -0
  7. colonymind/api/credentials.py +788 -0
  8. colonymind/api/errors.py +108 -0
  9. colonymind/api/legacy_scopes.py +324 -0
  10. colonymind/api/mcp.py +536 -0
  11. colonymind/api/middleware.py +391 -0
  12. colonymind/api/owner_location.py +70 -0
  13. colonymind/api/public_spec.py +308 -0
  14. colonymind/api/retired_routes.py +6 -0
  15. colonymind/api/routers/__init__.py +1 -0
  16. colonymind/api/routers/activity.py +54 -0
  17. colonymind/api/routers/agent_mailbox.py +94 -0
  18. colonymind/api/routers/branches.py +191 -0
  19. colonymind/api/routers/claims.py +120 -0
  20. colonymind/api/routers/context_card.py +34 -0
  21. colonymind/api/routers/curated_memory.py +186 -0
  22. colonymind/api/routers/devices.py +105 -0
  23. colonymind/api/routers/executions.py +80 -0
  24. colonymind/api/routers/hands.py +307 -0
  25. colonymind/api/routers/host.py +6807 -0
  26. colonymind/api/routers/instinct.py +154 -0
  27. colonymind/api/routers/inventory.py +113 -0
  28. colonymind/api/routers/mind.py +1599 -0
  29. colonymind/api/routers/opinions.py +139 -0
  30. colonymind/api/routers/owner_goals.py +208 -0
  31. colonymind/api/routers/people.py +689 -0
  32. colonymind/api/routers/secrets.py +414 -0
  33. colonymind/api/routers/social_state.py +256 -0
  34. colonymind/api/routers/temporal_followups.py +150 -0
  35. colonymind/api/routers/transport.py +110 -0
  36. colonymind/api/routers/transport_ingress_api.py +333 -0
  37. colonymind/api/routers/watch.py +24 -0
  38. colonymind/api/routers/work.py +267 -0
  39. colonymind/api/schemas/__init__.py +1 -0
  40. colonymind/api/schemas/approval_history.py +92 -0
  41. colonymind/api/schemas/context_card.py +16 -0
  42. colonymind/api/schemas/host.py +1233 -0
  43. colonymind/api/viewer.py +15 -0
  44. colonymind/atlas/__init__.py +10 -0
  45. colonymind/atlas/cli.py +78 -0
  46. colonymind/atlas/render.py +527 -0
  47. colonymind/atlas/site.py +444 -0
  48. colonymind/atlas/snapshot.py +271 -0
  49. colonymind/backup.py +1053 -0
  50. colonymind/beliefs/__init__.py +20 -0
  51. colonymind/beliefs/promotion.py +45 -0
  52. colonymind/beliefs/reextract.py +1334 -0
  53. colonymind/beliefs/reextract_cli.py +223 -0
  54. colonymind/beliefs/source_claims.py +1068 -0
  55. colonymind/beliefs/source_projection.py +2536 -0
  56. colonymind/beliefs/source_time.py +652 -0
  57. colonymind/beliefs/value_revision.py +124 -0
  58. colonymind/build_stamp.json +5 -0
  59. colonymind/build_stamp.py +41 -0
  60. colonymind/cli.py +1389 -0
  61. colonymind/commitments/__init__.py +18 -0
  62. colonymind/commitments/extract.py +1692 -0
  63. colonymind/commitments/parties.py +65 -0
  64. colonymind/commitments/store.py +1304 -0
  65. colonymind/commitments/work.py +96 -0
  66. colonymind/config.py +1245 -0
  67. colonymind/console/__init__.py +10 -0
  68. colonymind/console/app.py +269 -0
  69. colonymind/console/cli.py +53 -0
  70. colonymind/console/pages.py +487 -0
  71. colonymind/console/server.py +92 -0
  72. colonymind/console/source.py +203 -0
  73. colonymind/contacts/__init__.py +30 -0
  74. colonymind/contacts/affect.py +513 -0
  75. colonymind/contacts/affect_writer.py +95 -0
  76. colonymind/contacts/changes.py +164 -0
  77. colonymind/contacts/cli.py +105 -0
  78. colonymind/contacts/comms.py +756 -0
  79. colonymind/contacts/config.py +43 -0
  80. colonymind/contacts/continuity.py +70 -0
  81. colonymind/contacts/digest.py +238 -0
  82. colonymind/contacts/identity_links.py +313 -0
  83. colonymind/contacts/migrations/001_contacts_schema.sql +101 -0
  84. colonymind/contacts/migrations/002_trust_scopes.sql +36 -0
  85. colonymind/contacts/migrations/003_open_gateway_enum.sql +32 -0
  86. colonymind/contacts/migrations/004_contact_provision_operations.sql +4 -0
  87. colonymind/contacts/migrations/005_identity_links.sql +27 -0
  88. colonymind/contacts/migrations/006_may_contact.sql +14 -0
  89. colonymind/contacts/migrations/007_digest_lineage.sql +16 -0
  90. colonymind/contacts/migrations/008_location_report.sql +5 -0
  91. colonymind/contacts/models.py +337 -0
  92. colonymind/contacts/optout.py +78 -0
  93. colonymind/contacts/refresh.py +130 -0
  94. colonymind/contacts/source_lineage.py +112 -0
  95. colonymind/contacts/store.py +1873 -0
  96. colonymind/contacts/transport_ingress.py +475 -0
  97. colonymind/context_card/__init__.py +1 -0
  98. colonymind/context_card/builder.py +139 -0
  99. colonymind/context_card/inputs.py +209 -0
  100. colonymind/context_card/lifecycle.py +37 -0
  101. colonymind/context_card/measure.py +130 -0
  102. colonymind/context_card/refresh.py +207 -0
  103. colonymind/context_card/store.py +144 -0
  104. colonymind/credentials_cli.py +257 -0
  105. colonymind/demo.py +579 -0
  106. colonymind/devices.py +330 -0
  107. colonymind/devtools/__init__.py +7 -0
  108. colonymind/devtools/fake_model.py +313 -0
  109. colonymind/devtools/mind_loop_probe.py +637 -0
  110. colonymind/doctor.py +1248 -0
  111. colonymind/events/__init__.py +5 -0
  112. colonymind/events/activity.py +219 -0
  113. colonymind/events/broadcaster.py +105 -0
  114. colonymind/events/journal.py +848 -0
  115. colonymind/events/stream.py +131 -0
  116. colonymind/feedback/__init__.py +5 -0
  117. colonymind/feedback/store.py +145 -0
  118. colonymind/feeds/__init__.py +19 -0
  119. colonymind/feeds/cli.py +84 -0
  120. colonymind/feeds/engine.py +438 -0
  121. colonymind/feeds/example-feed.yaml +77 -0
  122. colonymind/feeds/hermes_cron.py +126 -0
  123. colonymind/feeds/manager.py +235 -0
  124. colonymind/feeds/spec.py +250 -0
  125. colonymind/feeds/template.py +202 -0
  126. colonymind/hands/__init__.py +5 -0
  127. colonymind/hands/admission.py +68 -0
  128. colonymind/hands/approvals.py +335 -0
  129. colonymind/hands/brief.py +134 -0
  130. colonymind/hands/budgets.py +66 -0
  131. colonymind/hands/cascade.py +34 -0
  132. colonymind/hands/cli.py +80 -0
  133. colonymind/hands/contracts.py +56 -0
  134. colonymind/hands/controller.py +1111 -0
  135. colonymind/hands/definition_queue.py +110 -0
  136. colonymind/hands/definitions.py +409 -0
  137. colonymind/hands/definitions_schema.py +129 -0
  138. colonymind/hands/digest.py +80 -0
  139. colonymind/hands/effects.py +63 -0
  140. colonymind/hands/evidence.py +208 -0
  141. colonymind/hands/health.py +91 -0
  142. colonymind/hands/locks.py +38 -0
  143. colonymind/hands/matrix.py +17 -0
  144. colonymind/hands/notices.py +110 -0
  145. colonymind/hands/obligations.py +114 -0
  146. colonymind/hands/provenance.py +495 -0
  147. colonymind/hands/retention.py +110 -0
  148. colonymind/hands/runtime.py +118 -0
  149. colonymind/hands/sessions.py +136 -0
  150. colonymind/hands/settings.py +28 -0
  151. colonymind/hands/store.py +415 -0
  152. colonymind/hands/types.py +405 -0
  153. colonymind/hands/wire.py +456 -0
  154. colonymind/health_counters.py +26 -0
  155. colonymind/identity/__init__.py +15 -0
  156. colonymind/identity/participants.py +237 -0
  157. colonymind/identity/policy.py +45 -0
  158. colonymind/identity/resolver.py +64 -0
  159. colonymind/inference_pool/__init__.py +5 -0
  160. colonymind/inference_pool/config.py +300 -0
  161. colonymind/inference_pool/scheduler.py +323 -0
  162. colonymind/inference_pool/server.py +603 -0
  163. colonymind/init.py +3454 -0
  164. colonymind/initiatives/__init__.py +16 -0
  165. colonymind/initiatives/context_freshness.py +42 -0
  166. colonymind/initiatives/models.py +372 -0
  167. colonymind/initiatives/store.py +1455 -0
  168. colonymind/initiatives/temporal_followup.py +362 -0
  169. colonymind/instance.py +102 -0
  170. colonymind/instinct/__init__.py +119 -0
  171. colonymind/instinct/calibration.py +236 -0
  172. colonymind/instinct/cli.py +319 -0
  173. colonymind/instinct/deliberate.py +184 -0
  174. colonymind/instinct/external.py +425 -0
  175. colonymind/instinct/fit.py +868 -0
  176. colonymind/instinct/log.py +690 -0
  177. colonymind/instinct/outcomes.py +1517 -0
  178. colonymind/instinct/points.py +347 -0
  179. colonymind/instinct/providers/__init__.py +78 -0
  180. colonymind/instinct/providers/local_decision.py +180 -0
  181. colonymind/instinct/providers/logits.py +342 -0
  182. colonymind/instinct/records/__init__.py +120 -0
  183. colonymind/instinct/records/local-decision-opt-out.json +58 -0
  184. colonymind/instinct/registry.py +232 -0
  185. colonymind/instinct/settings.py +254 -0
  186. colonymind/instinct/state.py +588 -0
  187. colonymind/instinct/tiers.py +658 -0
  188. colonymind/intelligence/__init__.py +1 -0
  189. colonymind/intelligence/components/__init__.py +9 -0
  190. colonymind/intelligence/components/preference_learner.py +496 -0
  191. colonymind/jobwatch/__init__.py +1 -0
  192. colonymind/jobwatch/activity.py +192 -0
  193. colonymind/jobwatch/cli.py +37 -0
  194. colonymind/jobwatch/network.py +22 -0
  195. colonymind/jobwatch/runner.py +480 -0
  196. colonymind/jobwatch/settings.py +69 -0
  197. colonymind/jobwatch/store.py +74 -0
  198. colonymind/loop_lag.py +78 -0
  199. colonymind/memory/__init__.py +1 -0
  200. colonymind/memory/blocks.py +423 -0
  201. colonymind/memory/branches.py +640 -0
  202. colonymind/memory/compass.py +840 -0
  203. colonymind/memory/curated.py +1463 -0
  204. colonymind/memory/curated_health.py +118 -0
  205. colonymind/memory/inventory.py +647 -0
  206. colonymind/memory/learning.py +661 -0
  207. colonymind/memory/live_recall.py +60 -0
  208. colonymind/memory/notice.py +206 -0
  209. colonymind/memory/producers.py +285 -0
  210. colonymind/memory/recall.py +661 -0
  211. colonymind/memory/recall_exit.py +188 -0
  212. colonymind/memory/recall_stats.py +98 -0
  213. colonymind/memory/recent.py +235 -0
  214. colonymind/memory/rerank_gate.py +155 -0
  215. colonymind/memory/search.py +412 -0
  216. colonymind/memory/selection.py +559 -0
  217. colonymind/memory/time_lane.py +462 -0
  218. colonymind/memory/trace.py +509 -0
  219. colonymind/memory/turn_budget.py +253 -0
  220. colonymind/memory/voice_affect.py +86 -0
  221. colonymind/memory/voice_recall.py +619 -0
  222. colonymind/memory_cli.py +154 -0
  223. colonymind/migrations.py +369 -0
  224. colonymind/mind/__init__.py +29 -0
  225. colonymind/mind/affect.py +979 -0
  226. colonymind/mind/affect_rules.py +44 -0
  227. colonymind/mind/allows.py +449 -0
  228. colonymind/mind/answer_turns.py +188 -0
  229. colonymind/mind/answers.py +976 -0
  230. colonymind/mind/approval_history.py +250 -0
  231. colonymind/mind/ask_origin.py +100 -0
  232. colonymind/mind/asks_report.py +362 -0
  233. colonymind/mind/audit.py +311 -0
  234. colonymind/mind/authority.py +1054 -0
  235. colonymind/mind/cli.py +603 -0
  236. colonymind/mind/compose.py +152 -0
  237. colonymind/mind/concerns.py +418 -0
  238. colonymind/mind/consolidate.py +1861 -0
  239. colonymind/mind/contradiction_keys.py +333 -0
  240. colonymind/mind/contradiction_keys_bounds.json +341 -0
  241. colonymind/mind/contradiction_keys_dev.json +66 -0
  242. colonymind/mind/deliberate.py +429 -0
  243. colonymind/mind/digest.py +102 -0
  244. colonymind/mind/drift.py +1201 -0
  245. colonymind/mind/drives.py +874 -0
  246. colonymind/mind/factory.py +79 -0
  247. colonymind/mind/gate.py +858 -0
  248. colonymind/mind/goals.py +271 -0
  249. colonymind/mind/idea_assets/calendar-planning-v1.svg +1 -0
  250. colonymind/mind/idea_assets/document-paperwork-v1.svg +1 -0
  251. colonymind/mind/idea_assets/packet-money-v1.svg +1 -0
  252. colonymind/mind/idea_assets/renewal-subscriptions-v1.svg +1 -0
  253. colonymind/mind/ideas.py +724 -0
  254. colonymind/mind/ideas_association.py +81 -0
  255. colonymind/mind/ideas_build.py +188 -0
  256. colonymind/mind/ideas_copy.py +205 -0
  257. colonymind/mind/ideas_presentation.py +31 -0
  258. colonymind/mind/learned.py +251 -0
  259. colonymind/mind/lesson_match.py +500 -0
  260. colonymind/mind/lesson_match_bounds.json +919 -0
  261. colonymind/mind/lesson_match_dev.json +87 -0
  262. colonymind/mind/lessons.py +1869 -0
  263. colonymind/mind/observations.py +415 -0
  264. colonymind/mind/opinions.py +808 -0
  265. colonymind/mind/outbox.py +978 -0
  266. colonymind/mind/outcomes.py +615 -0
  267. colonymind/mind/outreach.py +941 -0
  268. colonymind/mind/owner_actions.py +100 -0
  269. colonymind/mind/permit_asks.py +326 -0
  270. colonymind/mind/plain.py +419 -0
  271. colonymind/mind/questions.py +656 -0
  272. colonymind/mind/rank.py +229 -0
  273. colonymind/mind/reactions.py +703 -0
  274. colonymind/mind/skills.py +181 -0
  275. colonymind/mind/status.py +324 -0
  276. colonymind/mind/tick.py +6699 -0
  277. colonymind/mind/tool_origins.py +76 -0
  278. colonymind/mind/wishes.py +1219 -0
  279. colonymind/owner_goals/__init__.py +1 -0
  280. colonymind/owner_goals/artifacts.py +133 -0
  281. colonymind/owner_goals/authority.py +85 -0
  282. colonymind/owner_goals/hub.py +75 -0
  283. colonymind/owner_goals/reads.py +398 -0
  284. colonymind/owner_goals/schema.py +165 -0
  285. colonymind/owner_goals/sealing.py +197 -0
  286. colonymind/owner_goals/store.py +649 -0
  287. colonymind/owner_goals/validation.py +239 -0
  288. colonymind/plasticity/__init__.py +24 -0
  289. colonymind/plasticity/_io.py +139 -0
  290. colonymind/plasticity/admission.py +358 -0
  291. colonymind/plasticity/audience.py +111 -0
  292. colonymind/plasticity/budgets.py +122 -0
  293. colonymind/plasticity/builds.py +211 -0
  294. colonymind/plasticity/certificates.py +56 -0
  295. colonymind/plasticity/cli.py +84 -0
  296. colonymind/plasticity/compaction.py +62 -0
  297. colonymind/plasticity/comparator.py +433 -0
  298. colonymind/plasticity/erasure.py +488 -0
  299. colonymind/plasticity/forced.py +49 -0
  300. colonymind/plasticity/gates.py +407 -0
  301. colonymind/plasticity/learners/__init__.py +11 -0
  302. colonymind/plasticity/learners/action_success.py +586 -0
  303. colonymind/plasticity/learners/behaviour.py +617 -0
  304. colonymind/plasticity/learners/contact_reply_timing.py +539 -0
  305. colonymind/plasticity/learners/query_adapter.py +642 -0
  306. colonymind/plasticity/learners/reply_timing.py +599 -0
  307. colonymind/plasticity/learners/salience.py +899 -0
  308. colonymind/plasticity/learners/service_health.py +564 -0
  309. colonymind/plasticity/lineage.py +159 -0
  310. colonymind/plasticity/locking.py +94 -0
  311. colonymind/plasticity/manifests.py +196 -0
  312. colonymind/plasticity/nightly.py +1170 -0
  313. colonymind/plasticity/numeric.py +490 -0
  314. colonymind/plasticity/numeric_comparator.py +254 -0
  315. colonymind/plasticity/objectives.py +220 -0
  316. colonymind/plasticity/periods.py +123 -0
  317. colonymind/plasticity/query_adapter.py +421 -0
  318. colonymind/plasticity/query_log.py +235 -0
  319. colonymind/plasticity/recall_qualify.py +408 -0
  320. colonymind/plasticity/registration.py +144 -0
  321. colonymind/plasticity/revisions.py +443 -0
  322. colonymind/plasticity/rows.py +32 -0
  323. colonymind/plasticity/states.py +267 -0
  324. colonymind/plasticity/store.py +242 -0
  325. colonymind/plasticity/synthetic.py +383 -0
  326. colonymind/plasticity/teachers.py +144 -0
  327. colonymind/qualification/__init__.py +1 -0
  328. colonymind/qualification/benchmark.py +274 -0
  329. colonymind/qualification/benchmark_cases.py +143 -0
  330. colonymind/qualification/cases.py +90 -0
  331. colonymind/qualification/cli.py +93 -0
  332. colonymind/qualification/coding.py +207 -0
  333. colonymind/qualification/coding_sandbox.py +202 -0
  334. colonymind/qualification/coding_verify_worker.py +36 -0
  335. colonymind/qualification/coding_worker.py +98 -0
  336. colonymind/qualification/evidence_cases.py +185 -0
  337. colonymind/qualification/fixtures/coding-development.json +542 -0
  338. colonymind/qualification/fixtures/decision-role-screen-1/manifest.json +65 -0
  339. colonymind/qualification/fixtures/decision-role-screen-1/scenarios.json +2134 -0
  340. colonymind/qualification/fixtures/interaction-frame-a.png +0 -0
  341. colonymind/qualification/fixtures/interaction-frame-b.png +0 -0
  342. colonymind/qualification/fixtures/paired-agent-reviewed-2/manifest.json +44 -0
  343. colonymind/qualification/fixtures/paired-agent-reviewed-2/scenarios.json +3419 -0
  344. colonymind/qualification/fixtures/paired-agent-workflows-1/manifest.json +61 -0
  345. colonymind/qualification/fixtures/paired-agent-workflows-1/scenarios.json +4852 -0
  346. colonymind/qualification/fixtures/vision-arrangement.png +0 -0
  347. colonymind/qualification/fixtures/vision-covered-label.png +0 -0
  348. colonymind/qualification/fixtures/vision-labels.png +0 -0
  349. colonymind/qualification/formation_extended.py +213 -0
  350. colonymind/qualification/interaction_evidence.py +83 -0
  351. colonymind/qualification/memory_cases.py +222 -0
  352. colonymind/qualification/native.py +222 -0
  353. colonymind/qualification/native_authority.py +113 -0
  354. colonymind/qualification/native_authority_cases.py +62 -0
  355. colonymind/qualification/native_authority_worker.py +115 -0
  356. colonymind/qualification/native_coding.py +54 -0
  357. colonymind/qualification/native_contact_identity.py +113 -0
  358. colonymind/qualification/native_identity.py +51 -0
  359. colonymind/qualification/native_identity_cases.py +60 -0
  360. colonymind/qualification/native_identity_worker.py +127 -0
  361. colonymind/qualification/native_interactive.py +102 -0
  362. colonymind/qualification/native_interactive_cases.py +96 -0
  363. colonymind/qualification/native_interactive_worker.py +282 -0
  364. colonymind/qualification/native_memory.py +155 -0
  365. colonymind/qualification/native_memory_batch.py +136 -0
  366. colonymind/qualification/native_memory_cases.py +79 -0
  367. colonymind/qualification/native_memory_identity.py +31 -0
  368. colonymind/qualification/native_memory_worker.py +671 -0
  369. colonymind/qualification/native_perspective.py +155 -0
  370. colonymind/qualification/native_perspective_cases.py +49 -0
  371. colonymind/qualification/native_perspective_worker.py +69 -0
  372. colonymind/qualification/native_reasoning.py +36 -0
  373. colonymind/qualification/native_recovery.py +69 -0
  374. colonymind/qualification/native_recovery_worker.py +87 -0
  375. colonymind/qualification/native_semantic_recall.py +84 -0
  376. colonymind/qualification/native_semantic_worker.py +30 -0
  377. colonymind/qualification/native_unified.py +128 -0
  378. colonymind/qualification/native_unified_base_worker.py +75 -0
  379. colonymind/qualification/native_unified_cases.py +57 -0
  380. colonymind/qualification/native_unified_host.py +73 -0
  381. colonymind/qualification/native_unified_worker.py +157 -0
  382. colonymind/qualification/native_worker.py +136 -0
  383. colonymind/qualification/pack_batch.py +314 -0
  384. colonymind/qualification/pack_registry.py +61 -0
  385. colonymind/qualification/paired.py +787 -0
  386. colonymind/qualification/paired_arms.py +198 -0
  387. colonymind/qualification/paired_body.py +349 -0
  388. colonymind/qualification/paired_body_grading.py +488 -0
  389. colonymind/qualification/paired_cases.py +730 -0
  390. colonymind/qualification/paired_container.py +252 -0
  391. colonymind/qualification/paired_history.py +262 -0
  392. colonymind/qualification/paired_public.py +270 -0
  393. colonymind/qualification/paired_report.py +1350 -0
  394. colonymind/qualification/paired_statistics.py +195 -0
  395. colonymind/qualification/paired_suite.py +361 -0
  396. colonymind/qualification/paired_trace.py +70 -0
  397. colonymind/qualification/paired_transport.py +256 -0
  398. colonymind/qualification/paired_worker.py +1489 -0
  399. colonymind/qualification/paired_workflow_grading.py +93 -0
  400. colonymind/qualification/paired_workflow_runtime.py +454 -0
  401. colonymind/qualification/planning_effects.py +48 -0
  402. colonymind/qualification/public_export.py +392 -0
  403. colonymind/qualification/recall_capture.py +142 -0
  404. colonymind/qualification/records.py +111 -0
  405. colonymind/qualification/recovery_cases.py +51 -0
  406. colonymind/qualification/recovery_fixture.py +87 -0
  407. colonymind/qualification/report.py +153 -0
  408. colonymind/qualification/router_recovery.py +174 -0
  409. colonymind/qualification/router_recovery_proxy.py +81 -0
  410. colonymind/qualification/runner.py +394 -0
  411. colonymind/qualification/schema_cases.py +105 -0
  412. colonymind/qualification/screen_contracts.py +151 -0
  413. colonymind/qualification/semantic_recall_cases.py +100 -0
  414. colonymind/qualification/semantic_recall_host.py +186 -0
  415. colonymind/qualification/structured_cases.py +131 -0
  416. colonymind/qualification/vision_cases.py +70 -0
  417. colonymind/recall_cli.py +321 -0
  418. colonymind/redact/__init__.py +179 -0
  419. colonymind/removed_settings.py +112 -0
  420. colonymind/reports/__init__.py +7 -0
  421. colonymind/reports/arms.py +229 -0
  422. colonymind/reports/bench.py +454 -0
  423. colonymind/reports/claims.py +175 -0
  424. colonymind/reports/cli.py +187 -0
  425. colonymind/reports/faculties.py +1070 -0
  426. colonymind/reports/hands.py +51 -0
  427. colonymind/reports/instance.py +331 -0
  428. colonymind/reports/instinct.py +356 -0
  429. colonymind/reports/plasticity.py +319 -0
  430. colonymind/reports/registry.py +122 -0
  431. colonymind/reports/senses.py +68 -0
  432. colonymind/reports/text.py +152 -0
  433. colonymind/reports/usage.py +170 -0
  434. colonymind/reports/work.py +38 -0
  435. colonymind/reports/working_memory.py +114 -0
  436. colonymind/resources.py +44 -0
  437. colonymind/router/__init__.py +17 -0
  438. colonymind/router/complexity_scorer.py +137 -0
  439. colonymind/router/endpoints.py +156 -0
  440. colonymind/router/fallback.py +58 -0
  441. colonymind/router/functions.py +320 -0
  442. colonymind/router/router.py +840 -0
  443. colonymind/router/tiers.py +677 -0
  444. colonymind/router/tokens.py +45 -0
  445. colonymind/runtime_logging.py +195 -0
  446. colonymind/scope_bounds.py +7 -0
  447. colonymind/secrets/__init__.py +17 -0
  448. colonymind/secrets/claims.py +139 -0
  449. colonymind/secrets/cli.py +185 -0
  450. colonymind/secrets/config.py +133 -0
  451. colonymind/secrets/detect.py +21 -0
  452. colonymind/secrets/migrate.py +635 -0
  453. colonymind/secrets/refs.py +59 -0
  454. colonymind/secrets/saved_login.py +80 -0
  455. colonymind/secrets/seal.py +448 -0
  456. colonymind/secrets/vault.py +482 -0
  457. colonymind/self_model/__init__.py +10 -0
  458. colonymind/self_model/appraisals.py +1019 -0
  459. colonymind/self_model/brief.py +36 -0
  460. colonymind/self_model/expectations.py +1457 -0
  461. colonymind/self_model/journal.py +119 -0
  462. colonymind/self_model/judgments.py +1253 -0
  463. colonymind/self_model/perspective.py +200 -0
  464. colonymind/self_model/reply_forecasts.py +449 -0
  465. colonymind/self_model/runtime_forecasts.py +35 -0
  466. colonymind/self_model/store.py +618 -0
  467. colonymind/self_model/task_assessments.py +14 -0
  468. colonymind/server.py +744 -0
  469. colonymind/services/__init__.py +1 -0
  470. colonymind/services/instance.py +487 -0
  471. colonymind/session_archive/__init__.py +1 -0
  472. colonymind/session_archive/reparent.py +424 -0
  473. colonymind/session_archive/store.py +547 -0
  474. colonymind/setup.py +181 -0
  475. colonymind/telemetry.py +150 -0
  476. colonymind/turns/__init__.py +17 -0
  477. colonymind/turns/audio.py +156 -0
  478. colonymind/turns/board_observations.py +73 -0
  479. colonymind/turns/claim_keys.py +170 -0
  480. colonymind/turns/documents.py +239 -0
  481. colonymind/turns/erasure_hooks.py +28 -0
  482. colonymind/turns/executions.py +265 -0
  483. colonymind/turns/hermes_history.py +248 -0
  484. colonymind/turns/hermes_work.py +95 -0
  485. colonymind/turns/history_references.py +51 -0
  486. colonymind/turns/idempotency.py +1387 -0
  487. colonymind/turns/keywords.py +216 -0
  488. colonymind/turns/media.py +671 -0
  489. colonymind/turns/media_names.py +30 -0
  490. colonymind/turns/observed.py +415 -0
  491. colonymind/turns/projection_backlog.py +117 -0
  492. colonymind/turns/reported_workers.py +196 -0
  493. colonymind/turns/source_annotations.py +365 -0
  494. colonymind/turns/source_attribution.py +170 -0
  495. colonymind/turns/source_channels.py +88 -0
  496. colonymind/turns/source_read.py +406 -0
  497. colonymind/turns/source_replies.py +132 -0
  498. colonymind/turns/source_vectors.py +506 -0
  499. colonymind/turns/video.py +210 -0
  500. colonymind/twin/__init__.py +7 -0
  501. colonymind/twin/cli.py +231 -0
  502. colonymind/twin/config.py +164 -0
  503. colonymind/twin/primary.py +553 -0
  504. colonymind/twin/segment.py +127 -0
  505. colonymind/twin/source.py +361 -0
  506. colonymind/twin/standby.py +587 -0
  507. colonymind/twin/transport.py +152 -0
  508. colonymind/twin/trees.py +258 -0
  509. colonymind/twin/wal.py +136 -0
  510. colonymind/usage.py +647 -0
  511. colonymind/util/instance.py +39 -0
  512. colonymind/util/model_output.py +25 -0
  513. colonymind/util/session_safety.py +38 -0
  514. colonymind/util/state_access.py +41 -0
  515. colonymind/util/temporal.py +446 -0
  516. colonymind/vector/__init__.py +64 -0
  517. colonymind/vector/backfill.py +171 -0
  518. colonymind/vector/caption.py +114 -0
  519. colonymind/vector/collections.py +51 -0
  520. colonymind/vector/compaction.py +276 -0
  521. colonymind/vector/config.py +88 -0
  522. colonymind/vector/embedder.py +662 -0
  523. colonymind/vector/http_client.py +152 -0
  524. colonymind/vector/image_preprocess.py +459 -0
  525. colonymind/vector/image_store.py +271 -0
  526. colonymind/vector/indexes.py +219 -0
  527. colonymind/vector/local_embedder.py +331 -0
  528. colonymind/vector/migrate.py +308 -0
  529. colonymind/vector/multimodal_provider.py +393 -0
  530. colonymind/vector/multimodal_types.py +59 -0
  531. colonymind/vector/openai_provider.py +159 -0
  532. colonymind/vector/query.py +26 -0
  533. colonymind/vector/reranker.py +443 -0
  534. colonymind/vector/safety_image.py +159 -0
  535. colonymind/vector/scanner.py +197 -0
  536. colonymind/vector/store.py +796 -0
  537. colonymind/vector/tiers.py +263 -0
  538. colonymind/work/__init__.py +72 -0
  539. colonymind/work/__main__.py +5 -0
  540. colonymind/work/digest.py +13 -0
  541. colonymind/work/hands.py +49 -0
  542. colonymind/work/hermes_jobs.py +237 -0
  543. colonymind/work/ledger.py +229 -0
  544. colonymind/work/models.py +134 -0
  545. colonymind/work/night.py +107 -0
  546. colonymind/work/schema/work-envelope.json +560 -0
  547. colonymind/work/schema/work-event.json +602 -0
  548. colonymind/work/schema/work-status.json +35 -0
  549. colonymind/work/schema/work-titles.json +44 -0
  550. colonymind/work/store.py +714 -0
  551. colonymind/work/verdicts.py +209 -0
  552. colonymind/work/verification.py +87 -0
  553. colonymind-1.0.0.dist-info/METADATA +110 -0
  554. colonymind-1.0.0.dist-info/RECORD +566 -0
  555. colonymind-1.0.0.dist-info/WHEEL +5 -0
  556. colonymind-1.0.0.dist-info/entry_points.txt +3 -0
  557. colonymind-1.0.0.dist-info/licenses/LICENSE +21 -0
  558. colonymind-1.0.0.dist-info/top_level.txt +3 -0
  559. colonymind_hand_context/__init__.py +20 -0
  560. colonymind_shared/__init__.py +1 -0
  561. colonymind_shared/effects.py +490 -0
  562. colonymind_shared/host_owner.py +102 -0
  563. colonymind_shared/midturn.py +193 -0
  564. colonymind_shared/secret_detection.py +285 -0
  565. colonymind_shared/turn_bindings.py +84 -0
  566. colonymind_shared/write_origin.py +18 -0
colonymind/__init__.py ADDED
@@ -0,0 +1,40 @@
1
+ """ColonyMind cognition server for agent runtimes.
2
+
3
+ A standalone FastAPI server that agent hosts (Hermes plugin, MCP harnesses,
4
+ REST integrations) mount via the ``/v1/host`` API surface.
5
+ """
6
+
7
+ from pathlib import Path
8
+ import os
9
+
10
+ # Local first: LiteLLM, which routes model requests, downloads its model price list from the internet when it is
11
+ # imported unless told to use the copy it ships. ColonyMind never reads prices from it, so the flag is set here,
12
+ # before anything of ColonyMind can import the library, and starting the sidecar makes no network request. An
13
+ # operator who sets it explicitly (to false, for the downloaded list) keeps that choice.
14
+ os.environ.setdefault("LITELLM_LOCAL_MODEL_COST_MAP", "True")
15
+
16
+ try: # single source of truth: the installed package metadata (pyproject)
17
+ from importlib.metadata import version as _pkg_version
18
+ __version__ = _pkg_version("colonymind")
19
+ except Exception: # editable/unbuilt checkouts without installed metadata
20
+ __version__ = "0.0.0+unknown"
21
+
22
+
23
+ def get_state_dir() -> Path:
24
+ """Return the selected state directory without moving existing state.
25
+
26
+ The instance directory (``$COLONYMIND_HOME``, default ``~/.colonymind``)
27
+ holds the state once ``colonymind init`` has written ``colonymind.yaml``.
28
+ Unconfigured library use defaults to ``~/.colonymind/data``.
29
+
30
+ Creates the directory if it does not exist.
31
+ """
32
+ explicit = os.environ.get("COLONYMIND_STATE_DIR") or os.environ.get("COLONYMIND_HOME")
33
+ if explicit:
34
+ path = Path(explicit).expanduser()
35
+ elif (Path.home() / ".colonymind" / "colonymind.yaml").is_file():
36
+ path = Path.home() / ".colonymind"
37
+ else:
38
+ path = Path.home() / ".colonymind" / "data"
39
+ path.mkdir(parents=True, exist_ok=True)
40
+ return path
colonymind/__main__.py ADDED
@@ -0,0 +1,6 @@
1
+ """Run the same CLI used by the installed ``colonymind`` command."""
2
+ from colonymind.cli import main
3
+
4
+
5
+ if __name__ == "__main__":
6
+ main()
@@ -0,0 +1,411 @@
1
+ """Provider-neutral retained mail. Content is data; nothing here invokes a model.
2
+
3
+ SQLite WAL transactions retain identity, content and replay tombstones together.
4
+ Each operation owns a connection; SQLite serializes writers across processes.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ import base64
9
+ from contextlib import closing, contextmanager, nullcontext
10
+ from datetime import datetime, timezone
11
+ import hashlib
12
+ import json
13
+ import os
14
+ from pathlib import Path
15
+ import sqlite3
16
+ import threading
17
+ from typing import Literal
18
+ from uuid import uuid4
19
+
20
+ from pydantic import BaseModel, ConfigDict, Field, field_validator
21
+
22
+ from colonymind.util.temporal import now_utc
23
+
24
+
25
+ class MailboxError(ValueError):
26
+ def __init__(self, code: str, status: int = 422):
27
+ super().__init__(code)
28
+ self.code = code
29
+ self.status = status
30
+
31
+
32
+ class LocalIdentity(BaseModel):
33
+ model_config = ConfigDict(extra="forbid", strict=True)
34
+ local_part: str = Field(pattern=r"^[a-z0-9][a-z0-9-]{1,30}[a-z0-9]$", max_length=32)
35
+ display_name: str = Field(min_length=1, max_length=128)
36
+
37
+ @field_validator("display_name")
38
+ @classmethod
39
+ def name_present(cls, value):
40
+ if not value.strip() or any(ord(c) < 32 for c in value):
41
+ raise ValueError("invalid_display_name")
42
+ return value
43
+
44
+
45
+ class IdentityUpdate(BaseModel):
46
+ model_config = ConfigDict(extra="forbid", strict=True)
47
+ display_name: str | None = Field(default=None, min_length=1, max_length=128)
48
+ status: Literal["unprovisioned", "active", "disabled"] | None = None
49
+
50
+ @field_validator("display_name")
51
+ @classmethod
52
+ def name_present(cls, value):
53
+ return LocalIdentity.name_present(value) if value is not None else value
54
+
55
+
56
+ class InboundMessage(BaseModel):
57
+ """Trusted adapter's envelope. Sender and all text remain untrusted."""
58
+ model_config = ConfigDict(extra="forbid")
59
+ delivery_id: str = Field(min_length=1, max_length=256)
60
+ thread_id: str = Field(pattern=r"^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$", max_length=128)
61
+ sender: str = Field(min_length=1, max_length=320)
62
+ recipients: list[str] = Field(min_length=1, max_length=100)
63
+ subject: str = Field(default="", max_length=998)
64
+ text: str = Field(default="", max_length=262144)
65
+ occurred_at: datetime
66
+ in_reply_to: str | None = Field(default=None, max_length=256)
67
+
68
+ @field_validator("occurred_at")
69
+ @classmethod
70
+ def aware_time(cls, value):
71
+ if value.utcoffset() is None:
72
+ raise ValueError("timezone_required")
73
+ return value.astimezone(timezone.utc)
74
+
75
+ @field_validator("recipients")
76
+ @classmethod
77
+ def bounded_addresses(cls, value):
78
+ if any(not address or len(address) > 320 for address in value):
79
+ raise ValueError("invalid_recipients")
80
+ return value
81
+
82
+
83
+ def _json(value):
84
+ return json.dumps(value, ensure_ascii=True, sort_keys=True, separators=(",", ":"))
85
+
86
+
87
+ class MailboxStore:
88
+ def __init__(self, path: Path):
89
+ self.path = Path(path)
90
+ self._init_lock = threading.Lock()
91
+ self._write_lock = threading.Lock()
92
+ self._initialized = False
93
+
94
+ def _connect(self):
95
+ self.path.parent.mkdir(mode=0o700, parents=True, exist_ok=True)
96
+ # Create privately before SQLite also creates its WAL/shared-memory files.
97
+ fd = os.open(self.path, os.O_CREAT | os.O_RDWR, 0o600)
98
+ try:
99
+ os.fchmod(fd, 0o600)
100
+ finally:
101
+ os.close(fd)
102
+ conn = sqlite3.connect(self.path, check_same_thread=False)
103
+ try:
104
+ conn.row_factory = sqlite3.Row
105
+ conn.execute("PRAGMA busy_timeout=5000")
106
+ conn.execute("PRAGMA synchronous=NORMAL")
107
+ conn.execute("PRAGMA foreign_keys=ON")
108
+ conn.execute("PRAGMA secure_delete=ON")
109
+ except BaseException:
110
+ conn.close()
111
+ raise
112
+ return conn
113
+
114
+ @staticmethod
115
+ def _create_tables(conn):
116
+ """Initialize/migrate once, serialized with other store openers."""
117
+ version = conn.execute("PRAGMA user_version").fetchone()[0]
118
+ if version > 1:
119
+ raise sqlite3.DatabaseError("unsupported mailbox schema version")
120
+ if version == 1:
121
+ return
122
+ for statement in (
123
+ """CREATE TABLE IF NOT EXISTS mailboxes (
124
+ mailbox_id TEXT PRIMARY KEY,
125
+ address TEXT NOT NULL UNIQUE,
126
+ display_name TEXT NOT NULL,
127
+ status TEXT NOT NULL CHECK(status IN ('unprovisioned', 'active', 'disabled'))
128
+ )""",
129
+ """CREATE TABLE IF NOT EXISTS messages (
130
+ sequence INTEGER PRIMARY KEY AUTOINCREMENT,
131
+ mailbox_id TEXT NOT NULL REFERENCES mailboxes(mailbox_id),
132
+ message_id TEXT NOT NULL UNIQUE,
133
+ delivery_id TEXT NOT NULL,
134
+ thread_id TEXT NOT NULL,
135
+ sender TEXT NOT NULL,
136
+ recipients TEXT NOT NULL,
137
+ subject TEXT NOT NULL,
138
+ text TEXT NOT NULL,
139
+ occurred_at TEXT NOT NULL,
140
+ in_reply_to TEXT,
141
+ received_at TEXT NOT NULL,
142
+ read INTEGER NOT NULL DEFAULT 0 CHECK(read IN (0, 1)),
143
+ UNIQUE(mailbox_id, delivery_id)
144
+ )""",
145
+ """CREATE TABLE IF NOT EXISTS deliveries (
146
+ mailbox_id TEXT NOT NULL REFERENCES mailboxes(mailbox_id),
147
+ delivery_id TEXT NOT NULL,
148
+ message_id TEXT NOT NULL UNIQUE,
149
+ digest TEXT NOT NULL,
150
+ deleted INTEGER NOT NULL DEFAULT 0 CHECK(deleted IN (0, 1)),
151
+ PRIMARY KEY(mailbox_id, delivery_id)
152
+ )""",
153
+ """CREATE TABLE IF NOT EXISTS deleted_threads (
154
+ mailbox_id TEXT NOT NULL REFERENCES mailboxes(mailbox_id),
155
+ thread_id TEXT NOT NULL,
156
+ PRIMARY KEY(mailbox_id, thread_id)
157
+ )""",
158
+ "CREATE INDEX IF NOT EXISTS idx_messages_thread ON messages(mailbox_id, thread_id, sequence)",
159
+ "CREATE INDEX IF NOT EXISTS idx_messages_unread ON messages(mailbox_id, read, thread_id)",
160
+ ):
161
+ conn.execute(statement)
162
+ conn.execute("PRAGMA user_version=1")
163
+
164
+ def _initialize(self, conn):
165
+ if self._initialized:
166
+ return
167
+ with self._init_lock:
168
+ if self._initialized:
169
+ return
170
+ version = conn.execute("PRAGMA user_version").fetchone()[0]
171
+ if version > 1:
172
+ raise sqlite3.DatabaseError("unsupported mailbox schema version")
173
+ if version < 1:
174
+ conn.execute("PRAGMA journal_mode=WAL")
175
+ with conn:
176
+ conn.execute("BEGIN IMMEDIATE")
177
+ self._create_tables(conn)
178
+ self._initialized = True
179
+
180
+ @contextmanager
181
+ def _transaction(self, *, write=False, purge=False):
182
+ # Readers use their own WAL snapshots, without reserving a writer slot
183
+ # or waiting behind an in-process writer. Only initialization is shared.
184
+ with closing(self._connect()) as conn:
185
+ self._initialize(conn)
186
+ with self._write_lock if write else nullcontext():
187
+ with conn:
188
+ conn.execute("BEGIN IMMEDIATE" if write else "BEGIN")
189
+ yield conn
190
+ if purge:
191
+ # secure_delete overwrites deleted pages, but old WAL frames
192
+ # retain plaintext until a successful truncating checkpoint.
193
+ if conn.execute("PRAGMA wal_checkpoint(TRUNCATE)").fetchone()[0]:
194
+ raise MailboxError("mailbox_busy", 503)
195
+
196
+ @staticmethod
197
+ def _mailbox(conn, mailbox_id):
198
+ mailbox = conn.execute("SELECT * FROM mailboxes WHERE mailbox_id=?", (mailbox_id,)).fetchone()
199
+ if mailbox is None:
200
+ raise MailboxError("mailbox_not_found", 404)
201
+ return {key: mailbox[key] for key in sorted(mailbox.keys())}
202
+
203
+ @staticmethod
204
+ def _messages(conn, mailbox_id, thread_id, snapshot, position, limit):
205
+ rows = conn.execute(
206
+ "SELECT sequence, mailbox_id, message_id, thread_id, sender, recipients, subject, text, "
207
+ "occurred_at, in_reply_to, received_at, read FROM messages "
208
+ "WHERE mailbox_id=? AND thread_id=? AND sequence<=? AND sequence>? "
209
+ "ORDER BY sequence LIMIT ?",
210
+ (mailbox_id, thread_id, snapshot, position or 0, limit)).fetchall()
211
+ messages = []
212
+ for row in rows:
213
+ # Preserve the existing JSON projection's field order and value types.
214
+ message = {key: row[key] for key in sorted(row.keys())}
215
+ message["recipients"] = json.loads(message["recipients"])
216
+ message["read"] = bool(message["read"])
217
+ messages.append(message)
218
+ return messages
219
+
220
+ @staticmethod
221
+ def _counts(conn, mailbox_id):
222
+ return dict(conn.execute(
223
+ "SELECT COUNT(*) AS unread_count, COUNT(DISTINCT thread_id) AS unread_thread_count "
224
+ "FROM messages WHERE mailbox_id=? AND read=0", (mailbox_id,)).fetchone())
225
+
226
+ def identities(self):
227
+ with self._transaction() as conn:
228
+ return {"mailboxes": [{key: row[key] for key in sorted(row.keys())}
229
+ for row in conn.execute("SELECT * FROM mailboxes ORDER BY mailbox_id")]}
230
+
231
+ def create(self, body: LocalIdentity):
232
+ # The mail domain is the deployment's own value: no default, and no identity without it.
233
+ domain = os.environ.get("COLONYMIND_AGENT_MAIL_DOMAIN", "").strip().lower()
234
+ if not domain:
235
+ raise MailboxError("mail_domain_not_configured", 501)
236
+ address = body.local_part + "@" + domain
237
+ with self._transaction(write=True) as conn:
238
+ if conn.execute("SELECT 1 FROM mailboxes WHERE address=?", (address,)).fetchone():
239
+ raise MailboxError("address_already_registered", 409)
240
+ mailbox = {"mailbox_id": str(uuid4()), "address": address,
241
+ "display_name": body.display_name, "status": "unprovisioned"}
242
+ conn.execute("INSERT INTO mailboxes (mailbox_id, address, display_name, status) VALUES (?, ?, ?, ?)",
243
+ tuple(mailbox.values()))
244
+ return mailbox
245
+
246
+ def identity(self, mailbox_id):
247
+ with self._transaction() as conn:
248
+ return {**self._mailbox(conn, mailbox_id), **self._counts(conn, mailbox_id)}
249
+
250
+ def update(self, mailbox_id, body: IdentityUpdate):
251
+ with self._transaction(write=True) as conn:
252
+ mailbox = self._mailbox(conn, mailbox_id)
253
+ mailbox.update(body.model_dump(exclude_none=True))
254
+ conn.execute("UPDATE mailboxes SET display_name=?, status=? WHERE mailbox_id=?",
255
+ (mailbox["display_name"], mailbox["status"], mailbox_id))
256
+ return mailbox
257
+
258
+ def receive(self, mailbox_id: str, message: InboundMessage):
259
+ """Persist an adapter-authenticated delivery; never execute or trust its text."""
260
+ payload = message.model_dump(mode="json")
261
+ digest = hashlib.sha256(_json(payload).encode()).hexdigest()
262
+ with self._transaction(write=True) as conn:
263
+ mailbox = self._mailbox(conn, mailbox_id)
264
+ previous = conn.execute("SELECT * FROM deliveries WHERE mailbox_id=? AND delivery_id=?",
265
+ (mailbox_id, message.delivery_id)).fetchone()
266
+ if previous:
267
+ if previous["digest"] != digest:
268
+ raise MailboxError("delivery_conflict", 409)
269
+ return {"message_id": previous["message_id"], "thread_id": message.thread_id,
270
+ "duplicate": True, "deleted": bool(previous["deleted"])}
271
+ if mailbox["status"] != "active":
272
+ raise MailboxError("mailbox_inactive", 409)
273
+ if mailbox["address"] not in {a.lower() for a in message.recipients}:
274
+ raise MailboxError("recipient_mismatch")
275
+ conn.execute("DELETE FROM deleted_threads WHERE mailbox_id=? AND thread_id=?",
276
+ (mailbox_id, message.thread_id))
277
+ ident = str(uuid4())
278
+ conn.execute(
279
+ "INSERT INTO messages (mailbox_id, message_id, delivery_id, thread_id, sender, recipients, "
280
+ "subject, text, occurred_at, in_reply_to, received_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
281
+ (mailbox_id, ident, message.delivery_id, message.thread_id, message.sender,
282
+ _json(message.recipients), message.subject, message.text, payload["occurred_at"],
283
+ message.in_reply_to, now_utc().isoformat()))
284
+ conn.execute("INSERT INTO deliveries (mailbox_id, delivery_id, message_id, digest) VALUES (?, ?, ?, ?)",
285
+ (mailbox_id, message.delivery_id, ident, digest))
286
+ return {"message_id": ident, "thread_id": message.thread_id,
287
+ "duplicate": False, "deleted": False}
288
+
289
+ @staticmethod
290
+ def _page(conn, mailbox_id, thread_id, limit, cursor):
291
+ if not 1 <= limit <= 100:
292
+ raise MailboxError("invalid_limit")
293
+ row = conn.execute("SELECT seq FROM sqlite_sequence WHERE name='messages'").fetchone()
294
+ sequence = row[0] if row else 0
295
+ snapshot, position = sequence, None
296
+ if cursor is not None:
297
+ try:
298
+ if not cursor or len(cursor) > 1024:
299
+ raise ValueError
300
+ raw = base64.b64decode(cursor + "=" * (-len(cursor) % 4), altchars=b"-_", validate=True)
301
+ resource, snapshot, position = json.loads(raw)
302
+ if (resource != [mailbox_id, thread_id] or type(snapshot) is not int
303
+ or type(position) is not int or not 0 < position <= snapshot <= sequence):
304
+ raise ValueError
305
+ except (ValueError, TypeError, UnicodeError):
306
+ raise MailboxError("invalid_cursor") from None
307
+ return snapshot, position
308
+
309
+ @staticmethod
310
+ def _continuation(mailbox_id, thread_id, snapshot, position, has_more):
311
+ cursor = None
312
+ if has_more:
313
+ cursor = base64.urlsafe_b64encode(_json([[mailbox_id, thread_id], snapshot, position]).encode()).decode().rstrip("=")
314
+ return {"has_more": has_more, "next_cursor": cursor}
315
+
316
+ @staticmethod
317
+ def _thread(row):
318
+ unread = row["unread_count"]
319
+ return {"thread_id": row["thread_id"], "subject": row["subject"], "sender": row["sender"],
320
+ "summary": (row["summary"] or b"").decode("utf-8", errors="ignore")[:240],
321
+ "message_count": row["message_count"],
322
+ "received_at": row["received_at"], "unread_count": unread,
323
+ "unread": bool(unread), "content_trust": "untrusted"}
324
+
325
+ @staticmethod
326
+ def _thread_rows(conn, mailbox_id, snapshot, *, thread_id=None, position=None, limit=1):
327
+ parameters = [mailbox_id, snapshot]
328
+ predicate = "mailbox_id=? AND sequence<=?"
329
+ if thread_id is not None:
330
+ predicate += " AND thread_id=?"
331
+ parameters.append(thread_id)
332
+ continuation = ""
333
+ if position is not None:
334
+ continuation = "HAVING MAX(sequence)<?"
335
+ parameters.append(position)
336
+ parameters.append(limit)
337
+ # Aggregate metadata before joining the bounded page to its latest mail.
338
+ # A UTF-8 excerpt needs at most 4 bytes per character; BLOB substr also
339
+ # preserves embedded NULs that SQLite's TEXT substr would cut short.
340
+ return conn.execute(
341
+ f"""WITH page AS (
342
+ SELECT thread_id, MAX(sequence) AS sequence, COUNT(*) AS message_count
343
+ FROM messages WHERE {predicate} GROUP BY thread_id {continuation}
344
+ ORDER BY sequence DESC LIMIT ?
345
+ )
346
+ SELECT page.sequence, latest.thread_id, latest.subject, latest.sender,
347
+ substr(CAST(latest.text AS BLOB), 1, 960) AS summary,
348
+ page.message_count, latest.received_at,
349
+ (SELECT COUNT(*) FROM messages AS unread
350
+ WHERE unread.mailbox_id=latest.mailbox_id AND unread.thread_id=latest.thread_id
351
+ AND unread.read=0) AS unread_count
352
+ FROM page JOIN messages AS latest ON latest.sequence=page.sequence
353
+ ORDER BY page.sequence DESC""", parameters).fetchall()
354
+
355
+ def threads(self, mailbox_id, *, limit=50, cursor=None):
356
+ with self._transaction() as conn:
357
+ self._mailbox(conn, mailbox_id)
358
+ snapshot, position = self._page(conn, mailbox_id, None, limit, cursor)
359
+ rows = self._thread_rows(conn, mailbox_id, snapshot, position=position, limit=limit + 1)
360
+ page = rows[:limit]
361
+ return {"threads": [self._thread(row) for row in page], **self._counts(conn, mailbox_id),
362
+ **self._continuation(mailbox_id, None, snapshot,
363
+ page[-1]["sequence"] if page else 0, len(rows) > limit)}
364
+
365
+ def thread(self, mailbox_id, thread_id, *, limit=50, cursor=None):
366
+ with self._transaction() as conn:
367
+ self._mailbox(conn, mailbox_id)
368
+ snapshot, position = self._page(conn, mailbox_id, thread_id, limit, cursor)
369
+ rows = self._thread_rows(conn, mailbox_id, snapshot, thread_id=thread_id)
370
+ if not rows:
371
+ raise MailboxError("thread_not_found", 404)
372
+ page = self._messages(conn, mailbox_id, thread_id, snapshot, position, limit)
373
+ last = page[-1]["sequence"] if page else position or 0
374
+ has_more = bool(conn.execute(
375
+ "SELECT EXISTS(SELECT 1 FROM messages WHERE mailbox_id=? AND thread_id=? "
376
+ "AND sequence>? AND sequence<=?)", (mailbox_id, thread_id, last, snapshot)).fetchone()[0])
377
+ return {"thread": self._thread(rows[0]), "messages": [
378
+ {**{key: value for key, value in row.items() if key not in ("sequence", "delivery_id")},
379
+ "content_trust": "untrusted"} for row in page],
380
+ **self._continuation(mailbox_id, thread_id, snapshot, last, has_more)}
381
+
382
+ def read(self, mailbox_id, thread_id, through_message_id):
383
+ with self._transaction(write=True) as conn:
384
+ self._mailbox(conn, mailbox_id)
385
+ if not conn.execute("SELECT 1 FROM messages WHERE mailbox_id=? AND thread_id=?",
386
+ (mailbox_id, thread_id)).fetchone():
387
+ raise MailboxError("thread_not_found", 404)
388
+ marker = conn.execute(
389
+ "SELECT sequence FROM messages WHERE mailbox_id=? AND thread_id=? AND message_id=?",
390
+ (mailbox_id, thread_id, through_message_id)).fetchone()
391
+ if marker is None:
392
+ raise MailboxError("invalid_read_marker")
393
+ changed = conn.execute(
394
+ "UPDATE messages SET read=1 WHERE mailbox_id=? AND thread_id=? AND sequence<=? AND read=0",
395
+ (mailbox_id, thread_id, marker["sequence"])).rowcount
396
+ return {"thread_id": thread_id, "read_count": changed, **self._counts(conn, mailbox_id)}
397
+
398
+ def delete(self, mailbox_id, thread_id):
399
+ with self._transaction(write=True, purge=True) as conn:
400
+ self._mailbox(conn, mailbox_id)
401
+ parameters = (mailbox_id, thread_id)
402
+ present = conn.execute("SELECT 1 FROM messages WHERE mailbox_id=? AND thread_id=?", parameters).fetchone()
403
+ deleted = conn.execute("SELECT 1 FROM deleted_threads WHERE mailbox_id=? AND thread_id=?", parameters).fetchone()
404
+ if not present and not deleted:
405
+ raise MailboxError("thread_not_found", 404)
406
+ conn.execute("UPDATE deliveries SET deleted=1 WHERE mailbox_id=? AND message_id IN "
407
+ "(SELECT message_id FROM messages WHERE mailbox_id=? AND thread_id=?)",
408
+ (mailbox_id, mailbox_id, thread_id))
409
+ conn.execute("DELETE FROM messages WHERE mailbox_id=? AND thread_id=?", parameters)
410
+ conn.execute("INSERT OR IGNORE INTO deleted_threads (mailbox_id, thread_id) VALUES (?, ?)", parameters)
411
+ return {"thread_id": thread_id, "deleted": True, **self._counts(conn, mailbox_id)}
@@ -0,0 +1 @@
1
+ # api
@@ -0,0 +1,30 @@
1
+ """Deployment engine: durable pull queue only; all job logic runs in Hermes."""
2
+ import asyncio
3
+
4
+ from colonymind.session_archive.store import ArchiveError
5
+
6
+
7
+ class ArchiveQueueClient:
8
+ def __init__(self, store, *, timeout=15):
9
+ self.store, self.timeout = store, timeout
10
+
11
+ def available(self):
12
+ return self.store.worker_available()
13
+
14
+ def __call__(self, *, phase, **kwargs):
15
+ # Receipt reads consult only the overlay; no gateway code is imported.
16
+ if phase != 'validate':
17
+ raise ArchiveError('source_unavailable')
18
+
19
+ async def archive(self, session_id, body, authority):
20
+ await asyncio.to_thread(self.store.enqueue, session_id, body, authority)
21
+ deadline = asyncio.get_running_loop().time() + self.timeout
22
+ while True:
23
+ result = await asyncio.to_thread(self.store.plan_result, body['operation_id'])
24
+ if result is not None:
25
+ if 'error' in result:
26
+ raise ArchiveError(result['error'], **{k: v for k, v in result.items() if k != 'error'})
27
+ return result
28
+ if asyncio.get_running_loop().time() >= deadline:
29
+ raise ArchiveError('source_unavailable', pending=True, operation_id=body['operation_id'])
30
+ await asyncio.sleep(.1)