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,435 @@
|
|
|
1
|
+
"""Colony Briefing System — SQLite persistence store.
|
|
2
|
+
|
|
3
|
+
Persists Briefing objects, schedule state, and section engagement records.
|
|
4
|
+
Thread-safe via connection-per-thread (check_same_thread=False + threading.Lock).
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
import logging
|
|
11
|
+
import os
|
|
12
|
+
import sqlite3
|
|
13
|
+
import threading
|
|
14
|
+
from datetime import datetime, timezone
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from typing import List, Optional
|
|
17
|
+
|
|
18
|
+
from apsimo import get_state_dir
|
|
19
|
+
|
|
20
|
+
from .models import (
|
|
21
|
+
Briefing,
|
|
22
|
+
BriefingPriority,
|
|
23
|
+
BriefingSection,
|
|
24
|
+
BriefingStatus,
|
|
25
|
+
BriefingType,
|
|
26
|
+
ScheduleEntry,
|
|
27
|
+
SectionEngagementRecord,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
logger = logging.getLogger(__name__)
|
|
31
|
+
|
|
32
|
+
_briefing_store_instance: Optional["BriefingStore"] = None
|
|
33
|
+
|
|
34
|
+
_SCHEMA = """
|
|
35
|
+
CREATE TABLE IF NOT EXISTS briefings (
|
|
36
|
+
briefing_id TEXT PRIMARY KEY,
|
|
37
|
+
briefing_type TEXT NOT NULL,
|
|
38
|
+
status TEXT NOT NULL DEFAULT 'draft',
|
|
39
|
+
sections_json TEXT NOT NULL DEFAULT '[]',
|
|
40
|
+
priority TEXT NOT NULL DEFAULT 'normal',
|
|
41
|
+
triggered_by TEXT,
|
|
42
|
+
gateway TEXT,
|
|
43
|
+
created_at TEXT NOT NULL,
|
|
44
|
+
delivered_at TEXT,
|
|
45
|
+
read_at TEXT
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
CREATE INDEX IF NOT EXISTS idx_briefings_status ON briefings(status);
|
|
49
|
+
CREATE INDEX IF NOT EXISTS idx_briefings_type ON briefings(briefing_type);
|
|
50
|
+
CREATE INDEX IF NOT EXISTS idx_briefings_created ON briefings(created_at DESC);
|
|
51
|
+
|
|
52
|
+
CREATE TABLE IF NOT EXISTS briefing_schedule (
|
|
53
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
54
|
+
type TEXT NOT NULL UNIQUE,
|
|
55
|
+
last_run TEXT,
|
|
56
|
+
next_run TEXT
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
INSERT OR IGNORE INTO briefing_schedule (type) VALUES ('daily');
|
|
60
|
+
INSERT OR IGNORE INTO briefing_schedule (type) VALUES ('weekly');
|
|
61
|
+
|
|
62
|
+
CREATE TABLE IF NOT EXISTS section_engagement (
|
|
63
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
64
|
+
section_name TEXT NOT NULL,
|
|
65
|
+
briefing_id TEXT NOT NULL,
|
|
66
|
+
signal TEXT NOT NULL,
|
|
67
|
+
recorded_at TEXT NOT NULL,
|
|
68
|
+
context TEXT,
|
|
69
|
+
FOREIGN KEY (briefing_id) REFERENCES briefings(briefing_id)
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
CREATE INDEX IF NOT EXISTS idx_engagement_section ON section_engagement(section_name);
|
|
73
|
+
CREATE INDEX IF NOT EXISTS idx_engagement_briefing ON section_engagement(briefing_id);
|
|
74
|
+
CREATE INDEX IF NOT EXISTS idx_engagement_time ON section_engagement(recorded_at DESC);
|
|
75
|
+
|
|
76
|
+
CREATE TABLE IF NOT EXISTS suppressed_sections (
|
|
77
|
+
section_name TEXT PRIMARY KEY,
|
|
78
|
+
suppressed_at TEXT NOT NULL,
|
|
79
|
+
reason TEXT
|
|
80
|
+
);
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _dt_to_str(dt: Optional[datetime]) -> Optional[str]:
|
|
85
|
+
if dt is None:
|
|
86
|
+
return None
|
|
87
|
+
return dt.isoformat()
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _str_to_dt(s: Optional[str]) -> Optional[datetime]:
|
|
91
|
+
if not s:
|
|
92
|
+
return None
|
|
93
|
+
dt = datetime.fromisoformat(s)
|
|
94
|
+
if dt.tzinfo is None:
|
|
95
|
+
dt = dt.replace(tzinfo=timezone.utc)
|
|
96
|
+
return dt
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _briefing_to_row(b: Briefing) -> dict:
|
|
100
|
+
sections = [
|
|
101
|
+
{
|
|
102
|
+
"section_id": s.section_id,
|
|
103
|
+
"name": s.name,
|
|
104
|
+
"content": s.content,
|
|
105
|
+
"narrative": s.narrative,
|
|
106
|
+
"priority": s.priority,
|
|
107
|
+
"suppressed": s.suppressed,
|
|
108
|
+
"engagement": s.engagement,
|
|
109
|
+
}
|
|
110
|
+
for s in b.sections
|
|
111
|
+
]
|
|
112
|
+
return {
|
|
113
|
+
"briefing_id": b.briefing_id,
|
|
114
|
+
"briefing_type": b.briefing_type.value,
|
|
115
|
+
"status": b.status.value,
|
|
116
|
+
"sections_json": json.dumps(sections),
|
|
117
|
+
"priority": b.priority.value,
|
|
118
|
+
"triggered_by": b.triggered_by,
|
|
119
|
+
"gateway": b.gateway,
|
|
120
|
+
"created_at": _dt_to_str(b.created_at),
|
|
121
|
+
"delivered_at": _dt_to_str(b.delivered_at),
|
|
122
|
+
"read_at": _dt_to_str(b.read_at),
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def _row_to_briefing(row: sqlite3.Row) -> Briefing:
|
|
127
|
+
raw_sections = json.loads(row["sections_json"] or "[]")
|
|
128
|
+
sections = [
|
|
129
|
+
BriefingSection(
|
|
130
|
+
section_id=s.get("section_id", ""),
|
|
131
|
+
name=s.get("name", ""),
|
|
132
|
+
content=s.get("content", {}),
|
|
133
|
+
narrative=s.get("narrative", ""),
|
|
134
|
+
priority=s.get("priority", 50),
|
|
135
|
+
suppressed=s.get("suppressed", False),
|
|
136
|
+
engagement=s.get("engagement"),
|
|
137
|
+
)
|
|
138
|
+
for s in raw_sections
|
|
139
|
+
]
|
|
140
|
+
return Briefing(
|
|
141
|
+
briefing_id=row["briefing_id"],
|
|
142
|
+
briefing_type=BriefingType(row["briefing_type"]),
|
|
143
|
+
status=BriefingStatus(row["status"]),
|
|
144
|
+
sections=sections,
|
|
145
|
+
priority=BriefingPriority(row["priority"]),
|
|
146
|
+
triggered_by=row["triggered_by"],
|
|
147
|
+
gateway=row["gateway"],
|
|
148
|
+
created_at=_str_to_dt(row["created_at"]) or datetime.now(timezone.utc),
|
|
149
|
+
delivered_at=_str_to_dt(row["delivered_at"]),
|
|
150
|
+
read_at=_str_to_dt(row["read_at"]),
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
class BriefingStore:
|
|
155
|
+
"""Thread-safe SQLite-backed store for briefings and schedule state."""
|
|
156
|
+
|
|
157
|
+
def __init__(self, db_path: str = ":memory:") -> None:
|
|
158
|
+
self._db_path = db_path
|
|
159
|
+
self._lock = threading.Lock()
|
|
160
|
+
self._conn = sqlite3.connect(db_path, check_same_thread=False)
|
|
161
|
+
self._conn.row_factory = sqlite3.Row
|
|
162
|
+
self._init_schema()
|
|
163
|
+
|
|
164
|
+
def _init_schema(self) -> None:
|
|
165
|
+
with self._lock:
|
|
166
|
+
self._conn.executescript(_SCHEMA)
|
|
167
|
+
self._conn.commit()
|
|
168
|
+
|
|
169
|
+
def close(self) -> None:
|
|
170
|
+
self._conn.close()
|
|
171
|
+
|
|
172
|
+
@classmethod
|
|
173
|
+
def get_instance(cls) -> "BriefingStore":
|
|
174
|
+
"""Return the process-wide singleton backed by ~/.colony/data/briefings.db."""
|
|
175
|
+
global _briefing_store_instance
|
|
176
|
+
if _briefing_store_instance is None:
|
|
177
|
+
colony_home = get_state_dir()
|
|
178
|
+
_briefing_store_instance = cls(str(colony_home / "briefings.db"))
|
|
179
|
+
return _briefing_store_instance
|
|
180
|
+
|
|
181
|
+
def list(self, limit: int = 50, cursor: Optional[str] = None) -> dict:
|
|
182
|
+
"""Return a paginated dict of briefings for the API router."""
|
|
183
|
+
offset = 0
|
|
184
|
+
if cursor:
|
|
185
|
+
try:
|
|
186
|
+
offset = int(cursor)
|
|
187
|
+
except ValueError:
|
|
188
|
+
pass
|
|
189
|
+
briefings = self.list_recent(limit=limit + 1)
|
|
190
|
+
has_more = len(briefings) > limit
|
|
191
|
+
if has_more:
|
|
192
|
+
briefings = briefings[:limit]
|
|
193
|
+
items = [
|
|
194
|
+
{
|
|
195
|
+
"briefing_id": b.briefing_id,
|
|
196
|
+
"briefing_type": b.briefing_type.value,
|
|
197
|
+
"status": b.status.value,
|
|
198
|
+
"priority": b.priority.value,
|
|
199
|
+
"triggered_by": b.triggered_by,
|
|
200
|
+
"gateway": b.gateway,
|
|
201
|
+
"created_at": b.created_at.isoformat(),
|
|
202
|
+
"delivered_at": b.delivered_at.isoformat() if b.delivered_at else None,
|
|
203
|
+
"section_count": len(b.sections),
|
|
204
|
+
}
|
|
205
|
+
for b in briefings
|
|
206
|
+
]
|
|
207
|
+
return {
|
|
208
|
+
"data": items,
|
|
209
|
+
"meta": {
|
|
210
|
+
"total": len(items),
|
|
211
|
+
"page_size": limit,
|
|
212
|
+
"has_more": has_more,
|
|
213
|
+
"cursor": str(offset + limit) if has_more else None,
|
|
214
|
+
},
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
# ------------------------------------------------------------------
|
|
218
|
+
# Briefing CRUD
|
|
219
|
+
# ------------------------------------------------------------------
|
|
220
|
+
|
|
221
|
+
def save(self, briefing: Briefing) -> Briefing:
|
|
222
|
+
row = _briefing_to_row(briefing)
|
|
223
|
+
with self._lock:
|
|
224
|
+
self._conn.execute(
|
|
225
|
+
"""
|
|
226
|
+
INSERT INTO briefings
|
|
227
|
+
(briefing_id, briefing_type, status, sections_json, priority,
|
|
228
|
+
triggered_by, gateway, created_at, delivered_at, read_at)
|
|
229
|
+
VALUES
|
|
230
|
+
(:briefing_id, :briefing_type, :status, :sections_json, :priority,
|
|
231
|
+
:triggered_by, :gateway, :created_at, :delivered_at, :read_at)
|
|
232
|
+
ON CONFLICT(briefing_id) DO UPDATE SET
|
|
233
|
+
status = excluded.status,
|
|
234
|
+
sections_json = excluded.sections_json,
|
|
235
|
+
priority = excluded.priority,
|
|
236
|
+
triggered_by = excluded.triggered_by,
|
|
237
|
+
gateway = excluded.gateway,
|
|
238
|
+
delivered_at = excluded.delivered_at,
|
|
239
|
+
read_at = excluded.read_at
|
|
240
|
+
""",
|
|
241
|
+
row,
|
|
242
|
+
)
|
|
243
|
+
self._conn.commit()
|
|
244
|
+
try:
|
|
245
|
+
from apsimo.events.broadcaster import emit as _emit
|
|
246
|
+
_emit("briefing", {
|
|
247
|
+
"briefing_id": briefing.briefing_id,
|
|
248
|
+
"status": getattr(briefing.status, "value", str(briefing.status)),
|
|
249
|
+
"priority": briefing.priority,
|
|
250
|
+
})
|
|
251
|
+
except Exception:
|
|
252
|
+
pass
|
|
253
|
+
return briefing
|
|
254
|
+
|
|
255
|
+
def get(self, briefing_id: str) -> Optional[Briefing]:
|
|
256
|
+
with self._lock:
|
|
257
|
+
cur = self._conn.execute(
|
|
258
|
+
"SELECT * FROM briefings WHERE briefing_id = ?", (briefing_id,)
|
|
259
|
+
)
|
|
260
|
+
row = cur.fetchone()
|
|
261
|
+
return _row_to_briefing(row) if row else None
|
|
262
|
+
|
|
263
|
+
def list_by_status(self, status: BriefingStatus, limit: int = 50) -> List[Briefing]:
|
|
264
|
+
with self._lock:
|
|
265
|
+
cur = self._conn.execute(
|
|
266
|
+
"SELECT * FROM briefings WHERE status = ? ORDER BY created_at DESC LIMIT ?",
|
|
267
|
+
(status.value, limit),
|
|
268
|
+
)
|
|
269
|
+
rows = cur.fetchall()
|
|
270
|
+
return [_row_to_briefing(r) for r in rows]
|
|
271
|
+
|
|
272
|
+
def list_recent(self, limit: int = 10) -> List[Briefing]:
|
|
273
|
+
with self._lock:
|
|
274
|
+
cur = self._conn.execute(
|
|
275
|
+
"SELECT * FROM briefings ORDER BY created_at DESC LIMIT ?",
|
|
276
|
+
(limit,),
|
|
277
|
+
)
|
|
278
|
+
rows = cur.fetchall()
|
|
279
|
+
return [_row_to_briefing(r) for r in rows]
|
|
280
|
+
|
|
281
|
+
def mark_queued(self, briefing_id: str) -> None:
|
|
282
|
+
with self._lock:
|
|
283
|
+
self._conn.execute(
|
|
284
|
+
"UPDATE briefings SET status = 'queued' WHERE briefing_id = ?",
|
|
285
|
+
(briefing_id,),
|
|
286
|
+
)
|
|
287
|
+
self._conn.commit()
|
|
288
|
+
|
|
289
|
+
def mark_delivering(self, briefing_id: str) -> None:
|
|
290
|
+
with self._lock:
|
|
291
|
+
self._conn.execute(
|
|
292
|
+
"UPDATE briefings SET status = 'delivering' WHERE briefing_id = ?",
|
|
293
|
+
(briefing_id,),
|
|
294
|
+
)
|
|
295
|
+
self._conn.commit()
|
|
296
|
+
|
|
297
|
+
def mark_delivered(self, briefing_id: str, gateway: str) -> None:
|
|
298
|
+
now = _dt_to_str(datetime.now(timezone.utc))
|
|
299
|
+
with self._lock:
|
|
300
|
+
self._conn.execute(
|
|
301
|
+
"UPDATE briefings SET status = 'delivered', gateway = ?, delivered_at = ? WHERE briefing_id = ?",
|
|
302
|
+
(gateway, now, briefing_id),
|
|
303
|
+
)
|
|
304
|
+
self._conn.commit()
|
|
305
|
+
|
|
306
|
+
def mark_read(self, briefing_id: str) -> None:
|
|
307
|
+
now = _dt_to_str(datetime.now(timezone.utc))
|
|
308
|
+
with self._lock:
|
|
309
|
+
self._conn.execute(
|
|
310
|
+
"UPDATE briefings SET status = 'read', read_at = ? WHERE briefing_id = ?",
|
|
311
|
+
(now, briefing_id),
|
|
312
|
+
)
|
|
313
|
+
self._conn.commit()
|
|
314
|
+
|
|
315
|
+
def count_today(self) -> int:
|
|
316
|
+
today = datetime.now(timezone.utc).date().isoformat()
|
|
317
|
+
with self._lock:
|
|
318
|
+
cur = self._conn.execute(
|
|
319
|
+
"SELECT COUNT(*) FROM briefings WHERE created_at >= ?",
|
|
320
|
+
(today,),
|
|
321
|
+
)
|
|
322
|
+
return cur.fetchone()[0]
|
|
323
|
+
|
|
324
|
+
# ------------------------------------------------------------------
|
|
325
|
+
# Schedule
|
|
326
|
+
# ------------------------------------------------------------------
|
|
327
|
+
|
|
328
|
+
def get_schedule(self) -> List[ScheduleEntry]:
|
|
329
|
+
with self._lock:
|
|
330
|
+
cur = self._conn.execute("SELECT type, last_run, next_run FROM briefing_schedule")
|
|
331
|
+
rows = cur.fetchall()
|
|
332
|
+
return [
|
|
333
|
+
ScheduleEntry(
|
|
334
|
+
type=r["type"],
|
|
335
|
+
last_run=_str_to_dt(r["last_run"]),
|
|
336
|
+
next_run=_str_to_dt(r["next_run"]),
|
|
337
|
+
)
|
|
338
|
+
for r in rows
|
|
339
|
+
]
|
|
340
|
+
|
|
341
|
+
def get_schedule_entry(self, briefing_type: BriefingType) -> Optional[ScheduleEntry]:
|
|
342
|
+
with self._lock:
|
|
343
|
+
cur = self._conn.execute(
|
|
344
|
+
"SELECT type, last_run, next_run FROM briefing_schedule WHERE type = ?",
|
|
345
|
+
(briefing_type.value,),
|
|
346
|
+
)
|
|
347
|
+
row = cur.fetchone()
|
|
348
|
+
if not row:
|
|
349
|
+
return None
|
|
350
|
+
return ScheduleEntry(
|
|
351
|
+
type=row["type"],
|
|
352
|
+
last_run=_str_to_dt(row["last_run"]),
|
|
353
|
+
next_run=_str_to_dt(row["next_run"]),
|
|
354
|
+
)
|
|
355
|
+
|
|
356
|
+
def update_schedule_last_run(self, briefing_type: BriefingType) -> None:
|
|
357
|
+
now = _dt_to_str(datetime.now(timezone.utc))
|
|
358
|
+
with self._lock:
|
|
359
|
+
self._conn.execute(
|
|
360
|
+
"UPDATE briefing_schedule SET last_run = ? WHERE type = ?",
|
|
361
|
+
(now, briefing_type.value),
|
|
362
|
+
)
|
|
363
|
+
self._conn.commit()
|
|
364
|
+
|
|
365
|
+
# ------------------------------------------------------------------
|
|
366
|
+
# Engagement
|
|
367
|
+
# ------------------------------------------------------------------
|
|
368
|
+
|
|
369
|
+
def record_engagement(self, record: SectionEngagementRecord) -> None:
|
|
370
|
+
with self._lock:
|
|
371
|
+
self._conn.execute(
|
|
372
|
+
"""
|
|
373
|
+
INSERT INTO section_engagement
|
|
374
|
+
(section_name, briefing_id, signal, recorded_at, context)
|
|
375
|
+
VALUES (?, ?, ?, ?, ?)
|
|
376
|
+
""",
|
|
377
|
+
(
|
|
378
|
+
record.section_name,
|
|
379
|
+
record.briefing_id,
|
|
380
|
+
record.signal,
|
|
381
|
+
_dt_to_str(record.recorded_at),
|
|
382
|
+
record.context,
|
|
383
|
+
),
|
|
384
|
+
)
|
|
385
|
+
self._conn.commit()
|
|
386
|
+
|
|
387
|
+
def get_engagement_records(
|
|
388
|
+
self,
|
|
389
|
+
section_name: Optional[str] = None,
|
|
390
|
+
limit: int = 500,
|
|
391
|
+
) -> List[SectionEngagementRecord]:
|
|
392
|
+
with self._lock:
|
|
393
|
+
if section_name:
|
|
394
|
+
cur = self._conn.execute(
|
|
395
|
+
"SELECT * FROM section_engagement WHERE section_name = ? ORDER BY recorded_at DESC LIMIT ?",
|
|
396
|
+
(section_name, limit),
|
|
397
|
+
)
|
|
398
|
+
else:
|
|
399
|
+
cur = self._conn.execute(
|
|
400
|
+
"SELECT * FROM section_engagement ORDER BY recorded_at DESC LIMIT ?",
|
|
401
|
+
(limit,),
|
|
402
|
+
)
|
|
403
|
+
rows = cur.fetchall()
|
|
404
|
+
return [
|
|
405
|
+
SectionEngagementRecord(
|
|
406
|
+
section_name=r["section_name"],
|
|
407
|
+
briefing_id=r["briefing_id"],
|
|
408
|
+
signal=r["signal"],
|
|
409
|
+
recorded_at=_str_to_dt(r["recorded_at"]) or datetime.now(timezone.utc),
|
|
410
|
+
context=r["context"],
|
|
411
|
+
)
|
|
412
|
+
for r in rows
|
|
413
|
+
]
|
|
414
|
+
|
|
415
|
+
def get_suppressed_sections(self) -> List[str]:
|
|
416
|
+
with self._lock:
|
|
417
|
+
cur = self._conn.execute("SELECT section_name FROM suppressed_sections")
|
|
418
|
+
return [r["section_name"] for r in cur.fetchall()]
|
|
419
|
+
|
|
420
|
+
def suppress_section(self, section_name: str, reason: Optional[str] = None) -> None:
|
|
421
|
+
now = _dt_to_str(datetime.now(timezone.utc))
|
|
422
|
+
with self._lock:
|
|
423
|
+
self._conn.execute(
|
|
424
|
+
"INSERT OR REPLACE INTO suppressed_sections (section_name, suppressed_at, reason) VALUES (?, ?, ?)",
|
|
425
|
+
(section_name, now, reason),
|
|
426
|
+
)
|
|
427
|
+
self._conn.commit()
|
|
428
|
+
|
|
429
|
+
def unsuppress_section(self, section_name: str) -> None:
|
|
430
|
+
with self._lock:
|
|
431
|
+
self._conn.execute(
|
|
432
|
+
"DELETE FROM suppressed_sections WHERE section_name = ?",
|
|
433
|
+
(section_name,),
|
|
434
|
+
)
|
|
435
|
+
self._conn.commit()
|
apsimo/chain/__init__.py
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""ColonyChain — lightweight append-only identity and trust ledger.
|
|
2
|
+
|
|
3
|
+
Provides:
|
|
4
|
+
- Block model with SHA-256 hash chain and Ed25519 signatures
|
|
5
|
+
- Transaction types for colony lifecycle and governance
|
|
6
|
+
- SQLite-backed persistent chain storage
|
|
7
|
+
- Genesis admin capabilities
|
|
8
|
+
- Chain sync protocol messages
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from .block import Block, build_merkle_root, compute_block_hash
|
|
12
|
+
from .transactions import (
|
|
13
|
+
TxType,
|
|
14
|
+
Transaction,
|
|
15
|
+
ChainState,
|
|
16
|
+
ColonyRecord,
|
|
17
|
+
KeyHistoryEntry,
|
|
18
|
+
TrustEdge,
|
|
19
|
+
SentinelRecord,
|
|
20
|
+
ProtocolConfig,
|
|
21
|
+
UntrustEvent,
|
|
22
|
+
)
|
|
23
|
+
from .storage import ChainStore
|
|
24
|
+
from .validation import TransactionValidator, BlockValidator, ValidationResult
|
|
25
|
+
from .genesis import create_genesis_block, GenesisConfig
|
|
26
|
+
from .manager import ChainManager
|
|
27
|
+
|
|
28
|
+
__all__ = [
|
|
29
|
+
"Block",
|
|
30
|
+
"build_merkle_root",
|
|
31
|
+
"compute_block_hash",
|
|
32
|
+
"TxType",
|
|
33
|
+
"Transaction",
|
|
34
|
+
"ChainState",
|
|
35
|
+
"ColonyRecord",
|
|
36
|
+
"KeyHistoryEntry",
|
|
37
|
+
"TrustEdge",
|
|
38
|
+
"SentinelRecord",
|
|
39
|
+
"ProtocolConfig",
|
|
40
|
+
"UntrustEvent",
|
|
41
|
+
"ChainStore",
|
|
42
|
+
"TransactionValidator",
|
|
43
|
+
"BlockValidator",
|
|
44
|
+
"ValidationResult",
|
|
45
|
+
"create_genesis_block",
|
|
46
|
+
"GenesisConfig",
|
|
47
|
+
"ChainManager",
|
|
48
|
+
]
|
apsimo/chain/block.py
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"""Block model for ColonyChain.
|
|
2
|
+
|
|
3
|
+
SHA-256 hash chain with Ed25519 validator signatures and Merkle root
|
|
4
|
+
computation over transactions.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import hashlib
|
|
10
|
+
import json
|
|
11
|
+
from dataclasses import dataclass, field
|
|
12
|
+
from typing import TYPE_CHECKING, Any
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from .transactions import Transaction
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def build_merkle_root(tx_ids: list[str]) -> str:
|
|
19
|
+
"""Compute SHA-256 Merkle root over a list of transaction IDs."""
|
|
20
|
+
if not tx_ids:
|
|
21
|
+
return hashlib.sha256(b"").hexdigest()
|
|
22
|
+
level: list[str] = [hashlib.sha256(tid.encode()).hexdigest() for tid in tx_ids]
|
|
23
|
+
while len(level) > 1:
|
|
24
|
+
if len(level) % 2 == 1:
|
|
25
|
+
level.append(level[-1])
|
|
26
|
+
level = [
|
|
27
|
+
hashlib.sha256(
|
|
28
|
+
bytes.fromhex(level[i]) + bytes.fromhex(level[i + 1])
|
|
29
|
+
).hexdigest()
|
|
30
|
+
for i in range(0, len(level), 2)
|
|
31
|
+
]
|
|
32
|
+
return level[0]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def compute_block_hash(block_dict: dict[str, Any]) -> str:
|
|
36
|
+
"""Compute SHA-256 hash of a block dict (excluding 'signature' field)."""
|
|
37
|
+
fields = {k: v for k, v in block_dict.items() if k != "signature"}
|
|
38
|
+
canonical = json.dumps(fields, sort_keys=True, separators=(",", ":"))
|
|
39
|
+
return hashlib.sha256(canonical.encode()).hexdigest()
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@dataclass
|
|
43
|
+
class Block:
|
|
44
|
+
"""A single block in the ColonyChain."""
|
|
45
|
+
|
|
46
|
+
index: int
|
|
47
|
+
previous_hash: str
|
|
48
|
+
timestamp: str
|
|
49
|
+
transactions: list["Transaction"]
|
|
50
|
+
merkle_root: str
|
|
51
|
+
producer_id: str
|
|
52
|
+
signature: str
|
|
53
|
+
metadata: dict[str, Any] = field(default_factory=dict)
|
|
54
|
+
|
|
55
|
+
# Computed and cached after creation
|
|
56
|
+
_hash: str = field(default="", repr=False, compare=False)
|
|
57
|
+
|
|
58
|
+
def to_dict(self) -> dict[str, Any]:
|
|
59
|
+
return {
|
|
60
|
+
"index": self.index,
|
|
61
|
+
"previous_hash": self.previous_hash,
|
|
62
|
+
"timestamp": self.timestamp,
|
|
63
|
+
"transactions": [t.to_dict() for t in self.transactions],
|
|
64
|
+
"merkle_root": self.merkle_root,
|
|
65
|
+
"producer_id": self.producer_id,
|
|
66
|
+
"signature": self.signature,
|
|
67
|
+
"metadata": self.metadata,
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
def compute_hash(self) -> str:
|
|
71
|
+
"""Compute SHA-256 hash of this block (excluding signature)."""
|
|
72
|
+
d = self.to_dict()
|
|
73
|
+
return compute_block_hash(d)
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
def block_hash(self) -> str:
|
|
77
|
+
if not self._hash:
|
|
78
|
+
self._hash = self.compute_hash()
|
|
79
|
+
return self._hash
|
|
80
|
+
|
|
81
|
+
@classmethod
|
|
82
|
+
def from_dict(cls, d: dict[str, Any]) -> "Block":
|
|
83
|
+
from .transactions import Transaction # avoid circular at module load
|
|
84
|
+
|
|
85
|
+
txs = [Transaction.from_dict(t) for t in d.get("transactions", [])]
|
|
86
|
+
block = cls(
|
|
87
|
+
index=d["index"],
|
|
88
|
+
previous_hash=d["previous_hash"],
|
|
89
|
+
timestamp=d["timestamp"],
|
|
90
|
+
transactions=txs,
|
|
91
|
+
merkle_root=d["merkle_root"],
|
|
92
|
+
producer_id=d["producer_id"],
|
|
93
|
+
signature=d.get("signature", ""),
|
|
94
|
+
metadata=d.get("metadata", {}),
|
|
95
|
+
)
|
|
96
|
+
return block
|
|
97
|
+
|
|
98
|
+
def __post_init__(self) -> None:
|
|
99
|
+
# Reset cached hash if fields change
|
|
100
|
+
self._hash = ""
|