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,49 @@
1
+ """Web search native tool."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ from typing import Any, Dict
7
+
8
+ logger = logging.getLogger(__name__)
9
+
10
+
11
+ class WebSearchTool:
12
+ """Web search via Colony's SearchOrchestrator."""
13
+
14
+ def __init__(self, search_orchestrator=None):
15
+ self._orchestrator = search_orchestrator
16
+
17
+ @property
18
+ def name(self) -> str:
19
+ return "web_search"
20
+
21
+ @property
22
+ def schema(self) -> dict:
23
+ return {
24
+ "type": "object",
25
+ "properties": {
26
+ "query": {"type": "string", "description": "Search query"},
27
+ "max_results": {"type": "integer", "description": "Max results (default 5)", "default": 5},
28
+ },
29
+ "required": ["query"],
30
+ }
31
+
32
+ async def execute(self, args: Dict[str, Any]) -> Dict[str, Any]:
33
+ if not self._orchestrator or not self._orchestrator.has_providers:
34
+ return {"error": True, "message": "Web search not configured"}
35
+
36
+ query = args.get("query", "")
37
+ max_results = args.get("max_results", 5)
38
+
39
+ try:
40
+ results = await self._orchestrator.search(query, max_results)
41
+ return {
42
+ "results": [
43
+ {"title": r.title, "url": r.url, "snippet": r.snippet, "source": r.source}
44
+ for r in results
45
+ ],
46
+ "count": len(results),
47
+ }
48
+ except Exception as e:
49
+ return {"error": True, "message": str(e)}
@@ -0,0 +1,182 @@
1
+ """Typed authorization policy for sidecar-resident reasoning tools.
2
+
3
+ Model tool definitions are an ergonomics hint, not an enforcement boundary: a
4
+ model (or a direct API caller) can still name a tool that was not advertised.
5
+ This module gives every execution path the same small, conservative effect
6
+ classification and actor policy. Unknown and dynamically supplied tools are
7
+ mutating until explicitly classified.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from dataclasses import dataclass
13
+ from enum import Enum
14
+ from typing import Any, Mapping
15
+
16
+
17
+ class ToolEffect(str, Enum):
18
+ """The authority needed to execute a tool."""
19
+
20
+ PUBLIC_READ = "public_read"
21
+ PRIVATE_READ = "private_read"
22
+ MUTATION = "mutation"
23
+
24
+
25
+ @dataclass(frozen=True)
26
+ class ToolActorPolicy:
27
+ """Server-derived capabilities for one reasoning turn or direct call.
28
+
29
+ ``None`` remains the compatibility representation for trusted in-process
30
+ callers. HTTP handlers must always supply a concrete policy while P8 is
31
+ attached; request-body identities never populate these fields.
32
+ """
33
+
34
+ principal_id: str
35
+ viewer_person_id: str
36
+ allow_private_read: bool = False
37
+ allow_mutation: bool = False
38
+
39
+
40
+ # Pure/general information tools that do not expose Colony's private stores.
41
+ _PUBLIC_READ_TOOLS = frozenset({
42
+ "calculate",
43
+ "web_search",
44
+ })
45
+
46
+
47
+ # Tools that can expose owner, contact, repository, memory, or self-state.
48
+ # They are read-only, but are intentionally not guest-callable because most
49
+ # legacy handlers do not yet seal selectors in their argument dictionaries.
50
+ _PRIVATE_READ_TOOLS = frozenset({
51
+ "action_journal",
52
+ "belief_conflicts",
53
+ "colony_discover_connections",
54
+ "colony_get_briefing",
55
+ "colony_get_relationship",
56
+ "colony_list_boundaries",
57
+ "colony_list_goals",
58
+ "colony_memory_search",
59
+ "colony_query_entities",
60
+ "colony_recent_boundary_blocks",
61
+ "list_directory",
62
+ "list_projects",
63
+ "pending_contact_proposals",
64
+ "project_status",
65
+ "read_file",
66
+ "recall_skills",
67
+ "relationship_brief",
68
+ "repo_list_files",
69
+ "repo_read_file",
70
+ "repo_search",
71
+ "sandbox_status",
72
+ "self_status",
73
+ })
74
+
75
+
76
+ # Known state-changing tools. Unknown/dynamic tools also default to mutation.
77
+ _MUTATION_TOOLS = frozenset({
78
+ "abandon_project",
79
+ "colony_flag_boundary_concern",
80
+ "colony_initiative_feedback",
81
+ "colony_record_insight",
82
+ "colony_start_research",
83
+ "colony_task_complete",
84
+ "colony_task_dismiss",
85
+ "colony_task_snooze",
86
+ "create_project",
87
+ "link_contact",
88
+ "merge_contacts",
89
+ "sandbox_run",
90
+ "write_file",
91
+ })
92
+
93
+
94
+ def classify_tool(name: str) -> ToolEffect:
95
+ """Return the tool's conservative effect class.
96
+
97
+ The explicit mutation set documents current first-party tools. The final
98
+ branch is deliberately mutation: newly graduated/toolsmith tools cannot
99
+ silently inherit read authority merely because they are new.
100
+ """
101
+
102
+ normalized = str(name or "").strip()
103
+ if normalized in _PUBLIC_READ_TOOLS:
104
+ return ToolEffect.PUBLIC_READ
105
+ if normalized in _PRIVATE_READ_TOOLS:
106
+ return ToolEffect.PRIVATE_READ
107
+ if normalized in _MUTATION_TOOLS:
108
+ return ToolEffect.MUTATION
109
+ return ToolEffect.MUTATION
110
+
111
+
112
+ def actor_allows_tool(
113
+ name: str,
114
+ actor: ToolActorPolicy | None,
115
+ ) -> bool:
116
+ """Whether actor capabilities permit this tool before boundary checks."""
117
+
118
+ return actor_allows_effect(classify_tool(name), actor)
119
+
120
+
121
+ def actor_allows_effect(
122
+ effect: ToolEffect,
123
+ actor: ToolActorPolicy | None,
124
+ ) -> bool:
125
+ """Whether actor capabilities permit an already-resolved tool effect.
126
+
127
+ Callers with handler provenance must use this form. In particular, every
128
+ dynamic handler resolves to ``MUTATION`` even if its chosen name resembles
129
+ a shipped public or private read.
130
+ """
131
+
132
+ if actor is None: # trusted in-process compatibility caller
133
+ return True
134
+ if effect is ToolEffect.PUBLIC_READ:
135
+ return True
136
+ if effect is ToolEffect.PRIVATE_READ:
137
+ return actor.allow_private_read
138
+ return actor.allow_mutation
139
+
140
+
141
+ def filter_tools_for_actor(
142
+ names: list[str] | tuple[str, ...] | set[str] | frozenset[str],
143
+ actor: ToolActorPolicy | None,
144
+ ) -> list[str]:
145
+ """Preserve caller order while removing actor-inaccessible tool names."""
146
+
147
+ return [name for name in names if actor_allows_tool(name, actor)]
148
+
149
+
150
+ def action_target(arguments: Mapping[str, Any]) -> str:
151
+ """Extract a useful human/resource subject for DirectiveGuard matching."""
152
+
153
+ for key in (
154
+ "target",
155
+ "target_path",
156
+ "path",
157
+ "repo",
158
+ "url",
159
+ "endpoint",
160
+ "who",
161
+ "contact_id",
162
+ "person_id",
163
+ "entity_id",
164
+ "topic",
165
+ "query",
166
+ "purpose",
167
+ ):
168
+ value = arguments.get(key)
169
+ if isinstance(value, (str, int, float)) and str(value).strip():
170
+ return str(value).strip()
171
+ return ""
172
+
173
+
174
+ __all__ = [
175
+ "ToolActorPolicy",
176
+ "ToolEffect",
177
+ "action_target",
178
+ "actor_allows_effect",
179
+ "actor_allows_tool",
180
+ "classify_tool",
181
+ "filter_tools_for_actor",
182
+ ]
@@ -0,0 +1,176 @@
1
+ """Regex-based secret redaction for logs and tool output.
2
+
3
+ Applies pattern matching to mask API keys, tokens, and credentials
4
+ before they reach log files, verbose output, or gateway logs.
5
+
6
+ Short tokens (< 18 chars) are fully masked. Longer tokens preserve
7
+ the first 6 and last 4 characters for debuggability.
8
+ """
9
+
10
+ import logging
11
+ import os
12
+ import re
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+ # Known API key prefixes -- match the prefix + contiguous token chars
17
+ _PREFIX_PATTERNS = [
18
+ r"sk-[A-Za-z0-9_-]{10,}", # OpenAI / OpenRouter / Anthropic (sk-ant-*)
19
+ r"ghp_[A-Za-z0-9]{10,}", # GitHub PAT (classic)
20
+ r"github_pat_[A-Za-z0-9_]{10,}", # GitHub PAT (fine-grained)
21
+ r"xox[baprs]-[A-Za-z0-9-]{10,}", # Slack tokens
22
+ r"AIza[A-Za-z0-9_-]{30,}", # Google API keys
23
+ r"pplx-[A-Za-z0-9]{10,}", # Perplexity
24
+ r"fal_[A-Za-z0-9_-]{10,}", # Fal.ai
25
+ r"fc-[A-Za-z0-9]{10,}", # Firecrawl
26
+ r"bb_live_[A-Za-z0-9_-]{10,}", # BrowserBase
27
+ r"gAAAA[A-Za-z0-9_=-]{20,}", # Codex encrypted tokens
28
+ r"AKIA[A-Z0-9]{16}", # AWS Access Key ID
29
+ r"sk_live_[A-Za-z0-9]{10,}", # Stripe secret key (live)
30
+ r"sk_test_[A-Za-z0-9]{10,}", # Stripe secret key (test)
31
+ r"rk_live_[A-Za-z0-9]{10,}", # Stripe restricted key
32
+ r"SG\.[A-Za-z0-9_-]{10,}", # SendGrid API key
33
+ r"hf_[A-Za-z0-9]{10,}", # HuggingFace token
34
+ r"r8_[A-Za-z0-9]{10,}", # Replicate API token
35
+ r"npm_[A-Za-z0-9]{10,}", # npm access token
36
+ r"pypi-[A-Za-z0-9_-]{10,}", # PyPI API token
37
+ r"dop_v1_[A-Za-z0-9]{10,}", # DigitalOcean PAT
38
+ r"doo_v1_[A-Za-z0-9]{10,}", # DigitalOcean OAuth
39
+ r"am_[A-Za-z0-9_-]{10,}", # AgentMail API key
40
+ r"sk_[A-Za-z0-9_]{10,}", # ElevenLabs TTS key (sk_ underscore, not sk- dash)
41
+ r"tvly-[A-Za-z0-9]{10,}", # Tavily search API key
42
+ r"exa_[A-Za-z0-9]{10,}", # Exa search API key
43
+ ]
44
+
45
+ # ENV assignment patterns: KEY=value where KEY contains a secret-like name
46
+ _SECRET_ENV_NAMES = r"(?:API_?KEY|TOKEN|SECRET|PASSWORD|PASSWD|CREDENTIAL|AUTH)"
47
+ _ENV_ASSIGN_RE = re.compile(
48
+ rf"([A-Z_]*{_SECRET_ENV_NAMES}[A-Z_]*)\s*=\s*(['\"]?)(\S+)\2",
49
+ re.IGNORECASE,
50
+ )
51
+
52
+ # JSON field patterns: "apiKey": "value", "token": "value", etc.
53
+ _JSON_KEY_NAMES = r"(?:api_?[Kk]ey|token|secret|password|access_token|refresh_token|auth_token|bearer|secret_value|raw_secret|secret_input|key_material)"
54
+ _JSON_FIELD_RE = re.compile(
55
+ rf'("{_JSON_KEY_NAMES}")\s*:\s*"([^"]+)"',
56
+ re.IGNORECASE,
57
+ )
58
+
59
+ # Authorization headers
60
+ _AUTH_HEADER_RE = re.compile(
61
+ r"(Authorization:\s*Bearer\s+)(\S+)",
62
+ re.IGNORECASE,
63
+ )
64
+
65
+ # Telegram bot tokens: bot<digits>:<token> or <digits>:<token>,
66
+ # where token part is restricted to [-A-Za-z0-9_] and length >= 30
67
+ _TELEGRAM_RE = re.compile(
68
+ r"(bot)?(\d{8,}):([-A-Za-z0-9_]{30,})",
69
+ )
70
+
71
+ # Private key blocks: -----BEGIN RSA PRIVATE KEY----- ... -----END RSA PRIVATE KEY-----
72
+ _PRIVATE_KEY_RE = re.compile(
73
+ r"-----BEGIN[A-Z ]*PRIVATE KEY-----[\s\S]*?-----END[A-Z ]*PRIVATE KEY-----"
74
+ )
75
+
76
+ # Database connection strings: protocol://user:PASSWORD@host
77
+ # Catches postgres, mysql, mongodb, redis, amqp URLs and redacts the password
78
+ _DB_CONNSTR_RE = re.compile(
79
+ r"((?:postgres(?:ql)?|mysql|mongodb(?:\+srv)?|redis|amqp)://[^:]+:)([^@]+)(@)",
80
+ re.IGNORECASE,
81
+ )
82
+
83
+ # E.164 phone numbers: +<country><number>, 7-15 digits
84
+ # Negative lookahead prevents matching hex strings or identifiers
85
+ _SIGNAL_PHONE_RE = re.compile(r"(\+[1-9]\d{6,14})(?![A-Za-z0-9])")
86
+
87
+ # Compile known prefix patterns into one alternation
88
+ _PREFIX_RE = re.compile(
89
+ r"(?<![A-Za-z0-9_-])(" + "|".join(_PREFIX_PATTERNS) + r")(?![A-Za-z0-9_-])"
90
+ )
91
+
92
+
93
+ def _mask_token(token: str) -> str:
94
+ """Mask a token, preserving prefix for long tokens."""
95
+ if len(token) < 18:
96
+ return "***"
97
+ return f"{token[:6]}...{token[-4:]}"
98
+
99
+
100
+ def redact_sensitive_text(text: str) -> str:
101
+ """Apply all redaction patterns to a block of text.
102
+
103
+ Safe to call on any string -- non-matching text passes through unchanged.
104
+ Disabled when security.redact_secrets is false in config.yaml.
105
+ """
106
+ if not text:
107
+ return text
108
+ # Redaction bypass is only honoured when COLONY_ENV=development to prevent
109
+ # accidental disablement in production via .env files or container env overrides.
110
+ if os.getenv("COLONY_REDACT_SECRETS", "").lower() in ("0", "false", "no", "off"):
111
+ colony_env = os.getenv("COLONY_ENV", "production").lower()
112
+ if colony_env != "development":
113
+ # Silently ignore the bypass flag in non-development environments.
114
+ pass
115
+ else:
116
+ import logging as _log
117
+ _log.getLogger(__name__).warning(
118
+ "COLONY_REDACT_SECRETS bypass is active — log redaction disabled. "
119
+ "This must NOT be set in production."
120
+ )
121
+ return text
122
+
123
+ # Known prefixes (sk-, ghp_, etc.)
124
+ text = _PREFIX_RE.sub(lambda m: _mask_token(m.group(1)), text)
125
+
126
+ # ENV assignments: OPENAI_API_KEY=sk-abc...
127
+ def _redact_env(m):
128
+ name, quote, value = m.group(1), m.group(2), m.group(3)
129
+ return f"{name}={quote}{_mask_token(value)}{quote}"
130
+ text = _ENV_ASSIGN_RE.sub(_redact_env, text)
131
+
132
+ # JSON fields: "apiKey": "value"
133
+ def _redact_json(m):
134
+ key, value = m.group(1), m.group(2)
135
+ return f'{key}: "{_mask_token(value)}"'
136
+ text = _JSON_FIELD_RE.sub(_redact_json, text)
137
+
138
+ # Authorization headers
139
+ text = _AUTH_HEADER_RE.sub(
140
+ lambda m: m.group(1) + _mask_token(m.group(2)),
141
+ text,
142
+ )
143
+
144
+ # Telegram bot tokens
145
+ def _redact_telegram(m):
146
+ prefix = m.group(1) or ""
147
+ digits = m.group(2)
148
+ return f"{prefix}{digits}:***"
149
+ text = _TELEGRAM_RE.sub(_redact_telegram, text)
150
+
151
+ # Private key blocks
152
+ text = _PRIVATE_KEY_RE.sub("[REDACTED PRIVATE KEY]", text)
153
+
154
+ # Database connection string passwords
155
+ text = _DB_CONNSTR_RE.sub(lambda m: f"{m.group(1)}***{m.group(3)}", text)
156
+
157
+ # E.164 phone numbers (Signal, WhatsApp)
158
+ def _redact_phone(m):
159
+ phone = m.group(1)
160
+ if len(phone) <= 8:
161
+ return phone[:2] + "****" + phone[-2:]
162
+ return phone[:4] + "****" + phone[-4:]
163
+ text = _SIGNAL_PHONE_RE.sub(_redact_phone, text)
164
+
165
+ return text
166
+
167
+
168
+ class RedactingFormatter(logging.Formatter):
169
+ """Log formatter that redacts secrets from all log messages."""
170
+
171
+ def __init__(self, fmt=None, datefmt=None, style='%', **kwargs):
172
+ super().__init__(fmt, datefmt, style, **kwargs)
173
+
174
+ def format(self, record: logging.LogRecord) -> str:
175
+ original = super().format(record)
176
+ return redact_sensitive_text(original)
@@ -0,0 +1,5 @@
1
+ """Read-only repo mirrors + tools (owner-designated, boundary-gated)."""
2
+
3
+ from apsimo.repos.mirrors import RepoMirrorManager, parse_mirror_config
4
+
5
+ __all__ = ["RepoMirrorManager", "parse_mirror_config"]
@@ -0,0 +1,204 @@
1
+ """Read-only local repo mirrors for Colony's own research (best-of-B).
2
+
3
+ Owner-designated repos (env/config only, never hardcoded) are cloned/pulled
4
+ into a local mirror directory. Colony gets low-latency read access -- list,
5
+ read, search -- with NO write credentials anywhere. Every access is
6
+ boundary-gated: a standing "leave X alone" blocks even reads of X.
7
+
8
+ Config: COLONY_REPO_MIRRORS = "name=url[|alias1|alias2][,name2=url2...]"
9
+ Mirror dir: <state_dir>/repo-mirrors/<name>
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import fnmatch
15
+ import logging
16
+ import os
17
+ import subprocess
18
+ import time
19
+ from pathlib import Path
20
+ from typing import Any, Dict, List, Optional
21
+
22
+ logger = logging.getLogger(__name__)
23
+
24
+ _MAX_READ_BYTES = 64 * 1024
25
+ _SKIP_DIRS = {".git", "node_modules", "__pycache__", ".venv", "venv", "dist", "build"}
26
+
27
+
28
+ def parse_mirror_config(raw: str) -> Dict[str, Dict[str, str]]:
29
+ """Parse COLONY_REPO_MIRRORS into {name: {url, aliases}}."""
30
+ out: Dict[str, Dict[str, str]] = {}
31
+ for part in (raw or "").split(","):
32
+ part = part.strip()
33
+ if not part or "=" not in part:
34
+ continue
35
+ name, rest = part.split("=", 1)
36
+ pieces = rest.split("|")
37
+ url = pieces[0].strip()
38
+ aliases = "|".join(a.strip() for a in pieces[1:] if a.strip())
39
+ if name.strip() and url:
40
+ out[name.strip()] = {"url": url, "aliases": aliases}
41
+ return out
42
+
43
+
44
+ class RepoMirrorManager:
45
+ def __init__(
46
+ self,
47
+ mirror_dir: str,
48
+ config: Optional[Dict[str, Dict[str, str]]] = None,
49
+ directive_manager: Any = None,
50
+ ) -> None:
51
+ self._dir = Path(mirror_dir)
52
+ self._config = config if config is not None else parse_mirror_config(
53
+ os.environ.get("COLONY_REPO_MIRRORS", ""))
54
+ self._directives = directive_manager
55
+ self._last_refresh: Dict[str, float] = {}
56
+
57
+ def configured(self) -> Dict[str, Dict[str, str]]:
58
+ return dict(self._config)
59
+
60
+ def path_for(self, name: str) -> Optional[str]:
61
+ if name not in self._config:
62
+ return None
63
+ p = self._dir / name
64
+ return str(p) if (p / ".git").exists() or (p / "HEAD").exists() else None
65
+
66
+ # -- boundary gate --------------------------------------------------
67
+ def _boundary_ok(self, name: str, extra: str = "") -> Any:
68
+ if self._directives is None:
69
+ return None
70
+ try:
71
+ from apsimo.directives import Action
72
+ verdict = self._directives.check(Action(
73
+ kind="repo_read", text=f"{name} {extra}", target=name))
74
+ return verdict if not verdict.allowed else None
75
+ except Exception:
76
+ return None
77
+
78
+ # -- sync -------------------------------------------------------------
79
+ def refresh(self, name: str, min_interval_secs: float = 300.0) -> Dict[str, Any]:
80
+ """Clone or pull one mirror (read-only; no credentials added)."""
81
+ info = self._config.get(name)
82
+ if info is None:
83
+ return {"ok": False, "reason": "unknown_repo"}
84
+ blocked = self._boundary_ok(name, "refresh")
85
+ if blocked is not None:
86
+ return {"ok": False, "reason": blocked.reason}
87
+ now = time.time()
88
+ if now - self._last_refresh.get(name, 0) < min_interval_secs and self.path_for(name):
89
+ return {"ok": True, "action": "fresh"}
90
+ self._dir.mkdir(parents=True, exist_ok=True)
91
+ dest = self._dir / name
92
+ try:
93
+ if (dest / ".git").exists():
94
+ subprocess.run(["git", "-C", str(dest), "fetch", "--all", "--prune"],
95
+ capture_output=True, text=True, timeout=120, check=True)
96
+ subprocess.run(["git", "-C", str(dest), "reset", "--hard", "@{upstream}"],
97
+ capture_output=True, text=True, timeout=60, check=False)
98
+ action = "pulled"
99
+ else:
100
+ subprocess.run(["git", "clone", info["url"], str(dest)],
101
+ capture_output=True, text=True, timeout=300, check=True)
102
+ action = "cloned"
103
+ self._last_refresh[name] = now
104
+ return {"ok": True, "action": action}
105
+ except subprocess.CalledProcessError as exc:
106
+ err = (exc.stderr or "")[:200]
107
+ logger.warning("mirror refresh failed for %s: %s", name, err)
108
+ return {"ok": False, "reason": err}
109
+ except Exception as exc:
110
+ return {"ok": False, "reason": str(exc)[:200]}
111
+
112
+ def refresh_all(self) -> Dict[str, Any]:
113
+ return {name: self.refresh(name) for name in self._config}
114
+
115
+ # -- read tools ---------------------------------------------------------
116
+ def list_files(self, name: str, subpath: str = "", limit: int = 200) -> Dict[str, Any]:
117
+ blocked = self._boundary_ok(name, subpath)
118
+ if blocked is not None:
119
+ return {"error": blocked.reason, "status": "boundary_refused"}
120
+ root = self.path_for(name)
121
+ if root is None:
122
+ return {"error": f"repo {name!r} not mirrored", "status": "unavailable"}
123
+ base = Path(root) / subpath.lstrip("/")
124
+ base_r = base.resolve()
125
+ if not str(base_r).startswith(str(Path(root).resolve())):
126
+ return {"error": "path escapes repo", "status": "error"}
127
+ if not base_r.exists():
128
+ return {"error": f"no such path {subpath!r}", "status": "not_found"}
129
+ files: List[str] = []
130
+ for p in sorted(base_r.rglob("*")):
131
+ rel_parts = p.relative_to(root).parts
132
+ if any(part in _SKIP_DIRS for part in rel_parts):
133
+ continue
134
+ if p.is_file():
135
+ files.append(str(p.relative_to(root)))
136
+ if len(files) >= limit:
137
+ break
138
+ return {"repo": name, "path": subpath or "/", "count": len(files), "files": files}
139
+
140
+ def read_file(self, name: str, path: str, max_bytes: int = _MAX_READ_BYTES) -> Dict[str, Any]:
141
+ blocked = self._boundary_ok(name, path)
142
+ if blocked is not None:
143
+ return {"error": blocked.reason, "status": "boundary_refused"}
144
+ root = self.path_for(name)
145
+ if root is None:
146
+ return {"error": f"repo {name!r} not mirrored", "status": "unavailable"}
147
+ f = (Path(root) / path.lstrip("/")).resolve()
148
+ if not str(f).startswith(str(Path(root).resolve())):
149
+ return {"error": "path escapes repo", "status": "error"}
150
+ if not f.is_file():
151
+ return {"error": f"no such file {path!r}", "status": "not_found"}
152
+ try:
153
+ data = f.read_bytes()[: max(1024, min(max_bytes, _MAX_READ_BYTES))]
154
+ return {"repo": name, "path": path, "size": f.stat().st_size,
155
+ "content": data.decode("utf-8", errors="replace")}
156
+ except Exception as exc:
157
+ return {"error": str(exc)[:200], "status": "error"}
158
+
159
+ def search(self, name: str, query: str, glob: str = "",
160
+ max_results: int = 40) -> Dict[str, Any]:
161
+ blocked = self._boundary_ok(name, query)
162
+ if blocked is not None:
163
+ return {"error": blocked.reason, "status": "boundary_refused"}
164
+ root = self.path_for(name)
165
+ if root is None:
166
+ return {"error": f"repo {name!r} not mirrored", "status": "unavailable"}
167
+ if not query:
168
+ return {"error": "query required", "status": "error"}
169
+ try:
170
+ args = ["git", "-C", root, "grep", "-In", "--max-depth", "-1", "-e", query]
171
+ if glob:
172
+ args += ["--", glob]
173
+ out = subprocess.run(args, capture_output=True, text=True, timeout=30)
174
+ lines = [ln for ln in out.stdout.splitlines() if ln.strip()][:max_results]
175
+ return {"repo": name, "query": query, "count": len(lines),
176
+ "matches": lines}
177
+ except Exception as exc:
178
+ return {"error": str(exc)[:200], "status": "error"}
179
+
180
+ # -- world-model feed -----------------------------------------------------
181
+ async def register_entities(self, world_store: Any) -> int:
182
+ """Upsert each owner-designated repo as a Project entity (deterministic,
183
+ config-driven; not extraction guesswork)."""
184
+ if world_store is None:
185
+ return 0
186
+ n = 0
187
+ try:
188
+ from apsimo.world_model.entities import ProjectEntity
189
+ from apsimo.world_model.sqlite.backend import _generate_id
190
+ for name, info in self._config.items():
191
+ existing = await world_store.get_entity_by_external_id("repo_name", name)
192
+ if existing is not None:
193
+ continue
194
+ ent = ProjectEntity(
195
+ id=_generate_id("we"), name=name, entity_type="project",
196
+ confidence=0.95,
197
+ external_ids={"repo_name": name},
198
+ properties={"kind": "repo", "url": info.get("url", "")},
199
+ )
200
+ await world_store.upsert_entity(ent)
201
+ n += 1
202
+ except Exception:
203
+ logger.debug("repo entity registration failed", exc_info=True)
204
+ return n
@@ -0,0 +1,41 @@
1
+ """Colony Research-to-Artifact Pipeline (RAP).
2
+
3
+ A unified, autonomy-capable pipeline that takes a research goal and produces
4
+ a polished, gated deliverable with minimal human intervention.
5
+
6
+ Six pipeline stages:
7
+ 1. DECOMPOSE — break goal into sub-tasks
8
+ 2. GATHER — collect evidence from web, graph, documents, email
9
+ 3. SYNTHESIZE — cross-reference and extract insights
10
+ 4. OUTLINE — build structured artifact outline
11
+ 5. PRODUCE — render artifact in requested format
12
+ 6. REVIEW — quality and safety gate
13
+
14
+ Usage::
15
+
16
+ from apsimo.research import ResearchPipeline, ArtifactFormat
17
+
18
+ pipeline = ResearchPipeline()
19
+ result = await pipeline.run(
20
+ goal="Research competitor pricing and produce a report",
21
+ format=ArtifactFormat.MARKDOWN,
22
+ )
23
+ """
24
+
25
+ from apsimo.research.artifact import ArtifactFormat, Artifact
26
+ from apsimo.research.gatherer import EvidenceItem, EvidencePackage, SourceType
27
+ from apsimo.research.synthesizer import SynthesisReport
28
+ from apsimo.research.pipeline import ResearchPipeline, PipelineRun, PipelineStage, PipelineStatus
29
+
30
+ __all__ = [
31
+ "ResearchPipeline",
32
+ "PipelineRun",
33
+ "PipelineStage",
34
+ "PipelineStatus",
35
+ "ArtifactFormat",
36
+ "Artifact",
37
+ "EvidenceItem",
38
+ "EvidencePackage",
39
+ "SourceType",
40
+ "SynthesisReport",
41
+ ]