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,296 @@
|
|
|
1
|
+
"""Prospective native task turnaround learning, without quality claims.
|
|
2
|
+
|
|
3
|
+
Attachment is the prediction origin, so turnaround includes queue delay. Native
|
|
4
|
+
run elapsed time is retained separately. Hermes' ledger cannot attest which
|
|
5
|
+
processor actually served a request; configured overrides are not substituted.
|
|
6
|
+
"""
|
|
7
|
+
from contextlib import closing
|
|
8
|
+
from datetime import datetime, timezone
|
|
9
|
+
import hashlib
|
|
10
|
+
import json
|
|
11
|
+
import logging
|
|
12
|
+
import math
|
|
13
|
+
import time
|
|
14
|
+
|
|
15
|
+
from apsimo import get_state_dir
|
|
16
|
+
from apsimo.turns import get_turn_idempotency_ledger
|
|
17
|
+
from apsimo.turns.idempotency import SourceErased
|
|
18
|
+
from .expectations import expectations_enabled
|
|
19
|
+
|
|
20
|
+
logger = logging.getLogger(__name__)
|
|
21
|
+
VERSION = 'native-task-forecast-v1'
|
|
22
|
+
DECISION_VERSION = 'native-task-inspection-shadow-v1'
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _digest(value):
|
|
26
|
+
return hashlib.sha256(json.dumps(value, sort_keys=True, separators=(',', ':')).encode()).hexdigest()
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _identity(native):
|
|
30
|
+
return {key: native[key] for key in ('source_home_id', 'native_board', 'native_task_id')}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _reference(source_id):
|
|
34
|
+
return 'receipt:'+source_id
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _current(ledger, references, versions, owner):
|
|
38
|
+
ids = [r.removeprefix('receipt:') for r in references]
|
|
39
|
+
current = {r['source_id']:r['source_version'] for r in ledger.source_references(
|
|
40
|
+
ids, contact_id=owner, session_id='')}
|
|
41
|
+
if not all(current.get(identifier) == versions.get(_reference(identifier))
|
|
42
|
+
and not ledger.is_projection_erased(identifier) for identifier in ids):
|
|
43
|
+
return False
|
|
44
|
+
with closing(ledger._connect()) as db:
|
|
45
|
+
for identifier in ids:
|
|
46
|
+
if db.execute('SELECT 1 FROM source_annotations WHERE target_source_id=? LIMIT 1', (identifier,)).fetchone():
|
|
47
|
+
return False
|
|
48
|
+
return True
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _retain(ledger, *, source_id, owner, native, facts, occurred_at, dependencies=None):
|
|
52
|
+
# First observation survives replay. Native configuration can change later;
|
|
53
|
+
# never rebind an old receipt to whichever model is configured today.
|
|
54
|
+
with closing(ledger._connect()) as db:
|
|
55
|
+
exists = db.execute('SELECT 1 FROM turn_sources WHERE turn_id=?', (source_id,)).fetchone()
|
|
56
|
+
if not exists:
|
|
57
|
+
ledger.record_source(source_id, contact_id=owner,
|
|
58
|
+
session_id='native-runtime:'+native['native_task_id'],
|
|
59
|
+
occurred_at=datetime.fromtimestamp(occurred_at, timezone.utc).isoformat(),
|
|
60
|
+
messages=[{'role':'assistant', '_native_runtime_observation':VERSION, '_native_forecast_facts':facts,
|
|
61
|
+
**({'_supplied_sources': [{'source_id': r.removeprefix('receipt:'), 'source_version': v}
|
|
62
|
+
for r, v in dependencies.items()]} if dependencies else {}),
|
|
63
|
+
'content':'Runtime-owned task forecast/lifecycle observation. This is not an owner statement, '
|
|
64
|
+
'successful output evaluation, or permission for any action.\n'+json.dumps(facts, sort_keys=True)}],
|
|
65
|
+
derive_claims=False)
|
|
66
|
+
refs = ledger.source_references([source_id], contact_id=owner, session_id='')
|
|
67
|
+
if not refs or ledger.is_projection_erased(source_id):
|
|
68
|
+
raise SourceErased('runtime forecast evidence unavailable')
|
|
69
|
+
with closing(ledger._connect()) as db:
|
|
70
|
+
message = json.loads(db.execute('SELECT messages_json FROM turn_sources WHERE turn_id=?', (source_id,)).fetchone()[0])[0]
|
|
71
|
+
if message.get('_native_runtime_observation') != VERSION or message.get('_native_forecast_facts', {}).get('identity') != _identity(native):
|
|
72
|
+
raise ValueError('runtime forecast source binding conflict')
|
|
73
|
+
return {_reference(source_id):refs[0]['source_version']}, message['_native_forecast_facts']
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _outcome_facts(ledger, outcome):
|
|
77
|
+
if ledger is None or outcome is None:
|
|
78
|
+
return {}
|
|
79
|
+
with closing(ledger._connect()) as db:
|
|
80
|
+
row = db.execute('SELECT messages_json FROM turn_sources WHERE turn_id=?',
|
|
81
|
+
(outcome['receipt_ref'].removeprefix('receipt:'),)).fetchone()
|
|
82
|
+
facts = json.loads(row[0])[0].get('_native_forecast_facts', {}) if row else {}
|
|
83
|
+
return facts if facts.get('version') == VERSION and facts.get('kind') == 'duration_outcome' else {}
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _comparable(configuration, outcome_facts):
|
|
87
|
+
observed = outcome_facts.get('processor_observation') or {}
|
|
88
|
+
return bool(configuration and observed.get('configuration') == configuration
|
|
89
|
+
and observed.get('complete_observed_pairs') and observed.get('served_model'))
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _parts(review, native, state, owner):
|
|
93
|
+
from apsimo.api.routers import host
|
|
94
|
+
engine = host._expectations
|
|
95
|
+
if engine is None or not expectations_enabled():
|
|
96
|
+
return None
|
|
97
|
+
selection = (review.get('native_work') or {}).get('outcome_learning') or {}
|
|
98
|
+
if selection.get('version') != 'native-runtime-observation-v1':
|
|
99
|
+
return None
|
|
100
|
+
identity = _identity(native)
|
|
101
|
+
forecast_id = 'native-task:'+_digest(identity)
|
|
102
|
+
ledger = get_turn_idempotency_ledger(get_state_dir())
|
|
103
|
+
return engine.store, ledger, selection, identity, forecast_id
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def attach(review, native, state, owner):
|
|
107
|
+
"""Called after a verified prospective blocked task binding, before promote."""
|
|
108
|
+
parts = _parts(review, native, state, owner)
|
|
109
|
+
if parts is None:
|
|
110
|
+
return {'status':'disabled_or_unselected'}
|
|
111
|
+
store, ledger, selection, identity, fid = parts
|
|
112
|
+
existing = store.forecast_history(fid)['forecasts']
|
|
113
|
+
if existing:
|
|
114
|
+
return {'status':'existing', 'forecast_id':fid, 'horizon':existing[0]['horizon']}
|
|
115
|
+
if state['status'] != 'blocked' or state['attempt_count'] != 0:
|
|
116
|
+
return {'status':'not_prospective'}
|
|
117
|
+
bound = datetime.fromisoformat(selection['bound_at']).timestamp()
|
|
118
|
+
# Task/run timestamps have one-second precision. The no-runs binding above
|
|
119
|
+
# establishes ordering within that second; retain the precise bound too.
|
|
120
|
+
origin = math.floor(bound)
|
|
121
|
+
now = time.time()
|
|
122
|
+
config = state['forecast_configuration']
|
|
123
|
+
budget = config.get('runtime_budget_seconds')
|
|
124
|
+
prior = float(budget) if isinstance(budget, (int, float)) and budget > 0 else 480.
|
|
125
|
+
cohort = 'native-review:'+review['review']['action']
|
|
126
|
+
def current(prediction, outcome):
|
|
127
|
+
return (_current(ledger, prediction.evidence_refs, prediction.detail['source_versions'], owner)
|
|
128
|
+
and _current(ledger, outcome['evidence_refs'], outcome['source_versions'], owner)
|
|
129
|
+
and prediction.detail['model_provenance']['capabilities'] == config
|
|
130
|
+
and _comparable(config, _outcome_facts(ledger, outcome)))
|
|
131
|
+
estimate = store.estimate_duration(domain='task_duration',cohort=cohort,
|
|
132
|
+
subject_person_id=owner,viewer_scope='owner',prior_seconds=prior,
|
|
133
|
+
now=now,evidence_is_current=current)
|
|
134
|
+
horizon = origin + estimate['seconds']
|
|
135
|
+
if horizon <= now:
|
|
136
|
+
return {'status':'binding_too_old_for_prospective_forecast'}
|
|
137
|
+
source_id = 'native-forecast-binding:'+_digest(identity)
|
|
138
|
+
versions, retained = _retain(ledger,source_id=source_id,owner=owner,native=native,occurred_at=bound,
|
|
139
|
+
facts={'version':VERSION,'kind':'prospective_binding','identity':identity,
|
|
140
|
+
'bound_at':selection['bound_at'],'configuration':config,
|
|
141
|
+
'registered_action':review['review']['action'],
|
|
142
|
+
'estimate':estimate,'measurement':'attachment_to_terminal_turnaround'})
|
|
143
|
+
# Recovery after source capture uses the retained original estimate, not
|
|
144
|
+
# a later improved estimate presented as the earlier prediction.
|
|
145
|
+
estimate = retained['estimate']
|
|
146
|
+
horizon = origin + estimate['seconds']
|
|
147
|
+
config = retained['configuration']
|
|
148
|
+
prediction = store.issue_forecast(forecast_id=fid,subject='task:'+native['native_task_id'],
|
|
149
|
+
domain='task_duration',expectation='The accepted native review completes within its turnaround estimate',
|
|
150
|
+
confidence=estimate['confidence'],horizon=horizon,origin_at=origin,issued_at=origin,
|
|
151
|
+
evidence_refs=list(versions),source_versions=versions,source_kind='task_receipt',
|
|
152
|
+
cohort=cohort,method=estimate['method'],
|
|
153
|
+
model_provenance={'requested_role':'native_default_worker','served_model':None,
|
|
154
|
+
'model_revision':None,'capabilities':config,'fallback':None},
|
|
155
|
+
subject_person_id=owner,viewer_scope='owner',shareability='owner_private',
|
|
156
|
+
conditions={'measurement':'attachment_to_terminal_turnaround','timestamp_precision_seconds':1,
|
|
157
|
+
'bound_at':selection['bound_at'],'estimate':estimate})
|
|
158
|
+
return {'status':'issued','forecast_id':fid,'horizon':prediction.horizon,'estimate':estimate}
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def observe(review, native, state, owner):
|
|
162
|
+
"""Readback settles duration only. Pauses/failures are explicit censoring."""
|
|
163
|
+
parts = _parts(review, native, state, owner)
|
|
164
|
+
if parts is None:
|
|
165
|
+
return {'status':'disabled_or_unselected'}
|
|
166
|
+
store, ledger, selection, identity, fid = parts
|
|
167
|
+
history = store.forecast_history(fid)
|
|
168
|
+
if not history['forecasts']:
|
|
169
|
+
return {'status':'no_prospective_forecast'}
|
|
170
|
+
prediction = history['forecasts'][0]
|
|
171
|
+
observed = state.get('duration_observation')
|
|
172
|
+
if not observed or not isinstance(observed.get('ended_at'), (int,float)):
|
|
173
|
+
return {'status':'pending','forecast_id':fid}
|
|
174
|
+
ended = observed['ended_at']
|
|
175
|
+
if ended < math.floor(datetime.fromisoformat(selection['bound_at']).timestamp()) or ended > time.time():
|
|
176
|
+
return {'status':'unqualified_native_time'}
|
|
177
|
+
source_id = 'native-forecast-outcome:'+_digest({**identity,'native_run_id':state.get('native_run_id'),
|
|
178
|
+
'outcome':observed['outcome'],'ended_at':ended})
|
|
179
|
+
receipt = _reference(source_id)
|
|
180
|
+
if any(o['receipt_ref'] == receipt for o in history['outcomes']):
|
|
181
|
+
return {'status':'observed','forecast_id':fid,'receipt_ref':receipt}
|
|
182
|
+
valid = _current(ledger,prediction['evidence_refs'],prediction['detail']['source_versions'],owner)
|
|
183
|
+
paused = any(o['reason'] == 'paused' for o in history['outcomes'])
|
|
184
|
+
complete = state['status'] == 'done' and state.get('completed_run') and valid and not paused
|
|
185
|
+
reason = '' if complete else 'source_retracted' if not valid else 'paused' if paused or (state['status']=='blocked' and not state.get('gave_up')) else 'cancelled' if state['status'] in {'archived','cancelled'} else 'unavailable'
|
|
186
|
+
from .runtime_models import summarize
|
|
187
|
+
processor = summarize(ledger, owner, native, state.get('native_run_id'))
|
|
188
|
+
versions, retained = _retain(ledger,source_id=source_id,owner=owner,native=native,occurred_at=ended,
|
|
189
|
+
dependencies=processor['source_versions'],
|
|
190
|
+
facts={'version':VERSION,'kind':'duration_outcome','identity':identity,
|
|
191
|
+
'native_run_id':state.get('native_run_id'),'observation':observed,
|
|
192
|
+
'processor_observation':processor,
|
|
193
|
+
'native_status':state['status'],'forecast_id':fid,
|
|
194
|
+
'turnaround_seconds':ended-prediction['detail']['origin_at'],
|
|
195
|
+
'measurement':'attachment_to_terminal_turnaround','quality_evaluated':False})
|
|
196
|
+
versions = {**versions, **retained.get('processor_observation', {}).get('source_versions', {})}
|
|
197
|
+
result = store.record_forecast_outcome(forecast_id=fid,receipt_ref=receipt,
|
|
198
|
+
evidence_refs=list(versions),source_versions=versions,source_kind='task_receipt',
|
|
199
|
+
observed_at=ended,subject_person_id=owner,viewer_scope='owner',shareability='owner_private',
|
|
200
|
+
status='observed' if complete else 'censored',value=True if complete else None,
|
|
201
|
+
reason=reason,previous_revision=len(history['outcomes']))
|
|
202
|
+
return {'status':'observed','forecast_id':fid,'outcome_status':result['status'],
|
|
203
|
+
'receipt_ref':receipt,'quality_evaluated':False}
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def project(review, native, state, owner, *, now=None):
|
|
207
|
+
"""Project one existing forecast on a normal work read, without side effects.
|
|
208
|
+
|
|
209
|
+
The original horizon, not a revised or recomputed estimate, supplies the
|
|
210
|
+
shadow inspection decision. No suggestion, polling, retry or notification
|
|
211
|
+
is enabled by this projection, even when its forecast has many samples.
|
|
212
|
+
"""
|
|
213
|
+
parts = _parts(review, native, state, owner)
|
|
214
|
+
if parts is None:
|
|
215
|
+
return {'status':'disabled_or_unselected'}
|
|
216
|
+
store, ledger, _, _, fid = parts
|
|
217
|
+
history = store.forecast_history(fid)
|
|
218
|
+
if not history['forecasts']:
|
|
219
|
+
return {'status':'no_prospective_forecast'}
|
|
220
|
+
prediction = history['forecasts'][0]
|
|
221
|
+
detail = prediction['detail']
|
|
222
|
+
if (prediction['subject_person_id'] != owner or prediction['viewer_scope'] != 'owner'
|
|
223
|
+
or not _current(ledger,prediction['evidence_refs'],detail['source_versions'],owner)):
|
|
224
|
+
return {'status':'source_unavailable'}
|
|
225
|
+
stamp = time.time() if now is None else float(now)
|
|
226
|
+
if not math.isfinite(stamp) or stamp < detail['origin_at']:
|
|
227
|
+
return {'status':'unqualified_observation_time'}
|
|
228
|
+
estimate = detail['conditions']['estimate']
|
|
229
|
+
prior_horizon = detail['origin_at'] + estimate['prior_seconds']
|
|
230
|
+
recorded_configuration = detail['model_provenance']['capabilities']
|
|
231
|
+
same_conditions = bool(recorded_configuration) and state.get('forecast_configuration') == recorded_configuration
|
|
232
|
+
status = state.get('status')
|
|
233
|
+
active = status in {'ready','running'}
|
|
234
|
+
latest = history['outcomes'][-1] if history['outcomes'] else None
|
|
235
|
+
outcome_facts = _outcome_facts(ledger, latest)
|
|
236
|
+
processor = outcome_facts.get('processor_observation') or {}
|
|
237
|
+
if latest:
|
|
238
|
+
# Later profile/task edits do not rewrite the conditions witnessed by
|
|
239
|
+
# the terminal receipt. Original future processor fields stay unknown.
|
|
240
|
+
same_conditions = bool(recorded_configuration) and recorded_configuration == processor.get('configuration')
|
|
241
|
+
outcome_current = latest is None or _current(ledger,latest['evidence_refs'],latest['source_versions'],owner)
|
|
242
|
+
if not outcome_current:
|
|
243
|
+
decision = prior_decision = 'source_unavailable'
|
|
244
|
+
elif latest and latest['status'] == 'censored':
|
|
245
|
+
decision = prior_decision = 'censored'
|
|
246
|
+
elif not active:
|
|
247
|
+
decision = prior_decision = 'terminal' if status in {'done','archived','cancelled'} else 'not_running'
|
|
248
|
+
elif not same_conditions:
|
|
249
|
+
decision = prior_decision = 'conditions_unknown_or_changed'
|
|
250
|
+
else:
|
|
251
|
+
decision = 'inspect_recorded_state' if stamp >= prediction['horizon'] else 'continue_waiting'
|
|
252
|
+
prior_decision = 'inspect_recorded_state' if stamp >= prior_horizon else 'continue_waiting'
|
|
253
|
+
result = {'status':'shadow','version':DECISION_VERSION,
|
|
254
|
+
'decision_id':'native-inspection:'+_digest({'forecast_id':fid,'horizon':prediction['horizon']}),
|
|
255
|
+
'forecast_id':fid,'original_revision':prediction['detail']['revision'],
|
|
256
|
+
'original_horizon':prediction['horizon'],'prior_horizon':prior_horizon,
|
|
257
|
+
'observed_at':stamp,'native_status':status,'decision':decision,
|
|
258
|
+
'prior_decision':prior_decision,'changed_from_prior':decision != prior_decision,
|
|
259
|
+
'sample_n':estimate['sample_n'],'uncertain':estimate['uncertain'],
|
|
260
|
+
'configuration_matches':same_conditions,
|
|
261
|
+
'conditions_comparable':bool(outcome_current and latest and latest['status'] == 'observed'
|
|
262
|
+
and _comparable(recorded_configuration, outcome_facts)),
|
|
263
|
+
'served_model':processor.get('served_model'),
|
|
264
|
+
'original_served_model':detail['model_provenance']['served_model'],
|
|
265
|
+
'processor_observation':processor,
|
|
266
|
+
'evidence_refs':prediction['evidence_refs'],'source_versions':detail['source_versions'],
|
|
267
|
+
'suggestion_enabled':False,'quality_evaluated':False,
|
|
268
|
+
'enable_criteria':{'cohort_must_be_closed':True,'minimum_comparable_terminal_receipts':10,
|
|
269
|
+
'lower_mean_absolute_error':True,'no_more_premature_inspections':True,
|
|
270
|
+
'no_greater_mean_inspection_lateness':True}}
|
|
271
|
+
if latest and latest['status'] == 'observed' and latest['value'] is True and outcome_current:
|
|
272
|
+
ended = latest['observed_at']
|
|
273
|
+
result['comparison'] = {
|
|
274
|
+
'conditions_comparable':result['conditions_comparable'],
|
|
275
|
+
'observed_response_model':processor.get('served_model'),
|
|
276
|
+
'receipt_ref':latest['receipt_ref'],'outcome_source_versions':latest['source_versions'],
|
|
277
|
+
'turnaround_seconds':ended-detail['origin_at'],
|
|
278
|
+
'forecast_absolute_error_seconds':abs(ended-prediction['horizon']),
|
|
279
|
+
'prior_absolute_error_seconds':abs(ended-prior_horizon),
|
|
280
|
+
'forecast_premature_inspection':prediction['horizon'] < ended,
|
|
281
|
+
'prior_premature_inspection':prior_horizon < ended,
|
|
282
|
+
'forecast_inspection_lateness_seconds':max(0.,prediction['horizon']-ended),
|
|
283
|
+
'prior_inspection_lateness_seconds':max(0.,prior_horizon-ended),
|
|
284
|
+
'counterfactual_horizon_comparison':True,'projection_added_status_calls':0}
|
|
285
|
+
return result
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def safe(operation, review, native, state, owner):
|
|
289
|
+
"""A forecast observer failure is visible but cannot stall accepted work."""
|
|
290
|
+
try:
|
|
291
|
+
return operation(review,native,state,owner)
|
|
292
|
+
except SourceErased:
|
|
293
|
+
return {'status':'source_unavailable'}
|
|
294
|
+
except Exception as error:
|
|
295
|
+
logger.warning('Native task forecast observer unavailable: %s', type(error).__name__)
|
|
296
|
+
return {'status':'unavailable','error_type':type(error).__name__}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"""Canonical, prospective native request metadata; no transcript classifier."""
|
|
2
|
+
from contextlib import closing
|
|
3
|
+
import json
|
|
4
|
+
import time
|
|
5
|
+
|
|
6
|
+
from .runtime_forecasts import VERSION, _current, _digest, _identity, _retain
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _prefix(native, run_id):
|
|
10
|
+
return 'native-model:'+_digest({**_identity(native), 'native_run_id': run_id})+':'
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def retain(ledger, owner, native, state, value):
|
|
14
|
+
"""The API caller already verified the active native run and claim lock."""
|
|
15
|
+
request = {key: value.get(key) for key in
|
|
16
|
+
('api_request_id', 'phase', 'requested_model', 'provider', 'response_model')}
|
|
17
|
+
if request['phase'] != 'response':
|
|
18
|
+
request['response_model'] = None
|
|
19
|
+
source_id = _prefix(native, native['native_run_id'])+_digest([request['api_request_id'], request['phase']])
|
|
20
|
+
versions, retained = _retain(ledger, source_id=source_id, owner=owner, native=native,
|
|
21
|
+
occurred_at=time.time(), facts={'version': VERSION, 'kind': 'model_request',
|
|
22
|
+
'identity': _identity(native), 'native_run_id': native['native_run_id'],
|
|
23
|
+
'request': request, 'configuration': state['forecast_configuration']})
|
|
24
|
+
if retained['request'] != request:
|
|
25
|
+
raise ValueError('native_model_observation_conflict')
|
|
26
|
+
return {'accepted': True, 'evidence_refs': list(versions),
|
|
27
|
+
'identity_basis': 'provider_reported_response_model_not_weight_attestation'}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def summarize(ledger, owner, native, run_id):
|
|
31
|
+
"""Pair observed starts/ends; missing, erased and mixed remain explicit."""
|
|
32
|
+
prefix = _prefix(native, run_id)
|
|
33
|
+
with closing(ledger._connect()) as db:
|
|
34
|
+
rows = db.execute('SELECT turn_id,messages_json FROM turn_sources WHERE contact_id=? '
|
|
35
|
+
'AND substr(turn_id,1,?)=? ORDER BY turn_id LIMIT 257',
|
|
36
|
+
(owner, len(prefix), prefix)).fetchall()
|
|
37
|
+
erased = db.execute('SELECT 1 FROM source_erasures WHERE substr(turn_id,1,?)=? LIMIT 1',
|
|
38
|
+
(len(prefix), prefix)).fetchone()
|
|
39
|
+
refs = ledger.source_references([r['turn_id'] for r in rows], contact_id=owner, session_id='')
|
|
40
|
+
versions = {'receipt:'+r['source_id']: r['source_version'] for r in refs}
|
|
41
|
+
valid = bool(rows) and len(refs) == len(rows) and len(rows) <= 256 and not erased and _current(ledger, list(versions), versions, owner)
|
|
42
|
+
requests, configurations, models, requested = {}, [], set(), set()
|
|
43
|
+
for row in rows:
|
|
44
|
+
facts = json.loads(row['messages_json'])[0].get('_native_forecast_facts', {})
|
|
45
|
+
if (facts.get('version') != VERSION or facts.get('kind') != 'model_request'
|
|
46
|
+
or facts.get('identity') != _identity(native) or facts.get('native_run_id') != run_id):
|
|
47
|
+
valid = False
|
|
48
|
+
continue
|
|
49
|
+
request = facts['request']
|
|
50
|
+
requests.setdefault(request['api_request_id'], {})[request['phase']] = request
|
|
51
|
+
configurations.append(facts['configuration'])
|
|
52
|
+
if request.get('requested_model'):
|
|
53
|
+
requested.add(request['requested_model'])
|
|
54
|
+
if request['phase'] == 'response' and request.get('response_model'):
|
|
55
|
+
models.add(request['response_model'])
|
|
56
|
+
complete = valid and all(set(v) in ({'start', 'response'}, {'start', 'error'}) for v in requests.values())
|
|
57
|
+
missing_model = any('response' in v and not v['response'].get('response_model') for v in requests.values())
|
|
58
|
+
same_configuration = bool(configurations) and all(c == configurations[0] for c in configurations)
|
|
59
|
+
return {'basis': 'provider_reported_response_model_not_weight_attestation',
|
|
60
|
+
'coverage': 'registered native review API requests only; auxiliary and uninstrumented calls unknown',
|
|
61
|
+
'request_count': len(requests), 'complete_observed_pairs': bool(complete),
|
|
62
|
+
'response_models': sorted(models), 'requested_models': sorted(requested),
|
|
63
|
+
'served_model': next(iter(models)) if complete and not missing_model and len(models) == 1 else None,
|
|
64
|
+
'model_state': 'unknown' if not complete or missing_model or not models else 'mixed' if len(models) > 1 else 'observed',
|
|
65
|
+
'configuration': configurations[0] if same_configuration else None,
|
|
66
|
+
'configuration_state': 'observed' if same_configuration and complete else 'unknown_or_changed',
|
|
67
|
+
'evidence_refs': list(versions), 'source_versions': versions}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
"""Source settlement: resolving a concern settles what the concern points at.
|
|
2
|
+
|
|
3
|
+
A concern raised from a durable source (an overdue commitment, an anomaly, a
|
|
4
|
+
stale goal) carries that source in its ``sources`` list as ``"<kind>:<id>"``.
|
|
5
|
+
Resolving only the concern leaves the source open, and whatever ingests that
|
|
6
|
+
source re-raises the concern on the next tick — the owner's resolve silently
|
|
7
|
+
undone minutes later. The registry maps a source kind to a settle callback so
|
|
8
|
+
every surface (owner deck, agent tool, MCP, API) closes the whole chain with
|
|
9
|
+
one call.
|
|
10
|
+
|
|
11
|
+
Deployment-agnostic: the server wires settlers for whatever stores it runs;
|
|
12
|
+
unknown source kinds are skipped, and one failing settler never blocks the
|
|
13
|
+
others.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import hashlib
|
|
19
|
+
import logging
|
|
20
|
+
import threading
|
|
21
|
+
from typing import Any, Callable, Dict, List, Mapping, Optional
|
|
22
|
+
|
|
23
|
+
logger = logging.getLogger(__name__)
|
|
24
|
+
|
|
25
|
+
Settler = Callable[..., Optional[Dict[str, Any]]]
|
|
26
|
+
|
|
27
|
+
_SETTLERS: Dict[str, Settler] = {}
|
|
28
|
+
_RETRY_SAFE: set[str] = set()
|
|
29
|
+
_REGISTRY_LOCK = threading.RLock()
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class SettlementRetryUnsafe(ValueError):
|
|
33
|
+
"""One or more sources lack an exact operation-bound retry contract."""
|
|
34
|
+
|
|
35
|
+
def __init__(self, sources: List[str]) -> None:
|
|
36
|
+
super().__init__("cascade recovery requires retry-safe source settlers")
|
|
37
|
+
self.sources = list(sources)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class _SettlementOperationUnverified(ValueError):
|
|
41
|
+
settlement_error_code = "operation_unverified"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def source_operation_id(operation_root: str, source: str) -> str:
|
|
45
|
+
"""Derive the stable per-intent/per-source idempotency identity."""
|
|
46
|
+
|
|
47
|
+
root = str(operation_root or "").strip()
|
|
48
|
+
ref = str(source or "").strip()
|
|
49
|
+
if not root or not ref:
|
|
50
|
+
raise ValueError("source settlement operation identity is incomplete")
|
|
51
|
+
digest = hashlib.sha256(f"{root}\0{ref}".encode("utf-8")).hexdigest()
|
|
52
|
+
return f"concern-source-operation:{digest}"
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def register_settler(
|
|
56
|
+
kind: str,
|
|
57
|
+
fn: Settler,
|
|
58
|
+
*,
|
|
59
|
+
retry_safe: bool = False,
|
|
60
|
+
) -> None:
|
|
61
|
+
"""Register the settle callback for a source kind (e.g. "commitment").
|
|
62
|
+
|
|
63
|
+
Retry-safe callbacks additionally receive a stable ``operation_id`` and
|
|
64
|
+
must bind it together with outcome, note, and resolver to the durable
|
|
65
|
+
transition. Their detail must attest those exact fields. This is a
|
|
66
|
+
stronger contract than merely returning the same terminal state twice.
|
|
67
|
+
"""
|
|
68
|
+
if type(retry_safe) is not bool:
|
|
69
|
+
raise ValueError("settler retry_safe must be boolean")
|
|
70
|
+
with _REGISTRY_LOCK:
|
|
71
|
+
_SETTLERS[kind] = fn
|
|
72
|
+
if retry_safe:
|
|
73
|
+
_RETRY_SAFE.add(kind)
|
|
74
|
+
else:
|
|
75
|
+
_RETRY_SAFE.discard(kind)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def registered_kinds() -> List[str]:
|
|
79
|
+
with _REGISTRY_LOCK:
|
|
80
|
+
return sorted(_SETTLERS)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _settle_sources(
|
|
84
|
+
sources: List[str],
|
|
85
|
+
*,
|
|
86
|
+
callbacks: Mapping[str, Settler],
|
|
87
|
+
retry_safe_kinds: set[str],
|
|
88
|
+
outcome: str,
|
|
89
|
+
note: str,
|
|
90
|
+
resolved_by: str,
|
|
91
|
+
operation_root: Optional[str],
|
|
92
|
+
) -> List[Dict[str, Any]]:
|
|
93
|
+
results: List[Dict[str, Any]] = []
|
|
94
|
+
for src in sources:
|
|
95
|
+
kind, _, source_id = str(src).partition(":")
|
|
96
|
+
fn = callbacks.get(kind)
|
|
97
|
+
if fn is None or not source_id:
|
|
98
|
+
continue
|
|
99
|
+
entry: Dict[str, Any] = {"source": src, "settled": False}
|
|
100
|
+
operation_id = None
|
|
101
|
+
try:
|
|
102
|
+
kwargs: Dict[str, Any] = {
|
|
103
|
+
"outcome": outcome,
|
|
104
|
+
"note": note,
|
|
105
|
+
"resolved_by": resolved_by,
|
|
106
|
+
}
|
|
107
|
+
if operation_root is not None and kind in retry_safe_kinds:
|
|
108
|
+
operation_id = source_operation_id(operation_root, src)
|
|
109
|
+
kwargs["operation_id"] = operation_id
|
|
110
|
+
detail = fn(source_id, **kwargs)
|
|
111
|
+
if detail is not None:
|
|
112
|
+
if operation_id is not None:
|
|
113
|
+
note_digest = hashlib.sha256(note.encode("utf-8")).hexdigest()
|
|
114
|
+
if not isinstance(detail, Mapping) or any((
|
|
115
|
+
detail.get("operation_id") != operation_id,
|
|
116
|
+
detail.get("outcome") != outcome,
|
|
117
|
+
detail.get("note_digest") != note_digest,
|
|
118
|
+
detail.get("resolved_by") != resolved_by,
|
|
119
|
+
)):
|
|
120
|
+
raise _SettlementOperationUnverified(
|
|
121
|
+
"retry-safe settler did not attest the bound operation"
|
|
122
|
+
)
|
|
123
|
+
entry["settled"] = True
|
|
124
|
+
if isinstance(detail, Mapping):
|
|
125
|
+
entry.update({
|
|
126
|
+
key: value for key, value in detail.items()
|
|
127
|
+
if key not in {
|
|
128
|
+
"source", "settled", "error", "error_digest",
|
|
129
|
+
}
|
|
130
|
+
})
|
|
131
|
+
except Exception as exc:
|
|
132
|
+
logger.warning("settler for %s failed (%s)", src, type(exc).__name__)
|
|
133
|
+
error_code = getattr(exc, "settlement_error_code", "settler_error")
|
|
134
|
+
if error_code not in {
|
|
135
|
+
"operation_conflict", "operation_unverified", "settler_error",
|
|
136
|
+
}:
|
|
137
|
+
error_code = "settler_error"
|
|
138
|
+
entry["error"] = error_code
|
|
139
|
+
entry["error_digest"] = hashlib.sha256(
|
|
140
|
+
str(exc).encode("utf-8", errors="replace")
|
|
141
|
+
).hexdigest()
|
|
142
|
+
results.append(entry)
|
|
143
|
+
return results
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def settle_sources(
|
|
147
|
+
sources: Optional[List[str]],
|
|
148
|
+
*,
|
|
149
|
+
outcome: str = "done",
|
|
150
|
+
note: str = "",
|
|
151
|
+
resolved_by: str = "owner",
|
|
152
|
+
operation_root: Optional[str] = None,
|
|
153
|
+
) -> List[Dict[str, Any]]:
|
|
154
|
+
"""Settle every recognized source reference. Returns one entry per source
|
|
155
|
+
that had a registered settler, with ``settled`` and any settler detail."""
|
|
156
|
+
with _REGISTRY_LOCK:
|
|
157
|
+
callbacks = dict(_SETTLERS)
|
|
158
|
+
retry_safe_kinds = set(_RETRY_SAFE)
|
|
159
|
+
return _settle_sources(
|
|
160
|
+
list(sources or []),
|
|
161
|
+
callbacks=callbacks,
|
|
162
|
+
retry_safe_kinds=retry_safe_kinds,
|
|
163
|
+
outcome=outcome,
|
|
164
|
+
note=note,
|
|
165
|
+
resolved_by=resolved_by,
|
|
166
|
+
operation_root=operation_root,
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def retry_safe_settle_sources(
|
|
171
|
+
sources: Optional[List[str]],
|
|
172
|
+
*,
|
|
173
|
+
operation_root: str,
|
|
174
|
+
outcome: str = "done",
|
|
175
|
+
note: str = "",
|
|
176
|
+
resolved_by: str = "owner",
|
|
177
|
+
) -> List[Dict[str, Any]]:
|
|
178
|
+
"""Explicitly retry only sources with exact operation-bound semantics.
|
|
179
|
+
|
|
180
|
+
The complete source set is validated before the first callback. Unknown,
|
|
181
|
+
malformed, or merely state-idempotent settlers are rejected together and
|
|
182
|
+
remain pending for explicit reconciliation.
|
|
183
|
+
"""
|
|
184
|
+
|
|
185
|
+
refs = list(sources or [])
|
|
186
|
+
unsafe: List[str] = []
|
|
187
|
+
with _REGISTRY_LOCK:
|
|
188
|
+
callbacks = dict(_SETTLERS)
|
|
189
|
+
retry_safe_kinds = set(_RETRY_SAFE)
|
|
190
|
+
for src in refs:
|
|
191
|
+
kind, separator, source_id = str(src).partition(":")
|
|
192
|
+
if (
|
|
193
|
+
not separator or not source_id or kind not in callbacks
|
|
194
|
+
or kind not in retry_safe_kinds
|
|
195
|
+
):
|
|
196
|
+
unsafe.append(str(src))
|
|
197
|
+
if unsafe:
|
|
198
|
+
raise SettlementRetryUnsafe(unsafe)
|
|
199
|
+
return _settle_sources(
|
|
200
|
+
refs,
|
|
201
|
+
callbacks=callbacks,
|
|
202
|
+
retry_safe_kinds=retry_safe_kinds,
|
|
203
|
+
outcome=outcome,
|
|
204
|
+
note=note,
|
|
205
|
+
resolved_by=resolved_by,
|
|
206
|
+
operation_root=operation_root,
|
|
207
|
+
)
|