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,441 @@
|
|
|
1
|
+
"""Claim Colony agent_action jobs and hand them to the agent.
|
|
2
|
+
|
|
3
|
+
Installed as the ``colony-queue-worker`` console script (v0.20.0); the
|
|
4
|
+
logic moved here unchanged from
|
|
5
|
+
``plugins/hermes-plugin/poller/colony-queue-worker.py`` (v0.16.0),
|
|
6
|
+
which remains as a thin back-compat wrapper.
|
|
7
|
+
|
|
8
|
+
This is the execution half of the agent-as-sensor loop:
|
|
9
|
+
|
|
10
|
+
Colony notices a domain is stale → posts a read-only agent_sync_<domain>
|
|
11
|
+
job → THIS worker claims it and fires it to the agent webhook → the
|
|
12
|
+
agent observes through its own toolsets (github, terminal, web, ...) →
|
|
13
|
+
the agent POSTs observations back to Colony and completes the job →
|
|
14
|
+
Colony generates initiatives from what the agent saw.
|
|
15
|
+
|
|
16
|
+
The same path carries every other registered agent_action capability.
|
|
17
|
+
Run from cron every few minutes (the wizard installs ``*/5 * * * *``).
|
|
18
|
+
Without it, auto-approved agent_action jobs sit QUEUED forever.
|
|
19
|
+
|
|
20
|
+
The webhook prompt receives explicit lifecycle URLs so the agent can
|
|
21
|
+
close the loop with plain curl — no special tooling needed:
|
|
22
|
+
|
|
23
|
+
- report observations: POST {COLONY_URL}/v1/host/observations
|
|
24
|
+
- complete the job: POST {COLONY_URL}/v1/host/queue/jobs/{id}/complete
|
|
25
|
+
- fail the job: POST {COLONY_URL}/v1/host/queue/jobs/{id}/fail
|
|
26
|
+
|
|
27
|
+
Environment (unchanged from the v0.16 script):
|
|
28
|
+
COLONY_URL sidecar URL (default http://127.0.0.1:7777)
|
|
29
|
+
COLONY_API_KEY API key (default dev-mode-no-key)
|
|
30
|
+
COLONY_JOBS_WEBHOOK_URL agent webhook (default http://127.0.0.1:8644/webhooks/colony-jobs)
|
|
31
|
+
COLONY_WORKER_NODE_ID worker identity (default derived from COLONY_AGENT_NAME)
|
|
32
|
+
COLONY_WORKER_MAX_JOBS jobs per run (default 1 — each is a full agent invocation)
|
|
33
|
+
COLONY_AGENT_WORKER_ROUTES comma-separated exact no-effect lanes:
|
|
34
|
+
agent_sync, hermes_run (default both)
|
|
35
|
+
|
|
36
|
+
Stdlib-only on purpose — must run from cron without the sidecar deps.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
from __future__ import annotations
|
|
40
|
+
|
|
41
|
+
import argparse
|
|
42
|
+
import hashlib
|
|
43
|
+
import hmac
|
|
44
|
+
import json
|
|
45
|
+
import os
|
|
46
|
+
import time
|
|
47
|
+
import urllib.request
|
|
48
|
+
from datetime import datetime, timezone
|
|
49
|
+
from typing import Any, Dict, Optional
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
_AGENT_ACTION_BASE_CAPABILITIES = [
|
|
53
|
+
"agent_action",
|
|
54
|
+
"filesystem:read",
|
|
55
|
+
"memory:read",
|
|
56
|
+
"reasoning",
|
|
57
|
+
"web:read",
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
_AGENT_ACTION_ROUTE_CAPABILITY = {
|
|
61
|
+
"agent_sync": "agent_sync:v1",
|
|
62
|
+
"hermes_run": "hermes_run:v1",
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def agent_action_capabilities(routes: Optional[str] = None) -> list[str]:
|
|
67
|
+
"""Return the exact configured generic lanes, rejecting ambiguity."""
|
|
68
|
+
|
|
69
|
+
configured = (
|
|
70
|
+
os.environ.get("COLONY_AGENT_WORKER_ROUTES", "agent_sync,hermes_run")
|
|
71
|
+
if routes is None else routes
|
|
72
|
+
)
|
|
73
|
+
selected = {
|
|
74
|
+
item.strip().lower()
|
|
75
|
+
for item in str(configured or "").split(",")
|
|
76
|
+
if item.strip()
|
|
77
|
+
}
|
|
78
|
+
unknown = selected - set(_AGENT_ACTION_ROUTE_CAPABILITY)
|
|
79
|
+
if not selected or unknown:
|
|
80
|
+
detail = ",".join(sorted(unknown)) if unknown else "empty"
|
|
81
|
+
raise ValueError(
|
|
82
|
+
"COLONY_AGENT_WORKER_ROUTES must select agent_sync and/or "
|
|
83
|
+
f"hermes_run (invalid: {detail})"
|
|
84
|
+
)
|
|
85
|
+
return [
|
|
86
|
+
*_AGENT_ACTION_BASE_CAPABILITIES,
|
|
87
|
+
*(
|
|
88
|
+
_AGENT_ACTION_ROUTE_CAPABILITY[name]
|
|
89
|
+
for name in ("agent_sync", "hermes_run")
|
|
90
|
+
if name in selected
|
|
91
|
+
),
|
|
92
|
+
]
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
# Backward-compatible import for callers that intentionally use the combined
|
|
96
|
+
# generic pool. Runtime worker registration/claims use the function above so
|
|
97
|
+
# separate sync and text principals can advertise exact disjoint grants.
|
|
98
|
+
AGENT_ACTION_CAPABILITIES = agent_action_capabilities(
|
|
99
|
+
"agent_sync,hermes_run"
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def encode_hermes_webhook(
|
|
104
|
+
payload: Dict[str, Any],
|
|
105
|
+
*,
|
|
106
|
+
secret: Optional[str] = None,
|
|
107
|
+
timestamp: Optional[int] = None,
|
|
108
|
+
) -> tuple[bytes, Dict[str, str]]:
|
|
109
|
+
"""Serialize once and add Hermes' replay-resistant V2 HMAC."""
|
|
110
|
+
|
|
111
|
+
body = json.dumps(
|
|
112
|
+
payload, separators=(",", ":"), default=str,
|
|
113
|
+
).encode("utf-8")
|
|
114
|
+
headers = {"Content-Type": "application/json"}
|
|
115
|
+
configured = (
|
|
116
|
+
os.environ.get("COLONY_HERMES_WEBHOOK_SECRET", "")
|
|
117
|
+
if secret is None else secret
|
|
118
|
+
)
|
|
119
|
+
configured = str(configured or "")
|
|
120
|
+
if configured and configured != "INSECURE_NO_AUTH":
|
|
121
|
+
issued_at = int(time.time()) if timestamp is None else int(timestamp)
|
|
122
|
+
signed = str(issued_at).encode("ascii") + b"." + body
|
|
123
|
+
headers.update({
|
|
124
|
+
"X-Webhook-Timestamp": str(issued_at),
|
|
125
|
+
"X-Webhook-Signature-V2": hmac.new(
|
|
126
|
+
configured.encode("utf-8"), signed, hashlib.sha256,
|
|
127
|
+
).hexdigest(),
|
|
128
|
+
})
|
|
129
|
+
if os.environ.get(
|
|
130
|
+
"COLONY_HERMES_WEBHOOK_V1_COMPAT", "false"
|
|
131
|
+
).strip().lower() in {"1", "true", "yes", "on"}:
|
|
132
|
+
headers["X-Webhook-Signature"] = hmac.new(
|
|
133
|
+
configured.encode("utf-8"), body, hashlib.sha256,
|
|
134
|
+
).hexdigest()
|
|
135
|
+
inner = payload.get("payload") if isinstance(payload, dict) else None
|
|
136
|
+
if isinstance(inner, dict):
|
|
137
|
+
request_id = str(
|
|
138
|
+
inner.get("claim_attempt_id")
|
|
139
|
+
or inner.get("job_id")
|
|
140
|
+
or inner.get("id")
|
|
141
|
+
or ""
|
|
142
|
+
).strip()
|
|
143
|
+
if request_id:
|
|
144
|
+
headers["X-Request-ID"] = request_id[:256]
|
|
145
|
+
return body, headers
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def work_order_webhook_fields(params: Dict[str, Any]) -> Dict[str, Any]:
|
|
149
|
+
"""Return the bounded WorkOrder contract needed by an agent webhook."""
|
|
150
|
+
|
|
151
|
+
if params.get("schema") != "WorkOrderV1":
|
|
152
|
+
return {}
|
|
153
|
+
fields = (
|
|
154
|
+
"schema", "version", "source", "work_order_id",
|
|
155
|
+
"work_order_digest", "project_id", "step_id", "step_ordinal",
|
|
156
|
+
"objective", "success_criteria",
|
|
157
|
+
"context_refs", "capability_allowlist", "risk_class",
|
|
158
|
+
"recipient_scope", "max_runtime_seconds", "max_attempts",
|
|
159
|
+
"issued_at", "deadline", "action_hint",
|
|
160
|
+
)
|
|
161
|
+
order = {name: params.get(name) for name in fields}
|
|
162
|
+
effect_class = (
|
|
163
|
+
params.get("risk_class")
|
|
164
|
+
if params.get("risk_class") in {
|
|
165
|
+
"read_only", "mutation", "disclosure",
|
|
166
|
+
}
|
|
167
|
+
else "none"
|
|
168
|
+
)
|
|
169
|
+
return {
|
|
170
|
+
"work_order": order,
|
|
171
|
+
"result_contract": {
|
|
172
|
+
"executor_protocol": "work_order:v1",
|
|
173
|
+
"schema": "ExecutionResultV1",
|
|
174
|
+
"version": 1,
|
|
175
|
+
"work_order_id": params.get("work_order_id"),
|
|
176
|
+
"work_order_digest": params.get("work_order_digest"),
|
|
177
|
+
"work_order_version": params.get("version"),
|
|
178
|
+
"required_fields": [
|
|
179
|
+
"run_id", "attempt_number", "terminal_outcome",
|
|
180
|
+
"started_at", "ended_at", "executor_identity",
|
|
181
|
+
"effect_class", "receipt_refs", "summary", "error",
|
|
182
|
+
],
|
|
183
|
+
"effect_class": effect_class,
|
|
184
|
+
"terminal_outcomes": [
|
|
185
|
+
"succeeded", "failed", "skipped", "cancelled",
|
|
186
|
+
],
|
|
187
|
+
"verification_note": (
|
|
188
|
+
"Executor verification claims are observational; Colony "
|
|
189
|
+
"requires its configured independent evidence verifier."
|
|
190
|
+
),
|
|
191
|
+
"complete_body_shape": {
|
|
192
|
+
"claim_attempt_id": "<echo exact claim attempt>",
|
|
193
|
+
"output": {
|
|
194
|
+
"execution_result": {
|
|
195
|
+
"schema": "ExecutionResultV1",
|
|
196
|
+
"version": 1,
|
|
197
|
+
"work_order_id": params.get("work_order_id"),
|
|
198
|
+
"work_order_digest": params.get(
|
|
199
|
+
"work_order_digest"
|
|
200
|
+
),
|
|
201
|
+
"work_order_version": params.get("version"),
|
|
202
|
+
"run_id": "<executor run id>",
|
|
203
|
+
"attempt_number": "<1-based integer>",
|
|
204
|
+
"terminal_outcome": "<terminal outcome>",
|
|
205
|
+
"started_at": "<ISO-8601>",
|
|
206
|
+
"ended_at": "<ISO-8601>",
|
|
207
|
+
"executor_identity": "<claiming worker node id>",
|
|
208
|
+
"effect_class": effect_class,
|
|
209
|
+
"receipt_refs": ["<real durable evidence ref>"],
|
|
210
|
+
"verification_result": "unverified",
|
|
211
|
+
"summary": "<what actually happened>",
|
|
212
|
+
"error": "",
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def load_config() -> Dict[str, Any]:
|
|
221
|
+
"""Resolve worker configuration from the environment.
|
|
222
|
+
|
|
223
|
+
Read at call time (not import time) so cron env sourcing and tests
|
|
224
|
+
both behave predictably.
|
|
225
|
+
"""
|
|
226
|
+
node_id = os.environ.get("COLONY_WORKER_NODE_ID") or (
|
|
227
|
+
os.environ.get("COLONY_AGENT_NAME", "hermes").strip().lower().replace(" ", "-")
|
|
228
|
+
+ "-agent"
|
|
229
|
+
)
|
|
230
|
+
return {
|
|
231
|
+
"colony_url": os.environ.get("COLONY_URL", "http://127.0.0.1:7777"),
|
|
232
|
+
"api_key": os.environ.get("COLONY_API_KEY", "dev-mode-no-key"),
|
|
233
|
+
"webhook_url": os.environ.get(
|
|
234
|
+
"COLONY_JOBS_WEBHOOK_URL", "http://127.0.0.1:8644/webhooks/colony-jobs"
|
|
235
|
+
),
|
|
236
|
+
"node_id": node_id,
|
|
237
|
+
# How many jobs to hand to the agent per run. Keep small — each
|
|
238
|
+
# one is a full agent invocation.
|
|
239
|
+
"max_jobs": int(os.environ.get("COLONY_WORKER_MAX_JOBS", "1")),
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def _headers(cfg: Dict[str, Any]) -> Dict[str, str]:
|
|
244
|
+
return {"X-API-Key": cfg["api_key"], "Content-Type": "application/json"}
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def _post(cfg: Dict[str, Any], url: str, body: dict, timeout: int = 15) -> dict:
|
|
248
|
+
req = urllib.request.Request(
|
|
249
|
+
url, data=json.dumps(body).encode("utf-8"), headers=_headers(cfg), method="POST"
|
|
250
|
+
)
|
|
251
|
+
with urllib.request.urlopen(req, timeout=timeout) as resp:
|
|
252
|
+
raw = resp.read().decode("utf-8")
|
|
253
|
+
return json.loads(raw) if raw else {}
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def register_worker(cfg: Dict[str, Any]) -> None:
|
|
257
|
+
"""Registration is ephemeral (lost on sidecar restart) — re-register every run."""
|
|
258
|
+
capabilities = agent_action_capabilities()
|
|
259
|
+
_post(
|
|
260
|
+
cfg,
|
|
261
|
+
f"{cfg['colony_url']}/v1/host/queue/workers/register",
|
|
262
|
+
{
|
|
263
|
+
"node_id": cfg["node_id"],
|
|
264
|
+
"capabilities": capabilities,
|
|
265
|
+
"job_types": ["agent_action"],
|
|
266
|
+
"max_concurrent": 1,
|
|
267
|
+
"available": True,
|
|
268
|
+
"load": 0.0,
|
|
269
|
+
},
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def claim_job(cfg: Dict[str, Any]) -> Optional[dict]:
|
|
274
|
+
capabilities = agent_action_capabilities()
|
|
275
|
+
return _post(
|
|
276
|
+
cfg,
|
|
277
|
+
f"{cfg['colony_url']}/v1/host/queue/jobs/claim",
|
|
278
|
+
{
|
|
279
|
+
"node_id": cfg["node_id"],
|
|
280
|
+
"capabilities": capabilities,
|
|
281
|
+
"job_types": ["agent_action"],
|
|
282
|
+
"max_concurrent": 1,
|
|
283
|
+
},
|
|
284
|
+
) or None
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def start_job(
|
|
288
|
+
cfg: Dict[str, Any], job_id: str, claim_attempt_id: Optional[str],
|
|
289
|
+
) -> bool:
|
|
290
|
+
response = _post(
|
|
291
|
+
cfg,
|
|
292
|
+
f"{cfg['colony_url']}/v1/host/queue/jobs/{job_id}/start",
|
|
293
|
+
{"claim_attempt_id": claim_attempt_id},
|
|
294
|
+
)
|
|
295
|
+
return isinstance(response, dict) and response.get("success") is True
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
def release_job(
|
|
299
|
+
cfg: Dict[str, Any], job_id: str, claim_attempt_id: Optional[str],
|
|
300
|
+
) -> None:
|
|
301
|
+
_post(
|
|
302
|
+
cfg,
|
|
303
|
+
f"{cfg['colony_url']}/v1/host/queue/jobs/{job_id}/release",
|
|
304
|
+
{"claim_attempt_id": claim_attempt_id},
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def build_webhook_payload(cfg: Dict[str, Any], job: dict) -> dict:
|
|
309
|
+
"""Render the agent-webhook payload for a claimed job (pure — testable)."""
|
|
310
|
+
job_id = job.get("job_id") or job.get("id")
|
|
311
|
+
claim_attempt_id = job.get("claim_attempt_id")
|
|
312
|
+
params = job.get("payload") or job.get("params") or {}
|
|
313
|
+
colony_url = cfg["colony_url"]
|
|
314
|
+
return {
|
|
315
|
+
"type": "agent_job",
|
|
316
|
+
"occurred_at": datetime.now(timezone.utc).isoformat(),
|
|
317
|
+
"payload": {
|
|
318
|
+
"job_id": job_id,
|
|
319
|
+
"claim_attempt_id": claim_attempt_id,
|
|
320
|
+
"action_hint": params.get("action_hint", ""),
|
|
321
|
+
"domain": params.get("domain", ""),
|
|
322
|
+
"risk": params.get("risk", ""),
|
|
323
|
+
"description": params.get("description", ""),
|
|
324
|
+
"context": params.get("context", {}),
|
|
325
|
+
"report_example": params.get("report_example", {}),
|
|
326
|
+
**work_order_webhook_fields(params),
|
|
327
|
+
"colony_url": colony_url,
|
|
328
|
+
"observations_url": f"{colony_url}/v1/host/observations",
|
|
329
|
+
"heartbeat_url": f"{colony_url}/v1/host/queue/jobs/{job_id}/heartbeat",
|
|
330
|
+
"complete_url": f"{colony_url}/v1/host/queue/jobs/{job_id}/complete",
|
|
331
|
+
"fail_url": f"{colony_url}/v1/host/queue/jobs/{job_id}/fail",
|
|
332
|
+
"api_key_header": "X-API-Key",
|
|
333
|
+
},
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
def fire_to_agent(cfg: Dict[str, Any], job: dict) -> bool:
|
|
338
|
+
"""Hand the claimed job to the agent via the jobs webhook.
|
|
339
|
+
|
|
340
|
+
The webhook route's prompt template renders these fields; the agent
|
|
341
|
+
decides how to execute with its own toolsets and closes the job
|
|
342
|
+
lifecycle itself via the URLs in the payload.
|
|
343
|
+
"""
|
|
344
|
+
job_id = job.get("job_id") or job.get("id")
|
|
345
|
+
claim_attempt_id = job.get("claim_attempt_id")
|
|
346
|
+
payload = build_webhook_payload(cfg, job)
|
|
347
|
+
try:
|
|
348
|
+
body, headers = encode_hermes_webhook(payload)
|
|
349
|
+
req = urllib.request.Request(
|
|
350
|
+
cfg["webhook_url"],
|
|
351
|
+
data=body,
|
|
352
|
+
headers=headers,
|
|
353
|
+
method="POST",
|
|
354
|
+
)
|
|
355
|
+
urllib.request.urlopen(req, timeout=15)
|
|
356
|
+
return True
|
|
357
|
+
except Exception as exc:
|
|
358
|
+
print(f"Webhook fire failed for job {job_id}: {exc}")
|
|
359
|
+
# Release the claim so another run can retry
|
|
360
|
+
try:
|
|
361
|
+
_post(
|
|
362
|
+
cfg,
|
|
363
|
+
f"{cfg['colony_url']}/v1/host/queue/jobs/{job_id}/release",
|
|
364
|
+
{"claim_attempt_id": claim_attempt_id},
|
|
365
|
+
)
|
|
366
|
+
except Exception:
|
|
367
|
+
pass
|
|
368
|
+
return False
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
def run(cfg: Dict[str, Any]) -> int:
|
|
372
|
+
"""One cron tick: register, claim up to max_jobs, hand each to the agent."""
|
|
373
|
+
if os.environ.get(
|
|
374
|
+
"COLONY_AGENT_JOB_CLAIMS_ENABLED", "true"
|
|
375
|
+
).strip().lower() not in {"1", "true", "yes", "on"}:
|
|
376
|
+
print("Agent job claims disabled; initiative forwarding is unaffected.")
|
|
377
|
+
return 0
|
|
378
|
+
try:
|
|
379
|
+
register_worker(cfg)
|
|
380
|
+
except Exception as exc:
|
|
381
|
+
print(f"Worker registration failed (sidecar down?): {exc}")
|
|
382
|
+
return 0
|
|
383
|
+
|
|
384
|
+
handed_off = 0
|
|
385
|
+
for _ in range(cfg["max_jobs"]):
|
|
386
|
+
try:
|
|
387
|
+
job = claim_job(cfg)
|
|
388
|
+
except Exception as exc:
|
|
389
|
+
print(f"Claim failed: {exc}")
|
|
390
|
+
break
|
|
391
|
+
if not job:
|
|
392
|
+
break
|
|
393
|
+
job_id = job.get("job_id") or job.get("id")
|
|
394
|
+
hint = (job.get("payload") or {}).get("action_hint", "?")
|
|
395
|
+
try:
|
|
396
|
+
claim_attempt_id = job.get("claim_attempt_id")
|
|
397
|
+
if not start_job(cfg, str(job_id), claim_attempt_id):
|
|
398
|
+
raise RuntimeError("start transition was not accepted")
|
|
399
|
+
except Exception as exc:
|
|
400
|
+
print(f"Start failed for job {job_id}: {exc}")
|
|
401
|
+
try:
|
|
402
|
+
release_job(cfg, str(job_id), claim_attempt_id)
|
|
403
|
+
except Exception:
|
|
404
|
+
pass
|
|
405
|
+
continue
|
|
406
|
+
if fire_to_agent(cfg, job):
|
|
407
|
+
handed_off += 1
|
|
408
|
+
print(f"Handed job {job_id} ({hint}) to the agent")
|
|
409
|
+
|
|
410
|
+
if not handed_off:
|
|
411
|
+
print("No pending agent_action jobs.")
|
|
412
|
+
return handed_off
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
def main(argv: Optional[list] = None) -> int:
|
|
416
|
+
parser = argparse.ArgumentParser(
|
|
417
|
+
prog="colony-queue-worker",
|
|
418
|
+
description="Claim Colony agent_action jobs and hand them to the agent webhook.",
|
|
419
|
+
)
|
|
420
|
+
parser.add_argument(
|
|
421
|
+
"--dry-run",
|
|
422
|
+
action="store_true",
|
|
423
|
+
help="print the resolved configuration and exit without any network calls",
|
|
424
|
+
)
|
|
425
|
+
args = parser.parse_args(argv)
|
|
426
|
+
|
|
427
|
+
cfg = load_config()
|
|
428
|
+
if args.dry_run:
|
|
429
|
+
print("colony-queue-worker (dry run — no network calls):")
|
|
430
|
+
print(f" colony_url: {cfg['colony_url']}")
|
|
431
|
+
print(f" webhook_url: {cfg['webhook_url']}")
|
|
432
|
+
print(f" node_id: {cfg['node_id']}")
|
|
433
|
+
print(f" max_jobs: {cfg['max_jobs']}")
|
|
434
|
+
return 0
|
|
435
|
+
|
|
436
|
+
run(cfg)
|
|
437
|
+
return 0
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
if __name__ == "__main__":
|
|
441
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"""Report the agent's skill index to Colony.
|
|
2
|
+
|
|
3
|
+
Installed as the ``colony-skills-sync`` console script (v0.20.0); the
|
|
4
|
+
logic moved here unchanged from
|
|
5
|
+
``plugins/hermes-plugin/poller/colony-skills-sync.py`` (v0.18.0),
|
|
6
|
+
which remains as a thin back-compat wrapper.
|
|
7
|
+
|
|
8
|
+
Scans the Hermes skills directory for SKILL.md files, parses the
|
|
9
|
+
frontmatter (name / description / tags), and POSTs the index to
|
|
10
|
+
Colony's push-only "skills" observation domain. Colony's self-directed
|
|
11
|
+
thinking and capability-gap machinery read from there, so Colony
|
|
12
|
+
proposes work the agent can actually do.
|
|
13
|
+
|
|
14
|
+
Run from cron daily (the wizard installs ``0 9 * * *``) or after
|
|
15
|
+
installing new skills. The TypeScript OpenClaw plugin ships the same
|
|
16
|
+
sync built-in (src/hermes-skills.ts); this worker is for deployments
|
|
17
|
+
using the Python Hermes plugin.
|
|
18
|
+
|
|
19
|
+
Environment (unchanged from the v0.18 script):
|
|
20
|
+
COLONY_URL sidecar URL (default http://127.0.0.1:7777)
|
|
21
|
+
COLONY_API_KEY API key (default dev-mode-no-key)
|
|
22
|
+
HERMES_SKILLS_DIR skills tree (default ~/.hermes/skills)
|
|
23
|
+
|
|
24
|
+
Stdlib-only on purpose — must run from cron without the sidecar deps.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
from __future__ import annotations
|
|
28
|
+
|
|
29
|
+
import argparse
|
|
30
|
+
import json
|
|
31
|
+
import os
|
|
32
|
+
import pathlib
|
|
33
|
+
import re
|
|
34
|
+
import urllib.request
|
|
35
|
+
from typing import Optional
|
|
36
|
+
|
|
37
|
+
MAX_DEPTH = 4
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def skills_dir() -> pathlib.Path:
|
|
41
|
+
"""Resolve the skills directory from the environment at call time."""
|
|
42
|
+
return pathlib.Path(
|
|
43
|
+
os.environ.get("HERMES_SKILLS_DIR", "~/.hermes/skills")
|
|
44
|
+
).expanduser()
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _parse_frontmatter(text: str) -> dict:
|
|
48
|
+
"""Minimal SKILL.md frontmatter parse — name/description scalars and
|
|
49
|
+
tags (inline ``[a, b]`` or block list), no YAML dependency."""
|
|
50
|
+
match = re.match(r"\A---\s*\n(.*?)\n---", text, re.DOTALL)
|
|
51
|
+
if not match:
|
|
52
|
+
return {}
|
|
53
|
+
fm = match.group(1)
|
|
54
|
+
out: dict = {}
|
|
55
|
+
name = re.search(r"^name:\s*[\"']?([^\"'\n]+)", fm, re.MULTILINE)
|
|
56
|
+
desc = re.search(r"^description:\s*[\"']?([^\"\n]+?)[\"']?\s*$",
|
|
57
|
+
fm, re.MULTILINE)
|
|
58
|
+
if name:
|
|
59
|
+
out["name"] = name.group(1).strip()
|
|
60
|
+
if desc:
|
|
61
|
+
out["description"] = desc.group(1).strip()
|
|
62
|
+
inline = re.search(r"^\s*tags:\s*\[([^\]]*)\]", fm, re.MULTILINE)
|
|
63
|
+
if inline:
|
|
64
|
+
out["tags"] = [t.strip().strip("\"'")
|
|
65
|
+
for t in inline.group(1).split(",") if t.strip()]
|
|
66
|
+
else:
|
|
67
|
+
block = re.search(r"^\s*tags:\s*\n((?:\s+-\s+.*\n?)+)", fm,
|
|
68
|
+
re.MULTILINE)
|
|
69
|
+
if block:
|
|
70
|
+
out["tags"] = [ln.split("-", 1)[1].strip().strip("\"'")
|
|
71
|
+
for ln in block.group(1).splitlines()
|
|
72
|
+
if "-" in ln]
|
|
73
|
+
return out
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def scan(base: Optional[pathlib.Path] = None) -> list:
|
|
77
|
+
"""Scan the skills tree, returning observation dicts for each SKILL.md."""
|
|
78
|
+
base = base if base is not None else skills_dir()
|
|
79
|
+
observations = []
|
|
80
|
+
if not base.is_dir():
|
|
81
|
+
return observations
|
|
82
|
+
for skill_md in sorted(base.rglob("SKILL.md")):
|
|
83
|
+
rel = skill_md.relative_to(base)
|
|
84
|
+
if len(rel.parts) > MAX_DEPTH:
|
|
85
|
+
continue
|
|
86
|
+
try:
|
|
87
|
+
meta = _parse_frontmatter(
|
|
88
|
+
skill_md.read_text(encoding="utf-8", errors="replace"))
|
|
89
|
+
except OSError:
|
|
90
|
+
continue
|
|
91
|
+
name = meta.get("name") or skill_md.parent.name
|
|
92
|
+
observations.append({
|
|
93
|
+
"entity_id": name,
|
|
94
|
+
"payload": {
|
|
95
|
+
"description": meta.get("description", ""),
|
|
96
|
+
"tags": meta.get("tags", []),
|
|
97
|
+
"path": str(skill_md),
|
|
98
|
+
"source": "hermes",
|
|
99
|
+
},
|
|
100
|
+
})
|
|
101
|
+
return observations
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def report(observations: list) -> int:
|
|
105
|
+
"""POST the skill index to Colony's skills observation domain.
|
|
106
|
+
|
|
107
|
+
Returns the HTTP status code.
|
|
108
|
+
"""
|
|
109
|
+
colony_url = os.environ.get("COLONY_URL", "http://127.0.0.1:7777")
|
|
110
|
+
api_key = os.environ.get("COLONY_API_KEY", "dev-mode-no-key")
|
|
111
|
+
body = {"domain": "skills", "reported_by": "hermes-skills-sync",
|
|
112
|
+
"observations": observations}
|
|
113
|
+
req = urllib.request.Request(
|
|
114
|
+
f"{colony_url}/v1/host/observations",
|
|
115
|
+
data=json.dumps(body).encode("utf-8"),
|
|
116
|
+
headers={"X-API-Key": api_key, "Content-Type": "application/json"},
|
|
117
|
+
method="POST")
|
|
118
|
+
with urllib.request.urlopen(req, timeout=30) as resp:
|
|
119
|
+
return resp.status
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def main(argv: Optional[list] = None) -> int:
|
|
123
|
+
parser = argparse.ArgumentParser(
|
|
124
|
+
prog="colony-skills-sync",
|
|
125
|
+
description="Report the agent's installed skill index to Colony.",
|
|
126
|
+
)
|
|
127
|
+
parser.add_argument(
|
|
128
|
+
"--dry-run",
|
|
129
|
+
action="store_true",
|
|
130
|
+
help="scan and print what would be reported without POSTing to Colony",
|
|
131
|
+
)
|
|
132
|
+
args = parser.parse_args(argv)
|
|
133
|
+
|
|
134
|
+
base = skills_dir()
|
|
135
|
+
observations = scan(base)
|
|
136
|
+
if not observations:
|
|
137
|
+
print(f"No skills found under {base}")
|
|
138
|
+
return 0
|
|
139
|
+
if args.dry_run:
|
|
140
|
+
print("colony-skills-sync (dry run — nothing reported):")
|
|
141
|
+
for obs in observations:
|
|
142
|
+
print(f" - {obs['entity_id']}: {obs['payload'].get('description', '')}")
|
|
143
|
+
print(f"Would report {len(observations)} skills from {base}")
|
|
144
|
+
return 0
|
|
145
|
+
|
|
146
|
+
status = report(observations)
|
|
147
|
+
print(f"Reported {len(observations)} skills (HTTP {status}) from {base}")
|
|
148
|
+
return 0
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
if __name__ == "__main__":
|
|
152
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"""Colony World Model — persistent, structured entity graph.
|
|
2
|
+
|
|
3
|
+
Provides seven entity types (Person, Company, Project, Product, Location,
|
|
4
|
+
Event, Concept), entity extraction, resolution/deduplication, and graph
|
|
5
|
+
traversal. Backed by SQLite (default) or Neo4j.
|
|
6
|
+
|
|
7
|
+
Quick start::
|
|
8
|
+
|
|
9
|
+
from apsimo.world_model import WorldModelStore, WorldModelConfig
|
|
10
|
+
|
|
11
|
+
config = WorldModelConfig(backend="sqlite", sqlite_path=":memory:")
|
|
12
|
+
async with WorldModelStore(config) as store:
|
|
13
|
+
entity = await store.upsert_entity(PersonEntity(
|
|
14
|
+
id="we-000-test",
|
|
15
|
+
name="Alice Chen",
|
|
16
|
+
entity_type="person",
|
|
17
|
+
confidence=0.8,
|
|
18
|
+
))
|
|
19
|
+
result = await store.find_entities("Alice")
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from .store import WorldModelStore, GraphNeighborhoodResult, WorldModelStats
|
|
23
|
+
from .config import WorldModelConfig
|
|
24
|
+
from .entities import (
|
|
25
|
+
BaseEntity,
|
|
26
|
+
PersonEntity,
|
|
27
|
+
CompanyEntity,
|
|
28
|
+
ProjectEntity,
|
|
29
|
+
ProductEntity,
|
|
30
|
+
LocationEntity,
|
|
31
|
+
EventEntity,
|
|
32
|
+
ConceptEntity,
|
|
33
|
+
ENTITY_CLASS_MAP,
|
|
34
|
+
entity_from_dict,
|
|
35
|
+
)
|
|
36
|
+
from .relationships import WorldRelationship
|
|
37
|
+
from .constants import ENTITY_TYPES, RELATIONSHIP_TYPES, EXTERNAL_ID_KEYS
|
|
38
|
+
from .confidence import (
|
|
39
|
+
CONFIDENCE_BY_SOURCE,
|
|
40
|
+
compute_property_confidence,
|
|
41
|
+
boost_confidence,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
__all__ = [
|
|
45
|
+
# Store
|
|
46
|
+
"WorldModelStore",
|
|
47
|
+
"GraphNeighborhoodResult",
|
|
48
|
+
"WorldModelStats",
|
|
49
|
+
"WorldModelConfig",
|
|
50
|
+
# Entities
|
|
51
|
+
"BaseEntity",
|
|
52
|
+
"PersonEntity",
|
|
53
|
+
"CompanyEntity",
|
|
54
|
+
"ProjectEntity",
|
|
55
|
+
"ProductEntity",
|
|
56
|
+
"LocationEntity",
|
|
57
|
+
"EventEntity",
|
|
58
|
+
"ConceptEntity",
|
|
59
|
+
"ENTITY_CLASS_MAP",
|
|
60
|
+
"entity_from_dict",
|
|
61
|
+
# Relationships
|
|
62
|
+
"WorldRelationship",
|
|
63
|
+
# Constants
|
|
64
|
+
"ENTITY_TYPES",
|
|
65
|
+
"RELATIONSHIP_TYPES",
|
|
66
|
+
"EXTERNAL_ID_KEYS",
|
|
67
|
+
# Confidence
|
|
68
|
+
"CONFIDENCE_BY_SOURCE",
|
|
69
|
+
"compute_property_confidence",
|
|
70
|
+
"boost_confidence",
|
|
71
|
+
]
|