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.
- apsimo/__init__.py +38 -0
- apsimo/__main__.py +6 -0
- apsimo/agent/__init__.py +6 -0
- apsimo/agent/client.py +276 -0
- apsimo/agent/models.py +46 -0
- apsimo/agents/__init__.py +20 -0
- apsimo/agents/models.py +264 -0
- apsimo/agents/store.py +861 -0
- apsimo/agents/websocket.py +522 -0
- apsimo/api/__init__.py +1 -0
- apsimo/api/auth_telemetry.py +287 -0
- apsimo/api/authority.py +1203 -0
- apsimo/api/contact_grants.py +347 -0
- apsimo/api/middleware.py +483 -0
- apsimo/api/routers/__init__.py +1 -0
- apsimo/api/routers/commitment_work.py +265 -0
- apsimo/api/routers/context_gate.py +123 -0
- apsimo/api/routers/executions.py +140 -0
- apsimo/api/routers/followup_plans.py +147 -0
- apsimo/api/routers/governed_actions.py +162 -0
- apsimo/api/routers/host.py +14473 -0
- apsimo/api/routers/initiative_work.py +115 -0
- apsimo/api/routers/mining.py +104 -0
- apsimo/api/routers/observations.py +110 -0
- apsimo/api/routers/social_state.py +225 -0
- apsimo/api/routers/task_queue.py +2715 -0
- apsimo/api/routers/temporal_followups.py +251 -0
- apsimo/api/routers/transport.py +110 -0
- apsimo/api/routers/transport_ingress_api.py +240 -0
- apsimo/api/schemas/__init__.py +1 -0
- apsimo/api/schemas/host.py +1949 -0
- apsimo/autonomy/cli.py +110 -0
- apsimo/autonomy/condition_worker.py +437 -0
- apsimo/autonomy/config.py +424 -0
- apsimo/autonomy/loop.py +4316 -0
- apsimo/autonomy/registry.py +339 -0
- apsimo/autonomy/scheduler.py +1822 -0
- apsimo/autonomy/synthesis.py +449 -0
- apsimo/backup.py +962 -0
- apsimo/beliefs/__init__.py +23 -0
- apsimo/beliefs/contradictions.py +109 -0
- apsimo/beliefs/decay.py +61 -0
- apsimo/beliefs/engine.py +479 -0
- apsimo/beliefs/models.py +67 -0
- apsimo/beliefs/promotion.py +41 -0
- apsimo/beliefs/resolve.py +58 -0
- apsimo/beliefs/source_claims.py +690 -0
- apsimo/beliefs/source_projection.py +883 -0
- apsimo/beliefs/source_time.py +208 -0
- apsimo/beliefs/store.py +133 -0
- apsimo/briefings/aggregators.py +824 -0
- apsimo/briefings/composer.py +420 -0
- apsimo/briefings/config.py +55 -0
- apsimo/briefings/delivery.py +439 -0
- apsimo/briefings/engagement.py +97 -0
- apsimo/briefings/engine.py +274 -0
- apsimo/briefings/enhancer.py +99 -0
- apsimo/briefings/models.py +183 -0
- apsimo/briefings/scheduler.py +382 -0
- apsimo/briefings/store.py +435 -0
- apsimo/chain/__init__.py +48 -0
- apsimo/chain/block.py +100 -0
- apsimo/chain/cli.py +704 -0
- apsimo/chain/genesis.py +443 -0
- apsimo/chain/identity.py +416 -0
- apsimo/chain/keys.py +1025 -0
- apsimo/chain/local_keys.py +187 -0
- apsimo/chain/manager.py +290 -0
- apsimo/chain/node.py +163 -0
- apsimo/chain/plugin_transactions.py +371 -0
- apsimo/chain/protocol.py +220 -0
- apsimo/chain/state_machine.py +676 -0
- apsimo/chain/storage.py +503 -0
- apsimo/chain/transactions.py +250 -0
- apsimo/chain/validation.py +397 -0
- apsimo/channels/__init__.py +1 -0
- apsimo/channels/manifest.py +31 -0
- apsimo/channels/migrations/001_channels_schema.sql +12 -0
- apsimo/channels/phone_gateways.py +42 -0
- apsimo/channels/presence.py +188 -0
- apsimo/channels/router.py +235 -0
- apsimo/channels/store.py +231 -0
- apsimo/cli.py +2688 -0
- apsimo/cognition/__init__.py +11 -0
- apsimo/cognition/charter.py +398 -0
- apsimo/cognition/drive_governance.py +3530 -0
- apsimo/cognition/evidence_pipeline.py +1627 -0
- apsimo/cognition/external_events.py +932 -0
- apsimo/cognition/goal_spine.py +3488 -0
- apsimo/cognition/introspection.py +214 -0
- apsimo/cognition/prompt.py +150 -0
- apsimo/cognition/runtime.py +108 -0
- apsimo/cognition/trigger.py +154 -0
- apsimo/commitments/__init__.py +18 -0
- apsimo/commitments/local_work.py +355 -0
- apsimo/commitments/store.py +1052 -0
- apsimo/commitments/work.py +91 -0
- apsimo/compat.py +53 -0
- apsimo/compression/__init__.py +467 -0
- apsimo/connectors/__init__.py +21 -0
- apsimo/connectors/base.py +152 -0
- apsimo/connectors/caldav_calendar.py +125 -0
- apsimo/connectors/fs_documents.py +85 -0
- apsimo/connectors/imap_email.py +138 -0
- apsimo/connectors/manager.py +218 -0
- apsimo/connectors/webhook_pull.py +88 -0
- apsimo/contacts/__init__.py +33 -0
- apsimo/contacts/comms.py +357 -0
- apsimo/contacts/config.py +79 -0
- apsimo/contacts/exporters/__init__.py +1 -0
- apsimo/contacts/exporters/vcard.py +71 -0
- apsimo/contacts/identity_links.py +251 -0
- apsimo/contacts/importer.py +280 -0
- apsimo/contacts/importers/__init__.py +1 -0
- apsimo/contacts/importers/batch.py +43 -0
- apsimo/contacts/importers/macos_contacts.py +101 -0
- apsimo/contacts/migrations/001_contacts_schema.sql +141 -0
- apsimo/contacts/migrations/002_trust_scopes.sql +36 -0
- apsimo/contacts/migrations/003_open_gateway_enum.sql +32 -0
- apsimo/contacts/migrations/004_contact_provision_operations.sql +18 -0
- apsimo/contacts/migrations/005_identity_links.sql +27 -0
- apsimo/contacts/models.py +308 -0
- apsimo/contacts/scoring.py +16 -0
- apsimo/contacts/store.py +1623 -0
- apsimo/contacts/transport_ingress.py +252 -0
- apsimo/contacts/world_bridge.py +314 -0
- apsimo/contextgate/__init__.py +69 -0
- apsimo/contextgate/chunker.py +169 -0
- apsimo/contextgate/estimate.py +54 -0
- apsimo/contextgate/gate.py +313 -0
- apsimo/contextgate/retrieve.py +115 -0
- apsimo/delivery/__init__.py +16 -0
- apsimo/delivery/bridge.py +1260 -0
- apsimo/delivery/channels.py +526 -0
- apsimo/delivery/classification.py +50 -0
- apsimo/delivery/rate_limiter.py +268 -0
- apsimo/delivery/reachout_policy.py +206 -0
- apsimo/directed/__init__.py +22 -0
- apsimo/directed/audit.py +167 -0
- apsimo/directed/intake.py +95 -0
- apsimo/directed/models.py +191 -0
- apsimo/directed/service.py +509 -0
- apsimo/directives/__init__.py +25 -0
- apsimo/directives/evidence.py +87 -0
- apsimo/directives/extractor.py +188 -0
- apsimo/directives/guard.py +364 -0
- apsimo/directives/models.py +206 -0
- apsimo/directives/service.py +372 -0
- apsimo/directives/store.py +167 -0
- apsimo/doctor.py +2173 -0
- apsimo/environment.py +43 -0
- apsimo/events/__init__.py +33 -0
- apsimo/events/broadcaster.py +98 -0
- apsimo/events/bus.py +217 -0
- apsimo/events/journal.py +863 -0
- apsimo/events/stream.py +131 -0
- apsimo/events/types.py +150 -0
- apsimo/execution_results.py +357 -0
- apsimo/feedback/__init__.py +5 -0
- apsimo/feedback/store.py +76 -0
- apsimo/feeds/__init__.py +19 -0
- apsimo/feeds/cli.py +84 -0
- apsimo/feeds/engine.py +437 -0
- apsimo/feeds/example-feed.yaml +77 -0
- apsimo/feeds/hermes_cron.py +126 -0
- apsimo/feeds/manager.py +235 -0
- apsimo/feeds/spec.py +250 -0
- apsimo/feeds/template.py +202 -0
- apsimo/gate/__init__.py +18 -0
- apsimo/gate/audit.py +61 -0
- apsimo/gate/communication_policy.py +166 -0
- apsimo/gate/config.py +72 -0
- apsimo/gate/context_provenance.py +170 -0
- apsimo/gate/env_risk.py +226 -0
- apsimo/gate/guard_audit.py +353 -0
- apsimo/gate/layers/__init__.py +1 -0
- apsimo/gate/layers/base.py +15 -0
- apsimo/gate/layers/l1_recipient.py +66 -0
- apsimo/gate/layers/l2_pii.py +134 -0
- apsimo/gate/layers/l3_cross_context.py +50 -0
- apsimo/gate/layers/l4_trust_tier.py +78 -0
- apsimo/gate/layers/l5_injection.py +199 -0
- apsimo/gate/layers/l6_review.py +86 -0
- apsimo/gate/layers/l7_delay.py +100 -0
- apsimo/gate/layers/tom2_epistemic.py +185 -0
- apsimo/gate/models.py +64 -0
- apsimo/gate/pending_dispatch.py +5 -0
- apsimo/gate/pipeline.py +206 -0
- apsimo/gate/rejection.py +259 -0
- apsimo/gate/response_guard.py +700 -0
- apsimo/gate/rulesets/injection_v1.yaml +51 -0
- apsimo/gate/surface_policy.py +189 -0
- apsimo/gate/taint.py +226 -0
- apsimo/genesis.json +9 -0
- apsimo/goals/__init__.py +100 -0
- apsimo/goals/config.py +38 -0
- apsimo/goals/decomposer.py +421 -0
- apsimo/goals/engine.py +617 -0
- apsimo/goals/inference.py +354 -0
- apsimo/goals/models.py +302 -0
- apsimo/goals/priority.py +270 -0
- apsimo/goals/queue_bridge.py +149 -0
- apsimo/goals/replan.py +450 -0
- apsimo/goals/schema.sql +89 -0
- apsimo/goals/store.py +692 -0
- apsimo/governed_actions.py +1708 -0
- apsimo/harness_integration/__init__.py +45 -0
- apsimo/harness_integration/context.py +41 -0
- apsimo/harness_integration/skills.py +231 -0
- apsimo/identity/__init__.py +26 -0
- apsimo/identity/participants.py +181 -0
- apsimo/identity/resolver.py +329 -0
- apsimo/identity_bootstrap/__init__.py +5 -0
- apsimo/identity_bootstrap/builder.py +208 -0
- apsimo/identity_bootstrap/corpus.py +443 -0
- apsimo/identity_bootstrap/models.py +54 -0
- apsimo/identity_bootstrap/runner.py +353 -0
- apsimo/identity_bootstrap/seeders/__init__.py +25 -0
- apsimo/identity_bootstrap/seeders/briefings.py +109 -0
- apsimo/identity_bootstrap/seeders/chain.py +57 -0
- apsimo/identity_bootstrap/seeders/goals.py +128 -0
- apsimo/identity_bootstrap/seeders/memory.py +191 -0
- apsimo/identity_bootstrap/seeders/neo4j_cognition.py +79 -0
- apsimo/identity_bootstrap/seeders/relationship.py +152 -0
- apsimo/identity_bootstrap/seeders/sessions.py +67 -0
- apsimo/identity_bootstrap/seeders/skills.py +92 -0
- apsimo/identity_bootstrap/seeders/task_queue.py +72 -0
- apsimo/identity_bootstrap/seeders/world_model.py +143 -0
- apsimo/identity_bootstrap/self_query.py +92 -0
- apsimo/identity_bootstrap/self_reflection.py +155 -0
- apsimo/identity_bootstrap/skill.py +37 -0
- apsimo/identity_bootstrap/verifier.py +436 -0
- apsimo/initiatives/__init__.py +20 -0
- apsimo/initiatives/action_registry.py +454 -0
- apsimo/initiatives/approval_authority.py +2105 -0
- apsimo/initiatives/approval_policy.py +123 -0
- apsimo/initiatives/assignment.py +263 -0
- apsimo/initiatives/backup_evidence.py +100 -0
- apsimo/initiatives/context_freshness.py +103 -0
- apsimo/initiatives/models.py +318 -0
- apsimo/initiatives/native_work.py +270 -0
- apsimo/initiatives/standing_approvals.py +232 -0
- apsimo/initiatives/store.py +1081 -0
- apsimo/initiatives/temporal_followup.py +410 -0
- apsimo/intelligence/__init__.py +1 -0
- apsimo/intelligence/cognition/__init__.py +24 -0
- apsimo/intelligence/cognition/gap_detector.py +148 -0
- apsimo/intelligence/cognition/metalearner.py +547 -0
- apsimo/intelligence/cognition/metrics_collector.py +217 -0
- apsimo/intelligence/cognition/performance_index.py +299 -0
- apsimo/intelligence/cognition/registry.py +192 -0
- apsimo/intelligence/cognition/strategy_adjuster.py +222 -0
- apsimo/intelligence/cognition/types.py +16 -0
- apsimo/intelligence/components/__init__.py +66 -0
- apsimo/intelligence/components/anomaly_detector.py +413 -0
- apsimo/intelligence/components/initiative_engine.py +2643 -0
- apsimo/intelligence/components/preference_learner.py +521 -0
- apsimo/intelligence/components/research_orchestrator.py +358 -0
- apsimo/intelligence/components/self_directed_thinker.py +221 -0
- apsimo/intelligence/components/self_reflector.py +252 -0
- apsimo/intelligence/components/session_continuity.py +154 -0
- apsimo/intelligence/components/task_planner.py +320 -0
- apsimo/intelligence/components/tool_learner.py +217 -0
- apsimo/intelligence/graph/__init__.py +79 -0
- apsimo/intelligence/graph/client.py +2483 -0
- apsimo/intelligence/graph/consolidator.py +405 -0
- apsimo/intelligence/graph/distiller.py +312 -0
- apsimo/intelligence/graph/migrations.py +129 -0
- apsimo/intelligence/graph/queries.py +248 -0
- apsimo/intelligence/graph/recall.py +281 -0
- apsimo/intelligence/graph/reconciler.py +144 -0
- apsimo/intelligence/graph/schema.py +337 -0
- apsimo/intelligence/graph/selection.py +252 -0
- apsimo/intelligence/learning/__init__.py +17 -0
- apsimo/intelligence/learning/continuous_learner.py +245 -0
- apsimo/intelligence/learning/feedback_store.py +321 -0
- apsimo/intelligence/mind_model/__init__.py +1 -0
- apsimo/intelligence/mind_model/graph_baseline.py +136 -0
- apsimo/intelligence/mind_model/signal_collector.py +361 -0
- apsimo/intelligence/relationships/__init__.py +11 -0
- apsimo/intelligence/relationships/profiler.py +389 -0
- apsimo/intelligence/relationships/scorer.py +560 -0
- apsimo/intelligence/relationships/signal_floor.py +66 -0
- apsimo/intelligence/relationships/trust_tiers.py +300 -0
- apsimo/intelligence/synthesis/__init__.py +40 -0
- apsimo/intelligence/synthesis/connection_discoverer.py +379 -0
- apsimo/intelligence/synthesis/cross_domain_analyzer.py +287 -0
- apsimo/intelligence/synthesis/insight_deliverer.py +171 -0
- apsimo/intelligence/synthesis/insight_store.py +79 -0
- apsimo/intelligence/synthesis/insight_validator.py +183 -0
- apsimo/intelligence/synthesis/novelty_scorer.py +267 -0
- apsimo/intelligence/turn_middleware/__init__.py +15 -0
- apsimo/intelligence/turn_middleware/memory_sync.py +119 -0
- apsimo/mcp/__init__.py +41 -0
- apsimo/mcp/__main__.py +6 -0
- apsimo/mcp/config.py +287 -0
- apsimo/mcp/server.py +501 -0
- apsimo/migrations.py +187 -0
- apsimo/mining/__init__.py +27 -0
- apsimo/mining/corpus.py +239 -0
- apsimo/mining/escalations.py +289 -0
- apsimo/mining/models.py +169 -0
- apsimo/mining/store.py +210 -0
- apsimo/models/__init__.py +30 -0
- apsimo/models/memory.py +80 -0
- apsimo/models/mesh.py +72 -0
- apsimo/models/person.py +104 -0
- apsimo/models/signal.py +108 -0
- apsimo/observations/__init__.py +15 -0
- apsimo/observations/store.py +277 -0
- apsimo/patterns/__init__.py +6 -0
- apsimo/patterns/extract.py +187 -0
- apsimo/patterns/store.py +227 -0
- apsimo/persona/__init__.py +1 -0
- apsimo/persona/engine.py +611 -0
- apsimo/persona/manifest.py +140 -0
- apsimo/projects/__init__.py +28 -0
- apsimo/projects/engine.py +1681 -0
- apsimo/projects/event_outbox.py +188 -0
- apsimo/projects/models.py +216 -0
- apsimo/projects/planner.py +181 -0
- apsimo/projects/store.py +1446 -0
- apsimo/proposals/__init__.py +12 -0
- apsimo/proposals/engine.py +114 -0
- apsimo/proposals/models.py +207 -0
- apsimo/qualification/__init__.py +1 -0
- apsimo/qualification/cases.py +75 -0
- apsimo/qualification/cli.py +51 -0
- apsimo/qualification/memory_cases.py +209 -0
- apsimo/qualification/records.py +92 -0
- apsimo/qualification/report.py +87 -0
- apsimo/qualification/runner.py +311 -0
- apsimo/qualification/structured_cases.py +131 -0
- apsimo/reasoning/__init__.py +13 -0
- apsimo/reasoning/executor.py +506 -0
- apsimo/reasoning/loop.py +373 -0
- apsimo/reasoning/native_tools/__init__.py +16 -0
- apsimo/reasoning/native_tools/calculate.py +141 -0
- apsimo/reasoning/native_tools/file_ops.py +150 -0
- apsimo/reasoning/native_tools/web_search.py +49 -0
- apsimo/reasoning/tool_policy.py +182 -0
- apsimo/redact/__init__.py +176 -0
- apsimo/repos/__init__.py +5 -0
- apsimo/repos/mirrors.py +204 -0
- apsimo/research/__init__.py +41 -0
- apsimo/research/artifact.py +482 -0
- apsimo/research/gatherer.py +387 -0
- apsimo/research/pipeline.py +513 -0
- apsimo/research/search/__init__.py +7 -0
- apsimo/research/search/base.py +41 -0
- apsimo/research/search/brave.py +59 -0
- apsimo/research/search/cache.py +51 -0
- apsimo/research/search/duckduckgo.py +103 -0
- apsimo/research/search/orchestrator.py +119 -0
- apsimo/research/search/serpapi.py +59 -0
- apsimo/research/search/tavily.py +59 -0
- apsimo/research/synthesizer.py +309 -0
- apsimo/router/__init__.py +30 -0
- apsimo/router/complexity_scorer.py +148 -0
- apsimo/router/endpoints.py +153 -0
- apsimo/router/fallback.py +58 -0
- apsimo/router/functions.py +243 -0
- apsimo/router/native_policy.py +52 -0
- apsimo/router/router.py +762 -0
- apsimo/router/self_learning.py +174 -0
- apsimo/router/tiers.py +677 -0
- apsimo/sandbox/__init__.py +21 -0
- apsimo/sandbox/backend.py +195 -0
- apsimo/sandbox/manager.py +173 -0
- apsimo/scope_bounds.py +7 -0
- apsimo/secrets/__init__.py +6 -0
- apsimo/secrets/backends/__init__.py +8 -0
- apsimo/secrets/backends/base.py +42 -0
- apsimo/secrets/backends/env.py +110 -0
- apsimo/secrets/backends/keyring.py +72 -0
- apsimo/secrets/backends/onepassword.py +232 -0
- apsimo/secrets/cli.py +191 -0
- apsimo/secrets/manager.py +160 -0
- apsimo/secrets/migration.py +101 -0
- apsimo/secrets/types.py +98 -0
- apsimo/seed.py +41 -0
- apsimo/self_model/__init__.py +37 -0
- apsimo/self_model/appraisals.py +673 -0
- apsimo/self_model/benchmark.py +1314 -0
- apsimo/self_model/brief.py +40 -0
- apsimo/self_model/event_concerns.py +1128 -0
- apsimo/self_model/execution_forecasts.py +353 -0
- apsimo/self_model/expectations.py +1595 -0
- apsimo/self_model/experiments.py +1150 -0
- apsimo/self_model/journal.py +148 -0
- apsimo/self_model/judgments.py +705 -0
- apsimo/self_model/native_outcomes.py +55 -0
- apsimo/self_model/params.py +220 -0
- apsimo/self_model/perspective.py +246 -0
- apsimo/self_model/reconcile.py +183 -0
- apsimo/self_model/reply_forecasts.py +381 -0
- apsimo/self_model/runtime_forecasts.py +296 -0
- apsimo/self_model/runtime_models.py +67 -0
- apsimo/self_model/settlement.py +207 -0
- apsimo/self_model/situation.py +1731 -0
- apsimo/self_model/store.py +883 -0
- apsimo/self_model/supervised.py +137 -0
- apsimo/self_model/thinker.py +99 -0
- apsimo/self_model/trust.py +388 -0
- apsimo/self_model/workspace.py +2388 -0
- apsimo/server.py +4197 -0
- apsimo/services/__init__.py +1 -0
- apsimo/services/agent_bridge.py +474 -0
- apsimo/services/initiative_executor.py +914 -0
- apsimo/services/instance.py +297 -0
- apsimo/sessions/__init__.py +22 -0
- apsimo/sessions/config.py +13 -0
- apsimo/sessions/context_loader.py +88 -0
- apsimo/sessions/federation_session.py +75 -0
- apsimo/sessions/isolated_session.py +98 -0
- apsimo/sessions/reports.py +84 -0
- apsimo/sessions/store.py +148 -0
- apsimo/setup.py +2818 -0
- apsimo/setup_hermes.py +879 -0
- apsimo/setup_local_work.py +218 -0
- apsimo/setup_native_goals.py +134 -0
- apsimo/setup_native_reviews.py +115 -0
- apsimo/skills/__init__.py +10 -0
- apsimo/skills/base.py +108 -0
- apsimo/skills/budget.py +28 -0
- apsimo/skills/executor.py +493 -0
- apsimo/skills/executors/__init__.py +1 -0
- apsimo/skills/executors/behavioral_correction.py +75 -0
- apsimo/skills/executors/capability_gap.py +38 -0
- apsimo/skills/executors/data_quality.py +163 -0
- apsimo/skills/executors/knowledge_acquisition.py +41 -0
- apsimo/skills/executors/operational_hygiene.py +185 -0
- apsimo/skills/executors/subsystem_health.py +169 -0
- apsimo/skills/hermes_export.py +431 -0
- apsimo/skills/index.py +123 -0
- apsimo/skills/learning/__init__.py +21 -0
- apsimo/skills/learning/novelty_detector.py +206 -0
- apsimo/skills/learning/pattern_extractor.py +199 -0
- apsimo/skills/learning/triggers.py +159 -0
- apsimo/skills/loader.py +246 -0
- apsimo/skills/migrations/002_progressive_loading.sql +6 -0
- apsimo/skills/migrations/backfill_triggers.py +20 -0
- apsimo/skills/models.py +202 -0
- apsimo/skills/packager.py +128 -0
- apsimo/skills/protocols.py +70 -0
- apsimo/skills/registry.py +191 -0
- apsimo/skills/runtime.py +58 -0
- apsimo/skills/sandbox_runner.py +229 -0
- apsimo/skills/scheduler.py +129 -0
- apsimo/skills/schema.py +79 -0
- apsimo/skills/security/__init__.py +12 -0
- apsimo/skills/security/guards.py +53 -0
- apsimo/skills/security/scanner.py +223 -0
- apsimo/skills_memory/__init__.py +26 -0
- apsimo/skills_memory/distill.py +159 -0
- apsimo/skills_memory/models.py +85 -0
- apsimo/skills_memory/retrieve.py +62 -0
- apsimo/skills_memory/store.py +172 -0
- apsimo/surprise/__init__.py +6 -0
- apsimo/surprise/accumulation.py +57 -0
- apsimo/surprise/scorer.py +102 -0
- apsimo/surprise/store.py +203 -0
- apsimo/task_queue/__init__.py +69 -0
- apsimo/task_queue/action_receipts.py +148 -0
- apsimo/task_queue/approval_relay_canary.py +108 -0
- apsimo/task_queue/config.py +85 -0
- apsimo/task_queue/contract.py +361 -0
- apsimo/task_queue/events.py +130 -0
- apsimo/task_queue/governor.py +1031 -0
- apsimo/task_queue/handlers/__init__.py +16 -0
- apsimo/task_queue/handlers/base.py +37 -0
- apsimo/task_queue/handlers/inference.py +640 -0
- apsimo/task_queue/handlers/monitoring.py +116 -0
- apsimo/task_queue/handlers/registry.py +75 -0
- apsimo/task_queue/handlers/subtask_handler.py +173 -0
- apsimo/task_queue/handlers/system_maintenance.py +147 -0
- apsimo/task_queue/mesh_integration.py +111 -0
- apsimo/task_queue/models.py +317 -0
- apsimo/task_queue/queue_manager.py +8286 -0
- apsimo/task_queue/routing.py +287 -0
- apsimo/task_queue/scheduler.py +252 -0
- apsimo/task_queue/schema.sql +197 -0
- apsimo/task_queue/work_control.py +342 -0
- apsimo/task_queue/worker.py +993 -0
- apsimo/telemetry.py +145 -0
- apsimo/tom/__init__.py +6 -0
- apsimo/tom/affect.py +387 -0
- apsimo/tom/approvals.py +171 -0
- apsimo/tom/arcs.py +896 -0
- apsimo/tom/asymmetry.py +131 -0
- apsimo/tom/eligibility.py +248 -0
- apsimo/tom/engagement.py +214 -0
- apsimo/tom/exposure.py +214 -0
- apsimo/tom/extractor.py +306 -0
- apsimo/tom/fact_adapters.py +144 -0
- apsimo/tom/facts.py +326 -0
- apsimo/tom/integration.py +592 -0
- apsimo/tom/leveled.py +118 -0
- apsimo/tom/levels.py +247 -0
- apsimo/tom/recipient_audit.py +995 -0
- apsimo/tom/recipient_simulator.py +593 -0
- apsimo/tom/source_lineage.py +93 -0
- apsimo/tom/tom2.py +277 -0
- apsimo/tom/visibility.py +559 -0
- apsimo/tom/visibility_store.py +414 -0
- apsimo/tools/__init__.py +0 -0
- apsimo/tools/definitions.py +740 -0
- apsimo/tools/handlers.py +943 -0
- apsimo/toolsmith/__init__.py +26 -0
- apsimo/toolsmith/authority.py +166 -0
- apsimo/toolsmith/engine.py +559 -0
- apsimo/toolsmith/integrity.py +100 -0
- apsimo/toolsmith/miner.py +145 -0
- apsimo/toolsmith/policy.py +110 -0
- apsimo/toolsmith/registry.py +635 -0
- apsimo/turns/__init__.py +17 -0
- apsimo/turns/audio.py +134 -0
- apsimo/turns/documents.py +235 -0
- apsimo/turns/executions.py +486 -0
- apsimo/turns/hermes_history.py +245 -0
- apsimo/turns/hermes_kanban.py +268 -0
- apsimo/turns/hermes_work.py +96 -0
- apsimo/turns/idempotency.py +752 -0
- apsimo/turns/local_work.py +115 -0
- apsimo/turns/media.py +581 -0
- apsimo/turns/reported_workers.py +196 -0
- apsimo/turns/source_annotations.py +283 -0
- apsimo/turns/source_attribution.py +154 -0
- apsimo/turns/source_read.py +351 -0
- apsimo/turns/source_vectors.py +263 -0
- apsimo/turns/video.py +210 -0
- apsimo/util/autonomy_preset.py +220 -0
- apsimo/util/instance.py +92 -0
- apsimo/util/model_output.py +25 -0
- apsimo/util/quiet_hours.py +27 -0
- apsimo/util/session_safety.py +37 -0
- apsimo/util/temporal.py +343 -0
- apsimo/vector/__init__.py +75 -0
- apsimo/vector/backfill.py +171 -0
- apsimo/vector/caption.py +114 -0
- apsimo/vector/collections.py +51 -0
- apsimo/vector/config.py +102 -0
- apsimo/vector/embedder.py +670 -0
- apsimo/vector/image_preprocess.py +406 -0
- apsimo/vector/image_store.py +296 -0
- apsimo/vector/indexes.py +162 -0
- apsimo/vector/migrate.py +334 -0
- apsimo/vector/multimodal_provider.py +417 -0
- apsimo/vector/multimodal_types.py +87 -0
- apsimo/vector/openai_provider.py +119 -0
- apsimo/vector/query.py +49 -0
- apsimo/vector/reranker.py +565 -0
- apsimo/vector/safety_image.py +159 -0
- apsimo/vector/scanner.py +197 -0
- apsimo/vector/setup.py +289 -0
- apsimo/vector/store.py +533 -0
- apsimo/vector/tiers.py +263 -0
- apsimo/work_orders.py +925 -0
- apsimo/workers/__init__.py +21 -0
- apsimo/workers/agent_bridge.py +640 -0
- apsimo/workers/colony_worker.py +382 -0
- apsimo/workers/queue_worker.py +441 -0
- apsimo/workers/skills_sync.py +152 -0
- apsimo/world_model/__init__.py +71 -0
- apsimo/world_model/causal_maintenance.py +131 -0
- apsimo/world_model/causal_policy.py +43 -0
- apsimo/world_model/causal_query.py +125 -0
- apsimo/world_model/confidence.py +54 -0
- apsimo/world_model/config.py +64 -0
- apsimo/world_model/constants.py +97 -0
- apsimo/world_model/entities.py +145 -0
- apsimo/world_model/expectation_resolvers.py +177 -0
- apsimo/world_model/extraction/__init__.py +7 -0
- apsimo/world_model/extraction/base.py +62 -0
- apsimo/world_model/extraction/conversation_extractor.py +262 -0
- apsimo/world_model/extraction/detector.py +74 -0
- apsimo/world_model/extraction/document_extractor.py +78 -0
- apsimo/world_model/extraction/formats/__init__.py +24 -0
- apsimo/world_model/extraction/formats/csv_fmt.py +68 -0
- apsimo/world_model/extraction/formats/html_fmt.py +72 -0
- apsimo/world_model/extraction/formats/json_fmt.py +68 -0
- apsimo/world_model/extraction/formats/pdf.py +43 -0
- apsimo/world_model/extraction/formats/text.py +27 -0
- apsimo/world_model/extraction/llm_extractor.py +164 -0
- apsimo/world_model/extraction/pipeline.py +73 -0
- apsimo/world_model/integrations/__init__.py +5 -0
- apsimo/world_model/integrations/mind_model_bridge.py +115 -0
- apsimo/world_model/integrations/social_intel_bridge.py +120 -0
- apsimo/world_model/jobs/__init__.py +4 -0
- apsimo/world_model/jobs/extraction_job.py +168 -0
- apsimo/world_model/llm_extract.py +572 -0
- apsimo/world_model/neo4j/__init__.py +5 -0
- apsimo/world_model/neo4j/backend.py +654 -0
- apsimo/world_model/observations.py +155 -0
- apsimo/world_model/populator.py +307 -0
- apsimo/world_model/postgres/__init__.py +1 -0
- apsimo/world_model/postgres/backend.py +683 -0
- apsimo/world_model/relationships.py +25 -0
- apsimo/world_model/resolution/__init__.py +13 -0
- apsimo/world_model/resolution/entity_resolver.py +232 -0
- apsimo/world_model/resolution/merge_audit.py +16 -0
- apsimo/world_model/resolution/merge_workflow.py +117 -0
- apsimo/world_model/source_reports.py +121 -0
- apsimo/world_model/sqlite/__init__.py +4 -0
- apsimo/world_model/sqlite/backend.py +855 -0
- apsimo/world_model/sqlite/schema.sql +132 -0
- apsimo/world_model/store.py +545 -0
- apsimo-1.3.0.dist-info/METADATA +78 -0
- apsimo-1.3.0.dist-info/RECORD +614 -0
- apsimo-1.3.0.dist-info/WHEEL +5 -0
- apsimo-1.3.0.dist-info/entry_points.txt +11 -0
- apsimo-1.3.0.dist-info/licenses/LICENSE +21 -0
- apsimo-1.3.0.dist-info/top_level.txt +2 -0
- colony_sidecar/__init__.py +4 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""Belief maintenance (item 7): drive the graph's epistemic scaffolding.
|
|
2
|
+
|
|
3
|
+
Detects contradictions (same subject + predicate, conflicting value) across
|
|
4
|
+
world-model properties and graph memories, resolves them by recency >
|
|
5
|
+
confidence > source-trust (marking losers superseded, with an audit trail),
|
|
6
|
+
surfaces genuinely unresolvable conflicts as internal review initiatives,
|
|
7
|
+
and decays stale world-state confidence past its TTL.
|
|
8
|
+
|
|
9
|
+
Modes (COLONY_BELIEFS_MODE, default shadow = calibration): shadow detects,
|
|
10
|
+
records and surfaces review initiatives without mutating epistemic state;
|
|
11
|
+
live also resolves and decays. Resolutions are journaled (Amendment 1.4).
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from apsimo.beliefs.models import Claim, beliefs_mode
|
|
15
|
+
from apsimo.beliefs.store import BeliefStore
|
|
16
|
+
from apsimo.beliefs.contradictions import detect_conflicts, claims_from_text
|
|
17
|
+
from apsimo.beliefs.resolve import pick_winner, source_trust
|
|
18
|
+
from apsimo.beliefs.engine import BeliefEngine
|
|
19
|
+
|
|
20
|
+
__all__ = [
|
|
21
|
+
"Claim", "BeliefStore", "BeliefEngine", "detect_conflicts",
|
|
22
|
+
"claims_from_text", "pick_winner", "source_trust", "beliefs_mode",
|
|
23
|
+
]
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"""Contradiction detection: same subject + predicate, conflicting value.
|
|
2
|
+
|
|
3
|
+
Two claim sources:
|
|
4
|
+
- World-model entity properties (fully structured; exact).
|
|
5
|
+
- Graph memories (free text): a CONSERVATIVE copular extractor pulls
|
|
6
|
+
(subject, predicate, value) triples only from short, unambiguous
|
|
7
|
+
constructions, so detected conflicts are real rather than parser noise.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import re
|
|
13
|
+
from typing import Any, List, Optional, Tuple
|
|
14
|
+
|
|
15
|
+
from apsimo.beliefs.models import Claim, norm_value
|
|
16
|
+
|
|
17
|
+
# Conservative copular / relational patterns. Subject <= 4 tokens, sentence
|
|
18
|
+
# bounded, value bounded. Anything fancier waits for an LLM pass.
|
|
19
|
+
_PATTERNS: List[Tuple[re.Pattern, str]] = [
|
|
20
|
+
# "X's <predicate> is <value>"
|
|
21
|
+
(re.compile(r"^(?P<subj>[A-Z][\w .&-]{1,40}?)'s\s+(?P<pred>[a-z ]{2,25})\s+"
|
|
22
|
+
r"(?:is|are)\s+(?P<val>[^,.;]{1,60})[.,;]?$"), "possessive"),
|
|
23
|
+
# "X works at Y" / "X works for Y"
|
|
24
|
+
(re.compile(r"^(?P<subj>[A-Z][\w .&-]{1,40}?)\s+works?\s+(?:at|for)\s+"
|
|
25
|
+
r"(?P<val>[^,.;]{1,60})[.,;]?$"), "works_at"),
|
|
26
|
+
# "X lives in Y" / "X is based in Y" / "X is located in Y"
|
|
27
|
+
(re.compile(r"^(?P<subj>[A-Z][\w .&-]{1,40}?)\s+(?:lives|resides)\s+in\s+"
|
|
28
|
+
r"(?P<val>[^,.;]{1,60})[.,;]?$"), "location"),
|
|
29
|
+
(re.compile(r"^(?P<subj>[A-Z][\w .&-]{1,40}?)\s+is\s+(?:based|located)\s+in\s+"
|
|
30
|
+
r"(?P<val>[^,.;]{1,60})[.,;]?$"), "location"),
|
|
31
|
+
# "X is the <role> of/at Y" -> predicate role
|
|
32
|
+
(re.compile(r"^(?P<subj>[A-Z][\w .&-]{1,40}?)\s+is\s+the\s+"
|
|
33
|
+
r"(?P<pred>[a-z ]{2,25})\s+(?:of|at)\s+"
|
|
34
|
+
r"(?P<val>[^,.;]{1,60})[.,;]?$"), "role"),
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
_SENT_SPLIT = re.compile(r"(?<=[.!?])\s+|\n+")
|
|
38
|
+
|
|
39
|
+
# Values that carry no belief content.
|
|
40
|
+
_EMPTY_VALUES = frozenset({"", "unknown", "none", "n a", "na", "null", "tbd"})
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def claims_from_text(text: str, *, confidence: float = 0.5, ts: float = 0.0,
|
|
44
|
+
source: str = "inference", ref: str = "") -> List[Claim]:
|
|
45
|
+
"""Conservative triple extraction from one memory's content."""
|
|
46
|
+
out: List[Claim] = []
|
|
47
|
+
for sent in _SENT_SPLIT.split(text or ""):
|
|
48
|
+
sent = sent.strip()
|
|
49
|
+
if not sent or len(sent) > 140:
|
|
50
|
+
continue
|
|
51
|
+
for pat, kind in _PATTERNS:
|
|
52
|
+
m = pat.match(sent)
|
|
53
|
+
if not m:
|
|
54
|
+
continue
|
|
55
|
+
subj = m.group("subj").strip()
|
|
56
|
+
if len(subj.split()) > 4:
|
|
57
|
+
continue
|
|
58
|
+
pred = (m.groupdict().get("pred") or kind).strip().lower()
|
|
59
|
+
val = m.group("val").strip()
|
|
60
|
+
if norm_value(val) in _EMPTY_VALUES:
|
|
61
|
+
continue
|
|
62
|
+
out.append(Claim(subject=subj, predicate=pred, value=val,
|
|
63
|
+
confidence=confidence, ts=ts, source=source,
|
|
64
|
+
ref=ref, scope="graph"))
|
|
65
|
+
break
|
|
66
|
+
return out
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def property_claims(entity: Any) -> List[Claim]:
|
|
70
|
+
"""Structured claims from a world-model entity's properties."""
|
|
71
|
+
out: List[Claim] = []
|
|
72
|
+
props = getattr(entity, "properties", None) or {}
|
|
73
|
+
name = getattr(entity, "name", "") or ""
|
|
74
|
+
eid = getattr(entity, "id", "") or ""
|
|
75
|
+
updated = getattr(entity, "updated_at", None)
|
|
76
|
+
try:
|
|
77
|
+
ts = updated.timestamp() if hasattr(updated, "timestamp") else 0.0
|
|
78
|
+
except Exception:
|
|
79
|
+
ts = 0.0
|
|
80
|
+
base_conf = float(getattr(entity, "confidence", 0.5) or 0.5)
|
|
81
|
+
for key, value in props.items():
|
|
82
|
+
if key.startswith("_conf_") or value in (None, ""):
|
|
83
|
+
continue
|
|
84
|
+
conf = float(props.get(f"_conf_{key}", base_conf) or base_conf)
|
|
85
|
+
out.append(Claim(subject=name or eid, predicate=key, value=str(value),
|
|
86
|
+
confidence=conf, ts=ts, source="world_model",
|
|
87
|
+
ref=eid, scope="world_model"))
|
|
88
|
+
return out
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def detect_conflicts(claims: List[Claim]) -> List[Tuple[Claim, Claim]]:
|
|
92
|
+
"""Pairs of claims sharing subject+predicate with conflicting values.
|
|
93
|
+
|
|
94
|
+
Within a key, claims whose values agree (normalized/containment) are
|
|
95
|
+
treated as corroboration, not conflict.
|
|
96
|
+
"""
|
|
97
|
+
by_key = {}
|
|
98
|
+
for c in claims or []:
|
|
99
|
+
by_key.setdefault(c.key(), []).append(c)
|
|
100
|
+
out: List[Tuple[Claim, Claim]] = []
|
|
101
|
+
for _key, group in by_key.items():
|
|
102
|
+
if len(group) < 2:
|
|
103
|
+
continue
|
|
104
|
+
for i in range(len(group)):
|
|
105
|
+
for j in range(i + 1, len(group)):
|
|
106
|
+
a, b = group[i], group[j]
|
|
107
|
+
if not a.same_value(b):
|
|
108
|
+
out.append((a, b))
|
|
109
|
+
return out
|
apsimo/beliefs/decay.py
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""Stale world-state decay: entities unseen past the TTL lose confidence.
|
|
2
|
+
|
|
3
|
+
Graph memory decay already runs daily (_phase_memory_decay + ColonyGraph
|
|
4
|
+
half-life machinery); this module covers the WORLD MODEL side, which had no
|
|
5
|
+
staleness treatment at all.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import logging
|
|
11
|
+
from datetime import datetime, timezone
|
|
12
|
+
from typing import Any, List
|
|
13
|
+
|
|
14
|
+
from apsimo.beliefs.models import stale_ttl_days
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
_DECAY_FACTOR = 0.9
|
|
19
|
+
_MIN_CONFIDENCE = 0.1
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _age_days(entity: Any, now: datetime) -> float:
|
|
23
|
+
last = (getattr(entity, "last_seen", None)
|
|
24
|
+
or getattr(entity, "updated_at", None)
|
|
25
|
+
or getattr(entity, "created_at", None))
|
|
26
|
+
if last is None:
|
|
27
|
+
return 0.0
|
|
28
|
+
try:
|
|
29
|
+
if isinstance(last, str):
|
|
30
|
+
last = datetime.fromisoformat(last.replace("Z", "+00:00"))
|
|
31
|
+
if last.tzinfo is None:
|
|
32
|
+
last = last.replace(tzinfo=timezone.utc)
|
|
33
|
+
return max(0.0, (now - last).total_seconds() / 86400.0)
|
|
34
|
+
except Exception:
|
|
35
|
+
return 0.0
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
async def stale_entities(world_store: Any, limit: int = 500) -> List[Any]:
|
|
39
|
+
"""Entities whose last observation is older than the TTL."""
|
|
40
|
+
if world_store is None:
|
|
41
|
+
return []
|
|
42
|
+
try:
|
|
43
|
+
ents = await world_store.find_entities(query="", min_confidence=0.0,
|
|
44
|
+
limit=limit)
|
|
45
|
+
except Exception:
|
|
46
|
+
return []
|
|
47
|
+
now = datetime.now(timezone.utc)
|
|
48
|
+
ttl = stale_ttl_days()
|
|
49
|
+
return [e for e in ents or [] if _age_days(e, now) > ttl]
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
async def decay_entity(world_store: Any, entity: Any) -> float:
|
|
53
|
+
"""Lower one stale entity's confidence (bounded). Returns new value."""
|
|
54
|
+
new_conf = max(_MIN_CONFIDENCE,
|
|
55
|
+
float(getattr(entity, "confidence", 0.5)) * _DECAY_FACTOR)
|
|
56
|
+
entity.confidence = new_conf
|
|
57
|
+
try:
|
|
58
|
+
await world_store.upsert_entity(entity)
|
|
59
|
+
except Exception:
|
|
60
|
+
logger.debug("stale decay upsert failed", exc_info=True)
|
|
61
|
+
return new_conf
|
apsimo/beliefs/engine.py
ADDED
|
@@ -0,0 +1,479 @@
|
|
|
1
|
+
"""BeliefEngine -- periodic belief maintenance over graph + world model.
|
|
2
|
+
|
|
3
|
+
Runs from the autonomy loop's daily `_phase_belief_maintenance`:
|
|
4
|
+
1. World-model supersessions: snapshot-diff entity properties; a changed
|
|
5
|
+
value writes a supersession audit row (who/what/when/why survives even
|
|
6
|
+
though the store keeps only the winning value).
|
|
7
|
+
2. Graph contradictions: conservative claim extraction over recent semantic
|
|
8
|
+
memories; same subject+predicate with conflicting values -> conflict
|
|
9
|
+
record. Resolution (live mode) marks the loser's epistemic_state
|
|
10
|
+
"superseded" with an audit trail; unresolvable conflicts become internal
|
|
11
|
+
review initiatives (never a reach-out).
|
|
12
|
+
3. Stale decay: world-model entities unseen past the TTL lose confidence
|
|
13
|
+
(live mode).
|
|
14
|
+
|
|
15
|
+
Shadow mode (default) is the calibration stage: detect + record + surface,
|
|
16
|
+
mutate nothing. Every resolution/decay in live mode is journaled.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
import asyncio
|
|
22
|
+
import logging
|
|
23
|
+
from typing import Any, Dict, List, Optional
|
|
24
|
+
|
|
25
|
+
from apsimo.beliefs.contradictions import (
|
|
26
|
+
claims_from_text, detect_conflicts, property_claims,
|
|
27
|
+
)
|
|
28
|
+
from apsimo.beliefs.decay import decay_entity, stale_entities
|
|
29
|
+
from apsimo.beliefs.models import Claim, beliefs_mode
|
|
30
|
+
from apsimo.beliefs.resolve import pick_winner
|
|
31
|
+
from apsimo.beliefs.store import BeliefStore
|
|
32
|
+
|
|
33
|
+
logger = logging.getLogger(__name__)
|
|
34
|
+
|
|
35
|
+
_MEMORY_QUERY = """
|
|
36
|
+
MATCH (m:Memory)
|
|
37
|
+
WHERE m.type IN ['semantic', 'episodic']
|
|
38
|
+
AND m.superseded_by IS NULL
|
|
39
|
+
AND NOT coalesce(m.epistemic_state, 'inferred')
|
|
40
|
+
IN ['superseded', 'deprecated', 'archived']
|
|
41
|
+
RETURN m.id AS id, m.content AS content, m.source_type AS source_type,
|
|
42
|
+
coalesce(m.effective_confidence, m.base_confidence, 0.5) AS confidence,
|
|
43
|
+
m.created_at AS created_at
|
|
44
|
+
ORDER BY m.created_at DESC
|
|
45
|
+
LIMIT $limit
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class BeliefEngine:
|
|
50
|
+
def __init__(self, store: BeliefStore, *, world_store: Any = None,
|
|
51
|
+
graph: Any = None, initiative_store: Any = None,
|
|
52
|
+
journal: Any = None, self_model: Any = None) -> None:
|
|
53
|
+
self.store = store
|
|
54
|
+
self._world = world_store
|
|
55
|
+
self._graph = graph
|
|
56
|
+
self._initiatives = initiative_store
|
|
57
|
+
self._journal = journal
|
|
58
|
+
self._self_model = self_model
|
|
59
|
+
self.last_report: Dict[str, Any] = {}
|
|
60
|
+
|
|
61
|
+
# ------------------------------------------------------------------
|
|
62
|
+
# Inline hook: cheap detection at property-update time
|
|
63
|
+
# ------------------------------------------------------------------
|
|
64
|
+
|
|
65
|
+
def note_property_update(self, entity_id: str, key: str,
|
|
66
|
+
old_value: Any, new_value: Any,
|
|
67
|
+
old_confidence: float,
|
|
68
|
+
new_confidence: float) -> None:
|
|
69
|
+
"""Called inline when a world-model property changes value. Records
|
|
70
|
+
the superseded value; heavy resolution stays in the periodic run."""
|
|
71
|
+
from apsimo.beliefs.models import norm_value
|
|
72
|
+
if norm_value(old_value) == norm_value(new_value):
|
|
73
|
+
return
|
|
74
|
+
try:
|
|
75
|
+
self.store.record_supersession(
|
|
76
|
+
"world_model", entity_id, key, old_value, new_value,
|
|
77
|
+
old_confidence, new_confidence,
|
|
78
|
+
reason="higher-confidence update", actor="world_model_store")
|
|
79
|
+
except Exception:
|
|
80
|
+
logger.debug("inline supersession record failed", exc_info=True)
|
|
81
|
+
|
|
82
|
+
# ------------------------------------------------------------------
|
|
83
|
+
# Periodic run
|
|
84
|
+
# ------------------------------------------------------------------
|
|
85
|
+
|
|
86
|
+
def _effective_mode(self) -> str:
|
|
87
|
+
"""Env mode graduated by the trust engine (Amendment 1.2). Belief
|
|
88
|
+
RESOLUTION mutates epistemic state autonomously (no sub-gate), so it
|
|
89
|
+
requires the fully-earned act_first stage; env "live" remains the
|
|
90
|
+
owner override, "off" stays off.
|
|
91
|
+
|
|
92
|
+
The trust ladder had a catch-22 for beliefs: graduation ask_first ->
|
|
93
|
+
act_first needs a real (non-shadow) track record, but the engine only
|
|
94
|
+
acted at act_first, so every outcome stayed shadow and the domain
|
|
95
|
+
could never graduate. COLONY_BELIEFS_SUPERVISED_LIVE=1 adds a
|
|
96
|
+
'supervised' rung at ask_first: REVERSIBLE mutations only (epistemic
|
|
97
|
+
supersession that preserves the old value on the superseded node,
|
|
98
|
+
and bounded stale-confidence decay — no deletion), journaled with
|
|
99
|
+
prior state, outcomes recorded shadow=False. Destructive resolution
|
|
100
|
+
(anything that deletes or merges nodes) must still gate on the full
|
|
101
|
+
"live" mode, i.e. act_first or the explicit env override.
|
|
102
|
+
|
|
103
|
+
H1.2: the rung logic itself now lives in self_model/supervised.py
|
|
104
|
+
(generic across domains; enable via COLONY_SUPERVISED_LIVE_DOMAINS=
|
|
105
|
+
beliefs or the legacy alias COLONY_BELIEFS_SUPERVISED_LIVE=1). The
|
|
106
|
+
reversible operations named above are pinned in its
|
|
107
|
+
REVERSIBLE_CONTRACT under ("beliefs", "supersede"/"decay")."""
|
|
108
|
+
from apsimo.self_model.supervised import effective_mode
|
|
109
|
+
return effective_mode("beliefs", beliefs_mode(),
|
|
110
|
+
getattr(self._self_model, "trust", None))
|
|
111
|
+
|
|
112
|
+
async def run(self) -> Dict[str, Any]:
|
|
113
|
+
mode = self._effective_mode()
|
|
114
|
+
report: Dict[str, Any] = {
|
|
115
|
+
"mode": mode, "supersessions": 0, "conflicts_detected": 0,
|
|
116
|
+
"resolved": 0, "review_initiatives": 0, "decayed": 0,
|
|
117
|
+
"causal_conflicts": 0, "causal_decayed": 0,
|
|
118
|
+
"pass_errors": 0,
|
|
119
|
+
}
|
|
120
|
+
self.last_report = report
|
|
121
|
+
if mode == "off":
|
|
122
|
+
return report
|
|
123
|
+
try:
|
|
124
|
+
await self._world_model_pass(report)
|
|
125
|
+
except Exception:
|
|
126
|
+
report["pass_errors"] += 1
|
|
127
|
+
logger.debug("belief world-model pass failed", exc_info=True)
|
|
128
|
+
try:
|
|
129
|
+
await self._graph_pass(report, mode)
|
|
130
|
+
except Exception:
|
|
131
|
+
report["pass_errors"] += 1
|
|
132
|
+
logger.debug("belief graph pass failed", exc_info=True)
|
|
133
|
+
try:
|
|
134
|
+
await self._decay_pass(report, mode)
|
|
135
|
+
except Exception:
|
|
136
|
+
report["pass_errors"] += 1
|
|
137
|
+
logger.debug("belief decay pass failed", exc_info=True)
|
|
138
|
+
try:
|
|
139
|
+
await self._causal_pass(report, mode)
|
|
140
|
+
except Exception:
|
|
141
|
+
report["pass_errors"] += 1
|
|
142
|
+
logger.debug("belief causal pass failed", exc_info=True)
|
|
143
|
+
logger.info(
|
|
144
|
+
"belief-maintenance[%s]: supersessions=%d conflicts=%d "
|
|
145
|
+
"resolved=%d review=%d decayed=%d causal_conflicts=%d "
|
|
146
|
+
"causal_decayed=%d errors=%d", mode,
|
|
147
|
+
report["supersessions"], report["conflicts_detected"],
|
|
148
|
+
report["resolved"], report["review_initiatives"],
|
|
149
|
+
report["decayed"], report["causal_conflicts"],
|
|
150
|
+
report["causal_decayed"], report["pass_errors"])
|
|
151
|
+
self._record_outcome(mode, report)
|
|
152
|
+
return report
|
|
153
|
+
|
|
154
|
+
def _record_outcome(self, mode: str, report: Dict[str, Any]) -> None:
|
|
155
|
+
"""Feed the trust ladder what the run EARNED (H1.3).
|
|
156
|
+
|
|
157
|
+
Strict (COLONY_TRUST_STRICT_OUTCOMES=1, the default): a run where
|
|
158
|
+
any pass raised records a failure; a run that actually mutated
|
|
159
|
+
state (resolved + decayed + supersessions > 0) records a success;
|
|
160
|
+
a no-op run records nothing. Supervised/live outcomes stay REAL
|
|
161
|
+
(shadow=False) — that is the whole point of the rung — but a
|
|
162
|
+
streak of no-op runs no longer builds a fake track record that
|
|
163
|
+
would graduate beliefs to act_first (and unlock destructive
|
|
164
|
+
resolution) unearned.
|
|
165
|
+
|
|
166
|
+
Legacy (flag=0): the historical unconditional per-run success.
|
|
167
|
+
"""
|
|
168
|
+
if self._self_model is None:
|
|
169
|
+
return
|
|
170
|
+
try:
|
|
171
|
+
from apsimo.self_model.supervised import (
|
|
172
|
+
strict_trust_outcomes,
|
|
173
|
+
)
|
|
174
|
+
shadow = mode not in ("live", "supervised")
|
|
175
|
+
if not strict_trust_outcomes():
|
|
176
|
+
self._self_model.record("beliefs", "success", shadow=shadow)
|
|
177
|
+
return
|
|
178
|
+
mutated = (report["resolved"] + report["decayed"]
|
|
179
|
+
+ report["supersessions"]
|
|
180
|
+
+ report.get("causal_decayed", 0)) > 0
|
|
181
|
+
if report["pass_errors"] > 0:
|
|
182
|
+
self._self_model.record("beliefs", "failure", shadow=shadow)
|
|
183
|
+
elif mutated:
|
|
184
|
+
self._self_model.record("beliefs", "success", shadow=shadow)
|
|
185
|
+
# else: no-op run — nothing earned, nothing recorded
|
|
186
|
+
except Exception:
|
|
187
|
+
pass
|
|
188
|
+
|
|
189
|
+
# -- world model: snapshot diff -> supersession audit -------------------
|
|
190
|
+
async def _world_model_pass(self, report: Dict[str, Any]) -> None:
|
|
191
|
+
if self._world is None:
|
|
192
|
+
return
|
|
193
|
+
try:
|
|
194
|
+
ents = await self._world.find_entities(query="",
|
|
195
|
+
min_confidence=0.0,
|
|
196
|
+
limit=500)
|
|
197
|
+
except Exception:
|
|
198
|
+
return
|
|
199
|
+
from apsimo.beliefs.models import norm_value
|
|
200
|
+
for e in ents or []:
|
|
201
|
+
for c in property_claims(e):
|
|
202
|
+
snap = self.store.snapshot_get(c.ref, c.predicate)
|
|
203
|
+
if snap is not None and norm_value(snap["value"]) != norm_value(c.value):
|
|
204
|
+
self.store.record_supersession(
|
|
205
|
+
"world_model", c.subject, c.predicate,
|
|
206
|
+
snap["value"], c.value,
|
|
207
|
+
float(snap.get("confidence") or 0.0), c.confidence,
|
|
208
|
+
reason="property value changed since last scan",
|
|
209
|
+
actor="belief_engine")
|
|
210
|
+
report["supersessions"] += 1
|
|
211
|
+
self.store.snapshot_put(c.ref, c.predicate, c.value,
|
|
212
|
+
c.confidence)
|
|
213
|
+
|
|
214
|
+
# -- graph: claim conflicts -> resolve / review ---------------------------
|
|
215
|
+
async def _graph_pass(self, report: Dict[str, Any], mode: str) -> None:
|
|
216
|
+
claims = await self._graph_claims()
|
|
217
|
+
if not claims:
|
|
218
|
+
return
|
|
219
|
+
for a, b in detect_conflicts(claims):
|
|
220
|
+
report["conflicts_detected"] += 1
|
|
221
|
+
cid = self.store.record_conflict(
|
|
222
|
+
"graph", a.subject, a.predicate, a.value, b.value,
|
|
223
|
+
meta_a={"ref": a.ref, "source": a.source,
|
|
224
|
+
"confidence": a.confidence, "ts": a.ts},
|
|
225
|
+
meta_b={"ref": b.ref, "source": b.source,
|
|
226
|
+
"confidence": b.confidence, "ts": b.ts})
|
|
227
|
+
picked = pick_winner(a, b)
|
|
228
|
+
if picked is None:
|
|
229
|
+
self.store.resolve_conflict(
|
|
230
|
+
cid, "unresolvable: equal recency/confidence/trust",
|
|
231
|
+
status="review")
|
|
232
|
+
self._surface_review(a, b, cid)
|
|
233
|
+
report["review_initiatives"] += 1
|
|
234
|
+
continue
|
|
235
|
+
winner, loser = picked
|
|
236
|
+
# Supersession is the REVERSIBLE resolution: the loser node is
|
|
237
|
+
# marked (epistemic_state + superseded_by pointer) with its old
|
|
238
|
+
# value preserved on it, and the transition is journaled — so
|
|
239
|
+
# the supervised rung may perform it. Any future DESTRUCTIVE
|
|
240
|
+
# resolution (deleting or merging nodes) must check
|
|
241
|
+
# mode == "live" here, never "supervised".
|
|
242
|
+
if mode not in ("live", "supervised"):
|
|
243
|
+
logger.info(
|
|
244
|
+
"SHADOW-BELIEF conflict %s: would supersede %r "
|
|
245
|
+
"(%s=%s) in favor of %r", cid, loser.value,
|
|
246
|
+
a.subject, a.predicate, winner.value)
|
|
247
|
+
continue
|
|
248
|
+
await self._supersede_memory(winner, loser, cid)
|
|
249
|
+
report["resolved"] += 1
|
|
250
|
+
|
|
251
|
+
async def _graph_claims(self, limit: int = 200) -> List[Claim]:
|
|
252
|
+
if self._graph is None or not hasattr(self._graph, "run_query"):
|
|
253
|
+
return []
|
|
254
|
+
try:
|
|
255
|
+
# Register this exact parameterized read query with the graph
|
|
256
|
+
# client's Cypher allowlist (single-sourced here).
|
|
257
|
+
if hasattr(type(self._graph), "register_allowed_cypher"):
|
|
258
|
+
type(self._graph).register_allowed_cypher(_MEMORY_QUERY)
|
|
259
|
+
rows = await self._graph.run_query(_MEMORY_QUERY,
|
|
260
|
+
{"limit": int(limit)})
|
|
261
|
+
except Exception as exc:
|
|
262
|
+
logger.debug("belief memory query failed: %s", exc)
|
|
263
|
+
return []
|
|
264
|
+
claims: List[Claim] = []
|
|
265
|
+
for r in rows or []:
|
|
266
|
+
content = str(r.get("content") or "")
|
|
267
|
+
created = r.get("created_at")
|
|
268
|
+
try:
|
|
269
|
+
ts = created.to_native().timestamp() if hasattr(
|
|
270
|
+
created, "to_native") else (
|
|
271
|
+
created.timestamp() if hasattr(created, "timestamp")
|
|
272
|
+
else 0.0)
|
|
273
|
+
except Exception:
|
|
274
|
+
ts = 0.0
|
|
275
|
+
claims.extend(claims_from_text(
|
|
276
|
+
content,
|
|
277
|
+
confidence=float(r.get("confidence") or 0.5),
|
|
278
|
+
ts=ts, source=str(r.get("source_type") or "inference"),
|
|
279
|
+
ref=str(r.get("id") or "")))
|
|
280
|
+
return claims
|
|
281
|
+
|
|
282
|
+
async def _supersede_memory(self, winner: Claim, loser: Claim,
|
|
283
|
+
conflict_id: str) -> None:
|
|
284
|
+
try:
|
|
285
|
+
if loser.ref and hasattr(self._graph, "transition_epistemic_state"):
|
|
286
|
+
await self._graph.transition_epistemic_state(
|
|
287
|
+
loser.ref, "superseded",
|
|
288
|
+
superseded_by=winner.ref or None)
|
|
289
|
+
self.store.record_supersession(
|
|
290
|
+
"graph", loser.subject, loser.predicate, loser.value,
|
|
291
|
+
winner.value, loser.confidence, winner.confidence,
|
|
292
|
+
reason=f"conflict {conflict_id}: lost on "
|
|
293
|
+
"recency/confidence/trust ordering")
|
|
294
|
+
self.store.resolve_conflict(
|
|
295
|
+
conflict_id,
|
|
296
|
+
f"kept {winner.value!r} ({winner.source}, "
|
|
297
|
+
f"conf={winner.confidence:.2f}); superseded {loser.ref}")
|
|
298
|
+
if self._journal is not None:
|
|
299
|
+
self._journal.record(
|
|
300
|
+
"beliefs",
|
|
301
|
+
f"superseded belief {loser.subject} {loser.predicate}="
|
|
302
|
+
f"{loser.value!r} in favor of {winner.value!r}",
|
|
303
|
+
reasoning="conflict resolution: recency > confidence > "
|
|
304
|
+
"source trust",
|
|
305
|
+
confidence=winner.confidence,
|
|
306
|
+
reversibility="recoverable", decision="acted",
|
|
307
|
+
ref=conflict_id)
|
|
308
|
+
except Exception:
|
|
309
|
+
logger.debug("supersede failed for %s", conflict_id,
|
|
310
|
+
exc_info=True)
|
|
311
|
+
|
|
312
|
+
def _surface_review(self, a: Claim, b: Claim, conflict_id: str) -> None:
|
|
313
|
+
"""Unresolvable -> internal review initiative (never a reach-out)."""
|
|
314
|
+
if self._initiatives is None:
|
|
315
|
+
return
|
|
316
|
+
try:
|
|
317
|
+
self._initiatives.create(
|
|
318
|
+
type="data_quality",
|
|
319
|
+
description=(
|
|
320
|
+
f"Belief conflict needs review: {a.subject} "
|
|
321
|
+
f"{a.predicate} = {a.value!r} vs {b.value!r} "
|
|
322
|
+
f"(equal recency/confidence/trust)"),
|
|
323
|
+
priority=0.55,
|
|
324
|
+
rationale=f"belief conflict {conflict_id}; sources "
|
|
325
|
+
f"{a.source} vs {b.source}",
|
|
326
|
+
dedup_key=f"belief_conflict:{conflict_id}",
|
|
327
|
+
source_type="belief_maintenance",
|
|
328
|
+
created_by="belief_engine",
|
|
329
|
+
)
|
|
330
|
+
except Exception:
|
|
331
|
+
logger.debug("review initiative creation failed", exc_info=True)
|
|
332
|
+
|
|
333
|
+
# -- stale decay -----------------------------------------------------------
|
|
334
|
+
async def _decay_pass(self, report: Dict[str, Any], mode: str) -> None:
|
|
335
|
+
stale = await stale_entities(self._world)
|
|
336
|
+
if not stale:
|
|
337
|
+
return
|
|
338
|
+
# Decay is reversible by construction (bounded multiplicative drop,
|
|
339
|
+
# floored at 0.1, never a deletion; prior value journaled below), so
|
|
340
|
+
# the supervised rung may perform it.
|
|
341
|
+
if mode not in ("live", "supervised"):
|
|
342
|
+
logger.info("SHADOW-BELIEF decay: %d stale entit(ies) would "
|
|
343
|
+
"lose confidence", len(stale))
|
|
344
|
+
return
|
|
345
|
+
for e in stale[:100]:
|
|
346
|
+
old = float(getattr(e, "confidence", 0.5) or 0.5)
|
|
347
|
+
new = await decay_entity(self._world, e)
|
|
348
|
+
report["decayed"] += 1
|
|
349
|
+
if self._journal is not None:
|
|
350
|
+
self._journal.record(
|
|
351
|
+
"beliefs",
|
|
352
|
+
f"decayed stale entity {getattr(e, 'name', '?')} "
|
|
353
|
+
f"confidence {old:.2f} -> {new:.2f}",
|
|
354
|
+
reasoning="unseen past TTL", decision="acted",
|
|
355
|
+
reversibility="recoverable",
|
|
356
|
+
ref=str(getattr(e, "id", "")))
|
|
357
|
+
|
|
358
|
+
# -- causal edges: contradiction + staleness (H2.3) -----------------------
|
|
359
|
+
async def _causal_pass(self, report: Dict[str, Any], mode: str) -> None:
|
|
360
|
+
"""Two causal fabrication controls:
|
|
361
|
+
|
|
362
|
+
CONTRADICTION: opposing causal claims (WM_CAUSES/WM_ENABLES vs
|
|
363
|
+
WM_BLOCKS/WM_INHIBITS over the same ordered pair) become a conflict
|
|
364
|
+
row (status "review") + review initiative and are NEVER auto-resolved
|
|
365
|
+
— not in live mode, not at any trust stage. Recency/confidence
|
|
366
|
+
ordering settles graph claims, but silently picking a causal winner
|
|
367
|
+
would let one confidently-worded fabrication erase a true edge.
|
|
368
|
+
|
|
369
|
+
STALENESS: edges unsupported past COLONY_CAUSAL_TTL_DAYS lose 0.05
|
|
370
|
+
confidence per run, floored at 0.2, never deleted — live/supervised
|
|
371
|
+
only (a bounded reversible mutation, so the supervised rung may
|
|
372
|
+
perform it, exactly like entity decay).
|
|
373
|
+
"""
|
|
374
|
+
if self._world is None:
|
|
375
|
+
return
|
|
376
|
+
from apsimo.world_model import causal_maintenance as cm
|
|
377
|
+
edges = await cm.load_causal_edges(self._world)
|
|
378
|
+
if not edges:
|
|
379
|
+
return
|
|
380
|
+
|
|
381
|
+
# Contradictions: detect + record + surface in every non-off mode
|
|
382
|
+
# (a conflict row is bookkeeping, not world-state mutation).
|
|
383
|
+
for pos, neg in cm.opposing_pairs(edges):
|
|
384
|
+
report["causal_conflicts"] += 1
|
|
385
|
+
pos_props = getattr(pos, "properties", None) or {}
|
|
386
|
+
neg_props = getattr(neg, "properties", None) or {}
|
|
387
|
+
cid = self.store.record_conflict(
|
|
388
|
+
"world_causal", f"{pos.source_id}->{pos.target_id}",
|
|
389
|
+
"causal_polarity", pos.relationship_type,
|
|
390
|
+
neg.relationship_type,
|
|
391
|
+
meta_a={"edge_id": pos.id, "confidence": pos.confidence,
|
|
392
|
+
"evidence": pos_props.get("evidence")},
|
|
393
|
+
meta_b={"edge_id": neg.id, "confidence": neg.confidence,
|
|
394
|
+
"evidence": neg_props.get("evidence")},
|
|
395
|
+
status="review")
|
|
396
|
+
self._surface_causal_review(pos, neg, cid)
|
|
397
|
+
report["review_initiatives"] += 1
|
|
398
|
+
|
|
399
|
+
# Staleness decay (live/supervised only).
|
|
400
|
+
stale = cm.stale_causal_edges(edges)
|
|
401
|
+
if not stale:
|
|
402
|
+
return
|
|
403
|
+
if mode not in ("live", "supervised"):
|
|
404
|
+
logger.info("SHADOW-BELIEF causal decay: %d stale causal "
|
|
405
|
+
"edge(s) would lose confidence", len(stale))
|
|
406
|
+
return
|
|
407
|
+
for e in stale[:100]:
|
|
408
|
+
old = float(getattr(e, "confidence", 0.0) or 0.0)
|
|
409
|
+
new = max(cm.CAUSAL_DECAY_FLOOR, old - cm.CAUSAL_DECAY_STEP)
|
|
410
|
+
if new >= old:
|
|
411
|
+
continue
|
|
412
|
+
support_ts = cm.support_timestamp(e)
|
|
413
|
+
e.confidence = new
|
|
414
|
+
props = dict(getattr(e, "properties", None) or {})
|
|
415
|
+
# Preserve the ORIGINAL support timestamp: the decay write must
|
|
416
|
+
# not reset the staleness clock, so the edge keeps losing 0.05
|
|
417
|
+
# per run until the floor or fresh corroboration.
|
|
418
|
+
if support_ts:
|
|
419
|
+
props.setdefault(cm.LAST_SUPPORT_PROP, support_ts)
|
|
420
|
+
props["stale_decays"] = int(props.get("stale_decays", 0) or 0) + 1
|
|
421
|
+
e.properties = props
|
|
422
|
+
try:
|
|
423
|
+
await self._world.upsert_relationship(e)
|
|
424
|
+
except Exception:
|
|
425
|
+
logger.debug("causal decay upsert failed", exc_info=True)
|
|
426
|
+
continue
|
|
427
|
+
report["causal_decayed"] += 1
|
|
428
|
+
if self._journal is not None:
|
|
429
|
+
self._journal.record(
|
|
430
|
+
"beliefs",
|
|
431
|
+
f"decayed stale causal edge {e.source_id} "
|
|
432
|
+
f"-{e.relationship_type}-> {e.target_id} confidence "
|
|
433
|
+
f"{old:.2f} -> {new:.2f}",
|
|
434
|
+
reasoning="no corroboration within "
|
|
435
|
+
f"{cm.causal_ttl_days():.0f}d TTL",
|
|
436
|
+
decision="acted", reversibility="recoverable",
|
|
437
|
+
ref=str(getattr(e, "id", "")))
|
|
438
|
+
|
|
439
|
+
def _surface_causal_review(self, pos: Any, neg: Any, cid: str) -> None:
|
|
440
|
+
"""Causal contradiction -> internal review initiative (never a
|
|
441
|
+
reach-out, never an auto-resolution)."""
|
|
442
|
+
if self._initiatives is None:
|
|
443
|
+
return
|
|
444
|
+
try:
|
|
445
|
+
self._initiatives.create(
|
|
446
|
+
type="data_quality",
|
|
447
|
+
description=(
|
|
448
|
+
f"Causal contradiction needs review: "
|
|
449
|
+
f"{pos.source_id} {pos.relationship_type} "
|
|
450
|
+
f"{pos.target_id} vs {neg.relationship_type} — "
|
|
451
|
+
"opposing causal claims are never auto-resolved"),
|
|
452
|
+
priority=0.6,
|
|
453
|
+
rationale=(f"causal conflict {cid}; confidences "
|
|
454
|
+
f"{float(pos.confidence or 0):.2f} vs "
|
|
455
|
+
f"{float(neg.confidence or 0):.2f}"),
|
|
456
|
+
dedup_key=f"causal_conflict:{cid}",
|
|
457
|
+
source_type="belief_maintenance",
|
|
458
|
+
created_by="belief_engine",
|
|
459
|
+
)
|
|
460
|
+
except Exception:
|
|
461
|
+
logger.debug("causal review initiative creation failed",
|
|
462
|
+
exc_info=True)
|
|
463
|
+
|
|
464
|
+
# -- reads -----------------------------------------------------------------
|
|
465
|
+
def conflicts(self, status: Optional[str] = None,
|
|
466
|
+
limit: int = 50) -> List[Dict[str, Any]]:
|
|
467
|
+
return self.store.conflicts(status=status, limit=limit)
|
|
468
|
+
|
|
469
|
+
def status(self) -> Dict[str, Any]:
|
|
470
|
+
return {
|
|
471
|
+
"mode": beliefs_mode(),
|
|
472
|
+
"effective_mode": self._effective_mode(),
|
|
473
|
+
"last_report": self.last_report,
|
|
474
|
+
"open_conflicts": len(self.store.conflicts(status="open",
|
|
475
|
+
limit=1000)),
|
|
476
|
+
"review_conflicts": len(self.store.conflicts(status="review",
|
|
477
|
+
limit=1000)),
|
|
478
|
+
"recent_supersessions": self.store.supersessions(limit=10),
|
|
479
|
+
}
|