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,410 @@
1
+ """Reply expectations on the commitment ledger, with no scheduler or sender.
2
+
3
+ Host adapters supply authenticated contact/source/transport evidence. The normal
4
+ Hermes tick calls due(), and the selected outbox remains the only delivery
5
+ owner. Worker preparation and sending each recheck preflight independently.
6
+ """
7
+ from contextlib import closing, contextmanager
8
+ from datetime import datetime, timezone
9
+ import hashlib
10
+ import json
11
+ import math
12
+ import time
13
+ from zoneinfo import ZoneInfo
14
+
15
+ from apsimo.commitments.store import OPEN_STATUSES
16
+
17
+
18
+ TERMINAL = frozenset({'resolved', 'cancelled', 'expired'})
19
+
20
+
21
+ def encoded(value):
22
+ return json.dumps(value, sort_keys=True, separators=(',', ':'), ensure_ascii=False)
23
+
24
+
25
+ def epoch(value):
26
+ if isinstance(value, bool):
27
+ raise ValueError('invalid time')
28
+ if isinstance(value, str):
29
+ value = datetime.fromisoformat(value.replace('Z', '+00:00'))
30
+ if value.tzinfo is None:
31
+ raise ValueError('local time requires an explicit UTC offset')
32
+ value = value.timestamp()
33
+ if not isinstance(value, (int, float)) or not math.isfinite(value) or value <= 0:
34
+ raise ValueError('invalid time')
35
+ return float(value)
36
+
37
+
38
+ def reference(value):
39
+ if not isinstance(value, str) or not value or len(value) > 256 or any(ord(c) < 32 for c in value):
40
+ raise ValueError('durable reference required')
41
+ return value
42
+
43
+
44
+ def minute(value):
45
+ if not isinstance(value, str) or len(value) != 5 or value[2] != ':':
46
+ raise ValueError('local window must use HH:MM')
47
+ hour, minutes = map(int, value.split(':'))
48
+ if not 0 <= hour <= 23 or not 0 <= minutes <= 59:
49
+ raise ValueError('invalid local window')
50
+ return hour * 60 + minutes
51
+
52
+
53
+ def in_window(current, start, end):
54
+ return start <= current < end if start < end else current >= start or current < end
55
+
56
+
57
+ def consent_matches(*, authority, binding, scope, now):
58
+ """Check existing consumed authority; never issue or consume another grant.
59
+
60
+ The host constructs both scope and ActionBinding from the immutable task
61
+ action (recipient, channels, purpose, bounds, expiry), never model prose.
62
+ A last consumed use may have exhausted its grant and remains valid until
63
+ expiry/revocation. An unrelated task or materially changed scope cannot fit.
64
+ """
65
+ if not scope or binding is None or authority is None or encoded(binding.scope) != encoded(scope):
66
+ return False
67
+ digest = hashlib.sha256(encoded(scope).encode()).hexdigest()
68
+ if digest != binding.scope_digest:
69
+ return False
70
+ use = authority.get_grant_use(binding.action_digest)
71
+ if not use or use.get('scope_digest') != digest or use.get('grant_status') not in {'active', 'exhausted'}:
72
+ return False
73
+ if use.get('grant_expires_at') is not None and epoch(use['grant_expires_at']) <= now:
74
+ return False
75
+ grants = authority.list_grants(now=datetime.fromtimestamp(now, timezone.utc))
76
+ return any(g['grant_id'] == use['grant_id'] and g['status'] in {'active', 'exhausted'}
77
+ and encoded(g['scope']) == encoded(scope) for g in grants)
78
+
79
+
80
+ class TemporalFollowups:
81
+ """One waiting row per stable condition, in the existing commitments DB.
82
+
83
+ authority_scope is an immutable *configuration match*, not authorization.
84
+ Caller identity, privacy visibility and task ownership are host concerns.
85
+ No delivery status is copied: outbox_state is observed at the send boundary.
86
+ """
87
+ def __init__(self, commitment_store, *, clock=time.time):
88
+ self.store, self.clock = commitment_store, clock
89
+ with closing(self.store._connect()) as db:
90
+ db.executescript('''
91
+ CREATE TABLE IF NOT EXISTS temporal_followups (
92
+ wait_id TEXT PRIMARY KEY, commitment_id TEXT NOT NULL,
93
+ work_id TEXT NOT NULL, contact_id TEXT NOT NULL,
94
+ state TEXT NOT NULL, revision INTEGER NOT NULL,
95
+ material_digest TEXT NOT NULL, payload TEXT NOT NULL,
96
+ created_at REAL NOT NULL, updated_at REAL NOT NULL
97
+ );
98
+ CREATE INDEX IF NOT EXISTS temporal_followups_contact_state
99
+ ON temporal_followups(contact_id,state);
100
+ ''')
101
+ db.commit()
102
+
103
+ @contextmanager
104
+ def transaction(self):
105
+ with closing(self.store._connect()) as db:
106
+ try:
107
+ db.execute('BEGIN IMMEDIATE')
108
+ yield db
109
+ db.commit()
110
+ except Exception:
111
+ db.rollback()
112
+ raise
113
+
114
+ @staticmethod
115
+ def _row(row):
116
+ if row is None:
117
+ raise ValueError('unknown reply expectation')
118
+ return {**json.loads(row['payload']), **{key: row[key] for key in (
119
+ 'wait_id', 'commitment_id', 'work_id', 'contact_id', 'state', 'revision', 'created_at', 'updated_at')}}
120
+
121
+ def _get(self, db, wait_id):
122
+ return self._row(db.execute('SELECT * FROM temporal_followups WHERE wait_id=?', (wait_id,)).fetchone())
123
+
124
+ def _save(self, db, row):
125
+ row['revision'] += 1
126
+ row['updated_at'] = self.clock()
127
+ db.execute('UPDATE temporal_followups SET state=?,revision=?,payload=?,updated_at=? WHERE wait_id=?',
128
+ (row['state'], row['revision'], encoded(row), row['updated_at'], row['wait_id']))
129
+ return row
130
+
131
+ def get(self, wait_id):
132
+ with closing(self.store._connect()) as db:
133
+ return self._get(db, wait_id)
134
+
135
+ def expect_reply(self, *, wait_id, commitment_id, work_id, contact_id,
136
+ outbound_ref, source_refs, expected_after_seconds, expires_at,
137
+ timezone_name='UTC', quiet_start=None, quiet_end=None,
138
+ availability_start=None, availability_end=None,
139
+ forecast_id='', authority_scope=None, promised_at=None,
140
+ original_local_text='', source_versions=None, source_session_id=''):
141
+ for value in (wait_id, commitment_id, work_id, contact_id, outbound_ref):
142
+ reference(value)
143
+ refs = sorted({reference(r) for r in source_refs})
144
+ if not refs or len(refs) > 40:
145
+ raise ValueError('reply expectation requires bounded source evidence')
146
+ versions = dict(source_versions or {})
147
+ if set(versions) != set(refs) or any(not isinstance(v, str) or not v for v in versions.values()):
148
+ raise ValueError('reply expectation requires exact source versions')
149
+ seconds = epoch(expected_after_seconds)
150
+ expiry = epoch(expires_at)
151
+ ZoneInfo(timezone_name)
152
+ for start, end in ((quiet_start, quiet_end), (availability_start, availability_end)):
153
+ if (start is None) != (end is None):
154
+ raise ValueError('both local window bounds required')
155
+ if start is not None and minute(start) == minute(end):
156
+ raise ValueError('local window must have nonzero duration')
157
+ if len(original_local_text) > 256:
158
+ raise ValueError('local interpretation too long')
159
+ material = dict(wait_id=wait_id, commitment_id=commitment_id, work_id=work_id,
160
+ contact_id=contact_id, outbound_ref=outbound_ref, source_refs=refs,
161
+ source_versions=versions, source_session_id=source_session_id, expected_after_seconds=seconds,
162
+ expires_at=expiry, timezone_name=timezone_name, quiet_start=quiet_start,
163
+ quiet_end=quiet_end, availability_start=availability_start,
164
+ availability_end=availability_end, forecast_id=forecast_id,
165
+ authority_scope=dict(authority_scope or {}), promised_at=epoch(promised_at) if promised_at else None,
166
+ original_local_text=original_local_text)
167
+ digest = hashlib.sha256(encoded(material).encode()).hexdigest()
168
+ with self.transaction() as db:
169
+ current = db.execute('SELECT * FROM temporal_followups WHERE wait_id=?', (wait_id,)).fetchone()
170
+ if current:
171
+ if current['material_digest'] != digest:
172
+ raise ValueError('reply condition identity conflict')
173
+ return self._row(current)
174
+ parent = db.execute('SELECT * FROM commitments WHERE id=?', (commitment_id,)).fetchone()
175
+ if not parent or parent['status'] not in OPEN_STATUSES:
176
+ raise ValueError('open parent commitment required')
177
+ now = self.clock()
178
+ if expiry <= now:
179
+ raise ValueError('reply expectation is already expired')
180
+ row = {**material, 'expected_at': None, 'dispatch_receipt_ref': None,
181
+ 'dispatch_occurred_at': None, 'next_review_at': None,
182
+ 'native_task_id': None, 'native_terminal_observed': False, 'native_terminal_status': None, 'reply': None, 'resolution_ref': None,
183
+ 'followup_receipt_ref': None, 'followup_action_digest': None}
184
+ db.execute('INSERT INTO temporal_followups VALUES (?,?,?,?,?,?,?,?,?,?)',
185
+ (wait_id, commitment_id, work_id, contact_id, 'open', 1, digest, encoded(row), now, now))
186
+ return self._get(db, wait_id)
187
+
188
+ def acknowledge_dispatch(self, wait_id, *, receipt_ref, occurred_at):
189
+ reference(receipt_ref)
190
+ occurred = epoch(occurred_at)
191
+ if occurred > self.clock():
192
+ raise ValueError('dispatch receipt cannot be in the future')
193
+ with self.transaction() as db:
194
+ row = self._get(db, wait_id)
195
+ if row['dispatch_receipt_ref'] is not None:
196
+ if (row['dispatch_receipt_ref'], row['dispatch_occurred_at']) != (receipt_ref, occurred):
197
+ raise ValueError('dispatch receipt conflict')
198
+ return row
199
+ row.update(dispatch_receipt_ref=receipt_ref, dispatch_occurred_at=occurred,
200
+ expected_at=occurred + row['expected_after_seconds'])
201
+ if row['state'] != 'deferred':
202
+ row['next_review_at'] = row['expected_at']
203
+ # A matching reply may arrive before this ACK. Do not reopen it.
204
+ return self._save(db, row)
205
+
206
+ def apply_reply(self, wait_id, match):
207
+ """Consume A's exact CommsLog match, never any-inbound activity.
208
+
209
+ Root obtains match from the authenticated ledger, not a tool argument.
210
+ Late replies remain linked even when waiting already expired/cancelled.
211
+ They do not rewrite the corresponding forecast deadline outcome.
212
+ """
213
+ with self.transaction() as db:
214
+ row = self._get(db, wait_id)
215
+ if match.get('status') != 'matched' or match.get('contact_id') != row['contact_id'] or match.get('outbound_ref') != row['outbound_ref']:
216
+ return row
217
+ matches = []
218
+ for item in match.get('matches', [])[:50]:
219
+ if item.get('reply_to_ref') != row['outbound_ref'] or not item.get('external_ref') or not item.get('receipt_ref') or not item.get('ts'):
220
+ continue
221
+ try:
222
+ stamp = epoch(item['ts'])
223
+ reference(item['external_ref'])
224
+ reference(item['receipt_ref'])
225
+ except (TypeError, ValueError):
226
+ continue
227
+ # Exact provider linkage can predate both wait registration
228
+ # and our first retained ACK. Use the parent obligation's
229
+ # actual origin, never an ACK timestamp as causal proof.
230
+ parent = db.execute('SELECT made_at FROM commitments WHERE id=?', (row['commitment_id'],)).fetchone()
231
+ if parent is None or stamp < epoch(parent['made_at']) or stamp > self.clock():
232
+ continue
233
+ matches.append({key: item.get(key) for key in ('external_ref', 'reply_to_ref', 'provider_reply_to_ref', 'receipt_ref', 'ts', 'channel', 'reaction')})
234
+ if not matches or row['reply'] is not None:
235
+ return row
236
+ row['reply'] = {'matches': matches, 'recorded_at': self.clock()}
237
+ row['resolution_ref'] = matches[0]['receipt_ref']
238
+ if row['state'] not in {'cancelled', 'expired'}:
239
+ row['state'] = 'resolved'
240
+ return self._save(db, row)
241
+
242
+ def cancel(self, wait_id, *, evidence_ref):
243
+ reference(evidence_ref)
244
+ with self.transaction() as db:
245
+ row = self._get(db, wait_id)
246
+ if row['state'] not in TERMINAL:
247
+ row.update(state='cancelled', resolution_ref=evidence_ref, cancelled_at=self.clock())
248
+ return self._save(db, row)
249
+ return row
250
+
251
+ def defer(self, wait_id, *, until, evidence_ref):
252
+ reference(evidence_ref)
253
+ stamp = epoch(until)
254
+ with self.transaction() as db:
255
+ row = self._get(db, wait_id)
256
+ if not self.clock() < stamp < row['expires_at'] or row['state'] in TERMINAL:
257
+ raise ValueError('cannot defer beyond expiry or after resolution')
258
+ row.update(state='deferred', next_review_at=stamp, resolution_ref=evidence_ref)
259
+ return self._save(db, row)
260
+
261
+ def _refresh(self, db, row, now):
262
+ parent = db.execute('SELECT status FROM commitments WHERE id=?', (row['commitment_id'],)).fetchone()
263
+ if row['state'] not in TERMINAL:
264
+ if not parent or parent['status'] not in OPEN_STATUSES:
265
+ row.update(state='cancelled', resolution_ref='commitment:'+row['commitment_id'])
266
+ self._save(db, row)
267
+ elif now >= row['expires_at']:
268
+ row.update(state='expired', resolution_ref=None)
269
+ self._save(db, row)
270
+ return row
271
+
272
+ @staticmethod
273
+ def _reason(row, now):
274
+ if row['state'] in TERMINAL:
275
+ return row['state']
276
+ if row['reply']:
277
+ return 'reply_observed'
278
+ if row['dispatch_receipt_ref'] is None:
279
+ return 'awaiting_dispatch_evidence'
280
+ if row['followup_receipt_ref']:
281
+ return 'followup_already_recorded'
282
+ if now < max(row['expected_at'], row['next_review_at'] or row['expected_at']):
283
+ return 'not_due'
284
+ local = datetime.fromtimestamp(now, ZoneInfo(row['timezone_name']))
285
+ current = local.hour * 60 + local.minute
286
+ if row['quiet_start'] is not None and in_window(current, minute(row['quiet_start']), minute(row['quiet_end'])):
287
+ return 'quiet_window'
288
+ if row['availability_start'] is not None and not in_window(current, minute(row['availability_start']), minute(row['availability_end'])):
289
+ return 'outside_availability'
290
+ return 'due'
291
+
292
+ def list_for_context(self, *, contact_id, limit=30, now=None, outbound_refs=None):
293
+ point = self.clock() if now is None else epoch(now)
294
+ where, params = 'contact_id=?', [contact_id]
295
+ if outbound_refs is not None:
296
+ refs = tuple(dict.fromkeys(outbound_refs))
297
+ if not refs:
298
+ return []
299
+ # Select the receipt's exact waits before applying the context cap.
300
+ where += " AND json_extract(payload,'$.outbound_ref') IN (" + ','.join('?' for _ in refs) + ')'
301
+ params.extend(refs)
302
+ params.append(min(100, max(1, int(limit))))
303
+ with self.transaction() as db:
304
+ values = db.execute('SELECT * FROM temporal_followups WHERE '+where+' ORDER BY updated_at DESC LIMIT ?', params).fetchall()
305
+ result = []
306
+ for value in values:
307
+ row = self._refresh(db, self._row(value), point)
308
+ result.append({**row, 'eligibility': self._reason(row, point),
309
+ 'overdue': row['state'] not in TERMINAL and row['expected_at'] is not None and point >= row['expected_at']})
310
+ return result
311
+
312
+ def due(self, *, now=None, limit=100):
313
+ point = self.clock() if now is None else epoch(now)
314
+ with self.transaction() as db:
315
+ # The installed tick is the only driver. Include terminal rows with
316
+ # native tasks so the adapter can reconcile cancellation after restart.
317
+ values = db.execute('SELECT * FROM temporal_followups WHERE state IN (\'open\',\'deferred\') OR (json_extract(payload,\'$.native_task_id\') IS NOT NULL AND NOT json_extract(payload,\'$.native_terminal_observed\')) ORDER BY updated_at').fetchall()
318
+ rows = [self._refresh(db, self._row(v), point) for v in values]
319
+ return [{**r, 'eligibility': self._reason(r, point)} for r in rows
320
+ if not r.get('native_terminal_observed') and (self._reason(r, point) == 'due' or r['native_task_id'])][:min(200, max(1, int(limit)))]
321
+
322
+ def preflight(self, wait_id, *, authority=None, binding=None, outbox_state='unknown', now=None):
323
+ point = self.clock() if now is None else epoch(now)
324
+ with self.transaction() as db:
325
+ row = self._refresh(db, self._get(db, wait_id), point)
326
+ reason = self._reason(row, point)
327
+ review = reason == 'due'
328
+ authorized = review and consent_matches(authority=authority, binding=binding, scope=row['authority_scope'], now=point)
329
+ dispatch = authorized and outbox_state == 'absent'
330
+ return {'wait_id': wait_id, 'revision': row['revision'], 'review_allowed': review,
331
+ 'dispatch_allowed': bool(dispatch), 'reason': reason if not review else
332
+ 'outbox_'+outbox_state if authorized and not dispatch else 'consent_missing' if not authorized else 'allowed',
333
+ 'native_task_id': row['native_task_id']}
334
+
335
+ def bind_native_task(self, wait_id, *, native_task_id):
336
+ reference(native_task_id)
337
+ with self.transaction() as db:
338
+ row = self._refresh(db, self._get(db, wait_id), self.clock())
339
+ if row['native_task_id']:
340
+ if row['native_task_id'] != native_task_id:
341
+ raise ValueError('reply wait already bound to another native task')
342
+ return row
343
+ # Binding is association only. Even a reply racing attachment
344
+ # must bind the blocked task so prepare can cancel it safely.
345
+ row['native_task_id'] = native_task_id
346
+ return self._save(db, row)
347
+
348
+ def observe_native_terminal(self, wait_id, *, native_task_id, native_status):
349
+ """Host calls only after independent native done/archive/failure readback."""
350
+ if native_status not in {'done', 'archived', 'cancelled', 'failed'}:
351
+ raise ValueError('native terminal observation required')
352
+ with self.transaction() as db:
353
+ row = self._get(db, wait_id)
354
+ if row['native_task_id'] != native_task_id:
355
+ raise ValueError('native followup association mismatch')
356
+ if not row.get('native_terminal_observed'):
357
+ row['native_terminal_observed'] = True
358
+ row['native_terminal_status'] = native_status
359
+ return self._save(db, row)
360
+ return row
361
+
362
+ def bind_authority_scope(self, wait_id, *, scope, evidence_ref):
363
+ """Trusted host binds its immutable task consent configuration once.
364
+
365
+ This grants nothing: preflight still requires the authority store's
366
+ consumed exact-scope grant at the actual send boundary. It lets a
367
+ passive wait receive task-scoped consent later without replacing work.
368
+ """
369
+ reference(evidence_ref)
370
+ if not isinstance(scope, dict) or not scope or len(encoded(scope)) > 16000:
371
+ raise ValueError('bounded task authority configuration required')
372
+ with self.transaction() as db:
373
+ row = self._refresh(db, self._get(db, wait_id), self.clock())
374
+ if row['authority_scope']:
375
+ if encoded(row['authority_scope']) != encoded(scope):
376
+ raise ValueError('task authority configuration is immutable')
377
+ return row
378
+ if row['state'] in TERMINAL:
379
+ raise ValueError('reply wait is already terminal')
380
+ row.update(authority_scope=scope, authority_configuration_ref=evidence_ref)
381
+ return self._save(db, row)
382
+
383
+ def mark_followup_dispatched(self, wait_id, *, action_digest, receipt_ref):
384
+ """Record the selected outbox's receipt, never manufacture a send ACK."""
385
+ reference(action_digest)
386
+ reference(receipt_ref)
387
+ with self.transaction() as db:
388
+ row = self._get(db, wait_id)
389
+ if row['followup_receipt_ref']:
390
+ if (row['followup_action_digest'], row['followup_receipt_ref']) != (action_digest, receipt_ref):
391
+ raise ValueError('followup receipt conflict')
392
+ return row
393
+ row.update(followup_action_digest=action_digest, followup_receipt_ref=receipt_ref,
394
+ followup_observed_at=self.clock())
395
+ return self._save(db, row)
396
+
397
+ def invalidate_sources(self, source_refs, *, evidence_ref):
398
+ """Cancel affected pending work after root's correction/erasure event."""
399
+ refs = set(source_refs)
400
+ reference(evidence_ref)
401
+ changed = []
402
+ with self.transaction() as db:
403
+ rows = db.execute('SELECT * FROM temporal_followups').fetchall()
404
+ for raw in rows:
405
+ row = self._row(raw)
406
+ if refs.intersection(row['source_refs']) and row['state'] not in TERMINAL:
407
+ row.update(state='cancelled', resolution_ref=evidence_ref)
408
+ self._save(db, row)
409
+ changed.append(row['wait_id'])
410
+ return changed
@@ -0,0 +1 @@
1
+ # intelligence
@@ -0,0 +1,24 @@
1
+ """Cognition package - MetaLearner and cognitive performance tracking."""
2
+ from .metalearner import MetaLearner, MetaLearnerConfig, CycleResult
3
+ from .performance_index import CognitivePerformanceIndex, CPIComponent, PerformanceIndexComputer
4
+ from .gap_detector import GapType, GapSeverity, Gap, GapDetector
5
+ from .strategy_adjuster import AdjustmentStatus, Adjustment, StrategyAdjuster
6
+ from .metrics_collector import MetricsCollector, MetricObservation
7
+
8
+ __all__ = [
9
+ "MetaLearner",
10
+ "MetaLearnerConfig",
11
+ "CycleResult",
12
+ "CognitivePerformanceIndex",
13
+ "CPIComponent",
14
+ "PerformanceIndexComputer",
15
+ "GapType",
16
+ "GapSeverity",
17
+ "Gap",
18
+ "GapDetector",
19
+ "AdjustmentStatus",
20
+ "Adjustment",
21
+ "StrategyAdjuster",
22
+ "MetricsCollector",
23
+ "MetricObservation",
24
+ ]
@@ -0,0 +1,148 @@
1
+ """Gap detection for cognitive performance."""
2
+ from dataclasses import dataclass
3
+ from typing import Dict, Any
4
+ from enum import Enum
5
+ from datetime import datetime
6
+
7
+ from .types import GapSeverity # noqa: F401 — re-exported for backward compat
8
+
9
+
10
+ class GapType(str, Enum):
11
+ """Types of cognitive performance gaps."""
12
+ LOW_MEMORY_QUALITY = "low_memory_quality"
13
+ SEMANTIC_MISMATCH = "semantic_mismatch"
14
+ INSUFFICIENT_DATA = "insufficient_data"
15
+ STALE_DATA = "stale_data"
16
+ MISSING_PREFERENCE = "missing_preference"
17
+ LOW_PREDICTION_ACCURACY = "low_prediction_accuracy"
18
+ TOOL_INEFFICIENCY = "tool_inefficiency"
19
+ INITIATIVE_MISMATCH = "initiative_mismatch"
20
+
21
+
22
+ @dataclass
23
+ class Gap:
24
+ """Detected performance gap."""
25
+ gap_type: GapType
26
+ severity: GapSeverity
27
+ description: str
28
+ component: str # Which CPI component affected
29
+ evidence: Dict[str, Any]
30
+ diagnosed_at: datetime = None
31
+
32
+ def __post_init__(self):
33
+ if self.diagnosed_at is None:
34
+ self.diagnosed_at = datetime.now()
35
+
36
+
37
+ class GapDetector:
38
+ """Detect performance gaps from CPI components."""
39
+
40
+ # Thresholds for gap detection
41
+ THRESHOLDS = {
42
+ "retrieval": 70,
43
+ "prediction": 60,
44
+ "tool_efficiency": 75,
45
+ "initiative": 50,
46
+ "goal_progress": 50,
47
+ "response_quality": 65,
48
+ }
49
+
50
+ # Component to gap type mapping
51
+ GAP_MAPPING = {
52
+ "retrieval": {
53
+ "low": GapType.LOW_MEMORY_QUALITY,
54
+ "mismatch": GapType.SEMANTIC_MISMATCH,
55
+ "stale": GapType.STALE_DATA,
56
+ },
57
+ "prediction": {
58
+ "low": GapType.LOW_PREDICTION_ACCURACY,
59
+ "insufficient": GapType.INSUFFICIENT_DATA,
60
+ },
61
+ "tool_efficiency": {
62
+ "low": GapType.TOOL_INEFFICIENCY,
63
+ },
64
+ "initiative": {
65
+ "low": GapType.INITIATIVE_MISMATCH,
66
+ },
67
+ "goal_progress": {
68
+ "insufficient": GapType.INSUFFICIENT_DATA,
69
+ },
70
+ "response_quality": {
71
+ "missing": GapType.MISSING_PREFERENCE,
72
+ },
73
+ }
74
+
75
+ def detect(self, cpi: "CognitivePerformanceIndex") -> list:
76
+ """Detect gaps from CPI snapshot."""
77
+ gaps = []
78
+
79
+ # Check each component against threshold
80
+ for component_name in ["retrieval", "prediction", "tool_efficiency", "initiative", "goal_progress", "response_quality"]:
81
+ component = getattr(cpi, component_name, None)
82
+ if not component or not getattr(component, "available", True):
83
+ continue
84
+
85
+ threshold = self.THRESHOLDS.get(component_name, 60)
86
+
87
+ if component.score < threshold:
88
+ severity = self._determine_severity(component.score, threshold)
89
+ gap_type = self._map_gap_type(component_name, component)
90
+
91
+ gaps.append(Gap(
92
+ gap_type=gap_type,
93
+ severity=severity,
94
+ description=f"{component_name.replace('_', ' ').title()} performance below threshold ({component.score:.0f} < {threshold})",
95
+ component=component_name,
96
+ evidence={
97
+ "score": component.score,
98
+ "threshold": threshold,
99
+ "trend": component.trend,
100
+ "metrics": component.metrics,
101
+ },
102
+ ))
103
+
104
+ # Check for declining trends
105
+ if component.trend == "declining":
106
+ gaps.append(Gap(
107
+ gap_type=GapType.SEMANTIC_MISMATCH,
108
+ severity=GapSeverity.INFO,
109
+ description=f"{component_name.replace('_', ' ').title()} performance declining",
110
+ component=component_name,
111
+ evidence={"trend": "declining", "score": component.score},
112
+ ))
113
+
114
+ return gaps
115
+
116
+ def _determine_severity(self, score: float, threshold: float) -> GapSeverity:
117
+ """Determine gap severity from score distance to threshold."""
118
+ gap = threshold - score
119
+
120
+ if gap > 30:
121
+ return GapSeverity.CRITICAL
122
+ elif gap > 15:
123
+ return GapSeverity.WARNING
124
+ else:
125
+ return GapSeverity.INFO
126
+
127
+ def _map_gap_type(self, component: str, component_data: "CPIComponent") -> GapType:
128
+ """Map component to specific gap type."""
129
+ mapping = self.GAP_MAPPING.get(component, {})
130
+
131
+ # Check metrics for specific signals
132
+ metrics = component_data.metrics
133
+
134
+ if "relevance_score" in metrics and metrics["relevance_score"] < 60:
135
+ return mapping.get("mismatch", GapType.LOW_MEMORY_QUALITY)
136
+
137
+ if component_data.trend == "declining":
138
+ return mapping.get("mismatch", GapType.SEMANTIC_MISMATCH)
139
+
140
+ if "total" in metrics and metrics.get("total", 0) < 5:
141
+ return mapping.get("insufficient", GapType.INSUFFICIENT_DATA)
142
+
143
+ return mapping.get("low", GapType.LOW_MEMORY_QUALITY)
144
+
145
+ def prioritize(self, gaps: list) -> list:
146
+ """Sort gaps by severity for action prioritization."""
147
+ severity_order = {GapSeverity.CRITICAL: 0, GapSeverity.WARNING: 1, GapSeverity.INFO: 2}
148
+ return sorted(gaps, key=lambda g: severity_order.get(g.severity, 99))