apsimo 1.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (614) hide show
  1. apsimo/__init__.py +38 -0
  2. apsimo/__main__.py +6 -0
  3. apsimo/agent/__init__.py +6 -0
  4. apsimo/agent/client.py +276 -0
  5. apsimo/agent/models.py +46 -0
  6. apsimo/agents/__init__.py +20 -0
  7. apsimo/agents/models.py +264 -0
  8. apsimo/agents/store.py +861 -0
  9. apsimo/agents/websocket.py +522 -0
  10. apsimo/api/__init__.py +1 -0
  11. apsimo/api/auth_telemetry.py +287 -0
  12. apsimo/api/authority.py +1203 -0
  13. apsimo/api/contact_grants.py +347 -0
  14. apsimo/api/middleware.py +483 -0
  15. apsimo/api/routers/__init__.py +1 -0
  16. apsimo/api/routers/commitment_work.py +265 -0
  17. apsimo/api/routers/context_gate.py +123 -0
  18. apsimo/api/routers/executions.py +140 -0
  19. apsimo/api/routers/followup_plans.py +147 -0
  20. apsimo/api/routers/governed_actions.py +162 -0
  21. apsimo/api/routers/host.py +14473 -0
  22. apsimo/api/routers/initiative_work.py +115 -0
  23. apsimo/api/routers/mining.py +104 -0
  24. apsimo/api/routers/observations.py +110 -0
  25. apsimo/api/routers/social_state.py +225 -0
  26. apsimo/api/routers/task_queue.py +2715 -0
  27. apsimo/api/routers/temporal_followups.py +251 -0
  28. apsimo/api/routers/transport.py +110 -0
  29. apsimo/api/routers/transport_ingress_api.py +240 -0
  30. apsimo/api/schemas/__init__.py +1 -0
  31. apsimo/api/schemas/host.py +1949 -0
  32. apsimo/autonomy/cli.py +110 -0
  33. apsimo/autonomy/condition_worker.py +437 -0
  34. apsimo/autonomy/config.py +424 -0
  35. apsimo/autonomy/loop.py +4316 -0
  36. apsimo/autonomy/registry.py +339 -0
  37. apsimo/autonomy/scheduler.py +1822 -0
  38. apsimo/autonomy/synthesis.py +449 -0
  39. apsimo/backup.py +962 -0
  40. apsimo/beliefs/__init__.py +23 -0
  41. apsimo/beliefs/contradictions.py +109 -0
  42. apsimo/beliefs/decay.py +61 -0
  43. apsimo/beliefs/engine.py +479 -0
  44. apsimo/beliefs/models.py +67 -0
  45. apsimo/beliefs/promotion.py +41 -0
  46. apsimo/beliefs/resolve.py +58 -0
  47. apsimo/beliefs/source_claims.py +690 -0
  48. apsimo/beliefs/source_projection.py +883 -0
  49. apsimo/beliefs/source_time.py +208 -0
  50. apsimo/beliefs/store.py +133 -0
  51. apsimo/briefings/aggregators.py +824 -0
  52. apsimo/briefings/composer.py +420 -0
  53. apsimo/briefings/config.py +55 -0
  54. apsimo/briefings/delivery.py +439 -0
  55. apsimo/briefings/engagement.py +97 -0
  56. apsimo/briefings/engine.py +274 -0
  57. apsimo/briefings/enhancer.py +99 -0
  58. apsimo/briefings/models.py +183 -0
  59. apsimo/briefings/scheduler.py +382 -0
  60. apsimo/briefings/store.py +435 -0
  61. apsimo/chain/__init__.py +48 -0
  62. apsimo/chain/block.py +100 -0
  63. apsimo/chain/cli.py +704 -0
  64. apsimo/chain/genesis.py +443 -0
  65. apsimo/chain/identity.py +416 -0
  66. apsimo/chain/keys.py +1025 -0
  67. apsimo/chain/local_keys.py +187 -0
  68. apsimo/chain/manager.py +290 -0
  69. apsimo/chain/node.py +163 -0
  70. apsimo/chain/plugin_transactions.py +371 -0
  71. apsimo/chain/protocol.py +220 -0
  72. apsimo/chain/state_machine.py +676 -0
  73. apsimo/chain/storage.py +503 -0
  74. apsimo/chain/transactions.py +250 -0
  75. apsimo/chain/validation.py +397 -0
  76. apsimo/channels/__init__.py +1 -0
  77. apsimo/channels/manifest.py +31 -0
  78. apsimo/channels/migrations/001_channels_schema.sql +12 -0
  79. apsimo/channels/phone_gateways.py +42 -0
  80. apsimo/channels/presence.py +188 -0
  81. apsimo/channels/router.py +235 -0
  82. apsimo/channels/store.py +231 -0
  83. apsimo/cli.py +2688 -0
  84. apsimo/cognition/__init__.py +11 -0
  85. apsimo/cognition/charter.py +398 -0
  86. apsimo/cognition/drive_governance.py +3530 -0
  87. apsimo/cognition/evidence_pipeline.py +1627 -0
  88. apsimo/cognition/external_events.py +932 -0
  89. apsimo/cognition/goal_spine.py +3488 -0
  90. apsimo/cognition/introspection.py +214 -0
  91. apsimo/cognition/prompt.py +150 -0
  92. apsimo/cognition/runtime.py +108 -0
  93. apsimo/cognition/trigger.py +154 -0
  94. apsimo/commitments/__init__.py +18 -0
  95. apsimo/commitments/local_work.py +355 -0
  96. apsimo/commitments/store.py +1052 -0
  97. apsimo/commitments/work.py +91 -0
  98. apsimo/compat.py +53 -0
  99. apsimo/compression/__init__.py +467 -0
  100. apsimo/connectors/__init__.py +21 -0
  101. apsimo/connectors/base.py +152 -0
  102. apsimo/connectors/caldav_calendar.py +125 -0
  103. apsimo/connectors/fs_documents.py +85 -0
  104. apsimo/connectors/imap_email.py +138 -0
  105. apsimo/connectors/manager.py +218 -0
  106. apsimo/connectors/webhook_pull.py +88 -0
  107. apsimo/contacts/__init__.py +33 -0
  108. apsimo/contacts/comms.py +357 -0
  109. apsimo/contacts/config.py +79 -0
  110. apsimo/contacts/exporters/__init__.py +1 -0
  111. apsimo/contacts/exporters/vcard.py +71 -0
  112. apsimo/contacts/identity_links.py +251 -0
  113. apsimo/contacts/importer.py +280 -0
  114. apsimo/contacts/importers/__init__.py +1 -0
  115. apsimo/contacts/importers/batch.py +43 -0
  116. apsimo/contacts/importers/macos_contacts.py +101 -0
  117. apsimo/contacts/migrations/001_contacts_schema.sql +141 -0
  118. apsimo/contacts/migrations/002_trust_scopes.sql +36 -0
  119. apsimo/contacts/migrations/003_open_gateway_enum.sql +32 -0
  120. apsimo/contacts/migrations/004_contact_provision_operations.sql +18 -0
  121. apsimo/contacts/migrations/005_identity_links.sql +27 -0
  122. apsimo/contacts/models.py +308 -0
  123. apsimo/contacts/scoring.py +16 -0
  124. apsimo/contacts/store.py +1623 -0
  125. apsimo/contacts/transport_ingress.py +252 -0
  126. apsimo/contacts/world_bridge.py +314 -0
  127. apsimo/contextgate/__init__.py +69 -0
  128. apsimo/contextgate/chunker.py +169 -0
  129. apsimo/contextgate/estimate.py +54 -0
  130. apsimo/contextgate/gate.py +313 -0
  131. apsimo/contextgate/retrieve.py +115 -0
  132. apsimo/delivery/__init__.py +16 -0
  133. apsimo/delivery/bridge.py +1260 -0
  134. apsimo/delivery/channels.py +526 -0
  135. apsimo/delivery/classification.py +50 -0
  136. apsimo/delivery/rate_limiter.py +268 -0
  137. apsimo/delivery/reachout_policy.py +206 -0
  138. apsimo/directed/__init__.py +22 -0
  139. apsimo/directed/audit.py +167 -0
  140. apsimo/directed/intake.py +95 -0
  141. apsimo/directed/models.py +191 -0
  142. apsimo/directed/service.py +509 -0
  143. apsimo/directives/__init__.py +25 -0
  144. apsimo/directives/evidence.py +87 -0
  145. apsimo/directives/extractor.py +188 -0
  146. apsimo/directives/guard.py +364 -0
  147. apsimo/directives/models.py +206 -0
  148. apsimo/directives/service.py +372 -0
  149. apsimo/directives/store.py +167 -0
  150. apsimo/doctor.py +2173 -0
  151. apsimo/environment.py +43 -0
  152. apsimo/events/__init__.py +33 -0
  153. apsimo/events/broadcaster.py +98 -0
  154. apsimo/events/bus.py +217 -0
  155. apsimo/events/journal.py +863 -0
  156. apsimo/events/stream.py +131 -0
  157. apsimo/events/types.py +150 -0
  158. apsimo/execution_results.py +357 -0
  159. apsimo/feedback/__init__.py +5 -0
  160. apsimo/feedback/store.py +76 -0
  161. apsimo/feeds/__init__.py +19 -0
  162. apsimo/feeds/cli.py +84 -0
  163. apsimo/feeds/engine.py +437 -0
  164. apsimo/feeds/example-feed.yaml +77 -0
  165. apsimo/feeds/hermes_cron.py +126 -0
  166. apsimo/feeds/manager.py +235 -0
  167. apsimo/feeds/spec.py +250 -0
  168. apsimo/feeds/template.py +202 -0
  169. apsimo/gate/__init__.py +18 -0
  170. apsimo/gate/audit.py +61 -0
  171. apsimo/gate/communication_policy.py +166 -0
  172. apsimo/gate/config.py +72 -0
  173. apsimo/gate/context_provenance.py +170 -0
  174. apsimo/gate/env_risk.py +226 -0
  175. apsimo/gate/guard_audit.py +353 -0
  176. apsimo/gate/layers/__init__.py +1 -0
  177. apsimo/gate/layers/base.py +15 -0
  178. apsimo/gate/layers/l1_recipient.py +66 -0
  179. apsimo/gate/layers/l2_pii.py +134 -0
  180. apsimo/gate/layers/l3_cross_context.py +50 -0
  181. apsimo/gate/layers/l4_trust_tier.py +78 -0
  182. apsimo/gate/layers/l5_injection.py +199 -0
  183. apsimo/gate/layers/l6_review.py +86 -0
  184. apsimo/gate/layers/l7_delay.py +100 -0
  185. apsimo/gate/layers/tom2_epistemic.py +185 -0
  186. apsimo/gate/models.py +64 -0
  187. apsimo/gate/pending_dispatch.py +5 -0
  188. apsimo/gate/pipeline.py +206 -0
  189. apsimo/gate/rejection.py +259 -0
  190. apsimo/gate/response_guard.py +700 -0
  191. apsimo/gate/rulesets/injection_v1.yaml +51 -0
  192. apsimo/gate/surface_policy.py +189 -0
  193. apsimo/gate/taint.py +226 -0
  194. apsimo/genesis.json +9 -0
  195. apsimo/goals/__init__.py +100 -0
  196. apsimo/goals/config.py +38 -0
  197. apsimo/goals/decomposer.py +421 -0
  198. apsimo/goals/engine.py +617 -0
  199. apsimo/goals/inference.py +354 -0
  200. apsimo/goals/models.py +302 -0
  201. apsimo/goals/priority.py +270 -0
  202. apsimo/goals/queue_bridge.py +149 -0
  203. apsimo/goals/replan.py +450 -0
  204. apsimo/goals/schema.sql +89 -0
  205. apsimo/goals/store.py +692 -0
  206. apsimo/governed_actions.py +1708 -0
  207. apsimo/harness_integration/__init__.py +45 -0
  208. apsimo/harness_integration/context.py +41 -0
  209. apsimo/harness_integration/skills.py +231 -0
  210. apsimo/identity/__init__.py +26 -0
  211. apsimo/identity/participants.py +181 -0
  212. apsimo/identity/resolver.py +329 -0
  213. apsimo/identity_bootstrap/__init__.py +5 -0
  214. apsimo/identity_bootstrap/builder.py +208 -0
  215. apsimo/identity_bootstrap/corpus.py +443 -0
  216. apsimo/identity_bootstrap/models.py +54 -0
  217. apsimo/identity_bootstrap/runner.py +353 -0
  218. apsimo/identity_bootstrap/seeders/__init__.py +25 -0
  219. apsimo/identity_bootstrap/seeders/briefings.py +109 -0
  220. apsimo/identity_bootstrap/seeders/chain.py +57 -0
  221. apsimo/identity_bootstrap/seeders/goals.py +128 -0
  222. apsimo/identity_bootstrap/seeders/memory.py +191 -0
  223. apsimo/identity_bootstrap/seeders/neo4j_cognition.py +79 -0
  224. apsimo/identity_bootstrap/seeders/relationship.py +152 -0
  225. apsimo/identity_bootstrap/seeders/sessions.py +67 -0
  226. apsimo/identity_bootstrap/seeders/skills.py +92 -0
  227. apsimo/identity_bootstrap/seeders/task_queue.py +72 -0
  228. apsimo/identity_bootstrap/seeders/world_model.py +143 -0
  229. apsimo/identity_bootstrap/self_query.py +92 -0
  230. apsimo/identity_bootstrap/self_reflection.py +155 -0
  231. apsimo/identity_bootstrap/skill.py +37 -0
  232. apsimo/identity_bootstrap/verifier.py +436 -0
  233. apsimo/initiatives/__init__.py +20 -0
  234. apsimo/initiatives/action_registry.py +454 -0
  235. apsimo/initiatives/approval_authority.py +2105 -0
  236. apsimo/initiatives/approval_policy.py +123 -0
  237. apsimo/initiatives/assignment.py +263 -0
  238. apsimo/initiatives/backup_evidence.py +100 -0
  239. apsimo/initiatives/context_freshness.py +103 -0
  240. apsimo/initiatives/models.py +318 -0
  241. apsimo/initiatives/native_work.py +270 -0
  242. apsimo/initiatives/standing_approvals.py +232 -0
  243. apsimo/initiatives/store.py +1081 -0
  244. apsimo/initiatives/temporal_followup.py +410 -0
  245. apsimo/intelligence/__init__.py +1 -0
  246. apsimo/intelligence/cognition/__init__.py +24 -0
  247. apsimo/intelligence/cognition/gap_detector.py +148 -0
  248. apsimo/intelligence/cognition/metalearner.py +547 -0
  249. apsimo/intelligence/cognition/metrics_collector.py +217 -0
  250. apsimo/intelligence/cognition/performance_index.py +299 -0
  251. apsimo/intelligence/cognition/registry.py +192 -0
  252. apsimo/intelligence/cognition/strategy_adjuster.py +222 -0
  253. apsimo/intelligence/cognition/types.py +16 -0
  254. apsimo/intelligence/components/__init__.py +66 -0
  255. apsimo/intelligence/components/anomaly_detector.py +413 -0
  256. apsimo/intelligence/components/initiative_engine.py +2643 -0
  257. apsimo/intelligence/components/preference_learner.py +521 -0
  258. apsimo/intelligence/components/research_orchestrator.py +358 -0
  259. apsimo/intelligence/components/self_directed_thinker.py +221 -0
  260. apsimo/intelligence/components/self_reflector.py +252 -0
  261. apsimo/intelligence/components/session_continuity.py +154 -0
  262. apsimo/intelligence/components/task_planner.py +320 -0
  263. apsimo/intelligence/components/tool_learner.py +217 -0
  264. apsimo/intelligence/graph/__init__.py +79 -0
  265. apsimo/intelligence/graph/client.py +2483 -0
  266. apsimo/intelligence/graph/consolidator.py +405 -0
  267. apsimo/intelligence/graph/distiller.py +312 -0
  268. apsimo/intelligence/graph/migrations.py +129 -0
  269. apsimo/intelligence/graph/queries.py +248 -0
  270. apsimo/intelligence/graph/recall.py +281 -0
  271. apsimo/intelligence/graph/reconciler.py +144 -0
  272. apsimo/intelligence/graph/schema.py +337 -0
  273. apsimo/intelligence/graph/selection.py +252 -0
  274. apsimo/intelligence/learning/__init__.py +17 -0
  275. apsimo/intelligence/learning/continuous_learner.py +245 -0
  276. apsimo/intelligence/learning/feedback_store.py +321 -0
  277. apsimo/intelligence/mind_model/__init__.py +1 -0
  278. apsimo/intelligence/mind_model/graph_baseline.py +136 -0
  279. apsimo/intelligence/mind_model/signal_collector.py +361 -0
  280. apsimo/intelligence/relationships/__init__.py +11 -0
  281. apsimo/intelligence/relationships/profiler.py +389 -0
  282. apsimo/intelligence/relationships/scorer.py +560 -0
  283. apsimo/intelligence/relationships/signal_floor.py +66 -0
  284. apsimo/intelligence/relationships/trust_tiers.py +300 -0
  285. apsimo/intelligence/synthesis/__init__.py +40 -0
  286. apsimo/intelligence/synthesis/connection_discoverer.py +379 -0
  287. apsimo/intelligence/synthesis/cross_domain_analyzer.py +287 -0
  288. apsimo/intelligence/synthesis/insight_deliverer.py +171 -0
  289. apsimo/intelligence/synthesis/insight_store.py +79 -0
  290. apsimo/intelligence/synthesis/insight_validator.py +183 -0
  291. apsimo/intelligence/synthesis/novelty_scorer.py +267 -0
  292. apsimo/intelligence/turn_middleware/__init__.py +15 -0
  293. apsimo/intelligence/turn_middleware/memory_sync.py +119 -0
  294. apsimo/mcp/__init__.py +41 -0
  295. apsimo/mcp/__main__.py +6 -0
  296. apsimo/mcp/config.py +287 -0
  297. apsimo/mcp/server.py +501 -0
  298. apsimo/migrations.py +187 -0
  299. apsimo/mining/__init__.py +27 -0
  300. apsimo/mining/corpus.py +239 -0
  301. apsimo/mining/escalations.py +289 -0
  302. apsimo/mining/models.py +169 -0
  303. apsimo/mining/store.py +210 -0
  304. apsimo/models/__init__.py +30 -0
  305. apsimo/models/memory.py +80 -0
  306. apsimo/models/mesh.py +72 -0
  307. apsimo/models/person.py +104 -0
  308. apsimo/models/signal.py +108 -0
  309. apsimo/observations/__init__.py +15 -0
  310. apsimo/observations/store.py +277 -0
  311. apsimo/patterns/__init__.py +6 -0
  312. apsimo/patterns/extract.py +187 -0
  313. apsimo/patterns/store.py +227 -0
  314. apsimo/persona/__init__.py +1 -0
  315. apsimo/persona/engine.py +611 -0
  316. apsimo/persona/manifest.py +140 -0
  317. apsimo/projects/__init__.py +28 -0
  318. apsimo/projects/engine.py +1681 -0
  319. apsimo/projects/event_outbox.py +188 -0
  320. apsimo/projects/models.py +216 -0
  321. apsimo/projects/planner.py +181 -0
  322. apsimo/projects/store.py +1446 -0
  323. apsimo/proposals/__init__.py +12 -0
  324. apsimo/proposals/engine.py +114 -0
  325. apsimo/proposals/models.py +207 -0
  326. apsimo/qualification/__init__.py +1 -0
  327. apsimo/qualification/cases.py +75 -0
  328. apsimo/qualification/cli.py +51 -0
  329. apsimo/qualification/memory_cases.py +209 -0
  330. apsimo/qualification/records.py +92 -0
  331. apsimo/qualification/report.py +87 -0
  332. apsimo/qualification/runner.py +311 -0
  333. apsimo/qualification/structured_cases.py +131 -0
  334. apsimo/reasoning/__init__.py +13 -0
  335. apsimo/reasoning/executor.py +506 -0
  336. apsimo/reasoning/loop.py +373 -0
  337. apsimo/reasoning/native_tools/__init__.py +16 -0
  338. apsimo/reasoning/native_tools/calculate.py +141 -0
  339. apsimo/reasoning/native_tools/file_ops.py +150 -0
  340. apsimo/reasoning/native_tools/web_search.py +49 -0
  341. apsimo/reasoning/tool_policy.py +182 -0
  342. apsimo/redact/__init__.py +176 -0
  343. apsimo/repos/__init__.py +5 -0
  344. apsimo/repos/mirrors.py +204 -0
  345. apsimo/research/__init__.py +41 -0
  346. apsimo/research/artifact.py +482 -0
  347. apsimo/research/gatherer.py +387 -0
  348. apsimo/research/pipeline.py +513 -0
  349. apsimo/research/search/__init__.py +7 -0
  350. apsimo/research/search/base.py +41 -0
  351. apsimo/research/search/brave.py +59 -0
  352. apsimo/research/search/cache.py +51 -0
  353. apsimo/research/search/duckduckgo.py +103 -0
  354. apsimo/research/search/orchestrator.py +119 -0
  355. apsimo/research/search/serpapi.py +59 -0
  356. apsimo/research/search/tavily.py +59 -0
  357. apsimo/research/synthesizer.py +309 -0
  358. apsimo/router/__init__.py +30 -0
  359. apsimo/router/complexity_scorer.py +148 -0
  360. apsimo/router/endpoints.py +153 -0
  361. apsimo/router/fallback.py +58 -0
  362. apsimo/router/functions.py +243 -0
  363. apsimo/router/native_policy.py +52 -0
  364. apsimo/router/router.py +762 -0
  365. apsimo/router/self_learning.py +174 -0
  366. apsimo/router/tiers.py +677 -0
  367. apsimo/sandbox/__init__.py +21 -0
  368. apsimo/sandbox/backend.py +195 -0
  369. apsimo/sandbox/manager.py +173 -0
  370. apsimo/scope_bounds.py +7 -0
  371. apsimo/secrets/__init__.py +6 -0
  372. apsimo/secrets/backends/__init__.py +8 -0
  373. apsimo/secrets/backends/base.py +42 -0
  374. apsimo/secrets/backends/env.py +110 -0
  375. apsimo/secrets/backends/keyring.py +72 -0
  376. apsimo/secrets/backends/onepassword.py +232 -0
  377. apsimo/secrets/cli.py +191 -0
  378. apsimo/secrets/manager.py +160 -0
  379. apsimo/secrets/migration.py +101 -0
  380. apsimo/secrets/types.py +98 -0
  381. apsimo/seed.py +41 -0
  382. apsimo/self_model/__init__.py +37 -0
  383. apsimo/self_model/appraisals.py +673 -0
  384. apsimo/self_model/benchmark.py +1314 -0
  385. apsimo/self_model/brief.py +40 -0
  386. apsimo/self_model/event_concerns.py +1128 -0
  387. apsimo/self_model/execution_forecasts.py +353 -0
  388. apsimo/self_model/expectations.py +1595 -0
  389. apsimo/self_model/experiments.py +1150 -0
  390. apsimo/self_model/journal.py +148 -0
  391. apsimo/self_model/judgments.py +705 -0
  392. apsimo/self_model/native_outcomes.py +55 -0
  393. apsimo/self_model/params.py +220 -0
  394. apsimo/self_model/perspective.py +246 -0
  395. apsimo/self_model/reconcile.py +183 -0
  396. apsimo/self_model/reply_forecasts.py +381 -0
  397. apsimo/self_model/runtime_forecasts.py +296 -0
  398. apsimo/self_model/runtime_models.py +67 -0
  399. apsimo/self_model/settlement.py +207 -0
  400. apsimo/self_model/situation.py +1731 -0
  401. apsimo/self_model/store.py +883 -0
  402. apsimo/self_model/supervised.py +137 -0
  403. apsimo/self_model/thinker.py +99 -0
  404. apsimo/self_model/trust.py +388 -0
  405. apsimo/self_model/workspace.py +2388 -0
  406. apsimo/server.py +4197 -0
  407. apsimo/services/__init__.py +1 -0
  408. apsimo/services/agent_bridge.py +474 -0
  409. apsimo/services/initiative_executor.py +914 -0
  410. apsimo/services/instance.py +297 -0
  411. apsimo/sessions/__init__.py +22 -0
  412. apsimo/sessions/config.py +13 -0
  413. apsimo/sessions/context_loader.py +88 -0
  414. apsimo/sessions/federation_session.py +75 -0
  415. apsimo/sessions/isolated_session.py +98 -0
  416. apsimo/sessions/reports.py +84 -0
  417. apsimo/sessions/store.py +148 -0
  418. apsimo/setup.py +2818 -0
  419. apsimo/setup_hermes.py +879 -0
  420. apsimo/setup_local_work.py +218 -0
  421. apsimo/setup_native_goals.py +134 -0
  422. apsimo/setup_native_reviews.py +115 -0
  423. apsimo/skills/__init__.py +10 -0
  424. apsimo/skills/base.py +108 -0
  425. apsimo/skills/budget.py +28 -0
  426. apsimo/skills/executor.py +493 -0
  427. apsimo/skills/executors/__init__.py +1 -0
  428. apsimo/skills/executors/behavioral_correction.py +75 -0
  429. apsimo/skills/executors/capability_gap.py +38 -0
  430. apsimo/skills/executors/data_quality.py +163 -0
  431. apsimo/skills/executors/knowledge_acquisition.py +41 -0
  432. apsimo/skills/executors/operational_hygiene.py +185 -0
  433. apsimo/skills/executors/subsystem_health.py +169 -0
  434. apsimo/skills/hermes_export.py +431 -0
  435. apsimo/skills/index.py +123 -0
  436. apsimo/skills/learning/__init__.py +21 -0
  437. apsimo/skills/learning/novelty_detector.py +206 -0
  438. apsimo/skills/learning/pattern_extractor.py +199 -0
  439. apsimo/skills/learning/triggers.py +159 -0
  440. apsimo/skills/loader.py +246 -0
  441. apsimo/skills/migrations/002_progressive_loading.sql +6 -0
  442. apsimo/skills/migrations/backfill_triggers.py +20 -0
  443. apsimo/skills/models.py +202 -0
  444. apsimo/skills/packager.py +128 -0
  445. apsimo/skills/protocols.py +70 -0
  446. apsimo/skills/registry.py +191 -0
  447. apsimo/skills/runtime.py +58 -0
  448. apsimo/skills/sandbox_runner.py +229 -0
  449. apsimo/skills/scheduler.py +129 -0
  450. apsimo/skills/schema.py +79 -0
  451. apsimo/skills/security/__init__.py +12 -0
  452. apsimo/skills/security/guards.py +53 -0
  453. apsimo/skills/security/scanner.py +223 -0
  454. apsimo/skills_memory/__init__.py +26 -0
  455. apsimo/skills_memory/distill.py +159 -0
  456. apsimo/skills_memory/models.py +85 -0
  457. apsimo/skills_memory/retrieve.py +62 -0
  458. apsimo/skills_memory/store.py +172 -0
  459. apsimo/surprise/__init__.py +6 -0
  460. apsimo/surprise/accumulation.py +57 -0
  461. apsimo/surprise/scorer.py +102 -0
  462. apsimo/surprise/store.py +203 -0
  463. apsimo/task_queue/__init__.py +69 -0
  464. apsimo/task_queue/action_receipts.py +148 -0
  465. apsimo/task_queue/approval_relay_canary.py +108 -0
  466. apsimo/task_queue/config.py +85 -0
  467. apsimo/task_queue/contract.py +361 -0
  468. apsimo/task_queue/events.py +130 -0
  469. apsimo/task_queue/governor.py +1031 -0
  470. apsimo/task_queue/handlers/__init__.py +16 -0
  471. apsimo/task_queue/handlers/base.py +37 -0
  472. apsimo/task_queue/handlers/inference.py +640 -0
  473. apsimo/task_queue/handlers/monitoring.py +116 -0
  474. apsimo/task_queue/handlers/registry.py +75 -0
  475. apsimo/task_queue/handlers/subtask_handler.py +173 -0
  476. apsimo/task_queue/handlers/system_maintenance.py +147 -0
  477. apsimo/task_queue/mesh_integration.py +111 -0
  478. apsimo/task_queue/models.py +317 -0
  479. apsimo/task_queue/queue_manager.py +8286 -0
  480. apsimo/task_queue/routing.py +287 -0
  481. apsimo/task_queue/scheduler.py +252 -0
  482. apsimo/task_queue/schema.sql +197 -0
  483. apsimo/task_queue/work_control.py +342 -0
  484. apsimo/task_queue/worker.py +993 -0
  485. apsimo/telemetry.py +145 -0
  486. apsimo/tom/__init__.py +6 -0
  487. apsimo/tom/affect.py +387 -0
  488. apsimo/tom/approvals.py +171 -0
  489. apsimo/tom/arcs.py +896 -0
  490. apsimo/tom/asymmetry.py +131 -0
  491. apsimo/tom/eligibility.py +248 -0
  492. apsimo/tom/engagement.py +214 -0
  493. apsimo/tom/exposure.py +214 -0
  494. apsimo/tom/extractor.py +306 -0
  495. apsimo/tom/fact_adapters.py +144 -0
  496. apsimo/tom/facts.py +326 -0
  497. apsimo/tom/integration.py +592 -0
  498. apsimo/tom/leveled.py +118 -0
  499. apsimo/tom/levels.py +247 -0
  500. apsimo/tom/recipient_audit.py +995 -0
  501. apsimo/tom/recipient_simulator.py +593 -0
  502. apsimo/tom/source_lineage.py +93 -0
  503. apsimo/tom/tom2.py +277 -0
  504. apsimo/tom/visibility.py +559 -0
  505. apsimo/tom/visibility_store.py +414 -0
  506. apsimo/tools/__init__.py +0 -0
  507. apsimo/tools/definitions.py +740 -0
  508. apsimo/tools/handlers.py +943 -0
  509. apsimo/toolsmith/__init__.py +26 -0
  510. apsimo/toolsmith/authority.py +166 -0
  511. apsimo/toolsmith/engine.py +559 -0
  512. apsimo/toolsmith/integrity.py +100 -0
  513. apsimo/toolsmith/miner.py +145 -0
  514. apsimo/toolsmith/policy.py +110 -0
  515. apsimo/toolsmith/registry.py +635 -0
  516. apsimo/turns/__init__.py +17 -0
  517. apsimo/turns/audio.py +134 -0
  518. apsimo/turns/documents.py +235 -0
  519. apsimo/turns/executions.py +486 -0
  520. apsimo/turns/hermes_history.py +245 -0
  521. apsimo/turns/hermes_kanban.py +268 -0
  522. apsimo/turns/hermes_work.py +96 -0
  523. apsimo/turns/idempotency.py +752 -0
  524. apsimo/turns/local_work.py +115 -0
  525. apsimo/turns/media.py +581 -0
  526. apsimo/turns/reported_workers.py +196 -0
  527. apsimo/turns/source_annotations.py +283 -0
  528. apsimo/turns/source_attribution.py +154 -0
  529. apsimo/turns/source_read.py +351 -0
  530. apsimo/turns/source_vectors.py +263 -0
  531. apsimo/turns/video.py +210 -0
  532. apsimo/util/autonomy_preset.py +220 -0
  533. apsimo/util/instance.py +92 -0
  534. apsimo/util/model_output.py +25 -0
  535. apsimo/util/quiet_hours.py +27 -0
  536. apsimo/util/session_safety.py +37 -0
  537. apsimo/util/temporal.py +343 -0
  538. apsimo/vector/__init__.py +75 -0
  539. apsimo/vector/backfill.py +171 -0
  540. apsimo/vector/caption.py +114 -0
  541. apsimo/vector/collections.py +51 -0
  542. apsimo/vector/config.py +102 -0
  543. apsimo/vector/embedder.py +670 -0
  544. apsimo/vector/image_preprocess.py +406 -0
  545. apsimo/vector/image_store.py +296 -0
  546. apsimo/vector/indexes.py +162 -0
  547. apsimo/vector/migrate.py +334 -0
  548. apsimo/vector/multimodal_provider.py +417 -0
  549. apsimo/vector/multimodal_types.py +87 -0
  550. apsimo/vector/openai_provider.py +119 -0
  551. apsimo/vector/query.py +49 -0
  552. apsimo/vector/reranker.py +565 -0
  553. apsimo/vector/safety_image.py +159 -0
  554. apsimo/vector/scanner.py +197 -0
  555. apsimo/vector/setup.py +289 -0
  556. apsimo/vector/store.py +533 -0
  557. apsimo/vector/tiers.py +263 -0
  558. apsimo/work_orders.py +925 -0
  559. apsimo/workers/__init__.py +21 -0
  560. apsimo/workers/agent_bridge.py +640 -0
  561. apsimo/workers/colony_worker.py +382 -0
  562. apsimo/workers/queue_worker.py +441 -0
  563. apsimo/workers/skills_sync.py +152 -0
  564. apsimo/world_model/__init__.py +71 -0
  565. apsimo/world_model/causal_maintenance.py +131 -0
  566. apsimo/world_model/causal_policy.py +43 -0
  567. apsimo/world_model/causal_query.py +125 -0
  568. apsimo/world_model/confidence.py +54 -0
  569. apsimo/world_model/config.py +64 -0
  570. apsimo/world_model/constants.py +97 -0
  571. apsimo/world_model/entities.py +145 -0
  572. apsimo/world_model/expectation_resolvers.py +177 -0
  573. apsimo/world_model/extraction/__init__.py +7 -0
  574. apsimo/world_model/extraction/base.py +62 -0
  575. apsimo/world_model/extraction/conversation_extractor.py +262 -0
  576. apsimo/world_model/extraction/detector.py +74 -0
  577. apsimo/world_model/extraction/document_extractor.py +78 -0
  578. apsimo/world_model/extraction/formats/__init__.py +24 -0
  579. apsimo/world_model/extraction/formats/csv_fmt.py +68 -0
  580. apsimo/world_model/extraction/formats/html_fmt.py +72 -0
  581. apsimo/world_model/extraction/formats/json_fmt.py +68 -0
  582. apsimo/world_model/extraction/formats/pdf.py +43 -0
  583. apsimo/world_model/extraction/formats/text.py +27 -0
  584. apsimo/world_model/extraction/llm_extractor.py +164 -0
  585. apsimo/world_model/extraction/pipeline.py +73 -0
  586. apsimo/world_model/integrations/__init__.py +5 -0
  587. apsimo/world_model/integrations/mind_model_bridge.py +115 -0
  588. apsimo/world_model/integrations/social_intel_bridge.py +120 -0
  589. apsimo/world_model/jobs/__init__.py +4 -0
  590. apsimo/world_model/jobs/extraction_job.py +168 -0
  591. apsimo/world_model/llm_extract.py +572 -0
  592. apsimo/world_model/neo4j/__init__.py +5 -0
  593. apsimo/world_model/neo4j/backend.py +654 -0
  594. apsimo/world_model/observations.py +155 -0
  595. apsimo/world_model/populator.py +307 -0
  596. apsimo/world_model/postgres/__init__.py +1 -0
  597. apsimo/world_model/postgres/backend.py +683 -0
  598. apsimo/world_model/relationships.py +25 -0
  599. apsimo/world_model/resolution/__init__.py +13 -0
  600. apsimo/world_model/resolution/entity_resolver.py +232 -0
  601. apsimo/world_model/resolution/merge_audit.py +16 -0
  602. apsimo/world_model/resolution/merge_workflow.py +117 -0
  603. apsimo/world_model/source_reports.py +121 -0
  604. apsimo/world_model/sqlite/__init__.py +4 -0
  605. apsimo/world_model/sqlite/backend.py +855 -0
  606. apsimo/world_model/sqlite/schema.sql +132 -0
  607. apsimo/world_model/store.py +545 -0
  608. apsimo-1.3.0.dist-info/METADATA +78 -0
  609. apsimo-1.3.0.dist-info/RECORD +614 -0
  610. apsimo-1.3.0.dist-info/WHEEL +5 -0
  611. apsimo-1.3.0.dist-info/entry_points.txt +11 -0
  612. apsimo-1.3.0.dist-info/licenses/LICENSE +21 -0
  613. apsimo-1.3.0.dist-info/top_level.txt +2 -0
  614. colony_sidecar/__init__.py +4 -0
