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,454 @@
1
+ """Action registry — named, allow-listed capabilities with risk tiers (v0.16.0).
2
+
3
+ ``action_hint`` on an agent-actionable initiative is a *named capability*
4
+ in this registry, never a raw command string. Colony builds initiatives
5
+ from graph data that can include untrusted content (contact messages,
6
+ repo READMEs, webhook payloads); a free-form ``{"tool": ..., "command":
7
+ <string>}`` payload would be a direct injection-to-execution path. The
8
+ registry is the allow-list: nothing executes that isn't registered.
9
+
10
+ Risk tiers (v0.18.0 graduated policy):
11
+
12
+ - ``read_only`` — auto-execute, no approval needed.
13
+ - ``mutating`` — reversible platform/system writes that touch no person
14
+ (close an issue, comment on a PR, webhook to own infra).
15
+ - ``outbound`` — reaches a PERSON outside the system (email, message,
16
+ reminder). Reserved strictly for human recipients; platform writes
17
+ belong in ``mutating``.
18
+ - ``destructive`` — deletes, overwrites, force-pushes, installs, spends,
19
+ or otherwise cannot be cheaply undone.
20
+
21
+ Approval policy (``COLONY_APPROVAL_POLICY``, default ``strict``):
22
+
23
+ - ``strict`` (v0.17 behavior): mutating/outbound/destructive all require
24
+ HUMAN OWNER approval. The agent cannot approve its own mutations; the
25
+ same actor on both sides of a gate is a log line, not a boundary.
26
+ - ``graduated`` (v0.18.0, opt-in): mutating auto-executes with an audit
27
+ tag; destructive requires owner approval; outbound requires approval
28
+ UNLESS the resolved target is an authorized contact
29
+ (``interaction_allowed=True`` — see ``approval_policy``). This encodes
30
+ the owner's policy: no manual gate unless the action is potentially
31
+ destructive or reaches an unauthorized individual.
32
+
33
+ Bounded grants are applied by the queue dispatch path after this pure policy
34
+ classification. They bind an immutable job digest to an exact action scope,
35
+ expiry, and use cap; classification itself never spends or invents authority.
36
+
37
+ ``COLONY_AGENT_AUTO_APPROVE=true`` collapses the gate for trusted
38
+ deployments (default false).
39
+ """
40
+
41
+ from __future__ import annotations
42
+
43
+ import os
44
+ from dataclasses import dataclass, field
45
+ from enum import Enum
46
+ from typing import Dict, List, Optional
47
+
48
+
49
+ class RiskTier(str, Enum):
50
+ """How much damage an action can do if it runs at the wrong time."""
51
+
52
+ READ_ONLY = "read_only"
53
+ MUTATING = "mutating"
54
+ OUTBOUND = "outbound"
55
+ DESTRUCTIVE = "destructive"
56
+
57
+
58
+ @dataclass(frozen=True)
59
+ class ActionSpec:
60
+ """A registered capability the runtime knows how to run.
61
+
62
+ ``command`` is a template the executing agent renders with values
63
+ from the initiative context ($ID, $PR, etc.). It documents intent;
64
+ the executing worker maps the action name to its own toolset and is
65
+ free to implement it differently. What matters for safety is the
66
+ name (allow-list membership) and the risk tier (approval gate).
67
+ """
68
+
69
+ name: str
70
+ tool: str
71
+ command: str
72
+ risk: RiskTier
73
+ description: str = ""
74
+ initiative_type: Optional[str] = None
75
+ required_params: List[str] = field(default_factory=list)
76
+ # v0.18.0: for OUTBOUND actions, the param that holds the recipient.
77
+ # The graduated policy resolves it against the contact store to decide
78
+ # whether the target is an authorized individual.
79
+ target_param: Optional[str] = None
80
+ # Selected internal reviews can use the ordinary native task dispatcher.
81
+ # This never makes effectful actions eligible or grants source prose authority.
82
+ native_review: bool = False
83
+
84
+ @property
85
+ def auto_executable(self) -> bool:
86
+ return self.risk == RiskTier.READ_ONLY
87
+
88
+
89
+ # The agent-as-sensor loop (v0.16.0): when a domain's observations go
90
+ # stale, the autonomy loop posts one of these read-only sync jobs. The
91
+ # agent observes through its own Hermes connections and POSTs results
92
+ # to /v1/host/observations. Colony never calls external APIs itself.
93
+ OBSERVATION_SYNC_ACTIONS: Dict[str, str] = {
94
+ "coding": "agent_sync_coding",
95
+ "task": "agent_sync_task",
96
+ "calendar": "agent_sync_calendar",
97
+ "research": "agent_sync_research",
98
+ "project": "agent_sync_project",
99
+ "system": "agent_sync_system",
100
+ }
101
+
102
+ _SPECS: List[ActionSpec] = [
103
+ # Deployment calibration only. The queue recognizes the complete
104
+ # server-derived ApprovalRelayCanaryV1 object and terminalizes it as
105
+ # CANCELLED for either decision; no executor can ever claim it.
106
+ ActionSpec(
107
+ name="approval_relay_canary",
108
+ tool="colony",
109
+ command="record canonical approval relay calibration decision",
110
+ risk=RiskTier.MUTATING,
111
+ description="Calibrate owner approval relay without executing an effect",
112
+ initiative_type="agent_action",
113
+ required_params=["idempotency_digest"],
114
+ ),
115
+ # P4 controlled learning. The ExperimentEngine builds an immutable digest
116
+ # over these exact constraints and either matches a reversible pregrant or
117
+ # routes it through the bounded owner-approval ledger.
118
+ ActionSpec(
119
+ name="cognition_experiment_mutation",
120
+ tool="colony",
121
+ command="run controlled experiment $REF=$VARIANT judged by $METRIC",
122
+ risk=RiskTier.MUTATING,
123
+ description="Run one receipt-attributed adaptive-parameter experiment",
124
+ initiative_type="meta_learning",
125
+ required_params=[
126
+ "REF", "VARIANT", "METRIC", "METRIC_VERSION",
127
+ "MAX_REGRESSION", "WINDOW_DAYS", "ASSIGNMENT_MODE",
128
+ ],
129
+ ),
130
+ # --- Observation sync (v0.16.0, agent-as-sensor) ---
131
+ ActionSpec(
132
+ name="agent_sync_coding",
133
+ tool="github",
134
+ command="gh pr list --state open --json number,title,url,isDraft,reviewDecision,statusCheckRollup",
135
+ risk=RiskTier.READ_ONLY,
136
+ description="Observe open PRs + CI status; report to /v1/host/observations (domain=coding)",
137
+ initiative_type="coding",
138
+ ),
139
+ ActionSpec(
140
+ name="agent_sync_task",
141
+ tool="github",
142
+ command="gh issue list --state open --json number,title,url,assignees,updatedAt",
143
+ risk=RiskTier.READ_ONLY,
144
+ description="Observe open issues/tasks; report to /v1/host/observations (domain=task)",
145
+ initiative_type="task",
146
+ ),
147
+ ActionSpec(
148
+ name="agent_sync_calendar",
149
+ tool="calendar",
150
+ command="list upcoming events for the next 48h",
151
+ risk=RiskTier.READ_ONLY,
152
+ description="Observe upcoming events; report to /v1/host/observations (domain=calendar)",
153
+ initiative_type="calendar",
154
+ ),
155
+ ActionSpec(
156
+ name="agent_sync_research",
157
+ tool="web",
158
+ command="check tracked papers/models for status changes",
159
+ risk=RiskTier.READ_ONLY,
160
+ description="Observe tracked research items; report to /v1/host/observations (domain=research)",
161
+ initiative_type="research",
162
+ ),
163
+ ActionSpec(
164
+ name="agent_sync_project",
165
+ tool="github",
166
+ command="gh api repos/{owner}/{repo}/milestones",
167
+ risk=RiskTier.READ_ONLY,
168
+ description="Observe milestones/boards; report to /v1/host/observations (domain=project)",
169
+ initiative_type="project",
170
+ ),
171
+ ActionSpec(
172
+ name="agent_sync_system",
173
+ tool="terminal",
174
+ command="check service health/latency/error rates",
175
+ risk=RiskTier.READ_ONLY,
176
+ description="Observe infrastructure health; report to /v1/host/observations (domain=system)",
177
+ initiative_type="system",
178
+ ),
179
+
180
+ # --- AGENT_ACTION (v0.13.0 hints, now registered) ---
181
+ ActionSpec(
182
+ name="agent_check_repo_status",
183
+ tool="terminal",
184
+ # The working checkout is deployment-specific: COLONY_WORK_REPO names
185
+ # the repo the hygiene check inspects (default: the worker's own cwd).
186
+ command=f"git -C {os.environ.get('COLONY_WORK_REPO', '.')} status --short",
187
+ risk=RiskTier.READ_ONLY,
188
+ description="Check working repos for uncommitted changes",
189
+ initiative_type="agent_action",
190
+ ),
191
+ ActionSpec(
192
+ name="agent_investigate_subsystem",
193
+ tool="terminal",
194
+ command="colony doctor --subsystem $ID",
195
+ risk=RiskTier.READ_ONLY,
196
+ description="Investigate a degraded Colony subsystem",
197
+ initiative_type="agent_action",
198
+ required_params=["ID"],
199
+ ),
200
+ ActionSpec(
201
+ name="agent_check_ci",
202
+ tool="terminal",
203
+ command="gh run list --limit 5 --json conclusion,headBranch",
204
+ risk=RiskTier.READ_ONLY,
205
+ description="Check recent CI runs",
206
+ initiative_type="agent_action",
207
+ ),
208
+ ActionSpec(
209
+ name="agent_cleanup_orphans",
210
+ tool="terminal",
211
+ command="colony graph cleanup-orphans $ID",
212
+ risk=RiskTier.DESTRUCTIVE, # deletes graph nodes
213
+ description="Delete orphan nodes from the knowledge graph",
214
+ initiative_type="agent_action",
215
+ required_params=["ID"],
216
+ ),
217
+ # Legacy destructive hints (loop.py v0.13.0 DESTRUCTIVE_HINTS).
218
+ # v0.18.0 audit: delete/overwrite/restart stay DESTRUCTIVE so the
219
+ # graduated policy keeps blocking them; plain commit/push (non-force)
220
+ # are reversible MUTATING writes.
221
+ ActionSpec("agent_git_push", "terminal", "git push", RiskTier.MUTATING,
222
+ "Push commits to a remote", "agent_action"),
223
+ ActionSpec("agent_git_commit", "terminal", "git commit", RiskTier.MUTATING,
224
+ "Create a commit", "agent_action"),
225
+ ActionSpec("agent_service_restart", "terminal", "systemctl restart $SERVICE",
226
+ RiskTier.DESTRUCTIVE, "Restart a system service", "agent_action",
227
+ ["SERVICE"]),
228
+ ActionSpec("agent_file_delete", "terminal", "rm $PATH", RiskTier.DESTRUCTIVE,
229
+ "Delete a file", "agent_action", ["PATH"]),
230
+ ActionSpec("agent_deploy", "terminal", "deploy $TARGET", RiskTier.DESTRUCTIVE,
231
+ "Deploy to an environment (overwrites the running version)",
232
+ "agent_action", ["TARGET"]),
233
+ # Deliver an OWED message/result to a person — the immediate-deliverable half of the
234
+ # per-turn introspection reflex (e.g. "text me the findings"). OUTBOUND because it reaches
235
+ # a person, with the recipient named so the graduated policy auto-passes when it resolves
236
+ # to an authorized contact (the person who asked for it). The executing agent renders this
237
+ # with its own send tool on whatever channel the contact uses.
238
+ ActionSpec("agent_deliver_message", "messaging", "send to $RECIPIENT: \"$MSG\"",
239
+ RiskTier.OUTBOUND, "Deliver an owed message/result to a person",
240
+ "agent_action", ["RECIPIENT", "MSG"], target_param="RECIPIENT"),
241
+
242
+ # --- TASK ---
243
+ ActionSpec("task_list_open", "terminal",
244
+ "gh issue list --state open --limit 20",
245
+ RiskTier.READ_ONLY, "List open issues", "task"),
246
+ ActionSpec("task_check_status", "terminal",
247
+ "gh issue view $ID --json state",
248
+ RiskTier.READ_ONLY, "Check one issue's state", "task", ["ID"]),
249
+ ActionSpec("task_update_status", "terminal",
250
+ "gh issue close $ID",
251
+ RiskTier.MUTATING, "Close or reopen an issue", "task", ["ID"]),
252
+
253
+ # --- CODING ---
254
+ ActionSpec("coding_list_prs", "terminal",
255
+ "gh pr list --state open --limit 20",
256
+ RiskTier.READ_ONLY, "List open PRs", "coding"),
257
+ ActionSpec("coding_check_ci", "terminal",
258
+ "gh pr checks $PR --json conclusion",
259
+ RiskTier.READ_ONLY, "Check CI status for a PR", "coding", ["PR"]),
260
+ ActionSpec("coding_review_pr", "terminal",
261
+ "gh pr review $PR --approve",
262
+ RiskTier.MUTATING, "Submit a PR review", "coding", ["PR"]),
263
+ ActionSpec("coding_merge_pr", "terminal",
264
+ "gh pr merge $PR --squash",
265
+ RiskTier.DESTRUCTIVE, "Merge a PR (rewrites the target branch)",
266
+ "coding", ["PR"]),
267
+ # Platform write — a PR comment doesn't message an individual.
268
+ ActionSpec("coding_comment_on_pr", "terminal",
269
+ "gh pr comment $PR --body \"$MSG\"",
270
+ RiskTier.MUTATING, "Comment on a PR", "coding", ["PR", "MSG"]),
271
+
272
+ # --- PROJECT ---
273
+ ActionSpec("project_list_milestones", "terminal",
274
+ "gh api repos/{owner}/{repo}/milestones",
275
+ RiskTier.READ_ONLY, "List milestones", "project"),
276
+ ActionSpec("project_check_progress", "terminal",
277
+ "gh api 'repos/{owner}/{repo}/issues?milestone=$ID'",
278
+ RiskTier.READ_ONLY, "Check milestone progress", "project", ["ID"]),
279
+ ActionSpec("project_update_milestone", "terminal",
280
+ "gh api repos/{owner}/{repo}/milestones/$ID --method PATCH",
281
+ RiskTier.MUTATING, "Update a milestone", "project", ["ID"]),
282
+
283
+ # --- SYSTEM ---
284
+ ActionSpec("system_check_health", "terminal",
285
+ "curl http://localhost:7777/health",
286
+ RiskTier.READ_ONLY, "Check sidecar health", "system", native_review=True),
287
+ ActionSpec("operational_review", "terminal",
288
+ "Review the observed local operational evidence and report coverage and unknowns; do not perform maintenance",
289
+ RiskTier.READ_ONLY, "Review local operational coverage", "operational", native_review=True),
290
+ ActionSpec("system_check_alerts", "terminal",
291
+ "journalctl -p err -n 20 --no-pager",
292
+ RiskTier.READ_ONLY, "Check recent error logs", "system"),
293
+ ActionSpec("system_restart_service", "terminal",
294
+ "systemctl restart $SERVICE",
295
+ RiskTier.DESTRUCTIVE, "Restart a service", "system", ["SERVICE"]),
296
+ # Webhook to own infrastructure — a platform write, not a person.
297
+ ActionSpec("system_send_alert", "terminal",
298
+ "curl -X POST $WEBHOOK_URL -d \"$MSG\"",
299
+ RiskTier.MUTATING, "Send an alert webhook (own infra)", "system",
300
+ ["WEBHOOK_URL", "MSG"]),
301
+
302
+ # --- CALENDAR ---
303
+ ActionSpec("calendar_list_events", "terminal",
304
+ "gcal list --upcoming 24h",
305
+ RiskTier.READ_ONLY, "List upcoming events", "calendar"),
306
+ ActionSpec("calendar_prepare_meeting", "terminal",
307
+ "gcal prepare $EVENT_ID",
308
+ RiskTier.READ_ONLY, "Gather prep material for a meeting",
309
+ "calendar", ["EVENT_ID"]),
310
+ # Reaches a person (the attendee) — OUTBOUND, with the recipient
311
+ # named so the graduated policy can resolve it against contacts.
312
+ ActionSpec("calendar_send_reminder", "terminal",
313
+ "curl -X POST $WEBHOOK_URL -d \"$MSG\"",
314
+ RiskTier.OUTBOUND, "Send a meeting reminder", "calendar",
315
+ ["WEBHOOK_URL", "MSG", "RECIPIENT"],
316
+ target_param="RECIPIENT"),
317
+
318
+ # --- COMMITMENT ---
319
+ ActionSpec("commitment_list_open", "terminal",
320
+ "colony commitments list --status pending",
321
+ RiskTier.READ_ONLY, "List open commitments", "commitment"),
322
+ ActionSpec("commitment_check_deadline", "terminal",
323
+ "colony commitments check $ID",
324
+ RiskTier.READ_ONLY, "Check a commitment's deadline",
325
+ "commitment", ["ID"]),
326
+ ActionSpec("commitment_mark_complete", "terminal",
327
+ "colony commitments complete $ID",
328
+ RiskTier.MUTATING, "Mark a commitment fulfilled",
329
+ "commitment", ["ID"]),
330
+
331
+ # --- RESEARCH ---
332
+ ActionSpec("research_check_paper", "terminal",
333
+ "curl https://arxiv.org/abs/$ID",
334
+ RiskTier.READ_ONLY, "Check a paper's status", "research", ["ID"]),
335
+ ActionSpec("research_check_weights", "terminal",
336
+ "curl https://huggingface.co/api/models/$MODEL",
337
+ RiskTier.READ_ONLY, "Check if model weights are published",
338
+ "research", ["MODEL"]),
339
+ ActionSpec("research_download_weights", "terminal",
340
+ "huggingface-cli download $MODEL",
341
+ RiskTier.MUTATING, "Download model weights", "research",
342
+ ["MODEL"]),
343
+ ]
344
+
345
+ ACTION_REGISTRY: Dict[str, ActionSpec] = {spec.name: spec for spec in _SPECS}
346
+
347
+
348
+ def get_action(name: Optional[str]) -> Optional[ActionSpec]:
349
+ """Look up a registered action. None for unregistered names."""
350
+ if not name:
351
+ return None
352
+ return ACTION_REGISTRY.get(name)
353
+
354
+
355
+ def is_registered(name: Optional[str]) -> bool:
356
+ return get_action(name) is not None
357
+
358
+
359
+ def requires_owner_approval(name: Optional[str]) -> bool:
360
+ """True when the action must not auto-run.
361
+
362
+ Unregistered actions also return True — fail closed — but callers
363
+ should refuse to queue them at all (see ``classify_agent_action``).
364
+ """
365
+ spec = get_action(name)
366
+ if spec is None:
367
+ return True
368
+ return spec.risk != RiskTier.READ_ONLY
369
+
370
+
371
+ APPROVAL_POLICY_STRICT = "strict"
372
+ APPROVAL_POLICY_GRADUATED = "graduated"
373
+
374
+
375
+ def get_approval_policy() -> str:
376
+ """Resolve the approval policy mode from the environment.
377
+
378
+ ``COLONY_APPROVAL_POLICY=graduated`` opts a deployment into the
379
+ graduated gate; anything else (including unset/unknown values) is
380
+ ``strict`` — fail closed on typos.
381
+ """
382
+ mode = os.environ.get("COLONY_APPROVAL_POLICY", APPROVAL_POLICY_STRICT)
383
+ mode = (mode or "").strip().lower()
384
+ if mode == APPROVAL_POLICY_GRADUATED:
385
+ return APPROVAL_POLICY_GRADUATED
386
+ return APPROVAL_POLICY_STRICT
387
+
388
+
389
+ def classify_agent_action(
390
+ action_hint: Optional[str],
391
+ params: Optional[Dict[str, object]] = None,
392
+ policy: Optional[str] = None,
393
+ target_authorized: Optional[bool] = None,
394
+ ) -> Dict[str, object]:
395
+ """Single gating decision for the dispatch path.
396
+
397
+ Args:
398
+ action_hint: the named capability to classify.
399
+ params: the job params/context (carried for callers; target
400
+ resolution itself is async — see
401
+ ``approval_policy.is_authorized_target``).
402
+ policy: ``strict`` or ``graduated``; defaults to
403
+ ``get_approval_policy()`` (env, default strict).
404
+ target_authorized: retained compatibility input for caller
405
+ classification. Contact resolution is context, not execution
406
+ authority; all outbound effects remain canonically gated.
407
+
408
+ Returns a dict with:
409
+ - ``registered``: the hint names a known capability
410
+ - ``executable``: safe to post to the task queue at all
411
+ - ``requires_approval``: must block on human-owner approval first
412
+ - ``risk``: the tier string, or None when unregistered
413
+ - ``reason``: why the gate decision was made
414
+ """
415
+ spec = get_action(action_hint)
416
+ if spec is None:
417
+ return {
418
+ "registered": False,
419
+ "executable": False,
420
+ "requires_approval": True,
421
+ "risk": None,
422
+ "reason": "unregistered_action",
423
+ }
424
+
425
+ mode = policy if policy in (APPROVAL_POLICY_STRICT, APPROVAL_POLICY_GRADUATED) \
426
+ else get_approval_policy()
427
+
428
+ if spec.risk == RiskTier.READ_ONLY:
429
+ requires_approval, reason = False, "read_only_auto"
430
+ elif mode == APPROVAL_POLICY_GRADUATED:
431
+ # Graduated policy may tune presentation/urgency, but it is no longer
432
+ # execution authority. Every effectful birth needs a durable direct
433
+ # decision or an exact bounded-grant use in the canonical ledger.
434
+ if spec.risk == RiskTier.MUTATING:
435
+ requires_approval, reason = True, "mutating_requires_owner"
436
+ elif spec.risk == RiskTier.DESTRUCTIVE:
437
+ requires_approval, reason = True, "destructive_requires_owner"
438
+ else:
439
+ requires_approval, reason = True, "outbound_requires_owner"
440
+ else: # strict — v0.17 behavior: everything non-read-only is gated
441
+ requires_approval, reason = True, "strict_policy_gate"
442
+
443
+ return {
444
+ "registered": True,
445
+ "executable": True,
446
+ "requires_approval": requires_approval,
447
+ "risk": spec.risk.value,
448
+ "reason": reason,
449
+ }
450
+
451
+
452
+ def actions_for_type(initiative_type: str) -> List[ActionSpec]:
453
+ """All registered actions for one initiative type."""
454
+ return [s for s in ACTION_REGISTRY.values() if s.initiative_type == initiative_type]