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,137 @@
|
|
|
1
|
+
"""The supervised-live rung, generically (H1.1).
|
|
2
|
+
|
|
3
|
+
The trust ladder has a structural catch-22: graduation ask_first ->
|
|
4
|
+
act_first requires a REAL (non-shadow) track record, but a domain that only
|
|
5
|
+
acts at act_first never produces one. The beliefs engine solved this for
|
|
6
|
+
itself (U26) with a domain-specific flag; this module is the same rung for
|
|
7
|
+
ANY domain, so every future consumer shares one definition of "supervised"
|
|
8
|
+
instead of growing private variants.
|
|
9
|
+
|
|
10
|
+
The rung: at trust stage ask_first, a domain listed in
|
|
11
|
+
COLONY_SUPERVISED_LIVE_DOMAINS (comma-separated, default EMPTY = rung off
|
|
12
|
+
everywhere) may perform REVERSIBLE operations only, journaled, with
|
|
13
|
+
outcomes recorded shadow=False — building exactly the track record
|
|
14
|
+
graduation needs. Destructive operations still require the full "live"
|
|
15
|
+
mode (act_first stage or explicit env override).
|
|
16
|
+
|
|
17
|
+
What counts as reversible is NOT the caller's judgment call: it is pinned
|
|
18
|
+
in ``REVERSIBLE_CONTRACT`` below, and ``reversible()`` fails CLOSED — an
|
|
19
|
+
unknown domain or an unlisted operation is non-reversible, full stop.
|
|
20
|
+
Trust-engine errors likewise degrade to the plain env mode (never upward).
|
|
21
|
+
|
|
22
|
+
Legacy alias: COLONY_BELIEFS_SUPERVISED_LIVE=1 still enables the rung for
|
|
23
|
+
the beliefs domain (the live deployments that set it keep working).
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
from __future__ import annotations
|
|
27
|
+
|
|
28
|
+
import logging
|
|
29
|
+
import os
|
|
30
|
+
from typing import Any, Dict, FrozenSet
|
|
31
|
+
|
|
32
|
+
logger = logging.getLogger(__name__)
|
|
33
|
+
|
|
34
|
+
# Domain -> operations that are reversible BY CONSTRUCTION (the operation
|
|
35
|
+
# preserves enough prior state to undo it, and never deletes or merges).
|
|
36
|
+
# Adding an entry here is a design review, not a config change: the
|
|
37
|
+
# operation's implementation must journal prior state and keep the old
|
|
38
|
+
# value recoverable.
|
|
39
|
+
REVERSIBLE_CONTRACT: Dict[str, FrozenSet[str]] = {
|
|
40
|
+
# Belief maintenance (see beliefs/engine.py):
|
|
41
|
+
# - supersede: loser node is MARKED (epistemic_state + superseded_by),
|
|
42
|
+
# old value preserved on it, transition journaled.
|
|
43
|
+
# - decay: bounded multiplicative confidence drop floored at 0.1,
|
|
44
|
+
# never a deletion, prior value journaled.
|
|
45
|
+
"beliefs": frozenset({"supersede", "decay"}),
|
|
46
|
+
# World-model LLM extraction (see world_model/llm_extract.py, H1.5):
|
|
47
|
+
# - entity_upsert: creates/updates an entity row, journaled with its
|
|
48
|
+
# id — removable without touching anything else.
|
|
49
|
+
# - alias_merge: adds an alias string to an existing entity; the
|
|
50
|
+
# alias list is additive and the addition is journaled.
|
|
51
|
+
# - edge_corroborate: bounded confidence bump on an EXISTING
|
|
52
|
+
# non-causal edge, journaled; never creates, deletes, or retypes.
|
|
53
|
+
# Edge CREATION and every causal edge type stay live-only (causal
|
|
54
|
+
# writes additionally require the explicit causal-extract unlock).
|
|
55
|
+
"world_model": frozenset({"entity_upsert", "alias_merge",
|
|
56
|
+
"edge_corroborate"}),
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
# Domain-specific flags that predate the generic rung; kept working forever.
|
|
60
|
+
_LEGACY_ALIASES: Dict[str, str] = {
|
|
61
|
+
"beliefs": "COLONY_BELIEFS_SUPERVISED_LIVE",
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
_TRUTHY = ("1", "true", "yes")
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def strict_trust_outcomes() -> bool:
|
|
68
|
+
"""H1.3: is trust-outcome integrity enforced? DEFAULT ON.
|
|
69
|
+
|
|
70
|
+
Strict means a maintenance run only feeds the trust ladder what it
|
|
71
|
+
earned: a run where a pass raised records a FAILURE; a run that
|
|
72
|
+
actually mutated state records a SUCCESS; a no-op run records NOTHING.
|
|
73
|
+
Without this, an engine that unconditionally logs "success" per run
|
|
74
|
+
graduates to act_first on a streak of doing nothing.
|
|
75
|
+
|
|
76
|
+
COLONY_TRUST_STRICT_OUTCOMES=0 restores the legacy unconditional
|
|
77
|
+
per-run success (a deliberate escape hatch, not a recommended mode).
|
|
78
|
+
"""
|
|
79
|
+
return os.environ.get("COLONY_TRUST_STRICT_OUTCOMES",
|
|
80
|
+
"1").strip().lower() in _TRUTHY
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def supervised_domains() -> FrozenSet[str]:
|
|
84
|
+
"""Domains the generic flag enables the rung for (default: none)."""
|
|
85
|
+
raw = os.environ.get("COLONY_SUPERVISED_LIVE_DOMAINS", "")
|
|
86
|
+
return frozenset(d.strip().lower() for d in raw.split(",") if d.strip())
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def supervised_enabled(domain: str) -> bool:
|
|
90
|
+
"""Is the supervised rung unlocked for ``domain``? (generic flag OR the
|
|
91
|
+
domain's legacy alias)."""
|
|
92
|
+
domain = (domain or "").strip().lower()
|
|
93
|
+
if not domain:
|
|
94
|
+
return False
|
|
95
|
+
if domain in supervised_domains():
|
|
96
|
+
return True
|
|
97
|
+
legacy = _LEGACY_ALIASES.get(domain)
|
|
98
|
+
if legacy:
|
|
99
|
+
return os.environ.get(legacy, "0").strip().lower() in _TRUTHY
|
|
100
|
+
return False
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def reversible(domain: str, op: str) -> bool:
|
|
104
|
+
"""Fail-closed reversibility check: True only for operations explicitly
|
|
105
|
+
listed in REVERSIBLE_CONTRACT. Unknown domain or op => non-reversible."""
|
|
106
|
+
ops = REVERSIBLE_CONTRACT.get((domain or "").strip().lower())
|
|
107
|
+
if not ops:
|
|
108
|
+
return False
|
|
109
|
+
return (op or "").strip().lower() in ops
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def effective_mode(domain: str, env_mode: str, trust: Any) -> str:
|
|
113
|
+
"""Env mode graduated by the trust engine, with the supervised rung.
|
|
114
|
+
|
|
115
|
+
Resolution:
|
|
116
|
+
* env "off"/"live" are the owner override — returned as-is.
|
|
117
|
+
* no trust engine, or any trust error => plain env mode (degrade,
|
|
118
|
+
never upgrade).
|
|
119
|
+
* stage act_first => "live".
|
|
120
|
+
* stage ask_first AND supervised_enabled(domain) => "supervised".
|
|
121
|
+
* otherwise => "shadow".
|
|
122
|
+
"""
|
|
123
|
+
if env_mode in ("off", "live"):
|
|
124
|
+
return env_mode
|
|
125
|
+
if trust is None:
|
|
126
|
+
return env_mode
|
|
127
|
+
try:
|
|
128
|
+
stage = trust.stage(domain, default="shadow")
|
|
129
|
+
except Exception:
|
|
130
|
+
logger.debug("trust stage lookup failed for %s; using env mode",
|
|
131
|
+
domain, exc_info=True)
|
|
132
|
+
return env_mode
|
|
133
|
+
if stage == "act_first":
|
|
134
|
+
return "live"
|
|
135
|
+
if stage == "ask_first" and supervised_enabled(domain):
|
|
136
|
+
return "supervised"
|
|
137
|
+
return "shadow"
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"""The workspace thinker: one bounded LLM reflection on a concern (Mind M2).
|
|
2
|
+
|
|
3
|
+
Kept separate from workspace.py so the engine stays model-agnostic. Given a
|
|
4
|
+
concern and a little real context (recent memory, the concern's own notes),
|
|
5
|
+
the LLM returns a small structured judgement: did it make progress, is it
|
|
6
|
+
resolved, a one-line note, and optionally an action to take (emit an
|
|
7
|
+
initiative, propose an experiment, or write a memory note). In shadow mode
|
|
8
|
+
actions are recorded but not executed.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import json
|
|
14
|
+
import logging
|
|
15
|
+
import re
|
|
16
|
+
from typing import Any, Awaitable, Callable, Dict, Optional
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
_JSON_RE = re.compile(r"\{.*\}", re.DOTALL)
|
|
21
|
+
|
|
22
|
+
_SYSTEM = """You are the reflective inner loop of an autonomous assistant.
|
|
23
|
+
You are given ONE concern currently on the assistant's mind, plus any prior
|
|
24
|
+
notes. Think about it for a moment and decide what, if anything, moves it
|
|
25
|
+
forward. Output ONE JSON object, no prose:
|
|
26
|
+
|
|
27
|
+
progress true if this thought advanced the concern
|
|
28
|
+
resolve true if the concern is now settled and should leave the mind
|
|
29
|
+
note one sentence: the substance of the thought (what you concluded)
|
|
30
|
+
action optional. One of:
|
|
31
|
+
{"kind":"initiative","title":..,"detail":..} surface something to the owner
|
|
32
|
+
{"kind":"experiment","hypothesis":..,"ref":..,"metric":..} propose a self-experiment
|
|
33
|
+
{"kind":"memory","content":..} record a durable note
|
|
34
|
+
{"kind":"none"} nothing to do yet
|
|
35
|
+
|
|
36
|
+
Be honest: most single thoughts make partial progress, not resolution. If
|
|
37
|
+
there is genuinely nothing to do, say progress=false, resolve=false,
|
|
38
|
+
action={"kind":"none"}. Never invent facts; reason only from what is given."""
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def build_thinker(router: Any, *, graph: Any = None
|
|
42
|
+
) -> Callable[[Any], Awaitable[Dict[str, Any]]]:
|
|
43
|
+
"""Return an async thinker(concern) -> outcome dict bound to the router."""
|
|
44
|
+
|
|
45
|
+
async def thinker(concern) -> Dict[str, Any]:
|
|
46
|
+
context = ""
|
|
47
|
+
mem_ids: list = []
|
|
48
|
+
if graph is not None:
|
|
49
|
+
try:
|
|
50
|
+
hits = await graph.recall(concern.summary, limit=4,
|
|
51
|
+
min_confidence=0.1)
|
|
52
|
+
if hits:
|
|
53
|
+
context = "\n".join(
|
|
54
|
+
f"- {str(h.get('content',''))[:160]}" for h in hits[:4])
|
|
55
|
+
# keep the ids of the memories we actually consulted, so the
|
|
56
|
+
# caller can record a real concern->memory provenance link
|
|
57
|
+
mem_ids = [h.get("id") for h in hits[:4] if h.get("id")]
|
|
58
|
+
except Exception:
|
|
59
|
+
context = ""
|
|
60
|
+
user = (
|
|
61
|
+
f"Concern ({concern.kind}, salience {concern.salience:.2f}, "
|
|
62
|
+
f"thought {concern.thoughts_spent} of {concern.max_thoughts}):\n"
|
|
63
|
+
f"{concern.summary}\n"
|
|
64
|
+
+ (f"\nPrior note: {concern.last_note}\n" if concern.last_note else "")
|
|
65
|
+
+ (f"\nRelevant memory:\n{context}\n" if context else ""))
|
|
66
|
+
try:
|
|
67
|
+
resp = await router.complete(
|
|
68
|
+
[{"role": "system", "content": _SYSTEM},
|
|
69
|
+
{"role": "user", "content": user}],
|
|
70
|
+
context={"task": "workspace_thinking"})
|
|
71
|
+
out = _parse(getattr(resp, "content", "") or "")
|
|
72
|
+
out["memory_refs"] = mem_ids
|
|
73
|
+
return out
|
|
74
|
+
except Exception as exc:
|
|
75
|
+
logger.warning("thinker LLM call failed: %s", exc)
|
|
76
|
+
return {"progress": False, "resolve": False,
|
|
77
|
+
"note": "", "action": {"kind": "none"},
|
|
78
|
+
"memory_refs": mem_ids}
|
|
79
|
+
|
|
80
|
+
return thinker
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _parse(content: str) -> Dict[str, Any]:
|
|
84
|
+
m = _JSON_RE.search(content)
|
|
85
|
+
if not m:
|
|
86
|
+
return {"progress": False, "resolve": False, "note": "",
|
|
87
|
+
"action": {"kind": "none"}}
|
|
88
|
+
try:
|
|
89
|
+
d = json.loads(m.group(0))
|
|
90
|
+
except json.JSONDecodeError:
|
|
91
|
+
return {"progress": False, "resolve": False, "note": "",
|
|
92
|
+
"action": {"kind": "none"}}
|
|
93
|
+
return {
|
|
94
|
+
"progress": bool(d.get("progress")),
|
|
95
|
+
"resolve": bool(d.get("resolve")),
|
|
96
|
+
"note": str(d.get("note", ""))[:400],
|
|
97
|
+
"action": d.get("action") if isinstance(d.get("action"), dict)
|
|
98
|
+
else {"kind": "none"},
|
|
99
|
+
}
|
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
"""TrustEngine -- graduated, earned autonomy per action class (Amendment 1).
|
|
2
|
+
|
|
3
|
+
Replaces static capability gating with calibrated trust:
|
|
4
|
+
|
|
5
|
+
- Every action class (domain) has a confidence score computed from REAL
|
|
6
|
+
outcomes (Laplace-smoothed success rate over non-shadow events), scaled by
|
|
7
|
+
owner reaction (TypeFeedbackStore multiplier) and penalized by audit
|
|
8
|
+
violations.
|
|
9
|
+
- Each domain carries a stage: shadow (calibration) -> ask_first -> act_first.
|
|
10
|
+
`gate()` turns (stage, confidence, floor) into a decision: act | ask | hold,
|
|
11
|
+
and journals it. Below-threshold confidence asks even at act_first.
|
|
12
|
+
- Auto-graduation: clean calibration promotes shadow -> ask_first; a real
|
|
13
|
+
track record promotes ask_first -> act_first. Each graduation queues an
|
|
14
|
+
owner NOTIFICATION (not a permission request) drained through the guarded
|
|
15
|
+
delivery path.
|
|
16
|
+
- Circuit breaker: N real failures inside the window, or ANY audit
|
|
17
|
+
violation, demotes the class to ask_first and journals why.
|
|
18
|
+
- Immutable floor: money movement, non-recoverable deletion,
|
|
19
|
+
credential/security changes, and bulk third-party messaging are never
|
|
20
|
+
self-decidable, regardless of confidence.
|
|
21
|
+
|
|
22
|
+
The env COLONY_<X>_MODE remains the owner override: "off" stays off and
|
|
23
|
+
"live" is live; "shadow" means "start in calibration and earn upward".
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
from __future__ import annotations
|
|
27
|
+
|
|
28
|
+
import logging
|
|
29
|
+
import os
|
|
30
|
+
import re
|
|
31
|
+
import sqlite3
|
|
32
|
+
import threading
|
|
33
|
+
import time
|
|
34
|
+
from collections import deque
|
|
35
|
+
from typing import Any, Dict, List, Optional
|
|
36
|
+
|
|
37
|
+
from apsimo.self_model.store import CompetenceStore
|
|
38
|
+
|
|
39
|
+
logger = logging.getLogger(__name__)
|
|
40
|
+
|
|
41
|
+
STAGES = ("shadow", "ask_first", "act_first")
|
|
42
|
+
|
|
43
|
+
# Immutable floor (Amendment 1.6): kept SMALL and principled
|
|
44
|
+
# (irreversibility x blast radius). Matched conservatively on action text.
|
|
45
|
+
_FLOOR_PATTERNS: Dict[str, re.Pattern] = {
|
|
46
|
+
"money_movement": re.compile(
|
|
47
|
+
r"\b(?:wire|transfer|send|move)\s+(?:\$|money|funds|payment)|"
|
|
48
|
+
r"\b(?:purchase|buy|pay|spend|subscribe)\b.{0,30}\$|"
|
|
49
|
+
r"\bpayment\s+(?:of|for)\b|\$\d{2,}", re.IGNORECASE),
|
|
50
|
+
"irreversible_deletion": re.compile(
|
|
51
|
+
r"\b(?:rm\s+-rf|drop\s+(?:table|database)|delete\s+permanently|"
|
|
52
|
+
r"wipe|purge\s+all|force[- ]?push|erase\s+(?:all|everything))\b",
|
|
53
|
+
re.IGNORECASE),
|
|
54
|
+
"credential_change": re.compile(
|
|
55
|
+
r"\b(?:rotate|change|reset|revoke|create)\b.{0,40}\b(?:credential|"
|
|
56
|
+
r"password|api[_ ]?key|secret|token|ssh[- ]?key|certificate)\b|"
|
|
57
|
+
r"\bsecurity\s+settings?\b", re.IGNORECASE),
|
|
58
|
+
"bulk_third_party_messaging": re.compile(
|
|
59
|
+
r"\b(?:bulk|mass|broadcast|blast|everyone|all\s+contacts)\b.{0,40}"
|
|
60
|
+
r"\b(?:message|text|email|sms|dm)\b|"
|
|
61
|
+
r"\b(?:message|text|email|sms|dm)\b.{0,40}\b(?:bulk|mass|broadcast|"
|
|
62
|
+
r"blast|everyone|all\s+contacts)\b", re.IGNORECASE),
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _fenv(name: str, default: float) -> float:
|
|
67
|
+
try:
|
|
68
|
+
return float(os.environ.get(name, str(default)))
|
|
69
|
+
except (TypeError, ValueError):
|
|
70
|
+
return default
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _ienv(name: str, default: int) -> int:
|
|
74
|
+
try:
|
|
75
|
+
return int(os.environ.get(name, str(default)))
|
|
76
|
+
except (TypeError, ValueError):
|
|
77
|
+
return default
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def autograduate_enabled() -> bool:
|
|
81
|
+
return os.environ.get(
|
|
82
|
+
"COLONY_TRUST_AUTOGRADUATE", "true").strip().lower() != "false"
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def floor_class(text: str) -> Optional[str]:
|
|
86
|
+
"""The immutable-floor class this action text falls into, if any."""
|
|
87
|
+
t = (text or "")
|
|
88
|
+
for name, pat in _FLOOR_PATTERNS.items():
|
|
89
|
+
if pat.search(t):
|
|
90
|
+
return name
|
|
91
|
+
return None
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class TrustEngine:
|
|
95
|
+
def __init__(self, store: CompetenceStore, *,
|
|
96
|
+
db_path: Optional[str] = None,
|
|
97
|
+
feedback_store: Any = None,
|
|
98
|
+
journal: Any = None) -> None:
|
|
99
|
+
self._store = store
|
|
100
|
+
self._feedback = feedback_store
|
|
101
|
+
self._journal = journal
|
|
102
|
+
self._lock = threading.RLock()
|
|
103
|
+
self._conn = sqlite3.connect(str(db_path) if db_path else ":memory:",
|
|
104
|
+
check_same_thread=False)
|
|
105
|
+
self._conn.row_factory = sqlite3.Row
|
|
106
|
+
with self._lock:
|
|
107
|
+
self._conn.execute(
|
|
108
|
+
"""CREATE TABLE IF NOT EXISTS trust_stage (
|
|
109
|
+
domain TEXT PRIMARY KEY, stage TEXT NOT NULL,
|
|
110
|
+
demotions INTEGER DEFAULT 0,
|
|
111
|
+
graduated_at REAL, updated_at REAL
|
|
112
|
+
)""")
|
|
113
|
+
# Durable notice queue: a graduation/demotion notice must survive
|
|
114
|
+
# restarts until the owner actually receives it (Amendment 1.2).
|
|
115
|
+
self._conn.execute(
|
|
116
|
+
"""CREATE TABLE IF NOT EXISTS trust_notices (
|
|
117
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
118
|
+
domain TEXT, stage TEXT, prior TEXT,
|
|
119
|
+
demotion INTEGER DEFAULT 0, reason TEXT,
|
|
120
|
+
ts REAL, delivered INTEGER DEFAULT 0
|
|
121
|
+
)""")
|
|
122
|
+
self._conn.commit()
|
|
123
|
+
# Back-compat in-process mirror (tests peek at it); the durable queue
|
|
124
|
+
# in trust_notices is authoritative.
|
|
125
|
+
self.pending_notices: deque = deque(maxlen=50)
|
|
126
|
+
|
|
127
|
+
# -- durable notice queue ----------------------------------------------
|
|
128
|
+
def _queue_notice(self, notice: Dict[str, Any]) -> None:
|
|
129
|
+
self.pending_notices.append(notice)
|
|
130
|
+
try:
|
|
131
|
+
with self._lock:
|
|
132
|
+
self._conn.execute(
|
|
133
|
+
"INSERT INTO trust_notices (domain, stage, prior, "
|
|
134
|
+
"demotion, reason, ts) VALUES (?, ?, ?, ?, ?, ?)",
|
|
135
|
+
(notice.get("domain"), notice.get("stage"),
|
|
136
|
+
notice.get("prior"), 1 if notice.get("demotion") else 0,
|
|
137
|
+
notice.get("reason"), notice.get("ts")))
|
|
138
|
+
self._conn.commit()
|
|
139
|
+
except Exception:
|
|
140
|
+
logger.debug("trust notice persist failed", exc_info=True)
|
|
141
|
+
|
|
142
|
+
def undelivered_notices(self, limit: int = 10) -> List[Dict[str, Any]]:
|
|
143
|
+
with self._lock:
|
|
144
|
+
rows = self._conn.execute(
|
|
145
|
+
"SELECT * FROM trust_notices WHERE delivered=0 "
|
|
146
|
+
"ORDER BY ts ASC LIMIT ?", (limit,)).fetchall()
|
|
147
|
+
return [dict(r) for r in rows]
|
|
148
|
+
|
|
149
|
+
def mark_notice_delivered(self, notice_id: int) -> None:
|
|
150
|
+
with self._lock:
|
|
151
|
+
self._conn.execute(
|
|
152
|
+
"UPDATE trust_notices SET delivered=1 WHERE id=?",
|
|
153
|
+
(notice_id,))
|
|
154
|
+
self._conn.commit()
|
|
155
|
+
|
|
156
|
+
@staticmethod
|
|
157
|
+
def _autonomy_evidence(events: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
|
|
158
|
+
"""Exclude self-attested worker wins while retaining negative evidence."""
|
|
159
|
+
|
|
160
|
+
return [
|
|
161
|
+
event for event in events
|
|
162
|
+
if not (
|
|
163
|
+
event.get("source") == "task_queue.governor"
|
|
164
|
+
and event.get("outcome") == "success"
|
|
165
|
+
and event.get("evidence_status") != "verified"
|
|
166
|
+
)
|
|
167
|
+
]
|
|
168
|
+
|
|
169
|
+
# -- confidence -------------------------------------------------------
|
|
170
|
+
def confidence(self, domain: str) -> float:
|
|
171
|
+
"""Calibrated confidence for a domain from REAL (non-shadow) outcomes."""
|
|
172
|
+
# An unresolved provenance gap is not negative evidence, but it is
|
|
173
|
+
# insufficient evidence for autonomy. Fail closed until an operator
|
|
174
|
+
# either invalidates exact rows or appends a gap resolution.
|
|
175
|
+
try:
|
|
176
|
+
if self._store.active_evidence_gaps(domain):
|
|
177
|
+
return 0.0
|
|
178
|
+
except (AttributeError, TypeError):
|
|
179
|
+
pass
|
|
180
|
+
events = self._autonomy_evidence(
|
|
181
|
+
self._store.events(domain, include_shadow=False)
|
|
182
|
+
)
|
|
183
|
+
n = len(events)
|
|
184
|
+
wins = sum(1 for e in events if e["outcome"] == "success")
|
|
185
|
+
violations = sum(1 for e in events if e.get("violation"))
|
|
186
|
+
conf = (wins + 1.0) / (n + 2.0) # Laplace-smoothed
|
|
187
|
+
if self._feedback is not None:
|
|
188
|
+
try:
|
|
189
|
+
mult = float(self._feedback.multiplier(domain))
|
|
190
|
+
conf *= max(0.7, min(1.2, mult)) # owner reaction, clamped
|
|
191
|
+
except Exception:
|
|
192
|
+
pass
|
|
193
|
+
conf -= 0.15 * min(violations, 3) # audit violations bite
|
|
194
|
+
# Calibration penalty: a domain whose STATED confidences run above
|
|
195
|
+
# its REALIZED outcomes is overconfident, and its earned trust is
|
|
196
|
+
# discounted accordingly (capped at -0.2, needs >=5 stated events).
|
|
197
|
+
# Underconfidence is never penalized.
|
|
198
|
+
try:
|
|
199
|
+
calib = self._store.calibration(domain)
|
|
200
|
+
if calib and calib.get("n", 0) >= 5:
|
|
201
|
+
over = max(0.0, float(calib.get("mean_stated", 0.0))
|
|
202
|
+
- float(calib.get("mean_realized", 0.0)))
|
|
203
|
+
conf -= min(0.2, 0.5 * over)
|
|
204
|
+
except Exception:
|
|
205
|
+
pass
|
|
206
|
+
return max(0.0, min(1.0, conf))
|
|
207
|
+
|
|
208
|
+
# -- stage persistence --------------------------------------------------
|
|
209
|
+
def stage(self, domain: str, default: str = "shadow") -> str:
|
|
210
|
+
domain = (domain or "unknown").strip().lower()
|
|
211
|
+
with self._lock:
|
|
212
|
+
row = self._conn.execute(
|
|
213
|
+
"SELECT stage FROM trust_stage WHERE domain=?",
|
|
214
|
+
(domain,)).fetchone()
|
|
215
|
+
if row is None:
|
|
216
|
+
return default if default in STAGES else "shadow"
|
|
217
|
+
return row["stage"]
|
|
218
|
+
|
|
219
|
+
def set_stage(self, domain: str, stage: str, *, reason: str = "",
|
|
220
|
+
demotion: bool = False, notify: bool = True) -> None:
|
|
221
|
+
domain = (domain or "unknown").strip().lower()
|
|
222
|
+
if stage not in STAGES:
|
|
223
|
+
return
|
|
224
|
+
prior = self.stage(domain, default="")
|
|
225
|
+
now = time.time()
|
|
226
|
+
with self._lock:
|
|
227
|
+
self._conn.execute(
|
|
228
|
+
"""INSERT INTO trust_stage (domain, stage, demotions,
|
|
229
|
+
graduated_at, updated_at)
|
|
230
|
+
VALUES (?, ?, ?, ?, ?)
|
|
231
|
+
ON CONFLICT(domain) DO UPDATE SET
|
|
232
|
+
stage=?, demotions=demotions+?, graduated_at=?,
|
|
233
|
+
updated_at=?""",
|
|
234
|
+
(domain, stage, 1 if demotion else 0, now, now,
|
|
235
|
+
stage, 1 if demotion else 0, now, now))
|
|
236
|
+
self._conn.commit()
|
|
237
|
+
if prior == stage:
|
|
238
|
+
return
|
|
239
|
+
verb = "demoted" if demotion else "graduated"
|
|
240
|
+
logger.info("Trust: %s %s %s -> %s (%s)", domain, verb,
|
|
241
|
+
prior or "(unset)", stage, reason or "threshold")
|
|
242
|
+
if self._journal is not None:
|
|
243
|
+
self._journal.record(
|
|
244
|
+
"trust", f"{domain} {verb} to {stage}",
|
|
245
|
+
reasoning=reason, confidence=self.confidence(domain),
|
|
246
|
+
decision="noted")
|
|
247
|
+
if notify:
|
|
248
|
+
self._queue_notice({
|
|
249
|
+
"domain": domain, "stage": stage, "prior": prior,
|
|
250
|
+
"demotion": demotion, "reason": reason, "ts": now,
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
def snapshot(self) -> List[Dict[str, Any]]:
|
|
254
|
+
from apsimo.self_model.supervised import supervised_enabled
|
|
255
|
+
with self._lock:
|
|
256
|
+
rows = self._conn.execute(
|
|
257
|
+
"SELECT * FROM trust_stage ORDER BY updated_at DESC").fetchall()
|
|
258
|
+
out = []
|
|
259
|
+
for r in rows:
|
|
260
|
+
d = dict(r)
|
|
261
|
+
d["confidence"] = round(self.confidence(d["domain"]), 3)
|
|
262
|
+
# Rung visibility (H1.4): whether the supervised rung is
|
|
263
|
+
# unlocked for the domain, and the rung it would actually run
|
|
264
|
+
# at — so "why is beliefs mutating at ask_first?" is answerable
|
|
265
|
+
# from GET /v1/host/self instead of by reading env vars.
|
|
266
|
+
try:
|
|
267
|
+
d["supervised_enabled"] = supervised_enabled(d["domain"])
|
|
268
|
+
d["effective_rung"] = (
|
|
269
|
+
"supervised"
|
|
270
|
+
if d["stage"] == "ask_first" and d["supervised_enabled"]
|
|
271
|
+
else d["stage"])
|
|
272
|
+
except Exception:
|
|
273
|
+
d["supervised_enabled"] = False
|
|
274
|
+
d["effective_rung"] = d.get("stage")
|
|
275
|
+
out.append(d)
|
|
276
|
+
return out
|
|
277
|
+
|
|
278
|
+
# -- the gate -----------------------------------------------------------
|
|
279
|
+
def gate(self, domain: str, description: str, *,
|
|
280
|
+
reasoning: str = "", reversibility: str = "reversible",
|
|
281
|
+
default_stage: str = "shadow", ref: str = "") -> Dict[str, Any]:
|
|
282
|
+
"""Decide act | ask | hold for one concrete action, and journal it.
|
|
283
|
+
|
|
284
|
+
The immutable floor always asks. Shadow (calibration) holds. ask_first
|
|
285
|
+
asks. act_first acts when confidence clears the threshold, otherwise
|
|
286
|
+
asks (uncertainty asks; it never silently drops work).
|
|
287
|
+
"""
|
|
288
|
+
domain = (domain or "unknown").strip().lower()
|
|
289
|
+
floor = floor_class(f"{description} {reasoning}")
|
|
290
|
+
conf = self.confidence(domain)
|
|
291
|
+
stage = self.stage(domain, default=default_stage)
|
|
292
|
+
if floor:
|
|
293
|
+
decision = "ask"
|
|
294
|
+
why = f"immutable floor ({floor}): never self-decidable"
|
|
295
|
+
elif stage == "shadow":
|
|
296
|
+
decision = "hold"
|
|
297
|
+
why = "calibration stage"
|
|
298
|
+
elif stage == "ask_first":
|
|
299
|
+
decision = "ask"
|
|
300
|
+
why = f"ask_first stage (confidence {conf:.2f})"
|
|
301
|
+
else: # act_first
|
|
302
|
+
if conf >= _fenv("COLONY_TRUST_ACT_THRESHOLD", 0.8):
|
|
303
|
+
decision = "act"
|
|
304
|
+
why = f"act_first, confidence {conf:.2f}"
|
|
305
|
+
else:
|
|
306
|
+
decision = "ask"
|
|
307
|
+
why = f"act_first but confidence {conf:.2f} below threshold"
|
|
308
|
+
journal_id = -1
|
|
309
|
+
if self._journal is not None:
|
|
310
|
+
journal_id = self._journal.record(
|
|
311
|
+
domain, description, reasoning=reasoning or why,
|
|
312
|
+
confidence=conf, reversibility=reversibility,
|
|
313
|
+
decision={"act": "acted", "ask": "asked",
|
|
314
|
+
"hold": "held"}[decision],
|
|
315
|
+
ref=ref)
|
|
316
|
+
return {"decision": decision, "stage": stage, "confidence": conf,
|
|
317
|
+
"floor": floor, "why": why, "journal_id": journal_id}
|
|
318
|
+
|
|
319
|
+
# -- breaker + graduation (invoked after every recorded outcome) --------
|
|
320
|
+
def after_outcome(self, domain: str) -> None:
|
|
321
|
+
domain = (domain or "unknown").strip().lower()
|
|
322
|
+
has_evidence_gap = False
|
|
323
|
+
try:
|
|
324
|
+
has_evidence_gap = bool(
|
|
325
|
+
self._store.active_evidence_gaps(domain))
|
|
326
|
+
except (AttributeError, TypeError):
|
|
327
|
+
pass
|
|
328
|
+
window = _fenv("COLONY_TRUST_BREAKER_WINDOW_HOURS", 24.0) * 3600.0
|
|
329
|
+
recent = self._autonomy_evidence(self._store.events(
|
|
330
|
+
domain,
|
|
331
|
+
since=time.time() - window,
|
|
332
|
+
include_shadow=False,
|
|
333
|
+
))
|
|
334
|
+
failures = sum(1 for e in recent
|
|
335
|
+
if e["outcome"] in ("failure", "timeout"))
|
|
336
|
+
violations = sum(1 for e in recent if e.get("violation"))
|
|
337
|
+
stage = self.stage(domain)
|
|
338
|
+
# Circuit breaker: demote on violations or clustered failures.
|
|
339
|
+
if stage == "act_first" and (
|
|
340
|
+
violations > 0
|
|
341
|
+
or failures >= _ienv("COLONY_TRUST_BREAKER_FAILURES", 3)):
|
|
342
|
+
self.set_stage(
|
|
343
|
+
domain, "ask_first", demotion=True,
|
|
344
|
+
reason=(f"{violations} violation(s)" if violations
|
|
345
|
+
else f"{failures} failures in "
|
|
346
|
+
f"{window / 3600:.0f}h window"))
|
|
347
|
+
return
|
|
348
|
+
# Unknown history blocks graduation, but it must not disable the
|
|
349
|
+
# safety circuit breaker for a new, attributable failure/violation.
|
|
350
|
+
if has_evidence_gap:
|
|
351
|
+
return
|
|
352
|
+
if not autograduate_enabled():
|
|
353
|
+
return
|
|
354
|
+
# Graduation.
|
|
355
|
+
if stage == "shadow":
|
|
356
|
+
cal = self._autonomy_evidence(
|
|
357
|
+
self._store.events(domain, include_shadow=True)
|
|
358
|
+
)
|
|
359
|
+
n = len(cal)
|
|
360
|
+
bad = sum(1 for e in cal if e["outcome"] != "success")
|
|
361
|
+
if n >= _ienv("COLONY_TRUST_ASK_MIN_N", 3) and bad == 0:
|
|
362
|
+
self.set_stage(domain, "ask_first",
|
|
363
|
+
reason=f"{n} clean calibration run(s)")
|
|
364
|
+
elif stage == "ask_first":
|
|
365
|
+
real = self._autonomy_evidence(
|
|
366
|
+
self._store.events(domain, include_shadow=False)
|
|
367
|
+
)
|
|
368
|
+
conf = self.confidence(domain)
|
|
369
|
+
if (len(real) >= _ienv("COLONY_TRUST_ACT_MIN_N", 5)
|
|
370
|
+
and conf >= _fenv("COLONY_TRUST_ACT_THRESHOLD", 0.8)
|
|
371
|
+
and not any(e.get("violation") for e in real)):
|
|
372
|
+
self.set_stage(domain, "act_first",
|
|
373
|
+
reason=f"confidence {conf:.2f} over "
|
|
374
|
+
f"{len(real)} real outcomes")
|
|
375
|
+
|
|
376
|
+
# -- adaptive delivery cap (Amendment 1.6) -------------------------------
|
|
377
|
+
def delivery_cap(self, base: int) -> int:
|
|
378
|
+
"""Per-recipient daily cap: base, earned upward with delivery track
|
|
379
|
+
record, bounded by COLONY_TRUST_DELIVERY_CAP_MAX."""
|
|
380
|
+
cap_max = _ienv("COLONY_TRUST_DELIVERY_CAP_MAX", 6)
|
|
381
|
+
real = self._store.events("delivery", include_shadow=False)
|
|
382
|
+
if len(real) < 10:
|
|
383
|
+
return base
|
|
384
|
+
conf = self.confidence("delivery")
|
|
385
|
+
if conf <= 0.8:
|
|
386
|
+
return base
|
|
387
|
+
extra = int((conf - 0.8) * 20) # 0.85 -> +1, 0.9 -> +2, 0.95 -> +3
|
|
388
|
+
return max(base, min(cap_max, base + extra))
|