apsimo/goals/replan.py ADDED
@@ -0,0 +1,450 @@
1
+ """ReplanEngine — adaptive replanning with failure strategies.
2
+
3
+ Classifies failures and selects the appropriate recovery strategy:
4
+ TRANSIENT → RETRY (up to max_retries)
5
+ CAPABILITY → SUBSTITUTE or ESCALATE
6
+ DEPENDENCY → PARTIAL_REPLAN
7
+ SCOPE_CHANGE → FULL_REPLAN (user confirmation if replan_count >= 2)
8
+ EXTERNAL → RETRY with backoff, then SUBSTITUTE
9
+ UNRECOVERABLE → ESCALATE
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import logging
15
+ from dataclasses import dataclass, field
16
+ from enum import Enum
17
+ from typing import Any, Dict, List, Optional
18
+
19
+ from apsimo.goals.models import (
20
+ Goal,
21
+ GoalDAG,
22
+ GoalPriority,
23
+ GoalStatus,
24
+ Subtask,
25
+ SubtaskStatus,
26
+ )
27
+
28
+ logger = logging.getLogger(__name__)
29
+
30
+ MAX_REPLANS_DEFAULT = 5
31
+
32
+
33
+ class FailureClass(str, Enum):
34
+ """Classification of subtask failure for replan strategy selection."""
35
+ TRANSIENT = "transient" # Temporary failure; retry expected to work
36
+ CAPABILITY = "capability" # Required capability not available
37
+ DEPENDENCY = "dependency" # Upstream subtask produced bad output
38
+ SCOPE_CHANGE = "scope_change" # Goal requirements changed mid-execution
39
+ EXTERNAL = "external" # External service, API, or resource failure
40
+ UNRECOVERABLE = "unrecoverable" # No recovery path exists
41
+
42
+
43
+ class ReplanStrategy(str, Enum):
44
+ """Available strategies for recovering from subtask failures."""
45
+ RETRY = "retry" # Re-queue the same subtask
46
+ SUBSTITUTE = "substitute" # Replace with an alternative subtask
47
+ SKIP = "skip" # Mark subtask as SKIPPED; continue
48
+ PARTIAL_REPLAN = "partial_replan" # Rebuild the failed subtask's subtree
49
+ FULL_REPLAN = "full_replan" # Rebuild the entire DAG
50
+ ESCALATE = "escalate" # Surface to user for manual intervention
51
+
52
+
53
+ @dataclass
54
+ class FailureAnalysis:
55
+ """Result of classifying a subtask failure."""
56
+ subtask_id: str
57
+ failure_class: FailureClass
58
+ error_message: str
59
+ retry_viable: bool
60
+ affected_subtasks: List[str] # Other subtasks that may be invalidated
61
+ suggested_strategy: str
62
+
63
+
64
+ @dataclass
65
+ class ReplanResult:
66
+ """Describes the proposed changes from a replan operation."""
67
+ goal_id: str
68
+ strategy: ReplanStrategy
69
+ requires_user_approval: bool
70
+ new_dag: Optional[GoalDAG] # Set for FULL_REPLAN / PARTIAL_REPLAN
71
+ retried_subtask_ids: List[str] = field(default_factory=list)
72
+ skipped_subtask_ids: List[str] = field(default_factory=list)
73
+ substituted_subtask_ids: List[str] = field(default_factory=list)
74
+ escalation_message: Optional[str] = None
75
+ dag_diff: Optional[str] = None # Human-readable diff description
76
+ estimated_completion_delta_hours: float = 0.0
77
+
78
+
79
+ # ── Failure classification heuristics ─────────────────────────────────────────
80
+
81
+ _TRANSIENT_PATTERNS = [
82
+ "timeout", "timed out", "connection reset", "temporary",
83
+ "retry", "rate limit", "too many requests", "503", "504",
84
+ ]
85
+ _CAPABILITY_PATTERNS = [
86
+ "capability", "no worker", "no node", "unsatisfied requirement",
87
+ "capability not available", "hardware not available",
88
+ ]
89
+ _EXTERNAL_PATTERNS = [
90
+ "api error", "http error", "network error", "connection refused",
91
+ "dns", "certificate", "ssl", "404", "401", "403",
92
+ ]
93
+ _UNRECOVERABLE_PATTERNS = [
94
+ "permission denied", "out of disk", "oom", "memory error",
95
+ "corrupt", "unrecoverable", "fatal",
96
+ ]
97
+
98
+
99
+ def _classify_error(error_message: str) -> FailureClass:
100
+ """Heuristically classify an error message."""
101
+ msg = (error_message or "").lower()
102
+ if any(p in msg for p in _UNRECOVERABLE_PATTERNS):
103
+ return FailureClass.UNRECOVERABLE
104
+ if any(p in msg for p in _CAPABILITY_PATTERNS):
105
+ return FailureClass.CAPABILITY
106
+ # Check EXTERNAL before TRANSIENT: explicit API/network failures are external
107
+ if any(p in msg for p in _EXTERNAL_PATTERNS):
108
+ return FailureClass.EXTERNAL
109
+ if any(p in msg for p in _TRANSIENT_PATTERNS):
110
+ return FailureClass.TRANSIENT
111
+ # Default to TRANSIENT for unknown errors (optimistic)
112
+ return FailureClass.TRANSIENT
113
+
114
+
115
+ def _downstream_subtasks(dag: GoalDAG, failed_id: str) -> List[str]:
116
+ """Return all subtask IDs that transitively depend on failed_id."""
117
+ affected: List[str] = []
118
+ visited: set = set()
119
+
120
+ def dfs(node_id: str) -> None:
121
+ for sid, s in dag.subtasks.items():
122
+ if node_id in s.depends_on and sid not in visited:
123
+ visited.add(sid)
124
+ affected.append(sid)
125
+ dfs(sid)
126
+
127
+ dfs(failed_id)
128
+ return affected
129
+
130
+
131
+ class ReplanEngine:
132
+ """Generate recovery plans for goal execution failures."""
133
+
134
+ def __init__(self, max_replans: int = MAX_REPLANS_DEFAULT) -> None:
135
+ self.max_replans = max_replans
136
+
137
+ def analyze_failure(
138
+ self,
139
+ goal: Goal,
140
+ dag: GoalDAG,
141
+ failed_subtask: Subtask,
142
+ ) -> FailureAnalysis:
143
+ """Classify the failure and identify affected downstream subtasks."""
144
+ error = failed_subtask.error or ""
145
+ failure_class = _classify_error(error)
146
+
147
+ # Override: if max retries exhausted, push toward non-retry strategy
148
+ retry_viable = (
149
+ failure_class in {FailureClass.TRANSIENT, FailureClass.EXTERNAL}
150
+ and failed_subtask.retry_count < failed_subtask.max_retries
151
+ )
152
+
153
+ affected = _downstream_subtasks(dag, failed_subtask.subtask_id)
154
+
155
+ # Select suggested strategy
156
+ if failure_class == FailureClass.UNRECOVERABLE:
157
+ strategy = ReplanStrategy.ESCALATE.value
158
+ elif failure_class == FailureClass.CAPABILITY:
159
+ strategy = ReplanStrategy.SUBSTITUTE.value
160
+ elif failure_class == FailureClass.DEPENDENCY:
161
+ strategy = ReplanStrategy.PARTIAL_REPLAN.value
162
+ elif failure_class == FailureClass.SCOPE_CHANGE:
163
+ strategy = ReplanStrategy.FULL_REPLAN.value
164
+ elif retry_viable:
165
+ strategy = ReplanStrategy.RETRY.value
166
+ else:
167
+ strategy = ReplanStrategy.SUBSTITUTE.value
168
+
169
+ return FailureAnalysis(
170
+ subtask_id=failed_subtask.subtask_id,
171
+ failure_class=failure_class,
172
+ error_message=error,
173
+ retry_viable=retry_viable,
174
+ affected_subtasks=affected,
175
+ suggested_strategy=strategy,
176
+ )
177
+
178
+ def generate_replan(
179
+ self,
180
+ goal: Goal,
181
+ dag: GoalDAG,
182
+ analysis: FailureAnalysis,
183
+ ) -> ReplanResult:
184
+ """Generate a recovery plan for the given failure.
185
+
186
+ Selection logic:
187
+ - TRANSIENT failures → RETRY (up to max_retries)
188
+ - CAPABILITY failures → SUBSTITUTE or ESCALATE
189
+ - DEPENDENCY failures → PARTIAL_REPLAN
190
+ - SCOPE_CHANGE → FULL_REPLAN (with user confirmation if replan_count >= 2)
191
+ - EXTERNAL failures → RETRY with backoff, then SUBSTITUTE
192
+ - UNRECOVERABLE → ESCALATE
193
+
194
+ Returns a ReplanResult describing the proposed changes.
195
+ """
196
+ # Circuit breaker
197
+ if goal.replan_count >= self.max_replans:
198
+ return ReplanResult(
199
+ goal_id=goal.goal_id,
200
+ strategy=ReplanStrategy.ESCALATE,
201
+ requires_user_approval=True,
202
+ new_dag=None,
203
+ escalation_message=(
204
+ f"Goal has been replanned {goal.replan_count} times "
205
+ f"(max {self.max_replans}). Manual intervention required."
206
+ ),
207
+ )
208
+
209
+ strategy = ReplanStrategy(analysis.suggested_strategy)
210
+ requires_approval = False
211
+
212
+ if strategy == ReplanStrategy.RETRY:
213
+ return self._plan_retry(goal, dag, analysis)
214
+
215
+ if strategy == ReplanStrategy.SUBSTITUTE:
216
+ return self._plan_substitute(goal, dag, analysis)
217
+
218
+ if strategy == ReplanStrategy.SKIP:
219
+ return self._plan_skip(goal, dag, analysis)
220
+
221
+ if strategy == ReplanStrategy.PARTIAL_REPLAN:
222
+ new_dag = self._partial_replan(goal, dag, analysis)
223
+ requires_approval = False
224
+ return ReplanResult(
225
+ goal_id=goal.goal_id,
226
+ strategy=strategy,
227
+ requires_user_approval=requires_approval,
228
+ new_dag=new_dag,
229
+ dag_diff=f"Rebuilt subtree rooted at {analysis.subtask_id}",
230
+ )
231
+
232
+ if strategy == ReplanStrategy.FULL_REPLAN:
233
+ # Require user approval after 2 replans
234
+ requires_approval = goal.replan_count >= 2
235
+ new_dag = self._full_replan(goal, dag)
236
+ return ReplanResult(
237
+ goal_id=goal.goal_id,
238
+ strategy=strategy,
239
+ requires_user_approval=requires_approval,
240
+ new_dag=new_dag,
241
+ dag_diff="Full DAG rebuild",
242
+ )
243
+
244
+ # ESCALATE
245
+ return ReplanResult(
246
+ goal_id=goal.goal_id,
247
+ strategy=ReplanStrategy.ESCALATE,
248
+ requires_user_approval=True,
249
+ new_dag=None,
250
+ escalation_message=analysis.error_message or "Subtask failure requires manual intervention.",
251
+ )
252
+
253
+ def apply_replan(
254
+ self,
255
+ goal: Goal,
256
+ dag: GoalDAG,
257
+ replan: ReplanResult,
258
+ ) -> GoalDAG:
259
+ """Apply the approved replan to produce a new (or updated) DAG version."""
260
+ if replan.strategy == ReplanStrategy.RETRY:
261
+ for sid in replan.retried_subtask_ids:
262
+ subtask = dag.subtasks.get(sid)
263
+ if subtask:
264
+ subtask.status = SubtaskStatus.PENDING
265
+ subtask.retry_count += 1
266
+ subtask.error = None
267
+ goal.replan_count += 1
268
+ return dag
269
+
270
+ if replan.strategy == ReplanStrategy.SKIP:
271
+ for sid in replan.skipped_subtask_ids:
272
+ subtask = dag.subtasks.get(sid)
273
+ if subtask:
274
+ subtask.status = SubtaskStatus.SKIPPED
275
+ goal.replan_count += 1
276
+ return dag
277
+
278
+ if replan.new_dag is not None:
279
+ replan.new_dag.version = dag.version + 1
280
+ goal.replan_count += 1
281
+ return replan.new_dag
282
+
283
+ goal.replan_count += 1
284
+ return dag
285
+
286
+ # ── Private helpers ────────────────────────────────────────────────────────
287
+
288
+ def _plan_retry(
289
+ self, goal: Goal, dag: GoalDAG, analysis: FailureAnalysis
290
+ ) -> ReplanResult:
291
+ subtask = dag.subtasks.get(analysis.subtask_id)
292
+ if subtask is None or subtask.retry_count >= subtask.max_retries:
293
+ # Escalate if retries exhausted
294
+ return ReplanResult(
295
+ goal_id=goal.goal_id,
296
+ strategy=ReplanStrategy.ESCALATE,
297
+ requires_user_approval=True,
298
+ new_dag=None,
299
+ escalation_message=f"Subtask {analysis.subtask_id} exhausted retries.",
300
+ )
301
+ return ReplanResult(
302
+ goal_id=goal.goal_id,
303
+ strategy=ReplanStrategy.RETRY,
304
+ requires_user_approval=False,
305
+ new_dag=None,
306
+ retried_subtask_ids=[analysis.subtask_id],
307
+ )
308
+
309
+ def _plan_substitute(
310
+ self, goal: Goal, dag: GoalDAG, analysis: FailureAnalysis
311
+ ) -> ReplanResult:
312
+ """Replace the failed subtask with a capability-agnostic fallback."""
313
+ subtask = dag.subtasks.get(analysis.subtask_id)
314
+ if subtask is None:
315
+ return ReplanResult(
316
+ goal_id=goal.goal_id,
317
+ strategy=ReplanStrategy.ESCALATE,
318
+ requires_user_approval=True,
319
+ new_dag=None,
320
+ escalation_message=f"Subtask {analysis.subtask_id} not found for substitution.",
321
+ )
322
+ # Reset capabilities and mark as pending
323
+ new_dag = GoalDAG(
324
+ goal_id=dag.goal_id,
325
+ subtasks=dict(dag.subtasks),
326
+ root_ids=list(dag.root_ids),
327
+ leaf_ids=list(dag.leaf_ids),
328
+ critical_path=list(dag.critical_path),
329
+ max_depth=dag.max_depth,
330
+ version=dag.version,
331
+ created_at=dag.created_at,
332
+ )
333
+ replacement = Subtask(
334
+ subtask_id=subtask.subtask_id,
335
+ goal_id=subtask.goal_id,
336
+ title=subtask.title + " [fallback]",
337
+ job_type="custom", # Capability-agnostic fallback
338
+ capabilities=[],
339
+ depends_on=list(subtask.depends_on),
340
+ estimated_hours=subtask.estimated_hours,
341
+ payload=subtask.payload,
342
+ max_retries=subtask.max_retries,
343
+ )
344
+ new_dag.subtasks[subtask.subtask_id] = replacement
345
+ return ReplanResult(
346
+ goal_id=goal.goal_id,
347
+ strategy=ReplanStrategy.SUBSTITUTE,
348
+ requires_user_approval=False,
349
+ new_dag=new_dag,
350
+ substituted_subtask_ids=[subtask.subtask_id],
351
+ dag_diff=f"Substituted {subtask.subtask_id} with capability-agnostic fallback",
352
+ )
353
+
354
+ def _plan_skip(
355
+ self, goal: Goal, dag: GoalDAG, analysis: FailureAnalysis
356
+ ) -> ReplanResult:
357
+ return ReplanResult(
358
+ goal_id=goal.goal_id,
359
+ strategy=ReplanStrategy.SKIP,
360
+ requires_user_approval=False,
361
+ new_dag=None,
362
+ skipped_subtask_ids=[analysis.subtask_id],
363
+ )
364
+
365
+ def _partial_replan(self, goal: Goal, dag: GoalDAG, analysis: FailureAnalysis) -> GoalDAG:
366
+ """Rebuild the failed subtask and its entire downstream subtree."""
367
+ import uuid as _uuid
368
+
369
+ affected = set(analysis.affected_subtasks + [analysis.subtask_id])
370
+ new_dag = GoalDAG(
371
+ goal_id=dag.goal_id,
372
+ subtasks={
373
+ sid: s for sid, s in dag.subtasks.items()
374
+ if sid not in affected
375
+ },
376
+ root_ids=list(dag.root_ids),
377
+ leaf_ids=list(dag.leaf_ids),
378
+ critical_path=list(dag.critical_path),
379
+ max_depth=dag.max_depth,
380
+ version=dag.version,
381
+ created_at=dag.created_at,
382
+ )
383
+
384
+ # Re-add the failed subtask as pending with incremented retry info
385
+ original = dag.subtasks.get(analysis.subtask_id)
386
+ if original:
387
+ new_subtask = Subtask(
388
+ subtask_id=original.subtask_id,
389
+ goal_id=original.goal_id,
390
+ title=original.title,
391
+ job_type=original.job_type,
392
+ capabilities=list(original.capabilities),
393
+ depends_on=list(original.depends_on),
394
+ estimated_hours=original.estimated_hours,
395
+ payload=dict(original.payload),
396
+ max_retries=original.max_retries,
397
+ retry_count=original.retry_count,
398
+ )
399
+ new_dag.subtasks[new_subtask.subtask_id] = new_subtask
400
+
401
+ # Re-add downstream subtasks as fresh pending
402
+ for sid in analysis.affected_subtasks:
403
+ original_s = dag.subtasks.get(sid)
404
+ if original_s:
405
+ fresh = Subtask(
406
+ subtask_id=original_s.subtask_id,
407
+ goal_id=original_s.goal_id,
408
+ title=original_s.title,
409
+ job_type=original_s.job_type,
410
+ capabilities=list(original_s.capabilities),
411
+ depends_on=list(original_s.depends_on),
412
+ estimated_hours=original_s.estimated_hours,
413
+ payload=dict(original_s.payload),
414
+ max_retries=original_s.max_retries,
415
+ )
416
+ new_dag.subtasks[fresh.subtask_id] = fresh
417
+
418
+ return new_dag
419
+
420
+ def _full_replan(self, goal: Goal, dag: GoalDAG) -> GoalDAG:
421
+ """Create a new GoalDAG, preserving completed subtasks."""
422
+ completed_ids = {
423
+ sid for sid, s in dag.subtasks.items()
424
+ if s.status in {SubtaskStatus.COMPLETED, SubtaskStatus.SKIPPED}
425
+ }
426
+ new_dag = GoalDAG(
427
+ goal_id=dag.goal_id,
428
+ subtasks={
429
+ sid: s for sid, s in dag.subtasks.items()
430
+ if sid in completed_ids
431
+ },
432
+ version=dag.version,
433
+ created_at=dag.created_at,
434
+ )
435
+ # Reset all non-completed subtasks to pending
436
+ for sid, s in dag.subtasks.items():
437
+ if sid not in completed_ids:
438
+ new_s = Subtask(
439
+ subtask_id=s.subtask_id,
440
+ goal_id=s.goal_id,
441
+ title=s.title,
442
+ job_type=s.job_type,
443
+ capabilities=list(s.capabilities),
444
+ depends_on=list(s.depends_on),
445
+ estimated_hours=s.estimated_hours,
446
+ payload=dict(s.payload),
447
+ max_retries=s.max_retries,
448
+ )
449
+ new_dag.subtasks[new_s.subtask_id] = new_s
450
+ return new_dag
@@ -0,0 +1,89 @@
1
+ -- Colony Goal Engine persistence schema
2
+
3
+ CREATE TABLE IF NOT EXISTS goals (
4
+ goal_id TEXT PRIMARY KEY,
5
+ title TEXT NOT NULL,
6
+ description TEXT NOT NULL DEFAULT '',
7
+ source TEXT NOT NULL DEFAULT 'explicit',
8
+ status TEXT NOT NULL DEFAULT 'proposed',
9
+ priority INTEGER NOT NULL DEFAULT 50,
10
+ outcome_json TEXT, -- GoalOutcome serialised as JSON
11
+ deadline TEXT, -- ISO-8601 datetime
12
+ parent_goal_id TEXT, -- self-reference for sub-goals
13
+ tags_json TEXT NOT NULL DEFAULT '{}',
14
+ context_json TEXT NOT NULL DEFAULT '{}',
15
+ created_at TEXT NOT NULL,
16
+ updated_at TEXT NOT NULL,
17
+ accepted_at TEXT,
18
+ completed_at TEXT,
19
+ abandoned_at TEXT,
20
+ abandon_reason TEXT,
21
+ replan_count INTEGER NOT NULL DEFAULT 0,
22
+ estimated_hours REAL,
23
+ progress_pct REAL NOT NULL DEFAULT 0.0,
24
+
25
+ -- Initiative management fields (v0.7.10)
26
+ last_initiative_at TEXT, -- ISO-8601: last time an initiative was generated for this goal
27
+ snoozed_until TEXT, -- ISO-8601: don't generate initiatives until this time
28
+ snooze_count INTEGER NOT NULL DEFAULT 0,
29
+ dismissal_reason TEXT, -- Why the goal was dismissed (if applicable)
30
+
31
+ FOREIGN KEY (parent_goal_id) REFERENCES goals(goal_id)
32
+ );
33
+
34
+ CREATE INDEX IF NOT EXISTS idx_goals_status ON goals(status);
35
+ CREATE INDEX IF NOT EXISTS idx_goals_priority ON goals(priority DESC);
36
+ CREATE INDEX IF NOT EXISTS idx_goals_deadline ON goals(deadline);
37
+ CREATE INDEX IF NOT EXISTS idx_goals_last_initiative ON goals(last_initiative_at);
38
+ CREATE INDEX IF NOT EXISTS idx_goals_snoozed_until ON goals(snoozed_until);
39
+
40
+ CREATE TABLE IF NOT EXISTS subtasks (
41
+ subtask_id TEXT PRIMARY KEY,
42
+ goal_id TEXT NOT NULL,
43
+ title TEXT NOT NULL,
44
+ job_type TEXT NOT NULL DEFAULT 'custom',
45
+ payload_json TEXT NOT NULL DEFAULT '{}',
46
+ capabilities_json TEXT NOT NULL DEFAULT '[]',
47
+ depends_on_json TEXT NOT NULL DEFAULT '[]',
48
+ status TEXT NOT NULL DEFAULT 'pending',
49
+ job_id TEXT, -- task queue job_id
50
+ result_json TEXT,
51
+ depth INTEGER NOT NULL DEFAULT 0,
52
+ is_critical_path INTEGER NOT NULL DEFAULT 0,
53
+ retry_count INTEGER NOT NULL DEFAULT 0,
54
+ max_retries INTEGER NOT NULL DEFAULT 2,
55
+ estimated_hours REAL,
56
+ started_at TEXT,
57
+ completed_at TEXT,
58
+ error TEXT,
59
+ dag_version INTEGER NOT NULL DEFAULT 1,
60
+ FOREIGN KEY (goal_id) REFERENCES goals(goal_id)
61
+ );
62
+
63
+ CREATE INDEX IF NOT EXISTS idx_subtasks_goal ON subtasks(goal_id);
64
+ CREATE INDEX IF NOT EXISTS idx_subtasks_status ON subtasks(status);
65
+ CREATE INDEX IF NOT EXISTS idx_subtasks_job ON subtasks(job_id);
66
+
67
+ CREATE TABLE IF NOT EXISTS goal_audit_log (
68
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
69
+ goal_id TEXT NOT NULL,
70
+ from_status TEXT NOT NULL,
71
+ to_status TEXT NOT NULL,
72
+ trigger TEXT NOT NULL,
73
+ created_at TEXT NOT NULL,
74
+ metadata_json TEXT NOT NULL DEFAULT '{}',
75
+ FOREIGN KEY (goal_id) REFERENCES goals(goal_id)
76
+ );
77
+
78
+ CREATE INDEX IF NOT EXISTS idx_audit_goal ON goal_audit_log(goal_id);
79
+ CREATE INDEX IF NOT EXISTS idx_audit_time ON goal_audit_log(created_at DESC);
80
+
81
+ CREATE TABLE IF NOT EXISTS goal_dag_versions (
82
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
83
+ goal_id TEXT NOT NULL,
84
+ version INTEGER NOT NULL,
85
+ dag_json TEXT NOT NULL, -- Full GoalDAG serialised as JSON
86
+ created_at TEXT NOT NULL,
87
+ UNIQUE(goal_id, version),
88
+ FOREIGN KEY (goal_id) REFERENCES goals(goal_id)
89
+ );