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,197 @@
|
|
|
1
|
+
-- Colony Distributed Task Queue — SQLite schema
|
|
2
|
+
-- WAL mode is set at connection time (PRAGMA journal_mode=WAL)
|
|
3
|
+
|
|
4
|
+
CREATE TABLE IF NOT EXISTS jobs (
|
|
5
|
+
job_id TEXT PRIMARY KEY,
|
|
6
|
+
job_type TEXT NOT NULL,
|
|
7
|
+
payload TEXT NOT NULL, -- JSON
|
|
8
|
+
priority INTEGER NOT NULL DEFAULT 50,
|
|
9
|
+
capabilities TEXT NOT NULL DEFAULT '[]', -- JSON array
|
|
10
|
+
deadline TEXT, -- ISO8601 UTC
|
|
11
|
+
max_retries INTEGER NOT NULL DEFAULT 3,
|
|
12
|
+
retry_count INTEGER NOT NULL DEFAULT 0,
|
|
13
|
+
timeout_secs REAL NOT NULL DEFAULT 3600.0,
|
|
14
|
+
depends_on TEXT NOT NULL DEFAULT '[]', -- JSON array of job_ids
|
|
15
|
+
posted_by TEXT NOT NULL DEFAULT '',
|
|
16
|
+
posted_at TEXT NOT NULL, -- ISO8601 UTC
|
|
17
|
+
status TEXT NOT NULL DEFAULT 'queued',
|
|
18
|
+
claimed_by TEXT,
|
|
19
|
+
claimed_at TEXT,
|
|
20
|
+
claim_attempt_id TEXT,
|
|
21
|
+
claim_expires_at TEXT,
|
|
22
|
+
last_heartbeat TEXT,
|
|
23
|
+
result TEXT, -- JSON
|
|
24
|
+
tags TEXT NOT NULL DEFAULT '{}' -- JSON object
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
CREATE INDEX IF NOT EXISTS idx_jobs_status ON jobs(status);
|
|
28
|
+
CREATE INDEX IF NOT EXISTS idx_jobs_priority ON jobs(priority DESC, posted_at ASC);
|
|
29
|
+
CREATE INDEX IF NOT EXISTS idx_jobs_deadline ON jobs(deadline) WHERE deadline IS NOT NULL;
|
|
30
|
+
CREATE INDEX IF NOT EXISTS idx_jobs_claimed_by ON jobs(claimed_by) WHERE claimed_by IS NOT NULL;
|
|
31
|
+
|
|
32
|
+
CREATE TABLE IF NOT EXISTS workers (
|
|
33
|
+
node_id TEXT PRIMARY KEY,
|
|
34
|
+
capabilities TEXT NOT NULL DEFAULT '[]', -- JSON array
|
|
35
|
+
capacity TEXT NOT NULL DEFAULT '{}', -- JSON object
|
|
36
|
+
max_concurrent INTEGER NOT NULL DEFAULT 4,
|
|
37
|
+
job_types TEXT NOT NULL DEFAULT '[]', -- JSON array
|
|
38
|
+
available INTEGER NOT NULL DEFAULT 1,
|
|
39
|
+
load REAL NOT NULL DEFAULT 0.0,
|
|
40
|
+
registered_at TEXT NOT NULL,
|
|
41
|
+
last_seen TEXT NOT NULL
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
CREATE TABLE IF NOT EXISTS job_audit (
|
|
45
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
46
|
+
job_id TEXT NOT NULL,
|
|
47
|
+
timestamp TEXT NOT NULL, -- ISO8601 UTC
|
|
48
|
+
from_status TEXT,
|
|
49
|
+
to_status TEXT NOT NULL,
|
|
50
|
+
node_id TEXT,
|
|
51
|
+
claim_attempt_id TEXT,
|
|
52
|
+
reason TEXT,
|
|
53
|
+
details TEXT -- JSON
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
CREATE INDEX IF NOT EXISTS idx_audit_job_id ON job_audit(job_id);
|
|
57
|
+
CREATE INDEX IF NOT EXISTS idx_audit_ts ON job_audit(timestamp DESC);
|
|
58
|
+
CREATE INDEX IF NOT EXISTS idx_workers_available ON workers(available);
|
|
59
|
+
CREATE INDEX IF NOT EXISTS idx_workers_last_seen ON workers(last_seen DESC);
|
|
60
|
+
|
|
61
|
+
CREATE TABLE IF NOT EXISTS heartbeats (
|
|
62
|
+
node_id TEXT NOT NULL,
|
|
63
|
+
job_id TEXT NOT NULL,
|
|
64
|
+
timestamp TEXT NOT NULL,
|
|
65
|
+
claim_attempt_id TEXT,
|
|
66
|
+
progress REAL,
|
|
67
|
+
PRIMARY KEY (node_id, job_id)
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
-- Completion/failure competence delivery is an outbox, not a best-effort
|
|
71
|
+
-- callback after the job transaction. event_id is stable per claim attempt.
|
|
72
|
+
CREATE TABLE IF NOT EXISTS worker_outcome_outbox (
|
|
73
|
+
event_id TEXT PRIMARY KEY,
|
|
74
|
+
job_id TEXT NOT NULL,
|
|
75
|
+
claim_attempt_id TEXT NOT NULL,
|
|
76
|
+
report TEXT NOT NULL,
|
|
77
|
+
verdict TEXT NOT NULL,
|
|
78
|
+
outcome TEXT NOT NULL,
|
|
79
|
+
worker_mode TEXT NOT NULL,
|
|
80
|
+
success_attested INTEGER NOT NULL DEFAULT 0,
|
|
81
|
+
latency REAL,
|
|
82
|
+
attempts INTEGER NOT NULL DEFAULT 0,
|
|
83
|
+
state TEXT NOT NULL DEFAULT 'pending',
|
|
84
|
+
delivery_attempts INTEGER NOT NULL DEFAULT 0,
|
|
85
|
+
last_error TEXT,
|
|
86
|
+
created_at TEXT NOT NULL,
|
|
87
|
+
delivered_at TEXT
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
CREATE INDEX IF NOT EXISTS idx_worker_outcome_pending
|
|
91
|
+
ON worker_outcome_outbox(state, created_at);
|
|
92
|
+
|
|
93
|
+
-- WorkControlV1 is an additive control ledger over real queue jobs. It does
|
|
94
|
+
-- not duplicate executable payloads and deliberately survives job pruning so
|
|
95
|
+
-- operation receipts remain readable after the work itself is archived.
|
|
96
|
+
CREATE TABLE IF NOT EXISTS work_control_targets (
|
|
97
|
+
target_id TEXT PRIMARY KEY,
|
|
98
|
+
run_id TEXT NOT NULL UNIQUE,
|
|
99
|
+
authority_digest TEXT NOT NULL,
|
|
100
|
+
revision INTEGER NOT NULL DEFAULT 0,
|
|
101
|
+
state_digest TEXT NOT NULL DEFAULT '',
|
|
102
|
+
projected_at TEXT NOT NULL
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
CREATE TABLE IF NOT EXISTS work_control_operations (
|
|
106
|
+
operation_id TEXT PRIMARY KEY,
|
|
107
|
+
target_id TEXT NOT NULL,
|
|
108
|
+
run_id TEXT NOT NULL,
|
|
109
|
+
authority_digest TEXT NOT NULL,
|
|
110
|
+
operation_type TEXT NOT NULL,
|
|
111
|
+
request_digest TEXT NOT NULL,
|
|
112
|
+
request_json TEXT NOT NULL,
|
|
113
|
+
requested_by TEXT NOT NULL,
|
|
114
|
+
request_authority TEXT NOT NULL DEFAULT '{}',
|
|
115
|
+
status TEXT NOT NULL,
|
|
116
|
+
expected_revision INTEGER NOT NULL,
|
|
117
|
+
expected_state_digest TEXT NOT NULL,
|
|
118
|
+
accepted_revision INTEGER NOT NULL,
|
|
119
|
+
result_revision INTEGER,
|
|
120
|
+
result_state_digest TEXT,
|
|
121
|
+
attempt_id TEXT,
|
|
122
|
+
worker_id TEXT,
|
|
123
|
+
from_job_status TEXT,
|
|
124
|
+
to_job_status TEXT,
|
|
125
|
+
effect_disposition TEXT NOT NULL,
|
|
126
|
+
ack_details TEXT NOT NULL DEFAULT '{}',
|
|
127
|
+
ack_authority TEXT NOT NULL DEFAULT '{}',
|
|
128
|
+
created_at TEXT NOT NULL,
|
|
129
|
+
ack_deadline TEXT,
|
|
130
|
+
acknowledged_at TEXT
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
CREATE INDEX IF NOT EXISTS idx_work_control_target
|
|
134
|
+
ON work_control_operations(target_id, created_at);
|
|
135
|
+
|
|
136
|
+
CREATE INDEX IF NOT EXISTS idx_work_control_pending_worker
|
|
137
|
+
ON work_control_operations(worker_id, status, created_at);
|
|
138
|
+
|
|
139
|
+
-- Accepted and outcome receipts are immutable append-only facts. The
|
|
140
|
+
-- operation row above is only a current projection for efficient polling.
|
|
141
|
+
CREATE TABLE IF NOT EXISTS work_control_receipts (
|
|
142
|
+
receipt_id TEXT PRIMARY KEY,
|
|
143
|
+
operation_id TEXT NOT NULL,
|
|
144
|
+
target_id TEXT NOT NULL,
|
|
145
|
+
run_id TEXT NOT NULL,
|
|
146
|
+
authority_digest TEXT NOT NULL,
|
|
147
|
+
phase TEXT NOT NULL,
|
|
148
|
+
payload_json TEXT NOT NULL,
|
|
149
|
+
receipt_digest TEXT NOT NULL UNIQUE,
|
|
150
|
+
created_at TEXT NOT NULL
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
CREATE INDEX IF NOT EXISTS idx_work_control_receipts_operation
|
|
154
|
+
ON work_control_receipts(operation_id, created_at, receipt_id);
|
|
155
|
+
|
|
156
|
+
-- A steer handler may finish before its acknowledgement reaches the queue.
|
|
157
|
+
-- This exact-attempt ledger lets a restarted worker retry the acknowledgement
|
|
158
|
+
-- before invoking the handler again. Steer handlers must additionally attest
|
|
159
|
+
-- durable operation-id idempotency to cover the handler/ledger crash window.
|
|
160
|
+
CREATE TABLE IF NOT EXISTS work_control_worker_outcomes (
|
|
161
|
+
operation_id TEXT PRIMARY KEY,
|
|
162
|
+
target_id TEXT NOT NULL,
|
|
163
|
+
attempt_id TEXT NOT NULL,
|
|
164
|
+
worker_id TEXT NOT NULL,
|
|
165
|
+
authority_digest TEXT NOT NULL,
|
|
166
|
+
outcome TEXT NOT NULL,
|
|
167
|
+
details_json TEXT NOT NULL,
|
|
168
|
+
outcome_digest TEXT NOT NULL UNIQUE,
|
|
169
|
+
recorded_at TEXT NOT NULL
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
-- Independent evidence is first-valid-decision-wins for an exact started
|
|
173
|
+
-- attempt. It can prove either that the effect applied or that it did not.
|
|
174
|
+
CREATE TABLE IF NOT EXISTS work_effect_reconciliations (
|
|
175
|
+
reconciliation_id TEXT PRIMARY KEY,
|
|
176
|
+
target_id TEXT NOT NULL,
|
|
177
|
+
attempt_id TEXT NOT NULL,
|
|
178
|
+
authority_digest TEXT NOT NULL,
|
|
179
|
+
finding TEXT NOT NULL,
|
|
180
|
+
evidence_json TEXT NOT NULL,
|
|
181
|
+
evidence_digest TEXT NOT NULL,
|
|
182
|
+
verifier_identity TEXT NOT NULL,
|
|
183
|
+
verifier_type TEXT NOT NULL,
|
|
184
|
+
verifier_authority TEXT NOT NULL,
|
|
185
|
+
created_at TEXT NOT NULL,
|
|
186
|
+
UNIQUE(target_id, attempt_id)
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
-- Pruning retires an identity permanently. Without this tombstone a
|
|
190
|
+
-- deterministic WorkOrder/job ID could be reposted after its evidence row was
|
|
191
|
+
-- deleted and accidentally acquire a second execution history.
|
|
192
|
+
CREATE TABLE IF NOT EXISTS job_tombstones (
|
|
193
|
+
job_id TEXT PRIMARY KEY,
|
|
194
|
+
final_status TEXT NOT NULL,
|
|
195
|
+
job_digest TEXT NOT NULL,
|
|
196
|
+
pruned_at TEXT NOT NULL
|
|
197
|
+
);
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
"""Generic, durable control contract for Colony queue work.
|
|
2
|
+
|
|
3
|
+
``WorkControlV1`` deliberately controls only real jobs owned by the durable
|
|
4
|
+
task queue. A queue job is the stable target, its server-minted claim is the
|
|
5
|
+
attempt, and a durable run ID binds both without copying job payloads into the
|
|
6
|
+
control ledger.
|
|
7
|
+
|
|
8
|
+
The module contains validation/projection helpers plus a small service facade.
|
|
9
|
+
Persistence and state transitions remain in :class:`QueueManager` so normal
|
|
10
|
+
job lifecycle writes and control compare-and-swap operations share one lock
|
|
11
|
+
and one SQLite transaction.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import hashlib
|
|
17
|
+
import json
|
|
18
|
+
import os
|
|
19
|
+
import re
|
|
20
|
+
from dataclasses import dataclass
|
|
21
|
+
from typing import Any, Dict, Mapping, Optional
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
WORK_CONTROL_SCHEMA = "WorkControlV1"
|
|
25
|
+
WORK_CONTROL_VERSION = 1
|
|
26
|
+
WORK_CONTROL_OPERATIONS = frozenset({"steer", "interrupt", "cancel", "retry"})
|
|
27
|
+
WORK_CONTROL_INTERRUPT_CAPABILITY_PREFIX = "work_control:interrupt:v1:"
|
|
28
|
+
WORK_CONTROL_STEER_CAPABILITY_PREFIX = "work_control:steer:v1:"
|
|
29
|
+
|
|
30
|
+
_OPERATION_ID_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9_.:@/+\-]{0,127}$")
|
|
31
|
+
_DIGEST_RE = re.compile(r"^[0-9a-f]{64}$")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class WorkControlError(RuntimeError):
|
|
35
|
+
"""Typed public failure from the WorkControl service."""
|
|
36
|
+
|
|
37
|
+
def __init__(self, code: str, message: str, *, status_code: int = 409):
|
|
38
|
+
super().__init__(message)
|
|
39
|
+
self.code = code
|
|
40
|
+
self.message = message
|
|
41
|
+
self.status_code = status_code
|
|
42
|
+
|
|
43
|
+
def detail(self) -> Dict[str, str]:
|
|
44
|
+
return {"code": self.code, "message": self.message}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def work_control_mode() -> str:
|
|
48
|
+
"""Return additive control-plane rollout posture.
|
|
49
|
+
|
|
50
|
+
``off`` removes the WorkControl mutation/delivery surface and advertised
|
|
51
|
+
capabilities. Mode-invariant lifecycle corrections still prevent false
|
|
52
|
+
active cancellation and effectful automatic retry; disabling the feature
|
|
53
|
+
deliberately does not restore those unsafe P6 behaviors.
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
value = os.environ.get("COLONY_WORK_CONTROL_MODE", "off").strip().lower()
|
|
57
|
+
if value not in {"off", "shadow", "live"}:
|
|
58
|
+
raise WorkControlError(
|
|
59
|
+
"work_control_configuration_invalid",
|
|
60
|
+
"COLONY_WORK_CONTROL_MODE must be off, shadow, or live",
|
|
61
|
+
status_code=503,
|
|
62
|
+
)
|
|
63
|
+
return value
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def work_control_ack_timeout_secs() -> float:
|
|
67
|
+
"""Bound a worker command lease so controls cannot strand work forever."""
|
|
68
|
+
|
|
69
|
+
raw = os.environ.get("COLONY_WORK_CONTROL_ACK_TIMEOUT_SECS", "30")
|
|
70
|
+
try:
|
|
71
|
+
value = float(raw)
|
|
72
|
+
except (TypeError, ValueError) as exc:
|
|
73
|
+
raise WorkControlError(
|
|
74
|
+
"work_control_configuration_invalid",
|
|
75
|
+
"COLONY_WORK_CONTROL_ACK_TIMEOUT_SECS must be numeric",
|
|
76
|
+
status_code=503,
|
|
77
|
+
) from exc
|
|
78
|
+
if not 1.0 <= value <= 300.0:
|
|
79
|
+
raise WorkControlError(
|
|
80
|
+
"work_control_configuration_invalid",
|
|
81
|
+
"COLONY_WORK_CONTROL_ACK_TIMEOUT_SECS must be within 1..300",
|
|
82
|
+
status_code=503,
|
|
83
|
+
)
|
|
84
|
+
return value
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def canonical_json(value: Any) -> str:
|
|
88
|
+
"""Encode a value exactly once for all WorkControl digests."""
|
|
89
|
+
|
|
90
|
+
return json.dumps(
|
|
91
|
+
value,
|
|
92
|
+
sort_keys=True,
|
|
93
|
+
separators=(",", ":"),
|
|
94
|
+
ensure_ascii=True,
|
|
95
|
+
allow_nan=False,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def digest_json(value: Any) -> str:
|
|
100
|
+
return hashlib.sha256(canonical_json(value).encode("utf-8")).hexdigest()
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def steer_capability(job_type: object) -> str:
|
|
104
|
+
value = getattr(job_type, "value", job_type)
|
|
105
|
+
return f"{WORK_CONTROL_STEER_CAPABILITY_PREFIX}{str(value)}"
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def interrupt_capability(job_type: object) -> str:
|
|
109
|
+
value = getattr(job_type, "value", job_type)
|
|
110
|
+
return f"{WORK_CONTROL_INTERRUPT_CAPABILITY_PREFIX}{str(value)}"
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def validate_operation_id(value: object) -> str:
|
|
114
|
+
operation_id = str(value or "").strip()
|
|
115
|
+
if not _OPERATION_ID_RE.fullmatch(operation_id):
|
|
116
|
+
raise WorkControlError(
|
|
117
|
+
"invalid_operation_id",
|
|
118
|
+
"operation_id must be a canonical 1..128 character identifier",
|
|
119
|
+
status_code=422,
|
|
120
|
+
)
|
|
121
|
+
return operation_id
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def validate_digest(value: object, *, field: str) -> str:
|
|
125
|
+
digest = str(value or "").strip().lower()
|
|
126
|
+
if not _DIGEST_RE.fullmatch(digest):
|
|
127
|
+
raise WorkControlError(
|
|
128
|
+
f"invalid_{field}",
|
|
129
|
+
f"{field} must be a lowercase SHA-256 digest",
|
|
130
|
+
status_code=422,
|
|
131
|
+
)
|
|
132
|
+
return digest
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def _bounded_text(value: object, *, field: str, maximum: int) -> str:
|
|
136
|
+
text = str(value or "").strip()
|
|
137
|
+
if not text or len(text) > maximum:
|
|
138
|
+
raise WorkControlError(
|
|
139
|
+
f"invalid_{field}",
|
|
140
|
+
f"{field} must be 1..{maximum} characters",
|
|
141
|
+
status_code=422,
|
|
142
|
+
)
|
|
143
|
+
return text
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def normalize_operation_request(
|
|
147
|
+
*,
|
|
148
|
+
operation_id: object,
|
|
149
|
+
operation: object,
|
|
150
|
+
target_id: object,
|
|
151
|
+
run_id: object,
|
|
152
|
+
attempt_id: Optional[object],
|
|
153
|
+
expected_revision: object,
|
|
154
|
+
expected_state_digest: object,
|
|
155
|
+
parameters: Optional[Mapping[str, Any]],
|
|
156
|
+
reason: Optional[object],
|
|
157
|
+
) -> Dict[str, Any]:
|
|
158
|
+
"""Validate and canonicalize one caller-authored control operation."""
|
|
159
|
+
|
|
160
|
+
op_id = validate_operation_id(operation_id)
|
|
161
|
+
op = str(operation or "").strip().lower()
|
|
162
|
+
if op not in WORK_CONTROL_OPERATIONS:
|
|
163
|
+
raise WorkControlError(
|
|
164
|
+
"unsupported_operation",
|
|
165
|
+
"operation must be steer, interrupt, cancel, or retry",
|
|
166
|
+
status_code=422,
|
|
167
|
+
)
|
|
168
|
+
target = _bounded_text(target_id, field="target_id", maximum=192)
|
|
169
|
+
run = _bounded_text(run_id, field="run_id", maximum=128)
|
|
170
|
+
attempt = str(attempt_id or "").strip() or None
|
|
171
|
+
if attempt is not None and len(attempt) > 128:
|
|
172
|
+
raise WorkControlError(
|
|
173
|
+
"invalid_attempt_id",
|
|
174
|
+
"attempt_id must be at most 128 characters",
|
|
175
|
+
status_code=422,
|
|
176
|
+
)
|
|
177
|
+
if type(expected_revision) is not int or expected_revision < 1:
|
|
178
|
+
raise WorkControlError(
|
|
179
|
+
"invalid_expected_revision",
|
|
180
|
+
"expected_revision must be a positive integer",
|
|
181
|
+
status_code=422,
|
|
182
|
+
)
|
|
183
|
+
state_digest = validate_digest(
|
|
184
|
+
expected_state_digest, field="expected_state_digest",
|
|
185
|
+
)
|
|
186
|
+
params = dict(parameters or {})
|
|
187
|
+
if op == "steer":
|
|
188
|
+
unknown = set(params) - {"directive", "context_refs"}
|
|
189
|
+
if unknown:
|
|
190
|
+
raise WorkControlError(
|
|
191
|
+
"invalid_steer_parameters",
|
|
192
|
+
"steer accepts only directive and context_refs",
|
|
193
|
+
status_code=422,
|
|
194
|
+
)
|
|
195
|
+
directive = _bounded_text(
|
|
196
|
+
params.get("directive"), field="directive", maximum=4000,
|
|
197
|
+
)
|
|
198
|
+
refs = params.get("context_refs", [])
|
|
199
|
+
if not isinstance(refs, list) or len(refs) > 32:
|
|
200
|
+
raise WorkControlError(
|
|
201
|
+
"invalid_context_refs",
|
|
202
|
+
"context_refs must be a list of at most 32 references",
|
|
203
|
+
status_code=422,
|
|
204
|
+
)
|
|
205
|
+
normalized_refs = []
|
|
206
|
+
for item in refs:
|
|
207
|
+
normalized_refs.append(
|
|
208
|
+
_bounded_text(item, field="context_ref", maximum=256)
|
|
209
|
+
)
|
|
210
|
+
params = {
|
|
211
|
+
"directive": directive,
|
|
212
|
+
"context_refs": list(dict.fromkeys(normalized_refs)),
|
|
213
|
+
}
|
|
214
|
+
elif params:
|
|
215
|
+
raise WorkControlError(
|
|
216
|
+
"unexpected_operation_parameters",
|
|
217
|
+
f"{op} does not accept parameters",
|
|
218
|
+
status_code=422,
|
|
219
|
+
)
|
|
220
|
+
try:
|
|
221
|
+
encoded_parameters = canonical_json(params)
|
|
222
|
+
except (TypeError, ValueError) as exc:
|
|
223
|
+
raise WorkControlError(
|
|
224
|
+
"invalid_operation_parameters",
|
|
225
|
+
"operation parameters must be finite JSON",
|
|
226
|
+
status_code=422,
|
|
227
|
+
) from exc
|
|
228
|
+
if len(encoded_parameters.encode("utf-8")) > 16 * 1024:
|
|
229
|
+
raise WorkControlError(
|
|
230
|
+
"operation_parameters_too_large",
|
|
231
|
+
"operation parameters exceed 16 KiB",
|
|
232
|
+
status_code=422,
|
|
233
|
+
)
|
|
234
|
+
reason_text = str(reason or "").strip()
|
|
235
|
+
if len(reason_text) > 500:
|
|
236
|
+
raise WorkControlError(
|
|
237
|
+
"invalid_reason", "reason must be at most 500 characters",
|
|
238
|
+
status_code=422,
|
|
239
|
+
)
|
|
240
|
+
return {
|
|
241
|
+
"schema": "WorkControlOperationV1",
|
|
242
|
+
"version": 1,
|
|
243
|
+
"operation_id": op_id,
|
|
244
|
+
"operation": op,
|
|
245
|
+
"target_id": target,
|
|
246
|
+
"run_id": run,
|
|
247
|
+
"attempt_id": attempt,
|
|
248
|
+
"expected_revision": expected_revision,
|
|
249
|
+
"expected_state_digest": state_digest,
|
|
250
|
+
"parameters": params,
|
|
251
|
+
"reason": reason_text,
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def operation_request_digest(request: Mapping[str, Any]) -> str:
|
|
256
|
+
return digest_json(dict(request))
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def build_receipt(row: Mapping[str, Any], *, replayed: bool = False) -> Dict[str, Any]:
|
|
260
|
+
"""Project a durable operation row without exposing job payloads."""
|
|
261
|
+
|
|
262
|
+
ack = json.loads(row.get("ack_details") or "{}")
|
|
263
|
+
receipt = {
|
|
264
|
+
"schema": "WorkControlReceiptV1",
|
|
265
|
+
"version": 1,
|
|
266
|
+
"operation_id": str(row["operation_id"]),
|
|
267
|
+
"operation": str(row["operation_type"]),
|
|
268
|
+
"request_digest": str(row["request_digest"]),
|
|
269
|
+
"requested_by": str(row["requested_by"]),
|
|
270
|
+
"request_authority": json.loads(row.get("request_authority") or "{}"),
|
|
271
|
+
"target_id": str(row["target_id"]),
|
|
272
|
+
"run_id": str(row["run_id"]),
|
|
273
|
+
"authority_digest": str(row["authority_digest"]),
|
|
274
|
+
"attempt_id": row.get("attempt_id"),
|
|
275
|
+
"worker_id": row.get("worker_id"),
|
|
276
|
+
"status": str(row["status"]),
|
|
277
|
+
"from_job_status": row.get("from_job_status"),
|
|
278
|
+
"to_job_status": row.get("to_job_status"),
|
|
279
|
+
"effect_disposition": str(row.get("effect_disposition") or "unknown"),
|
|
280
|
+
"accepted_revision": int(row["accepted_revision"]),
|
|
281
|
+
"result_revision": (
|
|
282
|
+
int(row["result_revision"])
|
|
283
|
+
if row.get("result_revision") is not None else None
|
|
284
|
+
),
|
|
285
|
+
"result_state_digest": row.get("result_state_digest"),
|
|
286
|
+
"created_at": str(row["created_at"]),
|
|
287
|
+
"ack_deadline": row.get("ack_deadline"),
|
|
288
|
+
"acknowledged_at": row.get("acknowledged_at"),
|
|
289
|
+
"acknowledgement": ack,
|
|
290
|
+
"ack_authority": json.loads(row.get("ack_authority") or "{}"),
|
|
291
|
+
"replayed": bool(replayed),
|
|
292
|
+
}
|
|
293
|
+
# The digest deliberately excludes itself and the transport-only replay bit.
|
|
294
|
+
bound = dict(receipt)
|
|
295
|
+
bound.pop("replayed", None)
|
|
296
|
+
receipt["receipt_digest"] = digest_json(bound)
|
|
297
|
+
return receipt
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
@dataclass(frozen=True)
|
|
301
|
+
class WorkControlService:
|
|
302
|
+
"""Public in-process facade over QueueManager's transactional methods."""
|
|
303
|
+
|
|
304
|
+
queue: Any
|
|
305
|
+
|
|
306
|
+
async def inspect(self, target_id: str) -> Dict[str, Any]:
|
|
307
|
+
return await self.queue.get_work_control_target(target_id)
|
|
308
|
+
|
|
309
|
+
async def operate(self, **request: Any) -> Dict[str, Any]:
|
|
310
|
+
return await self.queue.apply_work_control_operation(**request)
|
|
311
|
+
|
|
312
|
+
async def receipt(
|
|
313
|
+
self, target_id: str, operation_id: str,
|
|
314
|
+
) -> Dict[str, Any]:
|
|
315
|
+
return await self.queue.get_work_control_receipt(
|
|
316
|
+
target_id, operation_id,
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
async def reconcile_effect(self, **request: Any) -> Dict[str, Any]:
|
|
320
|
+
return await self.queue.reconcile_work_effect(**request)
|
|
321
|
+
|
|
322
|
+
async def pending_for_worker(self, worker_id: str) -> list[Dict[str, Any]]:
|
|
323
|
+
return await self.queue.pending_work_control_operations(worker_id)
|
|
324
|
+
|
|
325
|
+
async def acknowledge(
|
|
326
|
+
self,
|
|
327
|
+
*,
|
|
328
|
+
worker_id: str,
|
|
329
|
+
operation_id: str,
|
|
330
|
+
attempt_id: str,
|
|
331
|
+
outcome: str,
|
|
332
|
+
details: Optional[Mapping[str, Any]] = None,
|
|
333
|
+
ack_authority: Optional[Mapping[str, Any]] = None,
|
|
334
|
+
) -> Dict[str, Any]:
|
|
335
|
+
return await self.queue.acknowledge_work_control_operation(
|
|
336
|
+
worker_id=worker_id,
|
|
337
|
+
operation_id=operation_id,
|
|
338
|
+
attempt_id=attempt_id,
|
|
339
|
+
outcome=outcome,
|
|
340
|
+
details=dict(details or {}),
|
|
341
|
+
ack_authority=dict(ack_authority or {}),
|
|
342
|
+
)
|