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,421 @@
1
+ """GoalDecomposer — transform a Goal into an executable GoalDAG.
2
+
3
+ Strategies:
4
+ TemplateDecomposition — goal matches a known template; no LLM required
5
+ FreeFormDecomposition — novel goal; generates a sensible default DAG
6
+ DelegationDecomposition — remote plan provided; wrap into GoalDAG
7
+
8
+ Critical path is computed using EFT (Earliest Finish Time) algorithm.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import logging
14
+ from dataclasses import dataclass, field
15
+ from enum import Enum
16
+ from typing import Any, Dict, List, Optional
17
+
18
+ from apsimo.goals.models import Goal, GoalDAG, GoalPriority, Subtask, SubtaskStatus
19
+
20
+ logger = logging.getLogger(__name__)
21
+
22
+ MAX_DEPTH = 5
23
+ MAX_SUBTASKS = 50
24
+
25
+ # Standard capability names
26
+ CAPABILITY_MAP: Dict[str, List[str]] = {
27
+ "llm_inference": ["inference"],
28
+ "web_search": ["network"],
29
+ "document_write": ["filesystem"],
30
+ "code_execution": ["sandbox"],
31
+ "gpu_inference": ["gpu", "cuda"],
32
+ "apple_silicon": ["apple_silicon"],
33
+ "email_send": ["email_gateway"],
34
+ "calendar_access": ["calendar"],
35
+ }
36
+
37
+
38
+ class DecompositionStrategy(str, Enum):
39
+ TEMPLATE = "template"
40
+ HYBRID = "hybrid"
41
+ FREE_FORM = "free_form"
42
+ DELEGATION = "delegation"
43
+
44
+
45
+ class DecompositionError(ValueError):
46
+ """Raised when decomposition produces an invalid DAG."""
47
+
48
+
49
+ @dataclass
50
+ class SubtaskSpec:
51
+ """Specification for a subtask in a template."""
52
+ title: str
53
+ job_type: str = "custom"
54
+ capabilities: List[str] = field(default_factory=list)
55
+ depends_on_indices: List[int] = field(default_factory=list) # 0-based index in template list
56
+ estimated_hours: Optional[float] = None
57
+ payload: Dict[str, Any] = field(default_factory=dict)
58
+
59
+
60
+ @dataclass
61
+ class DecompositionTemplate:
62
+ """A reusable decomposition pattern for known goal types."""
63
+ name: str
64
+ keywords: List[str]
65
+ subtasks: List[SubtaskSpec]
66
+ description: str = ""
67
+
68
+
69
+ # ── Built-in templates ─────────────────────────────────────────────────────────
70
+
71
+ _BUILTIN_TEMPLATES: List[DecompositionTemplate] = [
72
+ DecompositionTemplate(
73
+ name="research_topic",
74
+ keywords=["research", "learn about", "find out", "investigate", "explore"],
75
+ description="Research and summarize a topic",
76
+ subtasks=[
77
+ SubtaskSpec(title="Search for relevant sources", job_type="research",
78
+ capabilities=["network"], estimated_hours=0.5),
79
+ SubtaskSpec(title="Read and extract key information", job_type="inference",
80
+ capabilities=["inference"], depends_on_indices=[0], estimated_hours=1.0),
81
+ SubtaskSpec(title="Synthesize and summarize findings", job_type="synthesis",
82
+ capabilities=["inference"], depends_on_indices=[1], estimated_hours=0.5),
83
+ ],
84
+ ),
85
+ DecompositionTemplate(
86
+ name="write_document",
87
+ keywords=["write", "draft", "create document", "prepare report", "compose"],
88
+ description="Write or draft a document",
89
+ subtasks=[
90
+ SubtaskSpec(title="Outline document structure", job_type="inference",
91
+ capabilities=["inference"], estimated_hours=0.25),
92
+ SubtaskSpec(title="Draft content sections", job_type="inference",
93
+ capabilities=["inference"], depends_on_indices=[0], estimated_hours=1.0),
94
+ SubtaskSpec(title="Review and refine draft", job_type="inference",
95
+ capabilities=["inference"], depends_on_indices=[1], estimated_hours=0.5),
96
+ SubtaskSpec(title="Save final document", job_type="data_processing",
97
+ capabilities=["filesystem"], depends_on_indices=[2], estimated_hours=0.1),
98
+ ],
99
+ ),
100
+ DecompositionTemplate(
101
+ name="send_email",
102
+ keywords=["send email", "email to", "write email", "compose email"],
103
+ description="Compose and send an email",
104
+ subtasks=[
105
+ SubtaskSpec(title="Draft email content", job_type="inference",
106
+ capabilities=["inference"], estimated_hours=0.25),
107
+ SubtaskSpec(title="Review and address email", job_type="inference",
108
+ capabilities=["inference"], depends_on_indices=[0], estimated_hours=0.1),
109
+ SubtaskSpec(title="Send email", job_type="custom",
110
+ capabilities=["email_gateway"], depends_on_indices=[1], estimated_hours=0.05),
111
+ ],
112
+ ),
113
+ DecompositionTemplate(
114
+ name="schedule_meeting",
115
+ keywords=["schedule meeting", "book meeting", "set up call", "arrange call", "calendar"],
116
+ description="Schedule a meeting or call",
117
+ subtasks=[
118
+ SubtaskSpec(title="Check calendar availability", job_type="custom",
119
+ capabilities=["calendar"], estimated_hours=0.1),
120
+ SubtaskSpec(title="Propose meeting time", job_type="inference",
121
+ capabilities=["inference"], depends_on_indices=[0], estimated_hours=0.1),
122
+ SubtaskSpec(title="Create calendar event", job_type="custom",
123
+ capabilities=["calendar"], depends_on_indices=[1], estimated_hours=0.1),
124
+ ],
125
+ ),
126
+ DecompositionTemplate(
127
+ name="code_task",
128
+ keywords=["implement", "code", "build", "develop", "fix bug", "write code", "program"],
129
+ description="Implement or fix a code task",
130
+ subtasks=[
131
+ SubtaskSpec(title="Analyse requirements and design approach", job_type="inference",
132
+ capabilities=["inference"], estimated_hours=0.5),
133
+ SubtaskSpec(title="Implement solution", job_type="custom",
134
+ capabilities=["sandbox"], depends_on_indices=[0], estimated_hours=2.0),
135
+ SubtaskSpec(title="Write tests", job_type="custom",
136
+ capabilities=["sandbox"], depends_on_indices=[1], estimated_hours=0.5),
137
+ SubtaskSpec(title="Run tests and fix issues", job_type="custom",
138
+ capabilities=["sandbox"], depends_on_indices=[2], estimated_hours=0.5),
139
+ ],
140
+ ),
141
+ ]
142
+
143
+
144
+ class GoalDecomposer:
145
+ """Transforms a Goal into an executable GoalDAG."""
146
+
147
+ MAX_DEPTH = MAX_DEPTH
148
+ MAX_SUBTASKS = MAX_SUBTASKS
149
+
150
+ def __init__(self) -> None:
151
+ self._templates: List[DecompositionTemplate] = list(_BUILTIN_TEMPLATES)
152
+
153
+ def register_template(self, template: DecompositionTemplate) -> None:
154
+ self._templates.append(template)
155
+
156
+ def get_template(self, goal_type: str) -> Optional[DecompositionTemplate]:
157
+ """Find a template by name."""
158
+ for t in self._templates:
159
+ if t.name == goal_type:
160
+ return t
161
+ return None
162
+
163
+ def decompose(self, goal: Goal) -> GoalDAG:
164
+ """Decompose a goal into a subtask DAG.
165
+
166
+ Steps:
167
+ 1. Select decomposition strategy
168
+ 2. Generate subtask list with dependencies
169
+ 3. Build adjacency structure
170
+ 4. Compute critical path
171
+ 5. Validate for cycles and orphaned nodes
172
+ 6. Return the GoalDAG
173
+
174
+ Raises:
175
+ DecompositionError: If the resulting DAG is invalid or exceeds limits.
176
+ """
177
+ strategy = self._select_strategy(goal)
178
+ dag = self._build_dag(goal, strategy)
179
+
180
+ # Validate
181
+ errors = dag.validate()
182
+ if errors:
183
+ raise DecompositionError(f"Invalid DAG for goal {goal.goal_id}: {errors}")
184
+
185
+ if len(dag.subtasks) > self.MAX_SUBTASKS:
186
+ raise DecompositionError(
187
+ f"Goal {goal.goal_id} decomposed into {len(dag.subtasks)} subtasks "
188
+ f"(max {self.MAX_SUBTASKS})"
189
+ )
190
+
191
+ self._assign_depths(dag)
192
+
193
+ if dag.max_depth > self.MAX_DEPTH:
194
+ raise DecompositionError(
195
+ f"Goal {goal.goal_id} DAG depth {dag.max_depth} exceeds max {self.MAX_DEPTH}"
196
+ )
197
+
198
+ critical = self._compute_critical_path(dag)
199
+ dag.critical_path = critical
200
+ self._mark_critical_path(dag)
201
+
202
+ # Compute root/leaf sets
203
+ all_ids = set(dag.subtasks.keys())
204
+ has_deps = {dep for s in dag.subtasks.values() for dep in s.depends_on}
205
+ dag.root_ids = [sid for sid in all_ids if not dag.subtasks[sid].depends_on]
206
+ dag.leaf_ids = [sid for sid in all_ids if sid not in has_deps]
207
+
208
+ return dag
209
+
210
+ def _select_strategy(self, goal: Goal) -> DecompositionStrategy:
211
+ """Select the decomposition strategy for a goal."""
212
+ text = (goal.title + " " + goal.description).lower()
213
+ for template in self._templates:
214
+ if any(kw in text for kw in template.keywords):
215
+ return DecompositionStrategy.TEMPLATE
216
+ return DecompositionStrategy.FREE_FORM
217
+
218
+ def _build_dag(self, goal: Goal, strategy: DecompositionStrategy) -> GoalDAG:
219
+ """Build the GoalDAG using the selected strategy."""
220
+ dag = GoalDAG(goal_id=goal.goal_id)
221
+
222
+ if strategy == DecompositionStrategy.TEMPLATE:
223
+ template = self._find_matching_template(goal)
224
+ if template:
225
+ return self._build_from_template(goal, dag, template)
226
+
227
+ # Free-form: build a minimal 3-step DAG
228
+ return self._build_free_form(goal, dag)
229
+
230
+ def _find_matching_template(self, goal: Goal) -> Optional[DecompositionTemplate]:
231
+ text = (goal.title + " " + goal.description).lower()
232
+ best: Optional[DecompositionTemplate] = None
233
+ best_count = 0
234
+ for template in self._templates:
235
+ count = sum(1 for kw in template.keywords if kw in text)
236
+ if count > best_count:
237
+ best_count = count
238
+ best = template
239
+ return best if best_count > 0 else None
240
+
241
+ def _build_from_template(
242
+ self, goal: Goal, dag: GoalDAG, template: DecompositionTemplate
243
+ ) -> GoalDAG:
244
+ """Instantiate subtasks from a template."""
245
+ import uuid as _uuid
246
+ subtask_ids: List[str] = []
247
+
248
+ for spec in template.subtasks:
249
+ sid = str(_uuid.uuid4())
250
+ subtask_ids.append(sid)
251
+ deps = [subtask_ids[i] for i in spec.depends_on_indices if i < len(subtask_ids)]
252
+ st = Subtask(
253
+ subtask_id=sid,
254
+ goal_id=goal.goal_id,
255
+ title=spec.title,
256
+ job_type=spec.job_type,
257
+ capabilities=list(spec.capabilities),
258
+ depends_on=deps,
259
+ estimated_hours=spec.estimated_hours,
260
+ payload=dict(spec.payload),
261
+ )
262
+ dag.add_subtask(st)
263
+ return dag
264
+
265
+ def _build_free_form(self, goal: Goal, dag: GoalDAG) -> GoalDAG:
266
+ """Build a generic 3-step DAG: analyse → execute → verify."""
267
+ import uuid as _uuid
268
+
269
+ s1_id = str(_uuid.uuid4())
270
+ s2_id = str(_uuid.uuid4())
271
+ s3_id = str(_uuid.uuid4())
272
+
273
+ dag.add_subtask(Subtask(
274
+ subtask_id=s1_id,
275
+ goal_id=goal.goal_id,
276
+ title=f"Analyse requirements: {goal.title}",
277
+ job_type="inference",
278
+ capabilities=["inference"],
279
+ depends_on=[],
280
+ estimated_hours=0.25,
281
+ ))
282
+ dag.add_subtask(Subtask(
283
+ subtask_id=s2_id,
284
+ goal_id=goal.goal_id,
285
+ title=f"Execute: {goal.title}",
286
+ job_type="custom",
287
+ capabilities=[],
288
+ depends_on=[s1_id],
289
+ estimated_hours=1.0,
290
+ ))
291
+ dag.add_subtask(Subtask(
292
+ subtask_id=s3_id,
293
+ goal_id=goal.goal_id,
294
+ title=f"Verify completion: {goal.title}",
295
+ job_type="inference",
296
+ capabilities=["inference"],
297
+ depends_on=[s2_id],
298
+ estimated_hours=0.25,
299
+ ))
300
+ return dag
301
+
302
+ def _assign_depths(self, dag: GoalDAG) -> None:
303
+ """Assign DAG depth to each subtask via BFS from root nodes."""
304
+ # Root nodes have depth 0
305
+ depth: Dict[str, int] = {}
306
+ in_degree: Dict[str, int] = {sid: 0 for sid in dag.subtasks}
307
+
308
+ # Build parent → children map
309
+ children: Dict[str, List[str]] = {sid: [] for sid in dag.subtasks}
310
+ for s in dag.subtasks.values():
311
+ for dep in s.depends_on:
312
+ if dep in dag.subtasks:
313
+ in_degree[s.subtask_id] = in_degree.get(s.subtask_id, 0) + 1
314
+ children[dep].append(s.subtask_id)
315
+
316
+ queue = [sid for sid, d in in_degree.items() if d == 0]
317
+ for sid in queue:
318
+ depth[sid] = 0
319
+
320
+ while queue:
321
+ next_queue = []
322
+ for sid in queue:
323
+ for child in children[sid]:
324
+ new_depth = depth[sid] + 1
325
+ if child not in depth or depth[child] < new_depth:
326
+ depth[child] = new_depth
327
+ next_queue.append(child)
328
+ queue = next_queue
329
+
330
+ for sid, st in dag.subtasks.items():
331
+ st.depth = depth.get(sid, 0)
332
+
333
+ dag.max_depth = max(depth.values()) if depth else 0
334
+
335
+ def _compute_critical_path(self, dag: GoalDAG) -> List[str]:
336
+ """Compute the critical path using EFT (Earliest Finish Time) algorithm.
337
+
338
+ Returns ordered list of subtask_ids from root to leaf along the
339
+ longest estimated execution path.
340
+ """
341
+ if not dag.subtasks:
342
+ return []
343
+
344
+ # Build children map
345
+ children: Dict[str, List[str]] = {sid: [] for sid in dag.subtasks}
346
+ for s in dag.subtasks.values():
347
+ for dep in s.depends_on:
348
+ if dep in dag.subtasks:
349
+ children[dep].append(s.subtask_id)
350
+
351
+ # EFT: earliest_finish[sid] = max(EFT of deps) + duration
352
+ eft: Dict[str, float] = {}
353
+ parent: Dict[str, Optional[str]] = {sid: None for sid in dag.subtasks}
354
+
355
+ # Topological order (Kahn's algorithm)
356
+ in_degree: Dict[str, int] = {sid: len(s.depends_on) for sid, s in dag.subtasks.items()}
357
+ queue = [sid for sid, d in in_degree.items() if d == 0]
358
+ topo: List[str] = []
359
+ tmp_queue = list(queue)
360
+ while tmp_queue:
361
+ node = tmp_queue.pop(0)
362
+ topo.append(node)
363
+ for child in children[node]:
364
+ in_degree[child] -= 1
365
+ if in_degree[child] == 0:
366
+ tmp_queue.append(child)
367
+
368
+ for sid in topo:
369
+ s = dag.subtasks[sid]
370
+ dur = s.estimated_hours or 1.0
371
+ if not s.depends_on:
372
+ eft[sid] = dur
373
+ else:
374
+ max_parent_eft = max(eft.get(dep, 0.0) for dep in s.depends_on)
375
+ eft[sid] = max_parent_eft + dur
376
+ # Track which parent gives the critical path
377
+ critical_dep = max(
378
+ (dep for dep in s.depends_on if dep in eft),
379
+ key=lambda d: eft.get(d, 0.0),
380
+ default=None,
381
+ )
382
+ parent[sid] = critical_dep
383
+
384
+ if not eft:
385
+ return []
386
+
387
+ # Find the leaf with maximum EFT
388
+ leaf_ids = set(dag.subtasks.keys()) - {dep for s in dag.subtasks.values() for dep in s.depends_on}
389
+ if not leaf_ids:
390
+ leaf_ids = set(dag.subtasks.keys())
391
+
392
+ end_node = max(leaf_ids, key=lambda sid: eft.get(sid, 0.0))
393
+
394
+ # Trace back from end_node to root
395
+ path = []
396
+ current: Optional[str] = end_node
397
+ while current is not None:
398
+ path.append(current)
399
+ current = parent.get(current)
400
+
401
+ return list(reversed(path))
402
+
403
+ def _mark_critical_path(self, dag: GoalDAG) -> None:
404
+ """Mark subtasks on the critical path with is_critical_path = True."""
405
+ cp_set = set(dag.critical_path)
406
+ for sid, st in dag.subtasks.items():
407
+ st.is_critical_path = sid in cp_set
408
+
409
+ def estimate_subtask_duration(self, subtask: Subtask) -> float:
410
+ """Return estimated hours for a subtask (default if not set)."""
411
+ if subtask.estimated_hours is not None:
412
+ return subtask.estimated_hours
413
+ # Default by job type
414
+ defaults = {
415
+ "inference": 0.5,
416
+ "synthesis": 0.5,
417
+ "research": 1.0,
418
+ "data_processing": 0.5,
419
+ "custom": 1.0,
420
+ }
421
+ return defaults.get(subtask.job_type, 1.0)