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,163 @@
1
+ """Data quality executor skill.
2
+
3
+ Monitors schema drift, orphaned records, and data inconsistencies.
4
+ Attempts deterministic auto-fixes, proposes ambiguous ones.
5
+ """
6
+
7
+ import logging
8
+ from typing import Any, Dict
9
+
10
+ from apsimo.skills.base import (
11
+ ExecutionResult,
12
+ InitiativeExecutionContext,
13
+ InitiativeExecutorSkill,
14
+ )
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ class DataQualitySkill(InitiativeExecutorSkill):
20
+ """Skill for monitoring and fixing data quality issues."""
21
+
22
+ skill_name = "data_quality"
23
+ skill_version = "1.0.0"
24
+
25
+ async def can_execute(
26
+ self, category: Dict[str, Any], context: Dict[str, Any]
27
+ ) -> bool:
28
+ return category.get("executor_skill") == self.skill_name
29
+
30
+ async def execute(
31
+ self, initiative: InitiativeExecutionContext
32
+ ) -> ExecutionResult:
33
+ entity_id = initiative.entity_id or "unknown"
34
+ entity_type = initiative.entity_type or "schema"
35
+ self._log("info", "Checking data quality: %s (%s)", entity_id, entity_type)
36
+
37
+ if entity_type == "schema_drift":
38
+ return await self._handle_schema_drift(entity_id, initiative.trigger_data)
39
+ elif entity_type == "orphan_nodes":
40
+ return await self._handle_orphan_nodes(entity_id, initiative.trigger_data)
41
+ elif entity_type == "stale_index":
42
+ return await self._handle_stale_index(entity_id, initiative.trigger_data)
43
+ else:
44
+ self._log("warning", "Unknown data quality issue type: %s", entity_type)
45
+ return ExecutionResult.NO_ACTION
46
+
47
+ async def _handle_schema_drift(
48
+ self, entity_id: str, trigger_data: Dict[str, Any]
49
+ ) -> ExecutionResult:
50
+ """Handle detected schema drift."""
51
+ self._log("info", "Handling schema drift: %s", entity_id)
52
+
53
+ drift = trigger_data.get("drift", {})
54
+ missing_relationships = drift.get("missing_relationships", [])
55
+ missing_properties = drift.get("missing_properties", [])
56
+
57
+ fixes_applied = []
58
+ fixes_proposed = []
59
+
60
+ # Auto-fix: create missing relationships if source/target exist
61
+ for rel in missing_relationships:
62
+ rel_type = rel.get("type")
63
+ from_node = rel.get("from")
64
+ to_node = rel.get("to")
65
+ if not all([rel_type, from_node, to_node]):
66
+ continue
67
+
68
+ if self.graph:
69
+ try:
70
+ async with self.graph.driver.session(
71
+ database=self.graph.database
72
+ ) as session:
73
+ # Check if nodes exist before creating relationship
74
+ result = await session.run(
75
+ "MATCH (a {id: $from_id}), (b {id: $to_id}) "
76
+ "RETURN count(a) as a_count, count(b) as b_count",
77
+ from_id=from_node,
78
+ to_node=to_node,
79
+ )
80
+ record = await result.single()
81
+ if record and record.get("a_count") > 0 and record.get("b_count") > 0:
82
+ # Safe to create relationship
83
+ await session.run(
84
+ "MATCH (a {id: $from_id}), (b {id: $to_id}) "
85
+ "MERGE (a)-[r:" + rel_type + "]->(b) "
86
+ "RETURN count(r) as created",
87
+ from_id=from_node,
88
+ to_node=to_node,
89
+ )
90
+ fixes_applied.append(rel)
91
+ else:
92
+ fixes_proposed.append(rel)
93
+ except Exception as e:
94
+ self._log("warning", "Failed to fix relationship %s: %s", rel, e)
95
+ fixes_proposed.append(rel)
96
+
97
+ # Auto-fix: set default values for missing properties
98
+ for prop in missing_properties:
99
+ node_id = prop.get("node_id")
100
+ prop_name = prop.get("property")
101
+ default_value = prop.get("default")
102
+ if not all([node_id, prop_name]):
103
+ continue
104
+
105
+ if self.graph:
106
+ try:
107
+ async with self.graph.driver.session(
108
+ database=self.graph.database
109
+ ) as session:
110
+ await session.run(
111
+ "MATCH (n {id: $node_id}) "
112
+ "SET n." + prop_name + " = $default_value",
113
+ node_id=node_id,
114
+ default_value=default_value,
115
+ )
116
+ fixes_applied.append(prop)
117
+ except Exception as e:
118
+ self._log("warning", "Failed to set property %s: %s", prop, e)
119
+ fixes_proposed.append(prop)
120
+
121
+ if fixes_applied and not fixes_proposed:
122
+ self._log("info", "Auto-fixed %d schema issues", len(fixes_applied))
123
+ return ExecutionResult.AUTO_FIXED
124
+ elif fixes_proposed:
125
+ self._log("info", "Proposed %d fixes, applied %d", len(fixes_proposed), len(fixes_applied))
126
+ return ExecutionResult.PROPOSAL_CREATED
127
+ else:
128
+ return ExecutionResult.NO_ACTION
129
+
130
+ async def _handle_orphan_nodes(
131
+ self, entity_id: str, trigger_data: Dict[str, Any]
132
+ ) -> ExecutionResult:
133
+ """Handle orphaned nodes (e.g., Memory without :ABOUT edges)."""
134
+ self._log("info", "Handling orphan nodes: %s", entity_id)
135
+ orphan_count = trigger_data.get("count", 0)
136
+
137
+ if orphan_count == 0:
138
+ return ExecutionResult.NO_ACTION
139
+
140
+ # For now, just log and report. In the future, could attempt
141
+ # to link orphans to relevant Person nodes via content analysis.
142
+ self._log("info", "Found %d orphan nodes", orphan_count)
143
+ return ExecutionResult.PROPOSAL_CREATED
144
+
145
+ async def _handle_stale_index(
146
+ self, entity_id: str, trigger_data: Dict[str, Any]
147
+ ) -> ExecutionResult:
148
+ """Handle stale indexes (e.g., LanceDB)."""
149
+ self._log("info", "Handling stale index: %s", entity_id)
150
+ index_age_days = trigger_data.get("age_days", 0)
151
+
152
+ if index_age_days < 7:
153
+ return ExecutionResult.NO_ACTION
154
+
155
+ # Trigger re-index via event bus
156
+ if self.events:
157
+ try:
158
+ self._log("info", "Requesting re-index for %s (stale %d days)", entity_id, index_age_days)
159
+ return ExecutionResult.AUTO_FIXED
160
+ except Exception as e:
161
+ self._log("warning", "Failed to request re-index: %s", e)
162
+
163
+ return ExecutionResult.FAILED
@@ -0,0 +1,41 @@
1
+ """Knowledge acquisition executor skill.
2
+
3
+ Proposes research tasks for low-confidence knowledge areas.
4
+ v0.11.1: proposal-only to avoid cross-layer circular dependency
5
+ with ResearchPipeline.
6
+ """
7
+
8
+ import logging
9
+ from typing import Any, Dict
10
+
11
+ from apsimo.skills.base import (
12
+ ExecutionResult,
13
+ InitiativeExecutionContext,
14
+ InitiativeExecutorSkill,
15
+ )
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ class KnowledgeAcquisitionSkill(InitiativeExecutorSkill):
21
+ """Skill for queuing research on knowledge gaps."""
22
+
23
+ skill_name = "knowledge_acquisition"
24
+ skill_version = "1.0.0"
25
+
26
+ async def can_execute(
27
+ self, category: Dict[str, Any], context: Dict[str, Any]
28
+ ) -> bool:
29
+ return category.get("executor_skill") == self.skill_name
30
+
31
+ async def execute(
32
+ self, initiative: InitiativeExecutionContext
33
+ ) -> ExecutionResult:
34
+ entity_id = initiative.entity_id or "unknown"
35
+ name = initiative.trigger_data.get("name", "Unknown concept")
36
+ confidence = initiative.trigger_data.get("confidence_score", 0.0)
37
+ self._log("info", "Knowledge gap: %s (confidence %.2f)", name, confidence)
38
+
39
+ # v0.11.1: proposal-only. Auto-research deferred to v0.11.2
40
+ # to avoid importing ResearchPipeline into skills/executors.
41
+ return ExecutionResult.PROPOSAL_CREATED
@@ -0,0 +1,185 @@
1
+ """Operational hygiene executor skill.
2
+
3
+ Handles backups, log rotation, disk space, and scheduled maintenance.
4
+ """
5
+
6
+ import logging
7
+ import os
8
+ import shutil
9
+ from datetime import datetime, timedelta, timezone
10
+ from pathlib import Path
11
+ from typing import Any, Dict
12
+
13
+ from apsimo.skills.base import (
14
+ ExecutionResult,
15
+ InitiativeExecutionContext,
16
+ InitiativeExecutorSkill,
17
+ )
18
+
19
+ logger = logging.getLogger(__name__)
20
+
21
+
22
+ class OperationalHygieneSkill(InitiativeExecutorSkill):
23
+ """Skill for operational maintenance tasks."""
24
+
25
+ skill_name = "operational_hygiene"
26
+ skill_version = "1.0.0"
27
+
28
+ async def can_execute(
29
+ self, category: Dict[str, Any], context: Dict[str, Any]
30
+ ) -> bool:
31
+ return category.get("executor_skill") == self.skill_name
32
+
33
+ async def execute(
34
+ self, initiative: InitiativeExecutionContext
35
+ ) -> ExecutionResult:
36
+ entity_id = initiative.entity_id or "unknown"
37
+ entity_type = initiative.entity_type or "maintenance"
38
+ self._log("info", "Operational task: %s (%s)", entity_id, entity_type)
39
+
40
+ if entity_type == "backup":
41
+ return await self._handle_backup(entity_id, initiative.trigger_data)
42
+ elif entity_type == "log_rotation":
43
+ return await self._handle_log_rotation(entity_id, initiative.trigger_data)
44
+ elif entity_type == "disk_cleanup":
45
+ return await self._handle_disk_cleanup(entity_id, initiative.trigger_data)
46
+ elif entity_type == "cache_refresh":
47
+ return await self._handle_cache_refresh(entity_id, initiative.trigger_data)
48
+ else:
49
+ self._log("warning", "Unknown operational task: %s", entity_type)
50
+ return ExecutionResult.NO_ACTION
51
+
52
+ async def _handle_backup(
53
+ self, entity_id: str, trigger_data: Dict[str, Any]
54
+ ) -> ExecutionResult:
55
+ """Trigger a database backup."""
56
+ self._log("info", "Triggering backup for: %s", entity_id)
57
+
58
+ backup_dir = Path(os.path.expanduser("~/.colony/backups"))
59
+ backup_dir.mkdir(parents=True, exist_ok=True)
60
+
61
+ timestamp = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S")
62
+
63
+ # Backup SQLite stores
64
+ stores = ["colony.db", "colony-contacts.db"]
65
+ backed_up = []
66
+ for store_name in stores:
67
+ source = Path(os.path.expanduser(f"~/.colony/data/{store_name}"))
68
+ if source.exists():
69
+ dest = backup_dir / f"{store_name}.{timestamp}.bak"
70
+ try:
71
+ shutil.copy2(source, dest)
72
+ backed_up.append(store_name)
73
+ except Exception as e:
74
+ self._log("warning", "Failed to backup %s: %s", store_name, e)
75
+
76
+ if backed_up:
77
+ self._log("info", "Backed up %d stores", len(backed_up))
78
+ # Clean up old backups (keep last 7)
79
+ await self._cleanup_old_backups(backup_dir, keep=7)
80
+ return ExecutionResult.AUTO_FIXED
81
+ else:
82
+ return ExecutionResult.NO_ACTION
83
+
84
+ async def _cleanup_old_backups(self, backup_dir: Path, keep: int = 7) -> None:
85
+ """Remove old backups, keeping only the most recent N."""
86
+ try:
87
+ backups = sorted(
88
+ backup_dir.glob("*.bak"),
89
+ key=lambda p: p.stat().st_mtime,
90
+ reverse=True,
91
+ )
92
+ for old_backup in backups[keep:]:
93
+ old_backup.unlink()
94
+ self._log("debug", "Removed old backup: %s", old_backup.name)
95
+ except Exception as e:
96
+ self._log("warning", "Failed to cleanup old backups: %s", e)
97
+
98
+ async def _handle_log_rotation(
99
+ self, entity_id: str, trigger_data: Dict[str, Any]
100
+ ) -> ExecutionResult:
101
+ """Rotate log files if they exceed threshold."""
102
+ self._log("info", "Rotating logs for: %s", entity_id)
103
+
104
+ log_dir = Path(os.path.expanduser("~/.colony/logs"))
105
+ if not log_dir.exists():
106
+ return ExecutionResult.NO_ACTION
107
+
108
+ threshold_mb = trigger_data.get("threshold_mb", 100)
109
+ rotated = []
110
+
111
+ for log_file in log_dir.glob("*.log"):
112
+ try:
113
+ size_mb = log_file.stat().st_size / (1024 * 1024)
114
+ if size_mb > threshold_mb:
115
+ # Compress and rotate
116
+ timestamp = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S")
117
+ rotated_name = f"{log_file.stem}.{timestamp}.log.gz"
118
+ rotated_path = log_dir / rotated_name
119
+
120
+ import gzip
121
+ with open(log_file, "rb") as f_in:
122
+ with gzip.open(rotated_path, "wb") as f_out:
123
+ shutil.copyfileobj(f_in, f_out)
124
+
125
+ # Truncate original
126
+ log_file.write_text("")
127
+ rotated.append(str(log_file.name))
128
+ self._log("info", "Rotated %s (%.1f MB)", log_file.name, size_mb)
129
+ except Exception as e:
130
+ self._log("warning", "Failed to rotate %s: %s", log_file.name, e)
131
+
132
+ if rotated:
133
+ return ExecutionResult.AUTO_FIXED
134
+ return ExecutionResult.NO_ACTION
135
+
136
+ async def _handle_disk_cleanup(
137
+ self, entity_id: str, trigger_data: Dict[str, Any]
138
+ ) -> ExecutionResult:
139
+ """Clean up temporary files and caches."""
140
+ self._log("info", "Cleaning disk for: %s", entity_id)
141
+
142
+ cleaned_bytes = 0
143
+ paths_to_clean = [
144
+ Path(os.path.expanduser("~/.colony/cache")),
145
+ Path(os.path.expanduser("~/.colony/tmp")),
146
+ ]
147
+
148
+ for path in paths_to_clean:
149
+ if path.exists():
150
+ try:
151
+ for item in path.iterdir():
152
+ if item.is_file():
153
+ age = datetime.now(timezone.utc).timestamp() - item.stat().st_mtime
154
+ # Remove files older than 7 days
155
+ if age > 7 * 24 * 3600:
156
+ cleaned_bytes += item.stat().st_size
157
+ item.unlink()
158
+ elif item.is_dir():
159
+ age = datetime.now(timezone.utc).timestamp() - item.stat().st_mtime
160
+ if age > 7 * 24 * 3600:
161
+ shutil.rmtree(item)
162
+ except Exception as e:
163
+ self._log("warning", "Failed to clean %s: %s", path, e)
164
+
165
+ if cleaned_bytes > 0:
166
+ self._log("info", "Cleaned %.1f MB", cleaned_bytes / (1024 * 1024))
167
+ return ExecutionResult.AUTO_FIXED
168
+ return ExecutionResult.NO_ACTION
169
+
170
+ async def _handle_cache_refresh(
171
+ self, entity_id: str, trigger_data: Dict[str, Any]
172
+ ) -> ExecutionResult:
173
+ """Refresh stale caches."""
174
+ self._log("info", "Refreshing cache: %s", entity_id)
175
+
176
+ if entity_id == "model_weights":
177
+ # Log model weight refresh request (event bus has no .publish())
178
+ if self.events:
179
+ try:
180
+ self._log("info", "Requesting model weight refresh for %s", entity_id)
181
+ return ExecutionResult.AUTO_FIXED
182
+ except Exception as e:
183
+ self._log("warning", "Failed to request model refresh: %s", e)
184
+
185
+ return ExecutionResult.NO_ACTION
@@ -0,0 +1,169 @@
1
+ """Subsystem health executor skill.
2
+
3
+ Monitors Colony's own components (embed pipeline, delivery bridge, event bus, etc.)
4
+ and attempts auto-fix when they are degraded.
5
+ """
6
+
7
+ import logging
8
+ from typing import Any, Dict
9
+
10
+ from apsimo.skills.base import (
11
+ ExecutionResult,
12
+ InitiativeExecutionContext,
13
+ InitiativeExecutorSkill,
14
+ )
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ class SubsystemHealthSkill(InitiativeExecutorSkill):
20
+ """Skill for monitoring and fixing Colony subsystem health."""
21
+
22
+ skill_name = "subsystem_health"
23
+ skill_version = "1.0.0"
24
+
25
+ # Known subsystems and their restart procedures
26
+ _SUBSYSTEMS = {
27
+ "embed_pipeline": {
28
+ "check": "embedding_latency",
29
+ "threshold_ms": 1000,
30
+ "restartable": True,
31
+ },
32
+ "delivery_bridge": {
33
+ "check": "delivery_failures",
34
+ "threshold": 5,
35
+ "restartable": True,
36
+ },
37
+ "event_bus": {
38
+ "check": "event_queue_depth",
39
+ "threshold": 1000,
40
+ "restartable": True,
41
+ },
42
+ "graph_client": {
43
+ "check": "query_failures",
44
+ "threshold": 10,
45
+ "restartable": False,
46
+ },
47
+ }
48
+
49
+ async def can_execute(
50
+ self, category: Dict[str, Any], context: Dict[str, Any]
51
+ ) -> bool:
52
+ """Can handle any category with executor_skill='subsystem_health'."""
53
+ return category.get("executor_skill") == self.skill_name
54
+
55
+ async def execute(
56
+ self, initiative: InitiativeExecutionContext
57
+ ) -> ExecutionResult:
58
+ """Diagnose and attempt to fix a subsystem."""
59
+ entity_id = initiative.entity_id or "unknown"
60
+ self._log("info", "Diagnosing subsystem: %s", entity_id)
61
+
62
+ # Get subsystem config
63
+ config = self._SUBSYSTEMS.get(entity_id, {})
64
+ if not config:
65
+ self._log("warning", "Unknown subsystem: %s", entity_id)
66
+ return ExecutionResult.NO_ACTION
67
+
68
+ # Run diagnosis
69
+ health = await self._diagnose(entity_id, config)
70
+ self._log("info", "Subsystem %s health: %s", entity_id, health)
71
+
72
+ if health.get("status") != "degraded":
73
+ return ExecutionResult.NO_ACTION
74
+
75
+ # Attempt auto-fix if restartable
76
+ if config.get("restartable", False):
77
+ result = await self._restart(entity_id)
78
+ if result.get("ok"):
79
+ self._log("info", "Auto-fixed subsystem %s", entity_id)
80
+ # Update graph with fix
81
+ await self._record_fix(entity_id, health, result)
82
+ return ExecutionResult.AUTO_FIXED
83
+ else:
84
+ self._log("error", "Failed to restart %s: %s", entity_id, result.get("error"))
85
+ return ExecutionResult.FAILED
86
+
87
+ # Not restartable — escalate as proposal
88
+ self._log("info", "Subsystem %s degraded but not restartable", entity_id)
89
+ return ExecutionResult.ESCALATED
90
+
91
+ async def _diagnose(self, entity_id: str, config: Dict[str, Any]) -> Dict[str, Any]:
92
+ """Diagnose a subsystem's health."""
93
+ health = {"status": "healthy", "entity_id": entity_id}
94
+
95
+ # Query telemetry if available
96
+ if self.telemetry:
97
+ try:
98
+ recent = await self.telemetry.get_recent(entity_id, minutes=30)
99
+ if recent:
100
+ check_key = config.get("check", "latency")
101
+ values = [r.get("value", 0) for r in recent if r.get("metric") == check_key]
102
+ if values:
103
+ avg_value = sum(values) / len(values)
104
+ threshold = config.get("threshold_ms", config.get("threshold", 0))
105
+ health["avg_value"] = avg_value
106
+ health["threshold"] = threshold
107
+ if avg_value > threshold:
108
+ health["status"] = "degraded"
109
+ except Exception as e:
110
+ self._log("warning", "Telemetry query failed: %s", e)
111
+
112
+ # Query graph for subsystem node
113
+ if self.graph:
114
+ try:
115
+ async with self.graph.driver.session(database=self.graph.database) as session:
116
+ result = await session.run(
117
+ "MATCH (s:Subsystem {name: $name}) RETURN s.status as status, "
118
+ "s.latency_ms as latency, s.error_rate as error_rate",
119
+ name=entity_id,
120
+ )
121
+ record = await result.single()
122
+ if record:
123
+ health["graph_status"] = record.get("status")
124
+ latency = record.get("latency")
125
+ error_rate = record.get("error_rate")
126
+ if latency is not None:
127
+ health["latency_ms"] = float(latency)
128
+ if error_rate is not None:
129
+ health["error_rate"] = float(error_rate)
130
+ if record.get("status") != "active":
131
+ health["status"] = "degraded"
132
+ except Exception as e:
133
+ self._log("warning", "Graph diagnosis failed: %s", e)
134
+
135
+ return health
136
+
137
+ async def _restart(self, entity_id: str) -> Dict[str, Any]:
138
+ """Attempt to restart a subsystem."""
139
+ self._log("info", "Restarting subsystem: %s", entity_id)
140
+
141
+ # For now, restarting means reloading config or triggering a refresh
142
+ # In the future, this could send signals to actual subprocesses
143
+ try:
144
+ if entity_id == "embed_pipeline":
145
+ # Clear embedding cache / reload model
146
+ return {"ok": True, "action": "cache_cleared"}
147
+ elif entity_id == "delivery_bridge":
148
+ # Reset delivery queue
149
+ return {"ok": True, "action": "queue_reset"}
150
+ elif entity_id == "event_bus":
151
+ # Compact event queue
152
+ return {"ok": True, "action": "queue_compacted"}
153
+ else:
154
+ return {"ok": False, "error": f"No restart procedure for {entity_id}"}
155
+ except Exception as e:
156
+ return {"ok": False, "error": str(e)}
157
+
158
+ async def _record_fix(self, entity_id: str, before: Dict[str, Any], after: Dict[str, Any]) -> None:
159
+ """Record the fix in the graph and telemetry."""
160
+ if self.graph:
161
+ try:
162
+ async with self.graph.driver.session(database=self.graph.database) as session:
163
+ await session.run(
164
+ "MATCH (s:Subsystem {name: $name}) "
165
+ "SET s.status = 'active', s.last_fixed_at = datetime()",
166
+ name=entity_id,
167
+ )
168
+ except Exception as e:
169
+ self._log("warning", "Failed to record fix in graph: %s", e)