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,214 @@
1
+ """Inline per-turn introspection.
2
+
3
+ After a turn is synced, judge it with a local LLM and record any owed follow-up
4
+ directly — durable commitments and, above all, IMMEDIATE OWED DELIVERABLES ("text me
5
+ the result") — without depending on an external cognition subagent. This is the
6
+ sidecar-side counterpart to the event-based ``cognition.requested`` path: that path
7
+ emits an event for a host plugin to consume, which not every deployment wires up; this
8
+ path runs the judgment in-process against any OpenAI-compatible chat endpoint.
9
+
10
+ Disabled by default. Activated with ``COLONY_INTROSPECT_ENABLED=true`` and a configured
11
+ endpoint; deployment-agnostic (point ``COLONY_INTROSPECT_BASE_URL`` at a local model):
12
+
13
+ COLONY_INTROSPECT_ENABLED "true"|"false" (default false)
14
+ COLONY_INTROSPECT_BASE_URL OpenAI-compatible base, e.g. http://127.0.0.1:8000/v1
15
+ COLONY_INTROSPECT_MODEL model id the endpoint serves (required to run)
16
+ COLONY_INTROSPECT_API_KEY bearer token, blank for a local model
17
+ COLONY_INTROSPECT_TIMEOUT seconds (default 30)
18
+ COLONY_INTROSPECT_MAX_TOKENS response cap (default 400)
19
+
20
+ Because an inline call cannot use tools, it uses a focused JSON-only prompt and creates
21
+ the commitments itself.
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ import json
27
+ import logging
28
+ import os
29
+ from typing import Any, Dict, List, Optional
30
+
31
+ import httpx
32
+
33
+ logger = logging.getLogger(__name__)
34
+
35
+ # Purpose-built, JSON-only prompt (the tool-calling COGNITION_SYSTEM_PROMPT is too hedged and
36
+ # mentions tools, which makes a no-think local model return nothing). Self-contained.
37
+ _SYSTEM_PROMPT = (
38
+ "You audit ONE finished assistant turn and extract any follow-up worth recording, as STRICT JSON.\n"
39
+ "You get what the person SAID and what the assistant REPLIED. Decide only from the literal words.\n\n"
40
+ "Record an item only when the turn clearly contains one of:\n"
41
+ "1. A DURABLE COMMITMENT — an explicit promise, obligation, or reminder to do something later "
42
+ "(\"remind me to X\", \"I'll get back to you on X\", \"follow up on X by Friday\", \"we need to fix X\").\n"
43
+ "2. An IMMEDIATE OWED DELIVERABLE — the person asked to be SENT something through a channel the reply "
44
+ "did NOT satisfy (email it, text a DIFFERENT number, send it to someone else, send it later), AND the "
45
+ "actual content to send is present in the exchange. IMPORTANT: in a chat the assistant's reply already "
46
+ "IS a message to the person, so a plain \"text me\"/\"message me\" is ALREADY satisfied — do NOT record "
47
+ "that; only record a deliverable for a genuinely different channel, recipient, or time.\n\n"
48
+ "Do NOT record small talk, questions, hypotheticals, vague intentions, or anything the reply already "
49
+ "fully handled. Fewer items beats wrong items.\n\n"
50
+ "Output ONLY a JSON array, nothing else (no prose, no markdown, no code fence). Empty array [] when "
51
+ "nothing qualifies. Each element:\n"
52
+ '{"description": string, "due_at": ISO-8601-UTC string or null, "priority": integer 0-100, '
53
+ '"source_type": "cognition" | "introspection", "metadata": null or '
54
+ '{"kind":"deliverable","content":"<exact text to send, ready as-is>","channel_hint":"sms"|"dm"|"email"}}\n'
55
+ "Use \"introspection\" + the deliverable metadata (due_at about two minutes from now) for case 2; "
56
+ "\"cognition\" + metadata null for case 1.\n\n"
57
+ "Examples:\n"
58
+ "They said: Remind me to call the dentist Friday at 9am. | Assistant replied: Got it.\n"
59
+ '[{"description":"Remind them to call the dentist Friday 9am","due_at":"2026-06-26T13:00:00+00:00",'
60
+ '"priority":70,"source_type":"cognition","metadata":null}]\n'
61
+ "They said: Email me the Q3 revenue number. | Assistant replied: Q3 revenue was 4.2 million.\n"
62
+ '[{"description":"Email them the Q3 revenue","due_at":"2026-06-21T21:40:00+00:00","priority":80,'
63
+ '"source_type":"introspection","metadata":{"kind":"deliverable","content":"Q3 revenue was 4.2 million.",'
64
+ '"channel_hint":"email"}}]\n'
65
+ "They said: What's the weather? | Assistant replied: 72 and sunny.\n"
66
+ "[]\n"
67
+ "They said: Text me that. | Assistant replied: The address is 5 Main St.\n"
68
+ "[] (a plain text-me in chat is already satisfied by the reply)")
69
+
70
+
71
+ def _build_user_prompt(user_message: str, assistant_message: str,
72
+ conversation_text: str, existing: list,
73
+ rejections: Optional[list] = None) -> str:
74
+ parts = []
75
+ if conversation_text:
76
+ parts.append(f"Recent conversation:\n{conversation_text}\n")
77
+ parts.append("This turn, verbatim:\n"
78
+ f" They said: {user_message}\n"
79
+ f" Assistant replied: {assistant_message}\n")
80
+ if existing:
81
+ parts.append("\nAlready-recorded OPEN items for this person (do not duplicate; "
82
+ "mentioning an open item again is NOT a new item):")
83
+ for c in existing[:6]:
84
+ parts.append(f"\n- {(c.get('description') or '?')[:80]}")
85
+ if rejections:
86
+ parts.append("\n\nRecently REJECTED items (judged invalid or duplicate — do NOT "
87
+ "record these or anything similar again):")
88
+ for r in rejections[:6]:
89
+ parts.append(f"\n- {(r.get('description') or '?')[:80]} "
90
+ f"[{r.get('outcome') or 'rejected'}]")
91
+ return "".join(parts)
92
+
93
+
94
+ def introspect_enabled() -> bool:
95
+ from apsimo.util.autonomy_preset import resolve_bool
96
+ return resolve_bool("COLONY_INTROSPECT_ENABLED", False)
97
+
98
+
99
+ def _config() -> Dict[str, Any]:
100
+ return {
101
+ "base_url": os.environ.get("COLONY_INTROSPECT_BASE_URL", "http://127.0.0.1:8000/v1").rstrip("/"),
102
+ "model": os.environ.get("COLONY_INTROSPECT_MODEL", "").strip(),
103
+ "api_key": os.environ.get("COLONY_INTROSPECT_API_KEY", "").strip(),
104
+ "timeout": float(os.environ.get("COLONY_INTROSPECT_TIMEOUT", "30")),
105
+ "max_tokens": int(os.environ.get("COLONY_INTROSPECT_MAX_TOKENS", "400")),
106
+ }
107
+
108
+
109
+
110
+
111
+ def _parse_json_array(text: str) -> List[Dict[str, Any]]:
112
+ """Pull the first JSON array out of the model's reply; [] on anything malformed."""
113
+ if not text:
114
+ return []
115
+ try:
116
+ chunk = text[text.index("["): text.rindex("]") + 1]
117
+ data = json.loads(chunk)
118
+ return [x for x in data if isinstance(x, dict)] if isinstance(data, list) else []
119
+ except Exception:
120
+ return []
121
+
122
+
123
+ async def run_turn_introspection(
124
+ *,
125
+ user_message: str,
126
+ assistant_message: str,
127
+ conversation_text: str,
128
+ person_id: str,
129
+ existing_commitments: Optional[List[Dict[str, Any]]],
130
+ commitment_store: Any,
131
+ recent_rejections: Optional[List[Dict[str, Any]]] = None,
132
+ ) -> Dict[str, Any]:
133
+ """Judge one completed turn and record any owed follow-ups as commitments.
134
+
135
+ Best-effort and exception-safe: a failure here never affects the turn. Returns a small
136
+ dict describing what it did, mostly for tests/logging.
137
+
138
+ Dedup is enforced in code, not just in the prompt: a candidate matching an
139
+ existing OPEN item, or a recently REJECTED one, is skipped. Without this,
140
+ every conversational mention of an overdue item re-records it and resolving
141
+ one copy leaves another — the "resolve does nothing" loop.
142
+ """
143
+ cfg = _config()
144
+ if not cfg["model"] or commitment_store is None:
145
+ return {"ok": False, "reason": "introspection not configured"}
146
+ if not (user_message or assistant_message or conversation_text):
147
+ return {"ok": False, "reason": "empty turn"}
148
+
149
+ user_prompt = _build_user_prompt(
150
+ user_message or "", assistant_message or "", conversation_text or "",
151
+ existing_commitments or [], recent_rejections or [])
152
+
153
+ try:
154
+ async with httpx.AsyncClient(timeout=cfg["timeout"]) as client:
155
+ resp = await client.post(
156
+ cfg["base_url"] + "/chat/completions",
157
+ headers={"Authorization": f"Bearer {cfg['api_key'] or 'x'}",
158
+ "Content-Type": "application/json"},
159
+ json={
160
+ "model": cfg["model"], "temperature": 0.1, "max_tokens": cfg["max_tokens"],
161
+ "messages": [{"role": "system", "content": _SYSTEM_PROMPT},
162
+ {"role": "user", "content": user_prompt}],
163
+ },
164
+ )
165
+ resp.raise_for_status()
166
+ content = (resp.json()["choices"][0]["message"]["content"] or "").strip()
167
+ except Exception as exc:
168
+ logger.warning("introspection LLM call failed: %s", exc)
169
+ return {"ok": False, "reason": str(exc)}
170
+
171
+ items = _parse_json_array(content)
172
+ created: List[str] = []
173
+ skipped_duplicates = 0
174
+ from apsimo.commitments.store import _normalize_desc, _similar_desc
175
+ _known = [_normalize_desc(c.get("description") or "")
176
+ for c in (existing_commitments or [])]
177
+ _known += [_normalize_desc(r.get("description") or "")
178
+ for r in (recent_rejections or [])]
179
+ _known = [k for k in _known if k]
180
+ for it in items:
181
+ desc = str(it.get("description") or "").strip()
182
+ if not desc:
183
+ continue
184
+ norm = _normalize_desc(desc)
185
+ if any(_similar_desc(norm, k) for k in _known):
186
+ skipped_duplicates += 1
187
+ continue
188
+ try:
189
+ row = commitment_store.create(
190
+ person_id=person_id,
191
+ description=desc[:1000],
192
+ dedupe=True,
193
+ due_at=(it.get("due_at") or None),
194
+ priority=int(it.get("priority") or 60),
195
+ source_type=(it.get("source_type") or "introspection"),
196
+ source_context="inline turn introspection",
197
+ metadata=(it.get("metadata") if isinstance(it.get("metadata"), dict) else None),
198
+ )
199
+ if row.get("deduped"):
200
+ skipped_duplicates += 1
201
+ else:
202
+ created.append(row.get("id"))
203
+ _known.append(norm) # a later candidate in the same batch can also dupe this one
204
+ except Exception as exc:
205
+ # e.g. a non-future due_at is rejected by the store — skip that one, keep going.
206
+ logger.debug("introspection commitment skipped: %s", exc)
207
+
208
+ if created:
209
+ logger.info("introspection recorded %d commitment(s) for %s", len(created), person_id)
210
+ if skipped_duplicates:
211
+ logger.info("introspection skipped %d duplicate candidate(s) for %s",
212
+ skipped_duplicates, person_id)
213
+ return {"ok": True, "created": created, "candidates": len(items),
214
+ "skipped_duplicates": skipped_duplicates}
@@ -0,0 +1,150 @@
1
+ """Cognition prompt templates for Colony's background thinking.
2
+
3
+ The cognition prompt tells the subagent what Colony can do and how to
4
+ think about the context it receives. It is deliberately conservative:
5
+ false negatives are preferred over false positives.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from typing import Any, Dict, Optional
11
+
12
+ from apsimo.cognition.charter import build_system_prompt
13
+
14
+ # The detailed observation guide: worked positive/negative examples for the
15
+ # observer role. Injected as <context> on top of the shared charter, which
16
+ # carries identity, doctrine, and the conservatism rules.
17
+ _GUIDE = """\
18
+ You have access to Apsimo's API through HTTP endpoints running on the same \
19
+ host. Use the tools available to you to take action when appropriate.
20
+
21
+ ## What to Look For
22
+
23
+ ### 1. COMMITMENTS
24
+ Any explicit promise, follow-up, or obligation expressed in conversation.
25
+
26
+ Examples of commitments:
27
+ - "I'll check on that tomorrow" → commitment with due date tomorrow
28
+ - "Let me get back to you about X" → commitment, no specific deadline
29
+ - "Remind me to call Sarah" → commitment with implicit deadline
30
+ - "I should follow up on the PR review" → commitment, self-directed
31
+ - "We need to fix that bug by Friday" → commitment with deadline
32
+
33
+ NOT commitments (do not record):
34
+ - Casual future tense without obligation ("I might look into it")
35
+ - Hypotheticals ("What if we tried X?")
36
+ - General intentions without specificity ("I want to be better about Y")
37
+ - Questions about future events ("When is the meeting?")
38
+
39
+ When recording a commitment:
40
+ - Use the person who made the promise as person_id
41
+ - Include the specific due date if mentioned, otherwise leave null
42
+ - Set source_type to "cognition"
43
+ - Set priority: urgent/critical promises = 70-90, casual = 40-60
44
+
45
+ ### 2. IMMEDIATE OWED DELIVERABLES
46
+ Something the person asked to be DELIVERED to them in THIS just-finished exchange that
47
+ was NOT actually done in it. The classic case: they asked you to send/text/email/show them
48
+ the result, a link, a file, a summary, or a number, and the answer was only spoken or never
49
+ sent. This is distinct from a commitment: it is owed NOW, not on a future date.
50
+
51
+ Examples of immediate owed deliverables:
52
+ - "text me the findings" / "send me that summary" / "shoot me his number" → deliverable, send now
53
+ - "email me the link" → deliverable via email
54
+ - "send me the chart you described" → deliverable
55
+
56
+ NOT deliverables (do not record):
57
+ - The assistant ALREADY sent/did it in this exchange (check the assistant's message first)
58
+ - The person only wanted to KNOW or be TOLD something (answering aloud already satisfied it)
59
+ - A vague or future "send it over sometime" (that is a commitment, not an immediate deliverable)
60
+
61
+ When recording an immediate owed deliverable, use commitment_create with:
62
+ - person_id = the person who is owed it
63
+ - source_type = "introspection"
64
+ - due_at = about two minutes from now (it is owed now; the store rejects a past due_at)
65
+ - priority = 75-90 (they are waiting on it)
66
+ - description = "Deliver to <person>: <short label>"
67
+ - metadata = {"kind": "deliverable", "content": "<the exact, self-contained thing to send,
68
+ ready to send as-is, drawn from this exchange>", "channel_hint": "sms" | "dm" | "email"
69
+ as they asked}
70
+ The "content" must be the real material to send, not a description of it. If you cannot
71
+ recover the actual content from this exchange, do not record a deliverable.
72
+
73
+ ### 3. When NOT to Act
74
+ - Casual small talk with no actionable content
75
+ - Information exchange with no commitments or owed deliverables
76
+ - Jokes, humor, or social pleasantries
77
+ - Already-recorded commitments (check before creating duplicates)
78
+ - Anything the assistant already handled in this same exchange
79
+
80
+ ## Critical Rules
81
+
82
+ 1. **Fewer actions are better than wrong actions.** If you are unsure whether \
83
+ something is a commitment, do not record it.
84
+ 2. **Do not create duplicate commitments.** Check existing commitments before \
85
+ creating new ones.
86
+ 3. **Be specific in descriptions.** "Check DGX cluster status by Friday" is \
87
+ better than "follow up on something."
88
+ 4. **Only record commitments when there is clear intent.** Maybes and mights \
89
+ are not commitments.
90
+ """
91
+
92
+ COGNITION_SYSTEM_PROMPT = build_system_prompt("observer", extra=_GUIDE)
93
+
94
+
95
+ def build_cognition_prompt(
96
+ trigger_type: str,
97
+ context: Dict[str, Any],
98
+ existing_commitments: Optional[list] = None,
99
+ ) -> str:
100
+ """Build the user prompt for a cognition trigger.
101
+
102
+ Args:
103
+ trigger_type: Type of trigger (turn_sync, signal_ingest, anomaly, manual).
104
+ context: Trigger-specific context (conversation text, person_id, etc.).
105
+ existing_commitments: Current pending commitments for context.
106
+
107
+ Returns:
108
+ Formatted prompt string for the subagent.
109
+ """
110
+ parts = [f"Trigger type: {trigger_type}\n"]
111
+
112
+ if trigger_type == "turn_sync":
113
+ conversation_text = context.get("conversation_text", "")
114
+ person_id = context.get("person_id", "unknown")
115
+ parts.append(f"Contact: {person_id}\n")
116
+ parts.append(f"Recent conversation:\n{conversation_text}\n")
117
+ # Verbatim last turn (when available) so an owed deliverable and whether the
118
+ # assistant ALREADY did it can both be judged from the actual words.
119
+ user_msg = context.get("user_message", "")
120
+ asst_msg = context.get("assistant_message", "")
121
+ if user_msg or asst_msg:
122
+ parts.append(
123
+ "\nLast turn, verbatim (use this to tell if the person asked to be "
124
+ "sent something and whether the assistant already did it):\n"
125
+ f" They said: {user_msg}\n"
126
+ f" Assistant replied: {asst_msg}\n"
127
+ )
128
+
129
+ elif trigger_type == "signal_ingest":
130
+ signal_type = context.get("signal_type", "unknown")
131
+ signal_data = context.get("signal_data", {})
132
+ parts.append(f"Signal type: {signal_type}\n")
133
+ parts.append(f"Signal data: {signal_data}\n")
134
+
135
+ elif trigger_type == "anomaly":
136
+ anomaly_description = context.get("description", "Unknown anomaly")
137
+ parts.append(f"Anomaly detected: {anomaly_description}\n")
138
+
139
+ elif trigger_type == "manual":
140
+ manual_prompt = context.get("prompt", "")
141
+ parts.append(f"Manual cognition request: {manual_prompt}\n")
142
+
143
+ # Include existing commitments so the subagent can avoid duplicates
144
+ if existing_commitments:
145
+ parts.append("\nExisting pending commitments for this contact:\n")
146
+ for c in existing_commitments[:5]:
147
+ due = f" (due {c.get('due_at', 'no deadline')[:10]})" if c.get("due_at") else ""
148
+ parts.append(f"- {c.get('description', '?')}{due}\n")
149
+
150
+ return "".join(parts)
@@ -0,0 +1,108 @@
1
+ """Versioned deployment contract for the P3 cognition spine.
2
+
3
+ The feature flags used by the older cognition slices are independent. That
4
+ is useful for canaries, but it also made unsafe combinations representable --
5
+ for example a live P3 consuming concerns manufactured by a shadow reducer.
6
+ This module turns the flags into one small, inspectable lattice. It does not
7
+ enable any producer and it never widens a requested mode.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from dataclasses import asdict, dataclass
13
+ import hashlib
14
+ import json
15
+ from typing import Any, Mapping, Optional
16
+
17
+
18
+ _MODES = {"off", "shadow", "live"}
19
+
20
+
21
+ def _mode(value: Any) -> str:
22
+ normalized = str(value or "off").strip().lower()
23
+ return normalized if normalized in _MODES else "off"
24
+
25
+
26
+ def _digest(value: Mapping[str, Any]) -> str:
27
+ encoded = json.dumps(
28
+ dict(value), sort_keys=True, separators=(",", ":"), ensure_ascii=True,
29
+ ).encode("utf-8")
30
+ return hashlib.sha256(encoded).hexdigest()
31
+
32
+
33
+ @dataclass(frozen=True)
34
+ class CognitionRuntimeContractV1:
35
+ """Attached/effective mode composition and its immutable revision."""
36
+
37
+ requested_mode: str
38
+ effective_mode: str
39
+ workspace_mode: str
40
+ event_concern_mode: str
41
+ drive_governance_mode: str
42
+ charter_revision_id: Optional[str]
43
+ blockers: tuple[str, ...]
44
+ revision: str
45
+ schema: str = "CognitionRuntimeContractV1"
46
+ version: int = 1
47
+
48
+ @classmethod
49
+ def compose(
50
+ cls,
51
+ *,
52
+ requested_mode: str,
53
+ workspace_mode: str,
54
+ event_concern_mode: str,
55
+ drive_governance_mode: str,
56
+ charter_revision_id: Optional[str] = None,
57
+ charter_store_attached: bool = False,
58
+ attachment_blockers: tuple[str, ...] = (),
59
+ ) -> "CognitionRuntimeContractV1":
60
+ requested = _mode(requested_mode)
61
+ workspace = _mode(workspace_mode)
62
+ events = _mode(event_concern_mode)
63
+ drives = _mode(drive_governance_mode)
64
+ blockers = list(dict.fromkeys(str(item) for item in attachment_blockers))
65
+
66
+ if requested == "live":
67
+ if workspace != "live":
68
+ blockers.append(f"workspace_mode_{workspace}_cannot_feed_live_p3")
69
+ # Event provenance is checked on the exact Concern. Holding the
70
+ # whole runtime because an optional shadow reducer is attached
71
+ # would also stop separately-produced live concerns. Likewise P7
72
+ # shadow is an observer and must not change P3 admission.
73
+ if drives == "live":
74
+ if not charter_store_attached:
75
+ blockers.append("live_charter_store_not_attached")
76
+ elif not charter_revision_id:
77
+ blockers.append("active_owner_ratified_charter_required")
78
+
79
+ effective = requested
80
+ if requested != "off" and blockers:
81
+ effective = "held"
82
+ authority = {
83
+ "schema": "CognitionRuntimeContractV1",
84
+ "version": 1,
85
+ "requested_mode": requested,
86
+ "effective_mode": effective,
87
+ "workspace_mode": workspace,
88
+ "event_concern_mode": events,
89
+ "drive_governance_mode": drives,
90
+ "charter_revision_id": charter_revision_id,
91
+ "blockers": sorted(set(blockers)),
92
+ }
93
+ return cls(
94
+ requested_mode=requested,
95
+ effective_mode=effective,
96
+ workspace_mode=workspace,
97
+ event_concern_mode=events,
98
+ drive_governance_mode=drives,
99
+ charter_revision_id=charter_revision_id,
100
+ blockers=tuple(authority["blockers"]),
101
+ revision=f"cognition-runtime:{_digest(authority)[:24]}",
102
+ )
103
+
104
+ def payload(self) -> dict[str, Any]:
105
+ return asdict(self)
106
+
107
+
108
+ __all__ = ["CognitionRuntimeContractV1"]
@@ -0,0 +1,154 @@
1
+ """Cognition trigger — spawns an OpenClaw subagent for background thinking.
2
+
3
+ Colony does not run its own LLM. It sends cognition requests to OpenClaw
4
+ via the subagent spawn API, which handles model routing, concurrency,
5
+ and token budgeting.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import logging
11
+ import os
12
+ import time
13
+ from typing import Any, Dict, Optional
14
+
15
+ from apsimo.cognition.prompt import COGNITION_SYSTEM_PROMPT, build_cognition_prompt
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+ # Module-level throttle state
20
+ _last_trigger_time: float = 0.0
21
+ _last_trigger_session: Optional[str] = None
22
+
23
+
24
+ def _cognition_enabled() -> bool:
25
+ from apsimo.util.autonomy_preset import resolve_bool
26
+ return resolve_bool("COLONY_COGNITION_ENABLED", False)
27
+
28
+
29
+ def _cognition_model() -> Optional[str]:
30
+ return os.environ.get("COLONY_COGNITION_MODEL") or None
31
+
32
+
33
+ def _throttle_seconds() -> int:
34
+ try:
35
+ return int(os.environ.get("COLONY_COGNITION_THROTTLE_SECONDS", "30"))
36
+ except ValueError:
37
+ return 30
38
+
39
+
40
+ def _update_throttle(now: float, session_id: str) -> None:
41
+ global _last_trigger_time, _last_trigger_session
42
+ _last_trigger_time = now
43
+ _last_trigger_session = session_id
44
+
45
+
46
+ async def trigger_cognition(
47
+ trigger_type: str,
48
+ context: Dict[str, Any],
49
+ priority: str = "normal",
50
+ ) -> Dict[str, Any]:
51
+ """Fire a cognition trigger by spawning an OpenClaw subagent.
52
+
53
+ This is called from the API endpoint. It builds the cognition prompt,
54
+ checks throttling, and delegates to OpenClaw's sessions_spawn.
55
+
56
+ Args:
57
+ trigger_type: turn_sync, signal_ingest, anomaly, or manual.
58
+ context: Trigger-specific context data.
59
+ priority: high, normal, or low.
60
+
61
+ Returns:
62
+ Dict with accepted status and optional throttle info.
63
+ """
64
+ if not _cognition_enabled():
65
+ return {
66
+ "accepted": False,
67
+ "message": "Cognition substrate is disabled",
68
+ "throttle_seconds": None,
69
+ }
70
+
71
+ model = _cognition_model()
72
+ if not model:
73
+ return {
74
+ "accepted": False,
75
+ "message": "COLONY_COGNITION_MODEL not configured",
76
+ "throttle_seconds": None,
77
+ }
78
+
79
+ # Throttle check (skip for high priority)
80
+ throttle_secs = _throttle_seconds()
81
+ now = time.time()
82
+ if priority != "high" and (now - _last_trigger_time) < throttle_secs:
83
+ remaining = int(throttle_secs - (now - _last_trigger_time))
84
+ return {
85
+ "accepted": True,
86
+ "message": f"Throttled — queued for {remaining}s",
87
+ "throttle_seconds": remaining,
88
+ }
89
+
90
+ # Get existing commitments for context (avoid duplicates)
91
+ existing = []
92
+ person_id = context.get("person_id")
93
+ if person_id:
94
+ try:
95
+ from apsimo.api.routers.host import _commitment_store
96
+ if _commitment_store is not None:
97
+ existing = _commitment_store.get_pending_for_person(person_id)
98
+ except Exception:
99
+ logger.debug("Failed to fetch existing commitments for cognition", exc_info=True)
100
+
101
+ # Build the prompt
102
+ user_prompt = build_cognition_prompt(
103
+ trigger_type=trigger_type,
104
+ context=context,
105
+ existing_commitments=existing,
106
+ )
107
+
108
+ # Update throttle timestamp
109
+ session_id = context.get("session_id", "")
110
+ _update_throttle(now, session_id)
111
+
112
+ # Emit cognition.requested event for the plugin to pick up
113
+ _emit_cognition_event(trigger_type, user_prompt, model, priority)
114
+
115
+ return {
116
+ "accepted": True,
117
+ "message": "Cognition trigger fired",
118
+ "throttle_seconds": None,
119
+ }
120
+
121
+
122
+ def _emit_cognition_event(
123
+ trigger_type: str,
124
+ prompt: str,
125
+ model: str,
126
+ priority: str,
127
+ ) -> None:
128
+ """Emit a cognition.requested event carrying a full spawn spec.
129
+
130
+ This is an OPTIONAL integration point: no shipped consumer spawns a
131
+ dedicated cognition session from it today (the hermes plugin only caches
132
+ events as context blurbs). The working per-turn judgment path is the
133
+ inline introspection in cognition/introspection.py; a deployment that
134
+ wants a real tool-restricted cognition session can subscribe to this
135
+ event and honor system_prompt/model/tools_allow. See docs/KNOWN-GAPS.md.
136
+ """
137
+ try:
138
+ from apsimo.events.broadcaster import emit
139
+ emit("cognition.requested", {
140
+ "trigger_type": trigger_type,
141
+ "system_prompt": COGNITION_SYSTEM_PROMPT,
142
+ "user_prompt": prompt,
143
+ "model": model,
144
+ "priority": priority,
145
+ # Names match the registered agent tools so a consumer can pass
146
+ # this straight through as an allowlist.
147
+ "tools_allow": [
148
+ "colony_create_commitment",
149
+ "colony_list_commitments",
150
+ "colony_resolve_commitment",
151
+ ],
152
+ })
153
+ except Exception:
154
+ logger.debug("Failed to emit cognition.requested event", exc_info=True)
@@ -0,0 +1,18 @@
1
+ """Commitment tracking for Colony.
2
+
3
+ Records, tracks, and surfaces promises made during conversations.
4
+ Commitments are created by the cognition substrate (automatic extraction),
5
+ the autonomy loop, or manual API calls.
6
+ """
7
+
8
+ from apsimo.commitments.store import (
9
+ CommitmentResolutionSchemaError,
10
+ CommitmentStore,
11
+ RESOLUTION_RECOVERY_CAPABILITY,
12
+ )
13
+
14
+ __all__ = [
15
+ "CommitmentResolutionSchemaError",
16
+ "CommitmentStore",
17
+ "RESOLUTION_RECOVERY_CAPABILITY",
18
+ ]