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,382 @@
|
|
|
1
|
+
"""Colony worker daemon -- claims typed jobs and executes them with an LLM
|
|
2
|
+
(cognition program, Phase B item 5).
|
|
3
|
+
|
|
4
|
+
This is the installable, capability-typed worker half of the Colony queue.
|
|
5
|
+
It authenticates to the sidecar, advertises its capabilities, claims eligible
|
|
6
|
+
jobs, reasons over each with an OpenAI-compatible LLM using a READ/ANALYSE
|
|
7
|
+
tool posture ONLY, and posts a structured report the sidecar audits.
|
|
8
|
+
|
|
9
|
+
Trust model: the worker is UNTRUSTED. It never performs mutations itself and
|
|
10
|
+
never reports one -- the sidecar's WorkerGovernor re-checks capability
|
|
11
|
+
coverage and owner boundaries at claim time and audits the report at
|
|
12
|
+
completion (a mutation on a read-only job is a server-side violation). Keeping
|
|
13
|
+
the worker read/analyse-only is what makes "never trust the client" cheap:
|
|
14
|
+
there is nothing dangerous for it to do.
|
|
15
|
+
|
|
16
|
+
Deliberately stdlib-only (like the other workers/ modules) so it runs on hosts
|
|
17
|
+
that have the agent but not the full sidecar dependency stack. The report
|
|
18
|
+
schema (summary, operations, files_touched, commits, branch, confidence)
|
|
19
|
+
matches what governor.audit_report expects.
|
|
20
|
+
|
|
21
|
+
Native alternative (preferred where available): rather than this bespoke
|
|
22
|
+
executor, a deployment can drive work through Hermes kanban (dispatcher +
|
|
23
|
+
worker subprocess) or the gateway's structured-runs API (/v1/runs + SSE +
|
|
24
|
+
approval), and let this governor enforce/audit the same way. This daemon is
|
|
25
|
+
the dependency-light reference path; see the private deployment layer for the
|
|
26
|
+
host/LLM/capability placement.
|
|
27
|
+
|
|
28
|
+
Environment:
|
|
29
|
+
COLONY_URL sidecar base URL (default http://127.0.0.1:7777)
|
|
30
|
+
COLONY_API_KEY sidecar API key (default dev-mode-no-key)
|
|
31
|
+
COLONY_WORKER_NODE_ID worker identity (default <agent>-worker)
|
|
32
|
+
COLONY_WORKER_CAPABILITIES csv capabilities (default research,analyst,read)
|
|
33
|
+
COLONY_WORKER_JOB_TYPES csv job types (default research)
|
|
34
|
+
COLONY_WORKER_MAX_JOBS jobs per cycle (default 1)
|
|
35
|
+
COLONY_WORKER_POLL_SECS seconds between polls (default 30; daemon mode)
|
|
36
|
+
COLONY_WORKER_HEARTBEAT_SECS heartbeat cadence while running (default 20)
|
|
37
|
+
COLONY_WORKER_LLM_BASE_URL OpenAI-compatible base (default OPENAI_BASE_URL)
|
|
38
|
+
COLONY_WORKER_LLM_MODEL model name (default gpt-4o-mini)
|
|
39
|
+
COLONY_WORKER_LLM_API_KEY LLM key (default OPENAI_API_KEY)
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
from __future__ import annotations
|
|
43
|
+
|
|
44
|
+
import argparse
|
|
45
|
+
import json
|
|
46
|
+
import os
|
|
47
|
+
import threading
|
|
48
|
+
import time
|
|
49
|
+
import urllib.request
|
|
50
|
+
from typing import Any, Dict, List, Optional
|
|
51
|
+
|
|
52
|
+
# Compact worker doctrine (mirrors cognition/charter.py role="worker"; inlined
|
|
53
|
+
# to keep this daemon stdlib-only and runnable without the sidecar package).
|
|
54
|
+
_WORKER_SYSTEM = (
|
|
55
|
+
"You are an Apsimo worker agent executing ONE claimed job with a read and "
|
|
56
|
+
"analysis posture only. You never modify files, commit, push, deploy, or "
|
|
57
|
+
"send anything -- you observe, reason, and report. Rules: stay strictly "
|
|
58
|
+
"inside the job's stated scope; verify before asserting; if the job cannot "
|
|
59
|
+
"be done, say so plainly with the reason; never fabricate a result. "
|
|
60
|
+
"Respond with ONLY a JSON object (no prose, no markdown fences):\n"
|
|
61
|
+
'{"summary": str (outcome first, then evidence), '
|
|
62
|
+
'"operations": [str] (only from: analyze, read, search), '
|
|
63
|
+
'"files_touched": [] (always empty -- you do not touch files), '
|
|
64
|
+
'"commits": 0, "branch": "", '
|
|
65
|
+
'"confidence": float 0.0-1.0 (calibrated: your honesty here trains earned '
|
|
66
|
+
'autonomy), "remaining_work": str}'
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def load_config() -> Dict[str, Any]:
|
|
71
|
+
"""Resolve worker config from the environment (at call time)."""
|
|
72
|
+
node_id = os.environ.get("COLONY_WORKER_NODE_ID") or (
|
|
73
|
+
os.environ.get("COLONY_AGENT_NAME", "colony").strip().lower().replace(" ", "-")
|
|
74
|
+
+ "-worker"
|
|
75
|
+
)
|
|
76
|
+
caps = [c.strip() for c in os.environ.get(
|
|
77
|
+
"COLONY_WORKER_CAPABILITIES", "research,analyst,read").split(",") if c.strip()]
|
|
78
|
+
job_types = [t.strip() for t in os.environ.get(
|
|
79
|
+
"COLONY_WORKER_JOB_TYPES", "research").split(",") if t.strip()]
|
|
80
|
+
if os.environ.get(
|
|
81
|
+
"COLONY_AGENT_JOB_CLAIMS_ENABLED", "true"
|
|
82
|
+
).strip().lower() not in {"1", "true", "yes", "on"}:
|
|
83
|
+
job_types = [item for item in job_types if item != "agent_action"]
|
|
84
|
+
return {
|
|
85
|
+
"colony_url": os.environ.get("COLONY_URL", "http://127.0.0.1:7777").rstrip("/"),
|
|
86
|
+
"api_key": os.environ.get("COLONY_API_KEY", "dev-mode-no-key"),
|
|
87
|
+
"node_id": node_id,
|
|
88
|
+
"capabilities": caps,
|
|
89
|
+
"job_types": job_types,
|
|
90
|
+
"max_jobs": int(os.environ.get("COLONY_WORKER_MAX_JOBS", "1")),
|
|
91
|
+
"poll_secs": float(os.environ.get("COLONY_WORKER_POLL_SECS", "30")),
|
|
92
|
+
"heartbeat_secs": max(
|
|
93
|
+
1.0,
|
|
94
|
+
float(os.environ.get("COLONY_WORKER_HEARTBEAT_SECS", "20")),
|
|
95
|
+
),
|
|
96
|
+
"llm_base_url": (os.environ.get("COLONY_WORKER_LLM_BASE_URL")
|
|
97
|
+
or os.environ.get("OPENAI_BASE_URL", "")).rstrip("/"),
|
|
98
|
+
"llm_model": os.environ.get("COLONY_WORKER_LLM_MODEL", "gpt-4o-mini"),
|
|
99
|
+
"llm_api_key": (os.environ.get("COLONY_WORKER_LLM_API_KEY")
|
|
100
|
+
or os.environ.get("OPENAI_API_KEY", "")),
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _headers(cfg: Dict[str, Any]) -> Dict[str, str]:
|
|
105
|
+
return {"X-API-Key": cfg["api_key"], "Content-Type": "application/json"}
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _post(cfg: Dict[str, Any], url: str, body: dict, timeout: int = 30) -> dict:
|
|
109
|
+
req = urllib.request.Request(
|
|
110
|
+
url, data=json.dumps(body).encode("utf-8"),
|
|
111
|
+
headers=_headers(cfg), method="POST")
|
|
112
|
+
with urllib.request.urlopen(req, timeout=timeout) as resp:
|
|
113
|
+
raw = resp.read().decode("utf-8")
|
|
114
|
+
return json.loads(raw) if raw else {}
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def register_worker(cfg: Dict[str, Any]) -> None:
|
|
118
|
+
"""Registration is ephemeral (lost on sidecar restart) -- re-register."""
|
|
119
|
+
_post(cfg, f"{cfg['colony_url']}/v1/host/queue/workers/register", {
|
|
120
|
+
"node_id": cfg["node_id"],
|
|
121
|
+
"capabilities": cfg["capabilities"],
|
|
122
|
+
"job_types": cfg["job_types"],
|
|
123
|
+
"max_concurrent": 1,
|
|
124
|
+
"available": True,
|
|
125
|
+
"load": 0.0,
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def claim_job(cfg: Dict[str, Any]) -> Optional[dict]:
|
|
130
|
+
return _post(cfg, f"{cfg['colony_url']}/v1/host/queue/jobs/claim", {
|
|
131
|
+
"node_id": cfg["node_id"],
|
|
132
|
+
"capabilities": cfg["capabilities"],
|
|
133
|
+
"job_types": cfg["job_types"],
|
|
134
|
+
}) or None
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def build_llm_messages(job: dict) -> List[Dict[str, str]]:
|
|
138
|
+
"""Compose the worker LLM messages for a claimed job (pure -- testable)."""
|
|
139
|
+
payload = job.get("payload") or {}
|
|
140
|
+
parts = []
|
|
141
|
+
for k in ("description", "action_hint", "domain"):
|
|
142
|
+
v = payload.get(k)
|
|
143
|
+
if v:
|
|
144
|
+
parts.append(f"{k}: {v}")
|
|
145
|
+
ctx = payload.get("context")
|
|
146
|
+
if ctx:
|
|
147
|
+
parts.append("context: " + json.dumps(ctx, default=str)[:2000])
|
|
148
|
+
user = "JOB:\n" + "\n".join(parts) if parts else "JOB: (no description)"
|
|
149
|
+
return [{"role": "system", "content": _WORKER_SYSTEM},
|
|
150
|
+
{"role": "user", "content": user}]
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def _parse_report(content: str) -> Dict[str, Any]:
|
|
154
|
+
"""Parse the LLM's JSON report; sanitise to the read/analyse contract."""
|
|
155
|
+
text = (content or "").strip()
|
|
156
|
+
if "```" in text:
|
|
157
|
+
import re
|
|
158
|
+
m = re.search(r"```(?:json)?\s*(.*?)```", text, re.DOTALL)
|
|
159
|
+
if m:
|
|
160
|
+
text = m.group(1).strip()
|
|
161
|
+
if not text.startswith("{"):
|
|
162
|
+
import re
|
|
163
|
+
m = re.search(r"\{.*\}", text, re.DOTALL)
|
|
164
|
+
text = m.group(0) if m else "{}"
|
|
165
|
+
parsed = False
|
|
166
|
+
try:
|
|
167
|
+
data = json.loads(text)
|
|
168
|
+
parsed = isinstance(data, dict) and bool(
|
|
169
|
+
str(data.get("summary") or "").strip()
|
|
170
|
+
)
|
|
171
|
+
except Exception:
|
|
172
|
+
data = {"summary": (content or "")[:600]}
|
|
173
|
+
allowed_ops = {"analyze", "read", "search"}
|
|
174
|
+
ops = [str(o).lower() for o in (data.get("operations") or [])
|
|
175
|
+
if str(o).lower() in allowed_ops] or ["analyze"]
|
|
176
|
+
conf = data.get("confidence")
|
|
177
|
+
try:
|
|
178
|
+
conf = max(0.0, min(1.0, float(conf)))
|
|
179
|
+
except (TypeError, ValueError):
|
|
180
|
+
conf = 0.5
|
|
181
|
+
# Read/analyse posture is enforced client-side too: never report a mutation.
|
|
182
|
+
report = {
|
|
183
|
+
"summary": str(data.get("summary", ""))[:2000],
|
|
184
|
+
"operations": ops,
|
|
185
|
+
"files_touched": [],
|
|
186
|
+
"commits": 0,
|
|
187
|
+
"branch": "",
|
|
188
|
+
"confidence": conf,
|
|
189
|
+
"remaining_work": str(data.get("remaining_work", ""))[:600],
|
|
190
|
+
}
|
|
191
|
+
if parsed:
|
|
192
|
+
report.update({
|
|
193
|
+
"status": "completed",
|
|
194
|
+
"action_plane": {"state": "completed"},
|
|
195
|
+
})
|
|
196
|
+
return report
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def _heartbeat_loop(
|
|
200
|
+
cfg: Dict[str, Any],
|
|
201
|
+
job_id: str,
|
|
202
|
+
claim_attempt_id: str,
|
|
203
|
+
stop: threading.Event,
|
|
204
|
+
) -> None:
|
|
205
|
+
"""Maintain the exact RUNNING lease around the blocking LLM request."""
|
|
206
|
+
|
|
207
|
+
interval = float(cfg.get("heartbeat_secs") or 20.0)
|
|
208
|
+
while not stop.wait(interval):
|
|
209
|
+
try:
|
|
210
|
+
_post(
|
|
211
|
+
cfg,
|
|
212
|
+
f"{cfg['colony_url']}/v1/host/queue/jobs/{job_id}/heartbeat",
|
|
213
|
+
{"claim_attempt_id": claim_attempt_id},
|
|
214
|
+
timeout=10,
|
|
215
|
+
)
|
|
216
|
+
except Exception as exc:
|
|
217
|
+
print(f"Heartbeat failed for job {job_id}: {exc}")
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def call_llm(cfg: Dict[str, Any], messages: List[Dict[str, str]]) -> Dict[str, Any]:
|
|
221
|
+
"""Call the OpenAI-compatible endpoint; return a parsed report."""
|
|
222
|
+
if not cfg["llm_base_url"]:
|
|
223
|
+
raise RuntimeError("no COLONY_WORKER_LLM_BASE_URL configured")
|
|
224
|
+
body = {"model": cfg["llm_model"], "messages": messages, "temperature": 0.2}
|
|
225
|
+
req = urllib.request.Request(
|
|
226
|
+
f"{cfg['llm_base_url']}/chat/completions",
|
|
227
|
+
data=json.dumps(body).encode("utf-8"),
|
|
228
|
+
headers={"Content-Type": "application/json",
|
|
229
|
+
"Authorization": f"Bearer {cfg['llm_api_key']}"},
|
|
230
|
+
method="POST")
|
|
231
|
+
with urllib.request.urlopen(req, timeout=120) as resp:
|
|
232
|
+
data = json.loads(resp.read().decode("utf-8"))
|
|
233
|
+
content = (data.get("choices") or [{}])[0].get("message", {}).get("content", "")
|
|
234
|
+
return _parse_report(content)
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def execute_job(cfg: Dict[str, Any], job: dict) -> bool:
|
|
238
|
+
"""Run one claimed job to completion (report) or failure."""
|
|
239
|
+
job_id = job.get("job_id") or job.get("id")
|
|
240
|
+
claim_attempt_id = job.get("claim_attempt_id")
|
|
241
|
+
try:
|
|
242
|
+
started = _post(
|
|
243
|
+
cfg,
|
|
244
|
+
f"{cfg['colony_url']}/v1/host/queue/jobs/{job_id}/start",
|
|
245
|
+
{"claim_attempt_id": claim_attempt_id},
|
|
246
|
+
)
|
|
247
|
+
if not isinstance(started, dict) or started.get("success") is not True:
|
|
248
|
+
raise RuntimeError("start transition was not accepted")
|
|
249
|
+
except Exception as exc:
|
|
250
|
+
print(f"Start failed for job {job_id}: {exc}")
|
|
251
|
+
try:
|
|
252
|
+
_post(
|
|
253
|
+
cfg,
|
|
254
|
+
f"{cfg['colony_url']}/v1/host/queue/jobs/{job_id}/release",
|
|
255
|
+
{"claim_attempt_id": claim_attempt_id},
|
|
256
|
+
)
|
|
257
|
+
except Exception:
|
|
258
|
+
pass
|
|
259
|
+
return False
|
|
260
|
+
heartbeat_stop = threading.Event()
|
|
261
|
+
heartbeat = threading.Thread(
|
|
262
|
+
target=_heartbeat_loop,
|
|
263
|
+
args=(cfg, str(job_id), str(claim_attempt_id), heartbeat_stop),
|
|
264
|
+
daemon=True,
|
|
265
|
+
name=f"colony-heartbeat-{job_id}",
|
|
266
|
+
)
|
|
267
|
+
try:
|
|
268
|
+
# Confirm this exact attempt is still authoritative before spending
|
|
269
|
+
# inference time, then keep it alive during the blocking request.
|
|
270
|
+
_post(
|
|
271
|
+
cfg,
|
|
272
|
+
f"{cfg['colony_url']}/v1/host/queue/jobs/{job_id}/heartbeat",
|
|
273
|
+
{"claim_attempt_id": claim_attempt_id},
|
|
274
|
+
timeout=10,
|
|
275
|
+
)
|
|
276
|
+
heartbeat.start()
|
|
277
|
+
report = call_llm(cfg, build_llm_messages(job))
|
|
278
|
+
except Exception as exc:
|
|
279
|
+
try:
|
|
280
|
+
_post(cfg, f"{cfg['colony_url']}/v1/host/queue/jobs/{job_id}/fail",
|
|
281
|
+
{"error": f"worker execution failed: {exc}"[:400],
|
|
282
|
+
"claim_attempt_id": claim_attempt_id})
|
|
283
|
+
except Exception:
|
|
284
|
+
pass
|
|
285
|
+
return False
|
|
286
|
+
finally:
|
|
287
|
+
heartbeat_stop.set()
|
|
288
|
+
if heartbeat.is_alive():
|
|
289
|
+
heartbeat.join(timeout=2.0)
|
|
290
|
+
try:
|
|
291
|
+
resp = _post(cfg, f"{cfg['colony_url']}/v1/host/queue/jobs/{job_id}/complete",
|
|
292
|
+
{"output": report, "claim_attempt_id": claim_attempt_id})
|
|
293
|
+
verdict = resp.get("verdict", "?")
|
|
294
|
+
semantic_success = (
|
|
295
|
+
resp.get("transitioned") is True
|
|
296
|
+
and resp.get("job_status") == "completed"
|
|
297
|
+
and resp.get("governor_outcome") == "success"
|
|
298
|
+
)
|
|
299
|
+
print(f"Completed job {job_id} (verdict={verdict}, "
|
|
300
|
+
f"status={resp.get('job_status', '?')}, "
|
|
301
|
+
f"confidence={report['confidence']})")
|
|
302
|
+
return semantic_success
|
|
303
|
+
except Exception as exc:
|
|
304
|
+
print(f"Complete post failed for {job_id}: {exc}")
|
|
305
|
+
return False
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def run_cycle(cfg: Dict[str, Any]) -> int:
|
|
309
|
+
"""One poll cycle: register, claim up to max_jobs, execute each."""
|
|
310
|
+
if not cfg.get("job_types"):
|
|
311
|
+
print("No enabled worker job types.")
|
|
312
|
+
return 0
|
|
313
|
+
try:
|
|
314
|
+
register_worker(cfg)
|
|
315
|
+
except Exception as exc:
|
|
316
|
+
print(f"Worker registration failed (sidecar down?): {exc}")
|
|
317
|
+
return 0
|
|
318
|
+
done = 0
|
|
319
|
+
for _ in range(cfg["max_jobs"]):
|
|
320
|
+
try:
|
|
321
|
+
job = claim_job(cfg)
|
|
322
|
+
except Exception as exc:
|
|
323
|
+
print(f"Claim failed: {exc}")
|
|
324
|
+
break
|
|
325
|
+
if not job:
|
|
326
|
+
break
|
|
327
|
+
if execute_job(cfg, job):
|
|
328
|
+
done += 1
|
|
329
|
+
if not done:
|
|
330
|
+
print("No eligible jobs.")
|
|
331
|
+
return done
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
def run_forever(cfg: Dict[str, Any]) -> None:
|
|
335
|
+
print(f"colony-worker {cfg['node_id']} polling {cfg['colony_url']} "
|
|
336
|
+
f"every {cfg['poll_secs']}s (caps={cfg['capabilities']}, "
|
|
337
|
+
f"types={cfg['job_types']})")
|
|
338
|
+
while True:
|
|
339
|
+
try:
|
|
340
|
+
run_cycle(cfg)
|
|
341
|
+
except Exception as exc:
|
|
342
|
+
print(f"cycle error: {exc}")
|
|
343
|
+
time.sleep(cfg["poll_secs"])
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
def main(argv: Optional[list] = None) -> int:
|
|
347
|
+
# Under launchd/systemd stdout is a pipe (block-buffered): without line
|
|
348
|
+
# buffering every print sits invisible in the buffer and the daemon's
|
|
349
|
+
# log file stays empty for hours. Flush per line.
|
|
350
|
+
import sys
|
|
351
|
+
try:
|
|
352
|
+
sys.stdout.reconfigure(line_buffering=True)
|
|
353
|
+
sys.stderr.reconfigure(line_buffering=True)
|
|
354
|
+
except Exception:
|
|
355
|
+
pass
|
|
356
|
+
parser = argparse.ArgumentParser(
|
|
357
|
+
prog="colony-worker",
|
|
358
|
+
description="Claim typed Colony jobs and execute them read/analyse-only.")
|
|
359
|
+
parser.add_argument("--dry-run", action="store_true",
|
|
360
|
+
help="print resolved config and exit (no network)")
|
|
361
|
+
parser.add_argument("--once", action="store_true",
|
|
362
|
+
help="run a single poll cycle and exit")
|
|
363
|
+
args = parser.parse_args(argv)
|
|
364
|
+
|
|
365
|
+
cfg = load_config()
|
|
366
|
+
if args.dry_run:
|
|
367
|
+
safe = dict(cfg)
|
|
368
|
+
safe["api_key"] = "***" if cfg["api_key"] else ""
|
|
369
|
+
safe["llm_api_key"] = "***" if cfg["llm_api_key"] else ""
|
|
370
|
+
print("colony-worker (dry run -- no network calls):")
|
|
371
|
+
for k, v in safe.items():
|
|
372
|
+
print(f" {k}: {v}")
|
|
373
|
+
return 0
|
|
374
|
+
if args.once:
|
|
375
|
+
run_cycle(cfg)
|
|
376
|
+
return 0
|
|
377
|
+
run_forever(cfg)
|
|
378
|
+
return 0
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
if __name__ == "__main__":
|
|
382
|
+
raise SystemExit(main())
|