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,506 @@
1
+ """ToolExecutor — dispatch tool calls and collect results.
2
+
3
+ The executor is pluggable: hosts can register tool handlers, or the
4
+ default executor can be used (which returns "not implemented" for
5
+ unknown tools, letting the host handle them client-side).
6
+
7
+ Colony-native tools are defined in tools/definitions.py and handlers
8
+ are in tools/handlers.py. These tools provide direct access to
9
+ Colony's intelligence systems (memory, goals, relationships, etc.)
10
+ without going through the host plugin.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import inspect
16
+ import json
17
+ import logging
18
+ import uuid
19
+ from collections.abc import Mapping
20
+ from typing import TYPE_CHECKING, Any, Callable, Coroutine
21
+
22
+ if TYPE_CHECKING:
23
+ from apsimo.autonomy.registry import SubsystemRegistry
24
+
25
+ from apsimo.tools.definitions import STATIC_TOOL_NAMES, get_tool_definitions
26
+ from apsimo.tools.handlers import TOOL_HANDLERS
27
+ from apsimo.reasoning.tool_policy import (
28
+ ToolActorPolicy,
29
+ ToolEffect,
30
+ action_target,
31
+ actor_allows_effect,
32
+ classify_tool,
33
+ )
34
+
35
+ logger = logging.getLogger(__name__)
36
+
37
+
38
+ # Type alias for tool handler functions
39
+ ToolHandler = Callable[..., Coroutine[Any, Any, str]]
40
+
41
+
42
+ class ToolRegistryError(RuntimeError):
43
+ """A dynamic tool surface could not be resolved without ambiguity."""
44
+
45
+ def __init__(self, code: str, message: str) -> None:
46
+ super().__init__(message)
47
+ self.code = code
48
+
49
+
50
+ class ToolExecutor:
51
+ """Dispatch tool calls and collect results.
52
+
53
+ Parameters
54
+ ----------
55
+ handlers :
56
+ Optional mapping of tool name → async handler function.
57
+ Handlers receive the tool's arguments dict and must return
58
+ a string result.
59
+ registry :
60
+ Optional SubsystemRegistry for Colony-native tools. When provided,
61
+ Colony tools are automatically available.
62
+ """
63
+
64
+ def __init__(
65
+ self,
66
+ handlers: dict[str, ToolHandler] | None = None,
67
+ registry: SubsystemRegistry | None = None,
68
+ graph_client = None,
69
+ ) -> None:
70
+ self._handlers: dict[str, ToolHandler] = handlers or {}
71
+ self._registry = registry
72
+ self._graph = graph_client
73
+ # Dynamic tools (e.g. toolsmith-built): a provider returning
74
+ # {name: (openai_definition, async_handler)} consulted at call time
75
+ # so newly-graduated tools appear without re-instantiation.
76
+ self._dynamic_provider = None
77
+ self._directive_manager = None
78
+ self._boundary_required = False
79
+
80
+ # Auto-register Colony-native tool handlers if registry is provided
81
+ if registry is not None:
82
+ for name, handler in TOOL_HANDLERS.items():
83
+ if name not in self._handlers:
84
+ # Wrap handler to inject registry
85
+ self._handlers[name] = lambda args, h=handler, r=registry: h(args, r)
86
+
87
+ def register(self, name: str, handler: ToolHandler) -> None:
88
+ """Register a tool handler."""
89
+ self._handlers[name] = handler
90
+
91
+ def register_native_tools(self, search_orchestrator=None, sandbox_dir: str = "") -> None:
92
+ """Register Colony-native tools that run inside the sidecar.
93
+
94
+ Tool classes expose ``.execute(args) -> dict``; the executor's
95
+ handler contract is ``(args) -> Awaitable[str|dict]``, so we bind
96
+ the bound-method ``tool.execute`` directly — registering the bare
97
+ instance would fail because the tool classes are not callable.
98
+ """
99
+ try:
100
+ from apsimo.reasoning.native_tools.calculate import CalculateTool
101
+ self.register("calculate", CalculateTool().execute)
102
+ except Exception as exc:
103
+ logger.warning("register calculate tool failed: %s", exc)
104
+
105
+ if search_orchestrator and search_orchestrator.has_providers:
106
+ try:
107
+ from apsimo.reasoning.native_tools.web_search import WebSearchTool
108
+ ws_tool = WebSearchTool(search_orchestrator)
109
+ self.register("web_search", ws_tool.execute)
110
+ except Exception as exc:
111
+ logger.warning("register web_search tool failed: %s", exc)
112
+
113
+ if sandbox_dir:
114
+ try:
115
+ from apsimo.reasoning.native_tools.file_ops import (
116
+ ReadFileTool, WriteFileTool, ListDirectoryTool,
117
+ )
118
+ self.register("read_file", ReadFileTool(sandbox_dir).execute)
119
+ self.register("write_file", WriteFileTool(sandbox_dir).execute)
120
+ self.register("list_directory", ListDirectoryTool(sandbox_dir).execute)
121
+ except Exception as exc:
122
+ logger.warning("register file_ops tools failed: %s", exc)
123
+
124
+ def unregister(self, name: str) -> None:
125
+ """Remove a tool handler."""
126
+ self._handlers.pop(name, None)
127
+
128
+ def set_dynamic_provider(self, provider) -> None:
129
+ """Register a provider callable returning a dict
130
+ {name: (openai_definition, async_handler)} of runtime tools
131
+ (e.g. toolsmith-graduated tools). Consulted on every turn."""
132
+ self._dynamic_provider = provider
133
+
134
+ def configure_execution_policy(
135
+ self,
136
+ *,
137
+ directive_manager,
138
+ boundary_required: bool = True,
139
+ ) -> None:
140
+ """Install the standing-boundary dependency for all executions.
141
+
142
+ Server startup uses ``boundary_required=True``. Tests and legacy
143
+ in-process embeddings that never configure a manager retain their
144
+ historical behavior; a configured live server does not silently run a
145
+ private/mutating tool when the boundary dependency is absent.
146
+ """
147
+
148
+ self._directive_manager = directive_manager
149
+ self._boundary_required = bool(boundary_required)
150
+
151
+ def _dynamic_tools(self) -> dict[str, Any]:
152
+ if self._dynamic_provider is None:
153
+ return {}
154
+ try:
155
+ supplied = self._dynamic_provider()
156
+ except Exception as exc:
157
+ logger.warning("dynamic tool provider failed: %s", exc)
158
+ raise ToolRegistryError(
159
+ "tool_registry_unavailable",
160
+ "dynamic tool provider is unavailable",
161
+ ) from exc
162
+ if supplied is None:
163
+ return {}
164
+ if not isinstance(supplied, Mapping):
165
+ raise ToolRegistryError(
166
+ "tool_registry_malformed",
167
+ "dynamic tool provider returned a malformed registry",
168
+ )
169
+
170
+ dynamic = dict(supplied)
171
+ names = set()
172
+ for name in dynamic:
173
+ if (
174
+ not isinstance(name, str)
175
+ or not name
176
+ or name.strip() != name
177
+ ):
178
+ raise ToolRegistryError(
179
+ "tool_registry_malformed",
180
+ "dynamic tool provider returned an invalid name",
181
+ )
182
+ names.add(name)
183
+
184
+ collisions = names.intersection(
185
+ set(STATIC_TOOL_NAMES).union(self._handlers)
186
+ )
187
+ if collisions:
188
+ listed = ", ".join(sorted(collisions))
189
+ logger.error("dynamic/static tool name collision: %s", listed)
190
+ raise ToolRegistryError(
191
+ "tool_name_collision",
192
+ f"dynamic tool name collides with first-party tool: {listed}",
193
+ )
194
+
195
+ validated: dict[str, Any] = {}
196
+ for name, value in dynamic.items():
197
+ if not isinstance(value, (tuple, list)) or len(value) != 2:
198
+ raise ToolRegistryError(
199
+ "tool_registry_malformed",
200
+ f"dynamic tool '{name}' has a malformed provider entry",
201
+ )
202
+ definition, handler = value
203
+ if (
204
+ self._definition_name(definition) != name
205
+ or not inspect.iscoroutinefunction(handler)
206
+ ):
207
+ raise ToolRegistryError(
208
+ "tool_registry_malformed",
209
+ f"dynamic tool '{name}' has invalid definition provenance",
210
+ )
211
+ validated[name] = (definition, handler)
212
+ return validated
213
+
214
+ @staticmethod
215
+ def _definition_name(definition: object) -> str | None:
216
+ if not isinstance(definition, Mapping):
217
+ return None
218
+ if definition.get("type") == "function":
219
+ function = definition.get("function")
220
+ if not isinstance(function, Mapping):
221
+ return None
222
+ name = function.get("name")
223
+ else:
224
+ name = definition.get("name")
225
+ return name if isinstance(name, str) and name else None
226
+
227
+ def _resolved_handler(
228
+ self,
229
+ name: str,
230
+ dynamic: Mapping[str, Any],
231
+ ) -> tuple[ToolHandler | None, ToolEffect | None, str | None]:
232
+ """Resolve executable and provenance before any authority decision."""
233
+
234
+ handler = self._handlers.get(name)
235
+ if handler is not None:
236
+ return handler, classify_tool(name), "static"
237
+ supplied = dynamic.get(name)
238
+ if supplied is not None:
239
+ # Dynamic capabilities are always mutations. Their human-selected
240
+ # names are never an authorization type declaration.
241
+ return supplied[1], ToolEffect.MUTATION, "dynamic"
242
+ return None, None, None
243
+
244
+ def available_names(self) -> list[str]:
245
+ """Return a stable, de-duplicated snapshot of executable tool names."""
246
+
247
+ dynamic = self._dynamic_tools()
248
+ return list(dict.fromkeys((
249
+ *self._handlers.keys(),
250
+ *dynamic.keys(),
251
+ )))
252
+
253
+ def filter_names_for_actor(
254
+ self,
255
+ names: list[str] | tuple[str, ...] | set[str] | frozenset[str] | None,
256
+ actor_policy: ToolActorPolicy | None,
257
+ ) -> list[str]:
258
+ """Filter one registry snapshot using resolved handler provenance."""
259
+
260
+ dynamic = self._dynamic_tools()
261
+ requested = (
262
+ (*self._handlers.keys(), *dynamic.keys())
263
+ if names is None else names
264
+ )
265
+ allowed = []
266
+ for name in dict.fromkeys(requested):
267
+ handler, effect, _provenance = self._resolved_handler(name, dynamic)
268
+ if (
269
+ handler is not None
270
+ and effect is not None
271
+ and actor_allows_effect(effect, actor_policy)
272
+ ):
273
+ allowed.append(name)
274
+ return allowed
275
+
276
+ def _boundary_verdict(
277
+ self,
278
+ name: str,
279
+ arguments: dict[str, Any],
280
+ effect: ToolEffect,
281
+ ) -> tuple[bool, str | None, str]:
282
+ """Return ``(allowed, error_code, reason)`` for standing boundaries."""
283
+
284
+ manager = self._directive_manager
285
+ if manager is None:
286
+ # Keep general information tools usable during a boundary-store
287
+ # outage. Private reads and mutations fail closed on a configured
288
+ # server because they can expose or alter durable owner state.
289
+ if not self._boundary_required or effect is ToolEffect.PUBLIC_READ:
290
+ return True, None, "boundary_not_required"
291
+ return False, "tool_boundary_unavailable", "directive manager unavailable"
292
+
293
+ try:
294
+ from apsimo.directives import Action
295
+
296
+ verdict = manager.check(Action(
297
+ kind=(
298
+ "read"
299
+ if effect in {ToolEffect.PUBLIC_READ, ToolEffect.PRIVATE_READ}
300
+ else "execute_tool"
301
+ ),
302
+ text=f"reasoning tool {name}",
303
+ target=action_target(arguments),
304
+ tool_name=name,
305
+ args=dict(arguments),
306
+ high_risk=(effect is ToolEffect.MUTATION),
307
+ ))
308
+ except Exception as exc:
309
+ logger.warning("ToolExecutor: boundary check failed for '%s': %s", name, exc)
310
+ if effect is ToolEffect.PUBLIC_READ:
311
+ return True, None, "public_read_boundary_unavailable"
312
+ return False, "tool_boundary_unavailable", "directive check unavailable"
313
+
314
+ allowed = getattr(verdict, "allowed", None)
315
+ if not isinstance(allowed, bool):
316
+ logger.warning("ToolExecutor: malformed boundary verdict for '%s'", name)
317
+ if effect is ToolEffect.PUBLIC_READ:
318
+ return True, None, "public_read_boundary_malformed"
319
+ return False, "tool_boundary_unavailable", "malformed directive verdict"
320
+ if not allowed:
321
+ return (
322
+ False,
323
+ "tool_boundary_denied",
324
+ str(getattr(verdict, "reason", "standing owner boundary") or
325
+ "standing owner boundary"),
326
+ )
327
+ return True, None, str(getattr(verdict, "reason", "ok") or "ok")
328
+
329
+ @staticmethod
330
+ def _error_result(
331
+ tool_call_id: str,
332
+ *,
333
+ code: str,
334
+ message: str,
335
+ tool_name: str,
336
+ ) -> dict[str, Any]:
337
+ return {
338
+ "tool_call_id": tool_call_id,
339
+ "content": json.dumps({
340
+ "error": code,
341
+ "message": message,
342
+ "tool_name": tool_name,
343
+ }),
344
+ "executed": False,
345
+ "error": code,
346
+ }
347
+
348
+ def get_definitions(self, available_tools: list[str] | None = None) -> list[dict[str, Any]]:
349
+ """Build OpenAI-format tool definitions for the LLM call.
350
+
351
+ Returns Colony-native tool definitions that can be executed server-side.
352
+ These are in addition to any host-side tools passed through the ReasoningLoop.
353
+
354
+ Parameters
355
+ ----------
356
+ available_tools :
357
+ Optional filter for specific tool names to include. When None,
358
+ defaults to the set of registered handlers.
359
+
360
+ Returns
361
+ -------
362
+ List of OpenAI-format tool definitions.
363
+ """
364
+ dynamic = self._dynamic_tools()
365
+ explicit_filter = available_tools is not None
366
+ names = list(dict.fromkeys(
367
+ available_tools if explicit_filter else self._handlers.keys()
368
+ ))
369
+ definitions = get_tool_definitions(tool_names=names)
370
+ defs = []
371
+ seen = set()
372
+ for definition in definitions:
373
+ name = self._definition_name(definition)
374
+ if name is None:
375
+ raise ToolRegistryError(
376
+ "tool_registry_malformed",
377
+ "first-party tool definition is malformed",
378
+ )
379
+ # The static catalog is authoritative. Keep one stable definition
380
+ # if a future composition accidentally repeats a shipped entry.
381
+ if name not in seen:
382
+ defs.append(definition)
383
+ seen.add(name)
384
+ for name, (definition, _handler) in dynamic.items():
385
+ # a filter must name a dynamic tool to include it; unfiltered
386
+ # turns see all graduated tools
387
+ if definition and (not explicit_filter or name in names):
388
+ if name in seen:
389
+ raise ToolRegistryError(
390
+ "tool_name_collision",
391
+ f"model tool definition collision for '{name}'",
392
+ )
393
+ defs.append(definition)
394
+ seen.add(name)
395
+ return defs
396
+
397
+ async def execute_batch(
398
+ self,
399
+ tool_calls: list[dict[str, Any]],
400
+ *,
401
+ session_id: str = "",
402
+ allowed_tools: set[str] | frozenset[str] | None = None,
403
+ actor_policy: ToolActorPolicy | None = None,
404
+ ) -> list[dict[str, Any]]:
405
+ """Execute a batch of tool calls and return results.
406
+
407
+ Each result dict has:
408
+ - tool_call_id: the ID from the original tool call
409
+ - content: the string result
410
+
411
+ Unknown tools return a "not implemented" result rather than
412
+ raising, so the LLM can see the failure and adjust.
413
+ """
414
+ results = []
415
+ try:
416
+ dynamic = self._dynamic_tools()
417
+ except ToolRegistryError as exc:
418
+ for tc in tool_calls:
419
+ tc_id = tc.get("id", str(uuid.uuid4()))
420
+ name = str(tc.get("name", "unknown"))
421
+ results.append(self._error_result(
422
+ tc_id,
423
+ code=exc.code,
424
+ message=str(exc),
425
+ tool_name=name,
426
+ ))
427
+ return results
428
+
429
+ for tc in tool_calls:
430
+ tc_id = tc.get("id", str(uuid.uuid4()))
431
+ name = tc.get("name", "unknown")
432
+ arguments = tc.get("arguments", {})
433
+ if not isinstance(arguments, dict):
434
+ arguments = {}
435
+
436
+ handler, effect, _provenance = self._resolved_handler(name, dynamic)
437
+ if handler is None or effect is None:
438
+ logger.debug("ToolExecutor: no handler for '%s' — returning error", name)
439
+ results.append({
440
+ "tool_call_id": tc_id,
441
+ "content": json.dumps({
442
+ "error": True,
443
+ "message": f"Tool '{name}' is not available. Try a different approach.",
444
+ "available_tools": list(self._handlers.keys())
445
+ + list(dynamic.keys()),
446
+ }),
447
+ "executed": False,
448
+ "error": "tool_unavailable",
449
+ })
450
+ continue
451
+
452
+ if allowed_tools is not None and name not in allowed_tools:
453
+ results.append(self._error_result(
454
+ tc_id,
455
+ code="tool_not_authorized",
456
+ message=f"Tool '{name}' was not authorized for this turn.",
457
+ tool_name=name,
458
+ ))
459
+ continue
460
+
461
+ if not actor_allows_effect(effect, actor_policy):
462
+ results.append(self._error_result(
463
+ tc_id,
464
+ code="tool_authority_denied",
465
+ message=f"Tool '{name}' exceeds caller authority.",
466
+ tool_name=name,
467
+ ))
468
+ continue
469
+
470
+ boundary_allowed, boundary_error, boundary_reason = (
471
+ self._boundary_verdict(name, arguments, effect)
472
+ )
473
+ if not boundary_allowed:
474
+ assert boundary_error is not None
475
+ results.append(self._error_result(
476
+ tc_id,
477
+ code=boundary_error,
478
+ message=boundary_reason,
479
+ tool_name=name,
480
+ ))
481
+ continue
482
+
483
+ try:
484
+ result = await handler(arguments)
485
+ if isinstance(result, (dict, list)):
486
+ content = json.dumps(result)
487
+ else:
488
+ content = str(result)
489
+ results.append({
490
+ "tool_call_id": tc_id,
491
+ "content": content,
492
+ "executed": True,
493
+ })
494
+ except Exception as exc:
495
+ logger.error("ToolExecutor: handler '%s' failed: %s", name, exc)
496
+ results.append({
497
+ "tool_call_id": tc_id,
498
+ "content": json.dumps({
499
+ "error": f"Tool '{name}' execution failed: {exc}",
500
+ "tool_name": name,
501
+ }),
502
+ "executed": False,
503
+ "error": "tool_execution_failed",
504
+ })
505
+
506
+ return results