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
apsimo/__init__.py ADDED
@@ -0,0 +1,38 @@
1
+ """Apsimo cognition server for agent runtimes.
2
+
3
+ A standalone FastAPI server that agent hosts (Hermes plugin, MCP harnesses,
4
+ REST integrations) mount via the ``/v1/host`` API surface.
5
+ """
6
+
7
+ from pathlib import Path
8
+ import os
9
+ from .environment import apply_environment_aliases
10
+
11
+ apply_environment_aliases()
12
+
13
+ try: # single source of truth: the installed package metadata (pyproject)
14
+ from importlib.metadata import version as _pkg_version
15
+ __version__ = _pkg_version("apsimo")
16
+ except Exception: # editable/unbuilt checkouts without installed metadata
17
+ __version__ = "0.0.0+unknown"
18
+
19
+
20
+ def get_state_dir() -> Path:
21
+ """Return the selected state directory without moving existing state.
22
+
23
+ Priority:
24
+ 1. APSIMO_STATE_DIR or legacy COLONY_STATE_DIR (explicit override)
25
+ 2. ~/.colony/data (retained unconfigured-library compatibility default)
26
+
27
+ Guided setup selects its explicit private instance directory. This fallback
28
+ stays aligned with older independently configured storage readers.
29
+
30
+ Creates the directory if it doesn't exist.
31
+ """
32
+ explicit = os.environ.get("APSIMO_STATE_DIR") or os.environ.get("COLONY_STATE_DIR")
33
+ if explicit:
34
+ path = Path(explicit)
35
+ else:
36
+ path = Path.home() / ".colony" / "data"
37
+ path.mkdir(parents=True, exist_ok=True)
38
+ return path
apsimo/__main__.py ADDED
@@ -0,0 +1,6 @@
1
+ """Run the same CLI used by the installed ``apsimo`` command."""
2
+ from apsimo.cli import main
3
+
4
+
5
+ if __name__ == "__main__":
6
+ main()
@@ -0,0 +1,6 @@
1
+ """Colony Agent SDK — Python client for remote agents."""
2
+
3
+ from .client import AgentClient
4
+ from .models import AgentConfig, NodeCertificate
5
+
6
+ __all__ = ["AgentClient", "AgentConfig", "NodeCertificate"]
apsimo/agent/client.py ADDED
@@ -0,0 +1,276 @@
1
+ """Colony Agent Client — WebSocket client for remote agents.
2
+
3
+ Usage:
4
+ from colony.agent import AgentClient
5
+
6
+ client = AgentClient(config_path="~/.colony/agent.json")
7
+
8
+ @client.on_initiative
9
+ async def handle_initiative(initiative):
10
+ print(f"Received: {initiative['description']}")
11
+ await client.acknowledge(initiative["id"])
12
+ result = await process_initiative(initiative)
13
+ await client.complete(initiative["id"], result=result)
14
+
15
+ await client.start()
16
+ """
17
+
18
+ import asyncio
19
+ import json
20
+ import time
21
+ from pathlib import Path
22
+ from typing import Any, Callable, Dict, Optional
23
+
24
+ import websockets
25
+
26
+ from .models import AgentConfig
27
+
28
+
29
+ class AgentClient:
30
+ """WebSocket client for remote Colony agents.
31
+
32
+ Handles:
33
+ - WebSocket connection with auth
34
+ - Heartbeat loop
35
+ - Initiative delivery
36
+ - Reconnection with exponential backoff
37
+ """
38
+
39
+ def __init__(
40
+ self,
41
+ config_path: str = "~/.colony/agent.json",
42
+ config: Optional[AgentConfig] = None,
43
+ ) -> None:
44
+ """Initialize agent client.
45
+
46
+ Args:
47
+ config_path: Path to agent config file
48
+ config: Optional pre-loaded config (skips file read)
49
+ """
50
+ if config:
51
+ self.config = config
52
+ else:
53
+ config_file = Path(config_path).expanduser()
54
+ if not config_file.exists():
55
+ raise FileNotFoundError(f"Agent config not found: {config_file}")
56
+ self.config = AgentConfig.parse_file(config_file)
57
+
58
+ self._ws: Optional[websockets.WebSocketClientProtocol] = None
59
+ self._running = False
60
+ self._seq = 0
61
+ self._pending_acks: Dict[int, asyncio.Future] = {}
62
+ self._handlers: Dict[str, Callable] = {
63
+ "initiative": None,
64
+ "config": None,
65
+ "disconnect": None,
66
+ }
67
+
68
+ # Reconnection state
69
+ self._reconnect_delay = 1.0
70
+ self._max_reconnect_delay = 60.0
71
+
72
+ def on_initiative(self, handler: Callable) -> Callable:
73
+ """Register initiative handler."""
74
+ self._handlers["initiative"] = handler
75
+ return handler
76
+
77
+ def on_config(self, handler: Callable) -> Callable:
78
+ """Register config update handler."""
79
+ self._handlers["config"] = handler
80
+ return handler
81
+
82
+ def on_disconnect(self, handler: Callable) -> Callable:
83
+ """Register disconnect handler."""
84
+ self._handlers["disconnect"] = handler
85
+ return handler
86
+
87
+ async def start(self) -> None:
88
+ """Connect to Colony and start message loop."""
89
+ if not self.config.websocket_url:
90
+ raise ValueError("No websocket_url in config (local mode?)")
91
+
92
+ self._running = True
93
+
94
+ while self._running:
95
+ try:
96
+ await self._connect()
97
+ await self._message_loop()
98
+ except Exception as e:
99
+ if not self._running:
100
+ break
101
+ print(f"Connection error: {e}, reconnecting in {self._reconnect_delay}s...")
102
+ await asyncio.sleep(self._reconnect_delay)
103
+ self._reconnect_delay = min(
104
+ self._reconnect_delay * 2,
105
+ self._max_reconnect_delay,
106
+ )
107
+
108
+ async def stop(self) -> None:
109
+ """Disconnect from Colony."""
110
+ self._running = False
111
+ if self._ws:
112
+ await self._ws.close()
113
+
114
+ async def _connect(self) -> None:
115
+ """Establish WebSocket connection with auth."""
116
+ headers = {}
117
+ if self.config.node_cert:
118
+ # Sign challenge with node cert
119
+ headers["Authorization"] = f"Bearer {self.config.node_cert.signature}"
120
+ headers["X-Agent-Id"] = self.config.agent_id
121
+
122
+ self._ws = await websockets.connect(
123
+ self.config.websocket_url,
124
+ extra_headers=headers,
125
+ ping_interval=30,
126
+ ping_timeout=10,
127
+ )
128
+ self._reconnect_delay = 1.0 # Reset on successful connection
129
+ print(f"Connected to Colony: {self.config.websocket_url}")
130
+
131
+ # Start heartbeat task
132
+ asyncio.create_task(self._heartbeat_loop())
133
+
134
+ async def _message_loop(self) -> None:
135
+ """Process incoming messages."""
136
+ if not self._ws:
137
+ return
138
+
139
+ async for message in self._ws:
140
+ try:
141
+ data = json.loads(message)
142
+ await self._handle_message(data)
143
+ except json.JSONDecodeError:
144
+ print(f"Invalid JSON: {message}")
145
+ except Exception as e:
146
+ print(f"Error handling message: {e}")
147
+
148
+ async def _handle_message(self, data: Dict[str, Any]) -> None:
149
+ """Handle incoming message."""
150
+ msg_type = data.get("type")
151
+ seq = data.get("seq")
152
+
153
+ # Handle ACKs for our messages
154
+ if msg_type == "ack" and seq in self._pending_acks:
155
+ self._pending_acks[seq].set_result(data)
156
+ return
157
+
158
+ # Handle initiative delivery
159
+ if msg_type == "initiative":
160
+ if self._handlers["initiative"]:
161
+ await self._handlers["initiative"](data.get("initiative", {}))
162
+ # Auto-ack receipt
163
+ await self._send_ack(seq)
164
+
165
+ # Handle config update
166
+ elif msg_type == "config":
167
+ if self._handlers["config"]:
168
+ await self._handlers["config"](data.get("config", {}))
169
+
170
+ # Handle disconnect notice
171
+ elif msg_type == "disconnect":
172
+ if self._handlers["disconnect"]:
173
+ await self._handlers["disconnect"](data.get("reason", "unknown"))
174
+ await self.stop()
175
+
176
+ # Handle ping
177
+ elif msg_type == "ping":
178
+ await self._send({"type": "pong", "seq": seq})
179
+
180
+ async def _heartbeat_loop(self) -> None:
181
+ """Send periodic heartbeats."""
182
+ # The colony side tracks current_assignments via the agents table
183
+ # (incremented/decremented when initiatives are assigned/completed),
184
+ # so the heartbeat does not need to carry it — the server only uses
185
+ # this message to update last_seen_at.
186
+ while self._running and self._ws:
187
+ try:
188
+ await self._send({
189
+ "type": "heartbeat",
190
+ "status": "online",
191
+ })
192
+ await asyncio.sleep(30)
193
+ except Exception:
194
+ break
195
+
196
+ async def _send(self, message: Dict[str, Any]) -> bool:
197
+ """Send message with sequencing."""
198
+ if not self._ws:
199
+ return False
200
+
201
+ self._seq += 1
202
+ message["seq"] = self._seq
203
+
204
+ try:
205
+ await self._ws.send(json.dumps(message))
206
+ return True
207
+ except Exception as e:
208
+ print(f"Send error: {e}")
209
+ return False
210
+
211
+ async def _send_ack(self, seq: Optional[int]) -> None:
212
+ """Send acknowledgment."""
213
+ await self._send({"type": "ack", "ack_seq": seq})
214
+
215
+ # --- Public API ---
216
+
217
+ async def acknowledge(self, initiative_id: str) -> bool:
218
+ """Acknowledge initiative receipt."""
219
+ return await self._send({
220
+ "type": "initiative_ack",
221
+ "initiative_id": initiative_id,
222
+ })
223
+
224
+ async def complete(
225
+ self,
226
+ initiative_id: str,
227
+ result: Optional[str] = None,
228
+ metadata: Optional[Dict[str, Any]] = None,
229
+ ) -> bool:
230
+ """Mark initiative as completed."""
231
+ return await self._send({
232
+ "type": "initiative_complete",
233
+ "initiative_id": initiative_id,
234
+ "result": result,
235
+ "result_metadata": metadata or {},
236
+ })
237
+
238
+ async def fail(
239
+ self,
240
+ initiative_id: str,
241
+ reason: str,
242
+ retry: bool = True,
243
+ ) -> bool:
244
+ """Mark initiative as failed."""
245
+ return await self._send({
246
+ "type": "initiative_fail",
247
+ "initiative_id": initiative_id,
248
+ "reason": reason,
249
+ "retry": retry,
250
+ })
251
+
252
+ async def delegate(
253
+ self,
254
+ initiative_id: str,
255
+ reason: str,
256
+ target_agent_id: Optional[str] = None,
257
+ ) -> bool:
258
+ """Delegate initiative to another agent."""
259
+ return await self._send({
260
+ "type": "initiative_delegate",
261
+ "initiative_id": initiative_id,
262
+ "reason": reason,
263
+ "target_agent_id": target_agent_id,
264
+ })
265
+
266
+ async def update_status(
267
+ self,
268
+ status: str = "online",
269
+ current_assignments: int = 0,
270
+ ) -> bool:
271
+ """Update agent status."""
272
+ return await self._send({
273
+ "type": "status_update",
274
+ "status": status,
275
+ "current_assignments": current_assignments,
276
+ })
apsimo/agent/models.py ADDED
@@ -0,0 +1,46 @@
1
+ """Agent SDK models."""
2
+
3
+ from datetime import datetime
4
+ from typing import Any, Dict, List, Optional
5
+
6
+ from pydantic import BaseModel, Field
7
+
8
+
9
+ class NodeCertificate(BaseModel):
10
+ """Node certificate structure."""
11
+
12
+ colony_id: str
13
+ node_id: str
14
+ public_key: Optional[str] = None # node_public_key_ed25519 in spec
15
+ issued_at: str # ISO timestamp
16
+ expires_at: Optional[str] = None # ISO timestamp
17
+ signature: str
18
+
19
+
20
+ class AgentConfig(BaseModel):
21
+ """Agent configuration file schema.
22
+
23
+ This is the structure saved to ~/.colony/agent.json after
24
+ running `colony agent connect`.
25
+ """
26
+
27
+ agent_id: str
28
+ node_id: str
29
+ colony_id: str
30
+ websocket_url: Optional[str] = None # For remote mode
31
+ name: str
32
+ capabilities: List[str] = Field(default_factory=list)
33
+ is_primary: bool = False
34
+ max_concurrent: int = 5
35
+ node_cert: Optional[NodeCertificate] = None
36
+ connection_mode: str = "remote"
37
+ registered_at: Optional[str] = None # ISO timestamp
38
+
39
+ # Optional fields
40
+ priority: int = 1
41
+ excluded_types: List[str] = Field(default_factory=list)
42
+ included_types: List[str] = Field(default_factory=list)
43
+ metadata: Dict[str, Any] = Field(default_factory=dict)
44
+
45
+ class Config:
46
+ extra = "allow" # Forward compatibility
@@ -0,0 +1,20 @@
1
+ """Multi-agent support for Colony.
2
+
3
+ Provides:
4
+ - AgentStore: Registry of connected agents
5
+ - InviteStore: Setup code management
6
+ - WebSocketManager: Remote agent connections
7
+ """
8
+
9
+ from .models import Agent, AgentStatus, AgentMetadata
10
+ from .store import AgentStore, InviteStore
11
+ from .websocket import WebSocketManager
12
+
13
+ __all__ = [
14
+ "Agent",
15
+ "AgentStatus",
16
+ "AgentMetadata",
17
+ "AgentStore",
18
+ "InviteStore",
19
+ "WebSocketManager",
20
+ ]
@@ -0,0 +1,264 @@
1
+ """Agent data models for multi-agent Colony.
2
+
3
+ Defines:
4
+ - AgentStatus enum
5
+ - AgentMetadata schema
6
+ - Agent dataclass
7
+ """
8
+
9
+ from dataclasses import dataclass, field
10
+ from datetime import datetime, timezone
11
+ from enum import Enum
12
+ from typing import Any, Dict, List, Optional
13
+
14
+
15
+ class AgentStatus(str, Enum):
16
+ """Agent status values."""
17
+
18
+ ONLINE = "online"
19
+ OFFLINE = "offline"
20
+ BUSY = "busy"
21
+ SUSPENDED = "suspended"
22
+ REVOKED = "revoked"
23
+
24
+ def is_active(self) -> bool:
25
+ """Can this agent receive assignments?"""
26
+ return self in (AgentStatus.ONLINE, AgentStatus.BUSY)
27
+
28
+ def can_reconnect(self) -> bool:
29
+ """Can this agent reconnect to Colony?"""
30
+ return self != AgentStatus.REVOKED
31
+
32
+
33
+ @dataclass
34
+ class AgentMetadata:
35
+ """Structured metadata about an agent's environment.
36
+
37
+ Standard fields:
38
+ - hostname: Machine hostname
39
+ - platform: OS platform (darwin, linux, windows)
40
+ - version: Colony version
41
+ - harness: Which harness (openclaw, claude-code, codex, crush)
42
+ - {harness}_version: Harness version
43
+ - python_version: Python version (for Python-based harnesses)
44
+ - started_at: ISO timestamp when agent started
45
+ - tz: IANA timezone
46
+ """
47
+
48
+ hostname: Optional[str] = None
49
+ platform: Optional[str] = None
50
+ version: Optional[str] = None
51
+ harness: Optional[str] = None
52
+ started_at: Optional[str] = None
53
+ tz: Optional[str] = None
54
+ last_connection_ip: Optional[str] = None
55
+ last_connection_ip_ts: Optional[str] = None
56
+ extra: Dict[str, Any] = field(default_factory=dict)
57
+
58
+ @classmethod
59
+ def from_dict(cls, data: Dict[str, Any]) -> "AgentMetadata":
60
+ """Create from dict, handling unknown fields."""
61
+ known = {
62
+ "hostname",
63
+ "platform",
64
+ "version",
65
+ "harness",
66
+ "started_at",
67
+ "tz",
68
+ "last_connection_ip",
69
+ "last_connection_ip_ts",
70
+ }
71
+ return cls(
72
+ hostname=data.get("hostname"),
73
+ platform=data.get("platform"),
74
+ version=data.get("version"),
75
+ harness=data.get("harness"),
76
+ started_at=data.get("started_at"),
77
+ tz=data.get("tz"),
78
+ last_connection_ip=data.get("last_connection_ip"),
79
+ last_connection_ip_ts=data.get("last_connection_ip_ts"),
80
+ extra={k: v for k, v in data.items() if k not in known},
81
+ )
82
+
83
+ def to_dict(self) -> Dict[str, Any]:
84
+ """Convert to dict for JSON serialization."""
85
+ result = {}
86
+ if self.hostname:
87
+ result["hostname"] = self.hostname
88
+ if self.platform:
89
+ result["platform"] = self.platform
90
+ if self.version:
91
+ result["version"] = self.version
92
+ if self.harness:
93
+ result["harness"] = self.harness
94
+ if self.started_at:
95
+ result["started_at"] = self.started_at
96
+ if self.tz:
97
+ result["tz"] = self.tz
98
+ if self.last_connection_ip:
99
+ result["last_connection_ip"] = self.last_connection_ip
100
+ if self.last_connection_ip_ts:
101
+ result["last_connection_ip_ts"] = self.last_connection_ip_ts
102
+ result.update(self.extra)
103
+ return result
104
+
105
+ def get_harness_version(self) -> Optional[str]:
106
+ """Get harness-specific version field."""
107
+ if not self.harness:
108
+ return None
109
+ return self.extra.get(f"{self.harness}_version")
110
+
111
+
112
+ @dataclass
113
+ class Agent:
114
+ """A connected agent (Hermes host, Claude Code, worker daemon, etc.).
115
+
116
+ Attributes:
117
+ agent_id: Unique identifier (UUID)
118
+ node_id: Device node ID
119
+ colony_id: Parent Colony ID
120
+ name: Human-readable name (e.g., "node-a", "workstation")
121
+ connection_mode: "local" (HTTP) or "remote" (WebSocket)
122
+ gateway_url: For local mode, URL to push initiatives
123
+ websocket_connected: For remote mode, is WebSocket active?
124
+ capabilities: What can this agent do? (messaging, calendar, coding)
125
+ is_primary: Preferred for user-facing initiatives
126
+ priority: 0=backup, 1=normal, 2=high
127
+ max_concurrent: Max simultaneous initiative assignments
128
+ max_initiatives_per_hour: Rate limit
129
+ excluded_types: Initiative types to skip
130
+ included_types: Only these types (if set)
131
+ status: Current status
132
+ current_assignments: Count of active assignments
133
+ last_seen_at: Last heartbeat/disconnect time
134
+ metadata: Environment info
135
+ registered_at: When agent was registered
136
+ node_cert: Signed certificate (JSON)
137
+ """
138
+
139
+ agent_id: str
140
+ node_id: str
141
+ colony_id: str
142
+ name: str
143
+ connection_mode: str = "local"
144
+ gateway_url: Optional[str] = None
145
+ websocket_connected: bool = False
146
+ capabilities: List[str] = field(default_factory=list)
147
+ is_primary: bool = False
148
+ priority: int = 1
149
+ max_concurrent: int = 5
150
+ max_initiatives_per_hour: int = 10
151
+ excluded_types: List[str] = field(default_factory=list)
152
+ included_types: List[str] = field(default_factory=list)
153
+ status: str = "offline"
154
+ current_assignments: int = 0
155
+ last_seen_at: Optional[datetime] = None
156
+ metadata: AgentMetadata = field(default_factory=AgentMetadata)
157
+ registered_at: Optional[datetime] = None
158
+ node_cert: Optional[Dict[str, Any]] = None
159
+
160
+ @property
161
+ def load(self) -> float:
162
+ """Current load as ratio (0.0-1.0)."""
163
+ if self.max_concurrent <= 0:
164
+ return 1.0
165
+ return self.current_assignments / self.max_concurrent
166
+
167
+ @property
168
+ def has_capacity(self) -> bool:
169
+ """Can accept more assignments?"""
170
+ return (
171
+ self.status in ("online", "busy")
172
+ and self.current_assignments < self.max_concurrent
173
+ )
174
+
175
+ def can_handle_type(self, initiative_type: str) -> bool:
176
+ """Check if agent can handle this initiative type."""
177
+ # Excluded types take priority
178
+ if initiative_type in self.excluded_types:
179
+ return False
180
+
181
+ # If included_types is set, must be in list
182
+ if self.included_types and initiative_type not in self.included_types:
183
+ return False
184
+
185
+ return True
186
+
187
+ def has_capability(self, capability: str) -> bool:
188
+ """Check if agent has a specific capability."""
189
+ return capability in self.capabilities
190
+
191
+ def has_capabilities(self, capabilities: List[str]) -> bool:
192
+ """Check if agent has all specified capabilities."""
193
+ return all(cap in self.capabilities for cap in capabilities)
194
+
195
+ @classmethod
196
+ def from_row(cls, row: dict) -> "Agent":
197
+ """Create from SQLite row dict."""
198
+ import json
199
+
200
+ metadata_raw = row.get("metadata", "{}")
201
+ if isinstance(metadata_raw, str):
202
+ metadata_dict = json.loads(metadata_raw)
203
+ else:
204
+ metadata_dict = metadata_raw
205
+
206
+ capabilities_raw = row.get("capabilities", "[]")
207
+ if isinstance(capabilities_raw, str):
208
+ capabilities = json.loads(capabilities_raw)
209
+ else:
210
+ capabilities = capabilities_raw
211
+
212
+ excluded_raw = row.get("excluded_types", "[]")
213
+ if isinstance(excluded_raw, str):
214
+ excluded_types = json.loads(excluded_raw)
215
+ else:
216
+ excluded_types = excluded_raw
217
+
218
+ included_raw = row.get("included_types", "[]")
219
+ if isinstance(included_raw, str):
220
+ included_types = json.loads(included_raw)
221
+ else:
222
+ included_types = included_raw
223
+
224
+ node_cert_raw = row.get("node_cert")
225
+ if node_cert_raw and isinstance(node_cert_raw, str):
226
+ node_cert = json.loads(node_cert_raw)
227
+ else:
228
+ node_cert = node_cert_raw
229
+
230
+ return cls(
231
+ agent_id=row["agent_id"],
232
+ node_id=row["node_id"],
233
+ colony_id=row["colony_id"],
234
+ name=row["name"],
235
+ connection_mode=row.get("connection_mode", "local"),
236
+ gateway_url=row.get("gateway_url"),
237
+ websocket_connected=bool(row.get("websocket_connected", 0)),
238
+ capabilities=capabilities,
239
+ is_primary=bool(row.get("is_primary", 0)),
240
+ priority=row.get("priority", 1),
241
+ max_concurrent=row.get("max_concurrent", 5),
242
+ max_initiatives_per_hour=row.get("max_initiatives_per_hour", 10),
243
+ excluded_types=excluded_types,
244
+ included_types=included_types,
245
+ status=row.get("status", "offline"),
246
+ current_assignments=row.get("current_assignments", 0),
247
+ last_seen_at=_parse_datetime(row.get("last_seen_at")),
248
+ metadata=AgentMetadata.from_dict(metadata_dict),
249
+ registered_at=_parse_datetime(row.get("registered_at")),
250
+ node_cert=node_cert,
251
+ )
252
+
253
+
254
+ def _parse_datetime(value: Optional[str]) -> Optional[datetime]:
255
+ """Parse ISO datetime string."""
256
+ if not value:
257
+ return None
258
+ try:
259
+ # Handle both with and without timezone
260
+ if value.endswith("Z"):
261
+ value = value[:-1] + "+00:00"
262
+ return datetime.fromisoformat(value)
263
+ except (ValueError, TypeError):
264
+ return None