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 @@
1
+ """Built-in sidecar services that auto-start alongside the server."""
@@ -0,0 +1,474 @@
1
+ """Built-in Agent Bridge service -- auto-wired inside the sidecar.
2
+
3
+ Closes the autonomy circuit by forwarding pending initiatives and queued
4
+ jobs to the agent webhook. Runs as an async background task alongside the
5
+ autonomy loop, so it works on any platform with zero external setup.
6
+
7
+ This is the internal counterpart to the ``colony-agent-bridge`` console
8
+ script. The console script polls Colony over HTTP and is for split
9
+ deployments where agent and sidecar run on different machines. This
10
+ service accesses the initiative store and autonomy loop directly (no
11
+ HTTP round-trip to itself) and is the default for single-machine setups.
12
+
13
+ What it does every cycle (default 60s):
14
+ - Drains pending initiatives from the store and POSTs them to the
15
+ agent's initiative webhook
16
+ - Claims queued agent_action jobs and POSTs them to the jobs webhook
17
+ - Monitors the autonomy circuit for silent failures:
18
+ * initiatives generated but never executed
19
+ * autonomy loop stuck (ticks not advancing)
20
+ * agent webhook unreachable
21
+ - Syncs agent skills to the observation store (periodic, default 24h)
22
+
23
+ Auto-starts at sidecar boot when COLONY_BRIDGE_ENABLED is not "false"
24
+ and an agent webhook URL is configured. Disabled automatically when no
25
+ webhook is set (the agent hasn't connected yet).
26
+
27
+ Environment / config:
28
+ COLONY_BRIDGE_ENABLED "true" (default) / "false"
29
+ COLONY_BRIDGE_WEBHOOK_URL agent initiative webhook (required)
30
+ COLONY_BRIDGE_JOBS_WEBHOOK_URL agent jobs webhook (optional, same host)
31
+ COLONY_BRIDGE_CYCLE_SECS cycle interval (default 60)
32
+ COLONY_BRIDGE_SKILLS_HOURS skills sync interval (default 24)
33
+ """
34
+
35
+ from __future__ import annotations
36
+
37
+ import asyncio
38
+ import json
39
+ import logging
40
+ import os
41
+ import urllib.error
42
+ import urllib.request
43
+ from datetime import datetime, timezone
44
+ from typing import Any, Optional
45
+
46
+ from apsimo.task_queue.models import JobType, WorkerCapabilities
47
+ from apsimo.workers.queue_worker import (
48
+ agent_action_capabilities,
49
+ encode_hermes_webhook,
50
+ work_order_webhook_fields,
51
+ )
52
+
53
+ logger = logging.getLogger(__name__)
54
+
55
+
56
+ class AgentBridgeService:
57
+ """Async service that bridges the sidecar's autonomy output to the agent."""
58
+
59
+ def __init__(
60
+ self,
61
+ initiative_store: Any,
62
+ autonomy_loop: Any = None,
63
+ task_queue: Any = None,
64
+ observation_store: Any = None,
65
+ webhook_url: str = "",
66
+ jobs_webhook_url: str = "",
67
+ cycle_secs: float = 60.0,
68
+ skills_sync_hours: float = 24.0,
69
+ api_key: str = "",
70
+ node_id: str = "sidecar-bridge",
71
+ ):
72
+ self._store = initiative_store
73
+ self._autonomy = autonomy_loop
74
+ self._queue = task_queue
75
+ self._obs_store = observation_store
76
+ self._webhook = webhook_url
77
+ self._jobs_webhook = jobs_webhook_url or webhook_url.replace(
78
+ "/colony-initiatives", "/colony-jobs"
79
+ )
80
+ self._cycle_secs = cycle_secs
81
+ self._skills_hours = skills_sync_hours
82
+ self._api_key = api_key
83
+ self._node_id = node_id
84
+
85
+ self._running = False
86
+ self._stop_event = asyncio.Event()
87
+ self._seen_ids: set = set()
88
+ self._seen_dedup: set = set()
89
+ self._last_skills_sync = 0.0
90
+ self._last_ticks = 0
91
+ self._consecutive_webhook_failures = 0
92
+ self._stats = {
93
+ "cycles": 0,
94
+ "initiatives_forwarded": 0,
95
+ "jobs_dispatched": 0,
96
+ "webhook_failures": 0,
97
+ "alerts_raised": 0,
98
+ }
99
+
100
+ @property
101
+ def is_running(self) -> bool:
102
+ return self._running
103
+
104
+ @property
105
+ def stats(self) -> dict:
106
+ return dict(self._stats)
107
+
108
+ # ------------------------------------------------------------------
109
+ # Lifecycle
110
+ # ------------------------------------------------------------------
111
+
112
+ async def start(self) -> None:
113
+ if not self._webhook:
114
+ logger.info("Agent bridge: no webhook URL configured, staying dormant")
115
+ return
116
+
117
+ self._running = True
118
+ self._stop_event.clear()
119
+ logger.info(
120
+ "Agent bridge starting (cycle=%ds, webhook=%s)",
121
+ int(self._cycle_secs), self._webhook,
122
+ )
123
+
124
+ try:
125
+ while not self._stop_event.is_set():
126
+ await self._cycle()
127
+ try:
128
+ await asyncio.wait_for(
129
+ self._stop_event.wait(), timeout=self._cycle_secs
130
+ )
131
+ except asyncio.TimeoutError:
132
+ pass
133
+ finally:
134
+ self._running = False
135
+ logger.info("Agent bridge stopped. Stats: %s", self._stats)
136
+
137
+ async def stop(self) -> None:
138
+ logger.info("Agent bridge stop requested")
139
+ self._stop_event.set()
140
+
141
+ # ------------------------------------------------------------------
142
+ # Main cycle
143
+ # ------------------------------------------------------------------
144
+
145
+ async def _cycle(self) -> None:
146
+ self._stats["cycles"] += 1
147
+
148
+ await self._forward_initiatives()
149
+ await self._dispatch_jobs()
150
+ self._check_circuit_health()
151
+ await self._maybe_sync_skills()
152
+
153
+ # ------------------------------------------------------------------
154
+ # Initiative forwarding
155
+ # ------------------------------------------------------------------
156
+
157
+ async def _forward_initiatives(self) -> None:
158
+ if self._store is None:
159
+ return
160
+
161
+ try:
162
+ loop = asyncio.get_event_loop()
163
+ pending = await loop.run_in_executor(
164
+ None, lambda: self._store.list(status=["pending"], limit=50)
165
+ )
166
+ except Exception as exc:
167
+ logger.warning("Failed to list pending initiatives: %s", exc)
168
+ return
169
+
170
+ for initiative in pending:
171
+ iid = getattr(initiative, "id", "")
172
+ dedup_key = getattr(initiative, "dedup_key", "") or ""
173
+
174
+ if iid in self._seen_ids:
175
+ continue
176
+ if dedup_key and dedup_key in self._seen_dedup:
177
+ self._seen_ids.add(iid)
178
+ continue
179
+
180
+ payload = {
181
+ "type": "initiative",
182
+ "payload": {
183
+ "id": iid,
184
+ "initiative_type": getattr(initiative, "initiative_type", ""),
185
+ "title": getattr(initiative, "title", ""),
186
+ "description": getattr(initiative, "description", ""),
187
+ "priority": getattr(initiative, "priority", 0.5),
188
+ "status": "pending",
189
+ "entity_id": getattr(initiative, "entity_id", None),
190
+ "dedup_key": dedup_key,
191
+ "context": getattr(initiative, "context", None) or {},
192
+ "created_at": str(getattr(initiative, "created_at", "")),
193
+ },
194
+ "occurred_at": str(getattr(initiative, "created_at", "")),
195
+ }
196
+
197
+ ok = await self._post_webhook(self._webhook, payload)
198
+ if ok:
199
+ self._seen_ids.add(iid)
200
+ if dedup_key:
201
+ self._seen_dedup.add(dedup_key)
202
+ self._stats["initiatives_forwarded"] += 1
203
+ logger.info(
204
+ "Forwarded initiative %s (%s)",
205
+ iid, getattr(initiative, "initiative_type", "?"),
206
+ )
207
+
208
+ self._rotate_seen_sets()
209
+
210
+ # ------------------------------------------------------------------
211
+ # Job dispatch
212
+ # ------------------------------------------------------------------
213
+
214
+ async def _dispatch_jobs(self) -> None:
215
+ if os.environ.get(
216
+ "COLONY_AGENT_JOB_CLAIMS_ENABLED", "true"
217
+ ).strip().lower() not in {"1", "true", "yes", "on"}:
218
+ return
219
+ if self._queue is None:
220
+ return
221
+
222
+ try:
223
+ capabilities = set(agent_action_capabilities())
224
+ except ValueError as exc:
225
+ logger.error("Agent bridge worker routes are invalid: %s", exc)
226
+ return
227
+
228
+ queue = getattr(self._queue, "queue", self._queue)
229
+ if not all(
230
+ hasattr(queue, method)
231
+ for method in ("claim_job", "start_job", "release_job")
232
+ ):
233
+ logger.warning(
234
+ "Agent bridge task queue lacks governed async lifecycle"
235
+ )
236
+ return
237
+ try:
238
+ job = await queue.claim_job(
239
+ self._node_id,
240
+ WorkerCapabilities(
241
+ node_id=self._node_id,
242
+ capabilities=capabilities,
243
+ max_concurrent=1,
244
+ job_types={JobType.AGENT_ACTION},
245
+ ),
246
+ )
247
+ except Exception as exc:
248
+ logger.debug("Job claim failed: %s", exc)
249
+ return
250
+
251
+ if not job:
252
+ return
253
+
254
+ job_id = str(getattr(job, "job_id", "") or "")
255
+ claim_attempt_id = getattr(job, "claim_attempt_id", None)
256
+ params = getattr(job, "payload", {}) or {}
257
+ if not job_id or not claim_attempt_id:
258
+ logger.warning("Agent bridge received an invalid governed claim")
259
+ return
260
+ try:
261
+ started = await queue.start_job(
262
+ job_id,
263
+ self._node_id,
264
+ claim_attempt_id=claim_attempt_id,
265
+ )
266
+ except Exception as exc:
267
+ logger.warning("Agent bridge job start failed for %s: %s", job_id, exc)
268
+ started = False
269
+ if not started:
270
+ try:
271
+ await queue.release_job(
272
+ job_id,
273
+ self._node_id,
274
+ claim_attempt_id=claim_attempt_id,
275
+ )
276
+ except Exception:
277
+ pass
278
+ return
279
+
280
+ colony_url = os.environ.get("COLONY_URL", "http://127.0.0.1:7777")
281
+ payload = {
282
+ "type": "agent_job",
283
+ "occurred_at": datetime.now(timezone.utc).isoformat(),
284
+ "payload": {
285
+ "job_id": job_id,
286
+ "claim_attempt_id": claim_attempt_id,
287
+ "action_hint": params.get("action_hint", ""),
288
+ "domain": params.get("domain", ""),
289
+ "risk": params.get("risk", ""),
290
+ "description": params.get("description", ""),
291
+ "context": params.get("context", {}),
292
+ "colony_url": colony_url,
293
+ "observations_url": f"{colony_url}/v1/host/observations",
294
+ "heartbeat_url": f"{colony_url}/v1/host/queue/jobs/{job_id}/heartbeat",
295
+ "complete_url": f"{colony_url}/v1/host/queue/jobs/{job_id}/complete",
296
+ "fail_url": f"{colony_url}/v1/host/queue/jobs/{job_id}/fail",
297
+ "api_key_header": "X-API-Key",
298
+ **work_order_webhook_fields(params),
299
+ },
300
+ }
301
+
302
+ ok = await self._post_webhook(self._jobs_webhook, payload)
303
+ if ok:
304
+ self._stats["jobs_dispatched"] += 1
305
+ logger.info("Dispatched job %s (%s)", job_id, params.get("action_hint", "?"))
306
+ else:
307
+ try:
308
+ await queue.release_job(
309
+ job_id,
310
+ self._node_id,
311
+ claim_attempt_id=claim_attempt_id,
312
+ )
313
+ except Exception:
314
+ logger.warning(
315
+ "Agent bridge could not release failed dispatch %s",
316
+ job_id,
317
+ exc_info=True,
318
+ )
319
+
320
+ # ------------------------------------------------------------------
321
+ # Circuit health monitoring
322
+ # ------------------------------------------------------------------
323
+
324
+ def _check_circuit_health(self) -> None:
325
+ if self._autonomy is None:
326
+ return
327
+
328
+ stats = getattr(self._autonomy, "stats", None)
329
+ if stats is None:
330
+ return
331
+
332
+ ticks = getattr(stats, "ticks", 0)
333
+ generated = getattr(stats, "initiatives_generated", 0)
334
+ executed = getattr(stats, "actions_executed", 0)
335
+
336
+ if self._last_ticks > 0 and ticks == self._last_ticks:
337
+ self._log_alert(
338
+ "autonomy_stuck",
339
+ f"Autonomy loop stuck at tick {ticks}",
340
+ )
341
+
342
+ if generated > 100 and executed == 0 and self._stats["initiatives_forwarded"] == 0:
343
+ self._log_alert(
344
+ "initiatives_never_delivered",
345
+ f"Generated {generated} initiatives but forwarded 0 to the agent. "
346
+ f"Check webhook URL and agent availability.",
347
+ )
348
+
349
+ if self._consecutive_webhook_failures >= 5:
350
+ self._log_alert(
351
+ "webhook_unreachable",
352
+ f"Agent webhook has failed {self._consecutive_webhook_failures} "
353
+ f"consecutive times ({self._webhook})",
354
+ )
355
+
356
+ self._last_ticks = ticks
357
+
358
+ def _log_alert(self, alert_type: str, message: str) -> None:
359
+ self._stats["alerts_raised"] += 1
360
+ logger.warning("[BRIDGE HEALTH] %s: %s", alert_type, message)
361
+
362
+ # ------------------------------------------------------------------
363
+ # Skills sync
364
+ # ------------------------------------------------------------------
365
+
366
+ async def _maybe_sync_skills(self) -> None:
367
+ if self._obs_store is None:
368
+ return
369
+
370
+ import time as _time
371
+ now = _time.monotonic()
372
+ if self._last_skills_sync > 0 and (now - self._last_skills_sync) < (self._skills_hours * 3600):
373
+ return
374
+ self._last_skills_sync = now
375
+
376
+ try:
377
+ from apsimo.workers.skills_sync import scan
378
+ observations = scan()
379
+ if not observations:
380
+ return
381
+
382
+ loop = asyncio.get_event_loop()
383
+ for obs in observations:
384
+ await loop.run_in_executor(
385
+ None,
386
+ lambda o=obs: self._obs_store.upsert(
387
+ domain="skills",
388
+ entity_id=o["entity_id"],
389
+ payload=o["payload"],
390
+ reported_by="agent-bridge",
391
+ ) if hasattr(self._obs_store, "upsert") else None,
392
+ )
393
+ logger.info("Skills sync: %d skills reported", len(observations))
394
+ except ImportError:
395
+ logger.debug("Skills sync: skills_sync module not available")
396
+ except Exception as exc:
397
+ logger.warning("Skills sync failed: %s", exc)
398
+
399
+ # ------------------------------------------------------------------
400
+ # Webhook helper
401
+ # ------------------------------------------------------------------
402
+
403
+ async def _post_webhook(self, url: str, payload: dict) -> bool:
404
+ loop = asyncio.get_event_loop()
405
+ try:
406
+ def _do_post():
407
+ body, headers = encode_hermes_webhook(payload)
408
+ req = urllib.request.Request(
409
+ url,
410
+ data=body,
411
+ headers=headers,
412
+ method="POST",
413
+ )
414
+ urllib.request.urlopen(req, timeout=10)
415
+
416
+ await loop.run_in_executor(None, _do_post)
417
+ self._consecutive_webhook_failures = 0
418
+ return True
419
+ except Exception as exc:
420
+ self._consecutive_webhook_failures += 1
421
+ self._stats["webhook_failures"] += 1
422
+ logger.debug("Webhook POST failed (%s): %s", url, exc)
423
+ return False
424
+
425
+ # ------------------------------------------------------------------
426
+ # Helpers
427
+ # ------------------------------------------------------------------
428
+
429
+ def _rotate_seen_sets(self) -> None:
430
+ if len(self._seen_ids) > 5000:
431
+ self._seen_ids = set(sorted(self._seen_ids)[-2000:])
432
+ if len(self._seen_dedup) > 5000:
433
+ self._seen_dedup = set(sorted(self._seen_dedup)[-2000:])
434
+
435
+
436
+ # ---------------------------------------------------------------------------
437
+ # Factory + wiring
438
+ # ---------------------------------------------------------------------------
439
+
440
+ def create_from_env(
441
+ initiative_store: Any = None,
442
+ autonomy_loop: Any = None,
443
+ task_queue: Any = None,
444
+ observation_store: Any = None,
445
+ ) -> Optional[AgentBridgeService]:
446
+ """Create an AgentBridgeService from environment variables.
447
+
448
+ Returns None if COLONY_BRIDGE_ENABLED is "false" or no webhook URL
449
+ is configured.
450
+ """
451
+ if os.environ.get("COLONY_BRIDGE_ENABLED", "true").lower() == "false":
452
+ logger.info("Agent bridge disabled (COLONY_BRIDGE_ENABLED=false)")
453
+ return None
454
+
455
+ webhook = os.environ.get("COLONY_BRIDGE_WEBHOOK_URL", "")
456
+ if not webhook:
457
+ logger.info(
458
+ "Agent bridge: no COLONY_BRIDGE_WEBHOOK_URL set, will not start. "
459
+ "Set it to the agent's initiative webhook to enable."
460
+ )
461
+ return None
462
+
463
+ return AgentBridgeService(
464
+ initiative_store=initiative_store,
465
+ autonomy_loop=autonomy_loop,
466
+ task_queue=task_queue,
467
+ observation_store=observation_store,
468
+ webhook_url=webhook,
469
+ jobs_webhook_url=os.environ.get("COLONY_BRIDGE_JOBS_WEBHOOK_URL", ""),
470
+ cycle_secs=float(os.environ.get("COLONY_BRIDGE_CYCLE_SECS", "60")),
471
+ skills_sync_hours=float(os.environ.get("COLONY_BRIDGE_SKILLS_HOURS", "24")),
472
+ api_key=os.environ.get("COLONY_API_KEY", ""),
473
+ node_id=os.environ.get("COLONY_BRIDGE_NODE_ID", "sidecar-bridge"),
474
+ )