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,172 @@
1
+ """SkillStore -- SQLite persistence for procedure memory + strategy notes."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ import os
7
+ import sqlite3
8
+ import threading
9
+ import time
10
+ from typing import Any, Dict, List, Optional
11
+
12
+ from apsimo.skills_memory.models import Skill, signature_overlap
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+ _DEFAULT_MAX = 200
17
+ _NOTE_CAP = 600
18
+
19
+
20
+ def skills_enabled() -> bool:
21
+ return os.environ.get("COLONY_SKILLS_ENABLED", "true").strip().lower() != "false"
22
+
23
+
24
+ def skills_distill_mode() -> str:
25
+ from apsimo.util.autonomy_preset import resolve
26
+ return resolve("COLONY_SKILLS_DISTILL",
27
+ ("off", "shadow", "live"), "shadow")
28
+
29
+
30
+ def skills_max() -> int:
31
+ try:
32
+ return max(10, int(os.environ.get("COLONY_SKILLS_MAX", str(_DEFAULT_MAX))))
33
+ except (TypeError, ValueError):
34
+ return _DEFAULT_MAX
35
+
36
+
37
+ class SkillStore:
38
+ def __init__(self, db_path: Optional[str] = None) -> None:
39
+ self._lock = threading.RLock()
40
+ self._conn = sqlite3.connect(str(db_path) if db_path else ":memory:",
41
+ check_same_thread=False)
42
+ self._conn.row_factory = sqlite3.Row
43
+ with self._lock:
44
+ self._conn.execute(
45
+ """CREATE TABLE IF NOT EXISTS skills (
46
+ id TEXT PRIMARY KEY, title TEXT NOT NULL, situation TEXT,
47
+ situation_signature TEXT, steps TEXT, gotchas TEXT,
48
+ domain TEXT, source_ref TEXT,
49
+ uses INTEGER DEFAULT 0, wins INTEGER DEFAULT 0,
50
+ losses INTEGER DEFAULT 0, confidence REAL DEFAULT 0.6,
51
+ created_at REAL, last_used_at REAL
52
+ )""")
53
+ self._conn.execute(
54
+ """CREATE TABLE IF NOT EXISTS strategy_notes (
55
+ domain TEXT PRIMARY KEY, note TEXT, updated_at REAL
56
+ )""")
57
+ self._conn.commit()
58
+
59
+ # -- skills ----------------------------------------------------------
60
+ def add(self, skill: Skill) -> Skill:
61
+ row = skill.to_row()
62
+ with self._lock:
63
+ cols = ", ".join(row); ph = ", ".join(["?"] * len(row))
64
+ self._conn.execute(
65
+ f"INSERT OR REPLACE INTO skills ({cols}) VALUES ({ph})",
66
+ list(row.values()))
67
+ self._conn.commit()
68
+ return skill
69
+
70
+ def get(self, skill_id: str) -> Optional[Skill]:
71
+ with self._lock:
72
+ r = self._conn.execute(
73
+ "SELECT * FROM skills WHERE id=?", (skill_id,)).fetchone()
74
+ return Skill.from_row(dict(r)) if r else None
75
+
76
+ def list(self, domain: Optional[str] = None, limit: int = 100) -> List[Skill]:
77
+ q = "SELECT * FROM skills"
78
+ params: List[Any] = []
79
+ if domain:
80
+ q += " WHERE domain=?"; params.append(domain)
81
+ q += " ORDER BY last_used_at DESC, created_at DESC LIMIT ?"
82
+ params.append(limit)
83
+ with self._lock:
84
+ rows = self._conn.execute(q, params).fetchall()
85
+ return [Skill.from_row(dict(r)) for r in rows]
86
+
87
+ def count(self) -> int:
88
+ with self._lock:
89
+ r = self._conn.execute("SELECT COUNT(*) AS n FROM skills").fetchone()
90
+ return int(r["n"])
91
+
92
+ def find_similar(self, signature: str, threshold: float = 0.8) -> Optional[Skill]:
93
+ """Existing skill whose signature overlaps `signature` above threshold."""
94
+ best, best_ov = None, 0.0
95
+ for s in self.list(limit=100000):
96
+ ov = signature_overlap(signature, s.situation_signature)
97
+ if ov > best_ov:
98
+ best, best_ov = s, ov
99
+ return best if best is not None and best_ov > threshold else None
100
+
101
+ def bump_use(self, skill_id: str) -> None:
102
+ with self._lock:
103
+ self._conn.execute(
104
+ "UPDATE skills SET uses=uses+1, last_used_at=? WHERE id=?",
105
+ (time.time(), skill_id))
106
+ self._conn.commit()
107
+
108
+ def record_outcome(self, skill_id: str, win: bool) -> None:
109
+ col = "wins" if win else "losses"
110
+ with self._lock:
111
+ self._conn.execute(
112
+ f"UPDATE skills SET {col}={col}+1 WHERE id=?", (skill_id,))
113
+ self._conn.commit()
114
+
115
+ def evict_to_cap(self, cap: Optional[int] = None) -> int:
116
+ """Drop lowest-scoring skills until at most `cap` remain."""
117
+ cap = cap if cap is not None else skills_max()
118
+ skills = self.list(limit=100000)
119
+ excess = len(skills) - cap
120
+ if excess <= 0:
121
+ return 0
122
+ now = time.time()
123
+ victims = sorted(skills, key=lambda s: s.score(now))[:excess]
124
+ with self._lock:
125
+ for v in victims:
126
+ self._conn.execute("DELETE FROM skills WHERE id=?", (v.id,))
127
+ self._conn.commit()
128
+ logger.info("skills_memory: evicted %d skill(s) to cap %d", excess, cap)
129
+ return excess
130
+
131
+ # -- per-domain strategy notes (failure post-mortems) -----------------
132
+ def record_failure_note(self, domain: str, note: str) -> None:
133
+ """Append/refresh the short strategy note for a domain (capped)."""
134
+ domain = (domain or "unknown").strip().lower()
135
+ note = (note or "").strip()
136
+ if not note:
137
+ return
138
+ with self._lock:
139
+ row = self._conn.execute(
140
+ "SELECT note FROM strategy_notes WHERE domain=?", (domain,)).fetchone()
141
+ existing = (row["note"] if row else "") or ""
142
+ if note in existing:
143
+ merged = existing
144
+ else:
145
+ merged = (existing + ("\n" if existing else "") + f"- {note}")
146
+ # keep the newest lines within the cap
147
+ if len(merged) > _NOTE_CAP:
148
+ lines = merged.splitlines()
149
+ while lines and len("\n".join(lines)) > _NOTE_CAP:
150
+ lines.pop(0)
151
+ merged = "\n".join(lines)
152
+ self._conn.execute(
153
+ """INSERT INTO strategy_notes (domain, note, updated_at)
154
+ VALUES (?, ?, ?)
155
+ ON CONFLICT(domain) DO UPDATE SET note=?, updated_at=?""",
156
+ (domain, merged, time.time(), merged, time.time()))
157
+ self._conn.commit()
158
+
159
+ def get_note(self, domain: str) -> str:
160
+ domain = (domain or "").strip().lower()
161
+ with self._lock:
162
+ row = self._conn.execute(
163
+ "SELECT note FROM strategy_notes WHERE domain=?", (domain,)).fetchone()
164
+ return (row["note"] if row else "") or ""
165
+
166
+ def snapshot(self) -> Dict[str, Any]:
167
+ return {
168
+ "count": self.count(),
169
+ "cap": skills_max(),
170
+ "distill_mode": skills_distill_mode(),
171
+ "skills": [s.to_row() for s in self.list(limit=200)],
172
+ }
@@ -0,0 +1,6 @@
1
+ """Surprise Engine — anomaly detection when observations deviate from patterns."""
2
+
3
+ from apsimo.surprise.store import SurpriseStore
4
+ from apsimo.surprise.scorer import compute_surprise
5
+
6
+ __all__ = ["SurpriseStore", "compute_surprise"]
@@ -0,0 +1,57 @@
1
+ """Surprise-accumulation consumer: close the loop into the workspace.
2
+
3
+ The condition worker emits ``surprise.accumulation`` when unresolved
4
+ surprises pile up, but nothing ever consumed it — the signal died on the
5
+ WebSocket unless a client happened to be watching. This consumer turns the
6
+ event into a workspace concern so accumulating surprises land on the
7
+ agent's mind (one strengthening concern, keyed stably, never a pile-up).
8
+
9
+ Clean no-op when the workspace is disabled or not wired: the event still
10
+ broadcasts and journals exactly as before.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import logging
16
+ from typing import Any, Dict
17
+
18
+ logger = logging.getLogger(__name__)
19
+
20
+ _DEDUP_KEY = "surprise:accumulation"
21
+
22
+
23
+ def handle_surprise_accumulation(event: Dict[str, Any]) -> bool:
24
+ """Raise/strengthen the accumulation concern. Returns True when a
25
+ concern was bumped, False on the (clean) no-op paths."""
26
+ try:
27
+ from apsimo.self_model.workspace import workspace_enabled
28
+ if not workspace_enabled():
29
+ return False
30
+ from apsimo.api.routers.host import _workspace
31
+ if _workspace is None:
32
+ return False
33
+ payload = event.get("payload") or {}
34
+ try:
35
+ count = int(payload.get("unresolved_count") or 0)
36
+ except (TypeError, ValueError):
37
+ count = 0
38
+ _workspace.bump(
39
+ kind="anomaly",
40
+ summary=(f"{count} unresolved surprise(s) accumulating — "
41
+ "reality keeps diverging from expectations; review "
42
+ "and resolve them"),
43
+ dedup_key=_DEDUP_KEY,
44
+ salience=min(0.9, 0.4 + 0.05 * count),
45
+ sources=["surprise-store"])
46
+ return True
47
+ except Exception:
48
+ logger.debug("surprise.accumulation -> workspace failed",
49
+ exc_info=True)
50
+ return False
51
+
52
+
53
+ def register() -> None:
54
+ """Subscribe the consumer. Enablement is checked at EVENT time (not
55
+ registration time), so a workspace flipped on later just starts working."""
56
+ from apsimo.events.broadcaster import subscribe
57
+ subscribe("surprise.accumulation", handle_surprise_accumulation)
@@ -0,0 +1,102 @@
1
+ """Surprise Scorer — compute surprise scores for observations.
2
+
3
+ Scoring logic:
4
+ - No matching pattern: surprise = 0.7 (moderately surprising)
5
+ - Pattern violated: surprise = 0.5 + (pattern_confidence * 0.5)
6
+ - Low-frequency pattern match: surprise = 0.2 (rare but known)
7
+ - High-frequency pattern match: surprise = 0.0 (expected)
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import logging
13
+ from typing import Any, Dict, Optional
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+
18
+ def compute_surprise(
19
+ observation: str,
20
+ *,
21
+ pattern_store: Any = None,
22
+ observation_type: str = "",
23
+ observation_data: Optional[Dict[str, Any]] = None,
24
+ ) -> Dict[str, Any]:
25
+ """Compute a surprise score for an observation.
26
+
27
+ Returns dict with:
28
+ - surprise_score: float (0.0 to 1.0)
29
+ - expected: what was expected (if a pattern was found)
30
+ - pattern_id: the matched/violated pattern ID (if any)
31
+ - reasoning: human-readable explanation
32
+ """
33
+ if pattern_store is None:
34
+ return {
35
+ "surprise_score": 0.5,
36
+ "expected": None,
37
+ "pattern_id": None,
38
+ "reasoning": "No pattern store available, defaulting to moderate surprise",
39
+ }
40
+
41
+ # Look for matching patterns by searching descriptions.
42
+ try:
43
+ all_patterns = pattern_store.list_patterns(active_only=True, limit=100)
44
+ except Exception:
45
+ return {
46
+ "surprise_score": 0.5,
47
+ "expected": None,
48
+ "pattern_id": None,
49
+ "reasoning": "Could not query pattern store",
50
+ }
51
+
52
+ # Find patterns that mention entities from the observation.
53
+ matching = []
54
+ obs_lower = observation.lower()
55
+ obs_words = set(obs_lower.split())
56
+
57
+ for pattern in all_patterns.get("patterns", []):
58
+ desc_lower = pattern.get("description", "").lower()
59
+ # Check for word overlap.
60
+ desc_words = set(desc_lower.split())
61
+ overlap = obs_words & desc_words
62
+ if overlap or observation_type == pattern.get("pattern_type", ""):
63
+ matching.append(pattern)
64
+
65
+ if not matching:
66
+ return {
67
+ "surprise_score": 0.7,
68
+ "expected": None,
69
+ "pattern_id": None,
70
+ "reasoning": "No matching pattern found for this observation",
71
+ }
72
+
73
+ # Check if the observation fits the pattern or violates it.
74
+ best_pattern = max(matching, key=lambda p: p.get("frequency", 0) * p.get("confidence", 0.5))
75
+ freq = best_pattern.get("frequency", 1)
76
+ conf = best_pattern.get("confidence", 0.5)
77
+
78
+ if freq >= 5:
79
+ # High-frequency pattern match — expected.
80
+ return {
81
+ "surprise_score": 0.0,
82
+ "expected": best_pattern["description"],
83
+ "pattern_id": best_pattern["id"],
84
+ "reasoning": f"Matches high-frequency pattern (seen {freq} times)",
85
+ }
86
+ elif freq >= 2:
87
+ # Low-frequency pattern match — rare but known.
88
+ return {
89
+ "surprise_score": 0.2,
90
+ "expected": best_pattern["description"],
91
+ "pattern_id": best_pattern["id"],
92
+ "reasoning": f"Matches low-frequency pattern (seen {freq} times)",
93
+ }
94
+ else:
95
+ # Pattern seen only once — could be a violation.
96
+ score = 0.5 + (conf * 0.5)
97
+ return {
98
+ "surprise_score": round(score, 4),
99
+ "expected": best_pattern["description"],
100
+ "pattern_id": best_pattern["id"],
101
+ "reasoning": f"Pattern seen only once (confidence {conf:.1f}), possible violation",
102
+ }
@@ -0,0 +1,203 @@
1
+ """Surprise Store — record and track unexpected observations."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import logging
7
+ import sqlite3
8
+ import uuid
9
+ from datetime import datetime, timezone
10
+ from typing import Any, Dict, List, Optional
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+
15
+ class SurpriseStore:
16
+ """SQLite-backed surprise observation store."""
17
+
18
+ def __init__(self, db_path: str) -> None:
19
+ self._db_path = db_path
20
+ self._conn = sqlite3.connect(db_path)
21
+ self._conn.row_factory = sqlite3.Row
22
+ self._conn.execute("PRAGMA journal_mode=WAL")
23
+ self._conn.execute("PRAGMA foreign_keys=ON")
24
+ self._create_tables()
25
+
26
+ def _create_tables(self) -> None:
27
+ self._conn.executescript("""
28
+ CREATE TABLE IF NOT EXISTS surprises (
29
+ id TEXT PRIMARY KEY,
30
+ observation TEXT NOT NULL,
31
+ expected TEXT,
32
+ surprise_score REAL NOT NULL,
33
+ pattern_id TEXT,
34
+ context TEXT,
35
+ timestamp TEXT NOT NULL,
36
+ resolved INTEGER NOT NULL DEFAULT 0,
37
+ resolution TEXT
38
+ );
39
+
40
+ CREATE INDEX IF NOT EXISTS idx_surprises_score
41
+ ON surprises(surprise_score DESC);
42
+ CREATE INDEX IF NOT EXISTS idx_surprises_timestamp
43
+ ON surprises(timestamp);
44
+ CREATE INDEX IF NOT EXISTS idx_surprises_resolved
45
+ ON surprises(resolved);
46
+ """)
47
+ self._conn.commit()
48
+
49
+ def create_surprise(
50
+ self,
51
+ *,
52
+ observation: str,
53
+ expected: Optional[str] = None,
54
+ surprise_score: float = 0.5,
55
+ pattern_id: Optional[str] = None,
56
+ context: Optional[Dict[str, Any]] = None,
57
+ ) -> Dict[str, Any]:
58
+ """Record a surprise observation.
59
+
60
+ Returns the created surprise dict.
61
+ """
62
+ surprise_score = max(0.0, min(1.0, surprise_score))
63
+
64
+ surprise_id = str(uuid.uuid4())
65
+ now = datetime.now(timezone.utc).isoformat()
66
+ ctx_json = json.dumps(context) if context else None
67
+
68
+ self._conn.execute(
69
+ """INSERT INTO surprises (id, observation, expected, surprise_score,
70
+ pattern_id, context, timestamp, resolved, resolution)
71
+ VALUES (?, ?, ?, ?, ?, ?, ?, 0, NULL)""",
72
+ (surprise_id, observation, expected, surprise_score,
73
+ pattern_id, ctx_json, now),
74
+ )
75
+ self._conn.commit()
76
+
77
+ result = self.get_surprise(surprise_id)
78
+ assert result is not None
79
+ return result
80
+
81
+ def get_surprise(self, surprise_id: str) -> Optional[Dict[str, Any]]:
82
+ row = self._conn.execute(
83
+ "SELECT * FROM surprises WHERE id = ?", (surprise_id,)
84
+ ).fetchone()
85
+ if row is None:
86
+ return None
87
+ d = dict(row)
88
+ d["resolved"] = bool(d["resolved"])
89
+ if d.get("context"):
90
+ try:
91
+ d["context"] = json.loads(d["context"])
92
+ except (json.JSONDecodeError, TypeError):
93
+ pass
94
+ return d
95
+
96
+ def list_surprises(
97
+ self,
98
+ *,
99
+ min_score: float = 0.0,
100
+ resolved: Optional[bool] = None,
101
+ limit: int = 50,
102
+ offset: int = 0,
103
+ ) -> Dict[str, Any]:
104
+ """List surprises with optional filters, sorted by score descending."""
105
+ clauses: List[str] = []
106
+ params: List[Any] = []
107
+
108
+ if min_score > 0:
109
+ clauses.append("surprise_score >= ?")
110
+ params.append(min_score)
111
+ if resolved is not None:
112
+ clauses.append("resolved = ?")
113
+ params.append(1 if resolved else 0)
114
+
115
+ where = f" WHERE {' AND '.join(clauses)}" if clauses else ""
116
+
117
+ total_row = self._conn.execute(
118
+ f"SELECT COUNT(*) as cnt FROM surprises{where}", params
119
+ ).fetchone()
120
+ total = total_row["cnt"] if total_row else 0
121
+
122
+ rows = self._conn.execute(
123
+ f"SELECT * FROM surprises{where} ORDER BY surprise_score DESC, timestamp DESC LIMIT ? OFFSET ?",
124
+ params + [limit, offset],
125
+ ).fetchall()
126
+
127
+ surprises = []
128
+ for row in rows:
129
+ d = dict(row)
130
+ d["resolved"] = bool(d["resolved"])
131
+ if d.get("context"):
132
+ try:
133
+ d["context"] = json.loads(d["context"])
134
+ except (json.JSONDecodeError, TypeError):
135
+ pass
136
+ surprises.append(d)
137
+
138
+ return {"surprises": surprises, "total": total, "limit": limit, "offset": offset}
139
+
140
+ def get_unresolved(self, min_score: float = 0.5, limit: int = 10) -> List[Dict[str, Any]]:
141
+ """Get unresolved high-score surprises."""
142
+ result = self.list_surprises(min_score=min_score, resolved=False, limit=limit)
143
+ return result["surprises"]
144
+
145
+ def resolve_surprise(
146
+ self,
147
+ surprise_id: str,
148
+ *,
149
+ resolution: Optional[str] = None,
150
+ ) -> Optional[Dict[str, Any]]:
151
+ """Mark a surprise as resolved. Returns updated surprise or None."""
152
+ existing = self.get_surprise(surprise_id)
153
+ if existing is None:
154
+ return None
155
+
156
+ self._conn.execute(
157
+ "UPDATE surprises SET resolved = 1, resolution = ? WHERE id = ?",
158
+ (resolution, surprise_id),
159
+ )
160
+ self._conn.commit()
161
+ return self.get_surprise(surprise_id)
162
+
163
+ def delete_surprise(self, surprise_id: str) -> bool:
164
+ """Delete a surprise. Returns True if deleted."""
165
+ cursor = self._conn.execute("DELETE FROM surprises WHERE id = ?", (surprise_id,))
166
+ self._conn.commit()
167
+ return cursor.rowcount > 0
168
+
169
+ def resolve_stale(self, ttl_days: float) -> int:
170
+ """Auto-resolve unresolved surprises older than ``ttl_days``.
171
+
172
+ A surprise nobody addressed in two weeks is stale context, not a
173
+ live signal — leaving it unresolved forever keeps it in context
174
+ assembly and inflates the accumulation trigger. Marks (never
175
+ deletes) with an explicit auto-resolution note. ``ttl_days <= 0``
176
+ disables. Returns the number resolved.
177
+ """
178
+ if ttl_days <= 0:
179
+ return 0
180
+ from datetime import timedelta
181
+ cutoff = (datetime.now(timezone.utc)
182
+ - timedelta(days=ttl_days)).isoformat()
183
+ cursor = self._conn.execute(
184
+ "UPDATE surprises SET resolved = 1, resolution = ? "
185
+ "WHERE resolved = 0 AND timestamp < ?",
186
+ (f"auto-resolved: unaddressed for {ttl_days:g} days "
187
+ "(COLONY_SURPRISE_TTL_DAYS)", cutoff),
188
+ )
189
+ self._conn.commit()
190
+ return cursor.rowcount
191
+
192
+ def count_unresolved(self, since_hours: float = 1.0) -> int:
193
+ """Count unresolved surprises within the last N hours."""
194
+ from datetime import timedelta
195
+ cutoff = (datetime.now(timezone.utc) - timedelta(hours=since_hours)).isoformat()
196
+ row = self._conn.execute(
197
+ "SELECT COUNT(*) as cnt FROM surprises WHERE resolved = 0 AND timestamp >= ?",
198
+ (cutoff,),
199
+ ).fetchone()
200
+ return row["cnt"] if row else 0
201
+
202
+ def close(self) -> None:
203
+ self._conn.close()
@@ -0,0 +1,69 @@
1
+ """Colony Distributed Task Queue.
2
+
3
+ Hardware-aware distributed job scheduling across the Colony mesh.
4
+ The Queen (Sovereign) schedules; Workers (Vassals) execute.
5
+
6
+ Public API::
7
+
8
+ from apsimo.task_queue import (
9
+ Job, JobType, JobStatus, JobPriority,
10
+ JobCapabilityRequirement, JobResult,
11
+ WorkerCapabilities, QueueManager, WorkerNode,
12
+ JobHandler, Scheduler, TaskQueueConfig,
13
+ )
14
+ """
15
+
16
+ from apsimo.task_queue.config import TaskQueueConfig
17
+ from apsimo.task_queue.mesh_integration import QueueMeshEventHandler
18
+ from apsimo.task_queue.models import (
19
+ AuditEntry,
20
+ CircularDependencyError,
21
+ FederatedJob,
22
+ HeartbeatPayload,
23
+ Job,
24
+ JobCapabilityRequirement,
25
+ JobPriority,
26
+ JobResult,
27
+ JobStatus,
28
+ JobType,
29
+ QueueStats,
30
+ WorkerCapabilities,
31
+ deadline_urgency,
32
+ )
33
+ from apsimo.task_queue.queue_manager import QueueManager
34
+ from apsimo.task_queue.scheduler import Scheduler
35
+ from apsimo.task_queue.worker import JobHandler, WorkerNode, detect_local_capabilities
36
+ from apsimo.task_queue.work_control import (
37
+ WorkControlError,
38
+ WorkControlService,
39
+ )
40
+
41
+ __all__ = [
42
+ # Models
43
+ "Job",
44
+ "JobType",
45
+ "JobStatus",
46
+ "JobPriority",
47
+ "JobCapabilityRequirement",
48
+ "JobResult",
49
+ "WorkerCapabilities",
50
+ "HeartbeatPayload",
51
+ "QueueStats",
52
+ "AuditEntry",
53
+ "FederatedJob",
54
+ "CircularDependencyError",
55
+ "deadline_urgency",
56
+ # Core components
57
+ "QueueManager",
58
+ "WorkControlService",
59
+ "WorkControlError",
60
+ "WorkerNode",
61
+ "JobHandler",
62
+ "Scheduler",
63
+ # Config
64
+ "TaskQueueConfig",
65
+ # Mesh integration
66
+ "QueueMeshEventHandler",
67
+ # Utilities
68
+ "detect_local_capabilities",
69
+ ]