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,45 @@
|
|
|
1
|
+
"""Harness integration - writes context files and skills to each harness.
|
|
2
|
+
|
|
3
|
+
This module provides automatic integration with:
|
|
4
|
+
- Crush (MCP + skill)
|
|
5
|
+
- Codex (MCP + skill)
|
|
6
|
+
- Claude Code (MCP + skill)
|
|
7
|
+
- OpenCode (MCP + skill)
|
|
8
|
+
- Hermes (plugin only)
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from .context import (
|
|
12
|
+
write_apsimo_context,
|
|
13
|
+
APSIMO_CONTEXT_TEMPLATE,
|
|
14
|
+
write_colony_context,
|
|
15
|
+
COLONY_CONTEXT_TEMPLATE,
|
|
16
|
+
)
|
|
17
|
+
from .skills import (
|
|
18
|
+
write_apsimo_skill,
|
|
19
|
+
write_apsimo_check_skill,
|
|
20
|
+
remove_apsimo_skill,
|
|
21
|
+
APSIMO_DIAGNOSTIC_SKILL,
|
|
22
|
+
APSIMO_CHECK_SKILL,
|
|
23
|
+
write_colony_skill,
|
|
24
|
+
write_colony_check_skill,
|
|
25
|
+
remove_colony_skill,
|
|
26
|
+
get_skill_path,
|
|
27
|
+
get_skill_config_path,
|
|
28
|
+
COLONY_DIAGNOSTIC_SKILL,
|
|
29
|
+
COLONY_CHECK_SKILL,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
__all__ = [
|
|
33
|
+
"write_apsimo_context", "write_apsimo_skill", "write_apsimo_check_skill",
|
|
34
|
+
"remove_apsimo_skill", "APSIMO_CONTEXT_TEMPLATE",
|
|
35
|
+
"APSIMO_DIAGNOSTIC_SKILL", "APSIMO_CHECK_SKILL",
|
|
36
|
+
"write_colony_context",
|
|
37
|
+
"write_colony_skill",
|
|
38
|
+
"write_colony_check_skill",
|
|
39
|
+
"remove_colony_skill",
|
|
40
|
+
"get_skill_path",
|
|
41
|
+
"get_skill_config_path",
|
|
42
|
+
"COLONY_CONTEXT_TEMPLATE",
|
|
43
|
+
"COLONY_DIAGNOSTIC_SKILL",
|
|
44
|
+
"COLONY_CHECK_SKILL",
|
|
45
|
+
]
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""Write a short, model-independent Apsimo reference into a workspace."""
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
APSIMO_CONTEXT_TEMPLATE = """# Apsimo integration
|
|
5
|
+
|
|
6
|
+
Apsimo is the cognition and continuity layer, formerly ColonyAI. Your agent's
|
|
7
|
+
private identity is separate from the platform name. Old-name searches refer to
|
|
8
|
+
the same platform and history.
|
|
9
|
+
|
|
10
|
+
Use `apsimo_health` to check connectivity and `apsimo_get_context` for relevant,
|
|
11
|
+
authenticated context. Check `apsimo_check_commitments` before promising work.
|
|
12
|
+
Recall useful evidence with `apsimo_lookup_facts`; cite the returned source and
|
|
13
|
+
ask about contradictions. Save durable facts with `apsimo_remember_fact` only
|
|
14
|
+
when their source and usefulness are clear. Corrections and forgetting govern
|
|
15
|
+
source-backed recall across sessions; do not create diagnostic junk memories.
|
|
16
|
+
|
|
17
|
+
Use `apsimo doctor` and `apsimo mcp detect` for installation checks. Hermes uses
|
|
18
|
+
its native Apsimo plugin and memory provider. Coding harnesses may use the MCP
|
|
19
|
+
server. Both connect to the same selected instance over its authenticated API;
|
|
20
|
+
sharing an instance does not grant every caller access to every private source.
|
|
21
|
+
|
|
22
|
+
APSIMO_* configuration names are preferred; existing COLONY_* names remain
|
|
23
|
+
accepted. Use the selected instance's actual configuration and credential
|
|
24
|
+
reference. Never guess a key or print credentials into diagnostic transcripts.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
# Public compatibility aliases point to the same template and writer.
|
|
28
|
+
COLONY_CONTEXT_TEMPLATE = APSIMO_CONTEXT_TEMPLATE
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def write_apsimo_context(workspace_dir: Path) -> bool:
|
|
32
|
+
if not workspace_dir.exists():
|
|
33
|
+
return False
|
|
34
|
+
try:
|
|
35
|
+
(workspace_dir / "APSIMO.md").write_text(APSIMO_CONTEXT_TEMPLATE)
|
|
36
|
+
return True
|
|
37
|
+
except OSError:
|
|
38
|
+
return False
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
write_colony_context = write_apsimo_context
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
"""Write Apsimo diagnostic skill to harness skills directories."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import shutil
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
# Skill directory paths for each harness
|
|
11
|
+
SKILL_PATHS = {
|
|
12
|
+
"openclaw": None, # Special case - uses workspace_dir
|
|
13
|
+
"crush": "~/.config/crush/skills/apsimo-diagnose",
|
|
14
|
+
"codex": "~/.codex/skills/apsimo-diagnose",
|
|
15
|
+
"claude-code": "~/.codex/skills/apsimo-diagnose", # Shares with Codex
|
|
16
|
+
"opencode": "~/.config/opencode/skills/apsimo-diagnose",
|
|
17
|
+
"hermes": None, # Plugin-based, no skill directory
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
# Config paths for skills_paths updates (Crush only)
|
|
21
|
+
CONFIG_PATHS = {
|
|
22
|
+
"crush": ["~/.crush.json", "~/.config/crush/crush.json"],
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
APSIMO_DIAGNOSTIC_SKILL = """---
|
|
27
|
+
name: apsimo-diagnose
|
|
28
|
+
description: Diagnose Apsimo or legacy ColonyAI connection failures and missing recalled context using the selected instance.
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
# Apsimo diagnostics
|
|
32
|
+
|
|
33
|
+
1. Run `apsimo status` and `apsimo doctor`. Read the reported endpoint and instance
|
|
34
|
+
instead of assuming a directory, machine, port, or database backend.
|
|
35
|
+
2. For coding harnesses, run `apsimo mcp detect`. Confirm one `apsimo` MCP server
|
|
36
|
+
is configured. Legacy `colony` entries are migrated by `apsimo mcp setup --harness <name>`.
|
|
37
|
+
3. Call `apsimo_health`. If it fails, report the actual transport or authentication
|
|
38
|
+
error. Use the selected instance's credential reference; never guess a key,
|
|
39
|
+
print its value, or dump a whole configuration into a transcript.
|
|
40
|
+
4. If health passes, use `apsimo_get_context` for the authenticated contact and
|
|
41
|
+
`apsimo_lookup_facts` for a known source-backed fact. Report what was actually
|
|
42
|
+
retrieved. Do not insert test facts into ordinary memory.
|
|
43
|
+
5. For Hermes, inspect its selected profile's plugin and memory-provider status.
|
|
44
|
+
Correct the indicated connection or configuration, then repeat the failed
|
|
45
|
+
probe once. A successful HTTP request alone does not prove useful recollection.
|
|
46
|
+
|
|
47
|
+
Apsimo is the platform's new name; Aeva or another private agent keeps its own
|
|
48
|
+
identity. Existing state paths and COLONY_* variables remain supported. Conflicting
|
|
49
|
+
explicit APSIMO_* and COLONY_* settings must be resolved at configuration load.
|
|
50
|
+
"""
|
|
51
|
+
COLONY_DIAGNOSTIC_SKILL = APSIMO_DIAGNOSTIC_SKILL
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def get_skill_path(harness_id: str) -> Path | None:
|
|
55
|
+
"""Get the skill directory path for a harness.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
harness_id: Harness identifier (e.g., 'crush', 'openclaw')
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
Path to skill directory, or None if harness doesn't support skills
|
|
62
|
+
"""
|
|
63
|
+
path_str = SKILL_PATHS.get(harness_id)
|
|
64
|
+
if path_str:
|
|
65
|
+
canonical = Path(path_str).expanduser()
|
|
66
|
+
legacy = canonical.with_name("colony-diagnose")
|
|
67
|
+
# Reuse an installed legacy directory, so setup does not discover two
|
|
68
|
+
# copies of one skill. New installations use the canonical directory.
|
|
69
|
+
return legacy if legacy.exists() and not canonical.exists() else canonical
|
|
70
|
+
return None
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def get_skill_config_path(harness_id: str) -> Path | None:
|
|
74
|
+
"""Get the harness config path for skills_paths update.
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
harness_id: Harness identifier
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
Path to config file, or None if not applicable
|
|
81
|
+
"""
|
|
82
|
+
config_paths = CONFIG_PATHS.get(harness_id, [])
|
|
83
|
+
for path_str in config_paths:
|
|
84
|
+
path = Path(path_str).expanduser()
|
|
85
|
+
if path.exists():
|
|
86
|
+
return path
|
|
87
|
+
return None
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def write_apsimo_skill(harness_id: str, workspace_dir: Path | None = None) -> bool:
|
|
91
|
+
"""Write Apsimo diagnostic skill to harness skills directory.
|
|
92
|
+
|
|
93
|
+
For Crush: also updates options.skills_paths in config.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
harness_id: Harness identifier (e.g., 'crush', 'openclaw')
|
|
97
|
+
workspace_dir: Optional workspace dir (used for OpenClaw to override)
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
True if written successfully, False otherwise (including if harness doesn't support skills)
|
|
101
|
+
"""
|
|
102
|
+
# Special handling for OpenClaw - use workspace skills dir
|
|
103
|
+
if harness_id == "openclaw" and workspace_dir:
|
|
104
|
+
skill_dir = workspace_dir / "skills" / "apsimo-diagnose"
|
|
105
|
+
else:
|
|
106
|
+
skill_dir = get_skill_path(harness_id)
|
|
107
|
+
|
|
108
|
+
# Harness doesn't support skills (e.g., Hermes)
|
|
109
|
+
if not skill_dir:
|
|
110
|
+
return False
|
|
111
|
+
|
|
112
|
+
try:
|
|
113
|
+
skill_dir.mkdir(parents=True, exist_ok=True)
|
|
114
|
+
skill_md = skill_dir / "SKILL.md"
|
|
115
|
+
skill_md.write_text(COLONY_DIAGNOSTIC_SKILL)
|
|
116
|
+
|
|
117
|
+
# For Crush, update skills_paths in config
|
|
118
|
+
if harness_id == "crush":
|
|
119
|
+
_update_crush_skills_paths()
|
|
120
|
+
|
|
121
|
+
return True
|
|
122
|
+
except Exception:
|
|
123
|
+
return False
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def remove_apsimo_skill(harness_id: str) -> bool:
|
|
127
|
+
"""Remove Apsimo diagnostic skill from harness skills directory.
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
harness_id: Harness identifier
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
True if removed successfully, False if not found or error
|
|
134
|
+
"""
|
|
135
|
+
skill_dir = get_skill_path(harness_id)
|
|
136
|
+
|
|
137
|
+
if not skill_dir or not skill_dir.exists():
|
|
138
|
+
return False
|
|
139
|
+
|
|
140
|
+
try:
|
|
141
|
+
shutil.rmtree(skill_dir)
|
|
142
|
+
return True
|
|
143
|
+
except Exception:
|
|
144
|
+
return False
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def _update_crush_skills_paths() -> bool:
|
|
148
|
+
"""Internal: Add ~/.config/crush/skills to Crush's skills_paths if not present.
|
|
149
|
+
|
|
150
|
+
Crush config can be at:
|
|
151
|
+
1. ~/.crush.json (checked first by Crush)
|
|
152
|
+
2. ~/.config/crush/crush.json (checked second)
|
|
153
|
+
|
|
154
|
+
We check both and update whichever exists, preferring ~/.crush.json.
|
|
155
|
+
If neither exists, we don't create one just for skills_paths.
|
|
156
|
+
"""
|
|
157
|
+
skill_path_to_add = "~/.config/crush/skills"
|
|
158
|
+
|
|
159
|
+
# Find existing config
|
|
160
|
+
config_path = get_skill_config_path("crush")
|
|
161
|
+
if not config_path:
|
|
162
|
+
return False # No config exists, don't create one
|
|
163
|
+
|
|
164
|
+
try:
|
|
165
|
+
data = json.loads(config_path.read_text())
|
|
166
|
+
except (json.JSONDecodeError, FileNotFoundError):
|
|
167
|
+
return False
|
|
168
|
+
|
|
169
|
+
# Check if skills_paths already includes our path
|
|
170
|
+
options = data.get("options", {})
|
|
171
|
+
skills_paths = options.get("skills_paths", [])
|
|
172
|
+
|
|
173
|
+
# Normalize paths for comparison
|
|
174
|
+
normalized_new = str(Path(skill_path_to_add).expanduser())
|
|
175
|
+
normalized_existing = [str(Path(p).expanduser()) for p in skills_paths]
|
|
176
|
+
|
|
177
|
+
if normalized_new in normalized_existing:
|
|
178
|
+
return True # Already present
|
|
179
|
+
|
|
180
|
+
# Add the path
|
|
181
|
+
skills_paths.append(skill_path_to_add)
|
|
182
|
+
options["skills_paths"] = skills_paths
|
|
183
|
+
data["options"] = options
|
|
184
|
+
|
|
185
|
+
try:
|
|
186
|
+
config_path.write_text(json.dumps(data, indent=2))
|
|
187
|
+
return True
|
|
188
|
+
except Exception:
|
|
189
|
+
return False
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
# ---------------------------------------------------------------------------
|
|
193
|
+
# Apsimo Check Skill (for on-demand initiative checking)
|
|
194
|
+
# ---------------------------------------------------------------------------
|
|
195
|
+
|
|
196
|
+
APSIMO_CHECK_SKILL = """---
|
|
197
|
+
name: apsimo-check
|
|
198
|
+
description: Review current commitments and useful work through the selected Apsimo instance.
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
Use `apsimo_get_context` and `apsimo_check_commitments` for the authenticated
|
|
202
|
+
contact. Summarize overdue work, current commitments, and source-backed surprises.
|
|
203
|
+
Distinguish observations from proposed actions. Respect existing task ownership
|
|
204
|
+
and execution limits; do not create duplicate work or claim that a task ran
|
|
205
|
+
without its execution evidence. Do not write a memory merely because this check
|
|
206
|
+
was performed. If a tool fails, report its actual blocker and change strategy.
|
|
207
|
+
"""
|
|
208
|
+
COLONY_CHECK_SKILL = APSIMO_CHECK_SKILL
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def write_apsimo_check_skill(workspace_dir: Path) -> bool:
|
|
212
|
+
"""Write apsimo-check skill to OpenClaw workspace skills directory.
|
|
213
|
+
|
|
214
|
+
Args:
|
|
215
|
+
workspace_dir: OpenClaw workspace directory
|
|
216
|
+
|
|
217
|
+
Returns:
|
|
218
|
+
True if written successfully, False otherwise
|
|
219
|
+
"""
|
|
220
|
+
skill_dir = workspace_dir / "skills" / "apsimo-check"
|
|
221
|
+
try:
|
|
222
|
+
skill_dir.mkdir(parents=True, exist_ok=True)
|
|
223
|
+
(skill_dir / "SKILL.md").write_text(COLONY_CHECK_SKILL)
|
|
224
|
+
return True
|
|
225
|
+
except Exception:
|
|
226
|
+
return False
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
write_colony_skill = write_apsimo_skill
|
|
230
|
+
write_colony_check_skill = write_apsimo_check_skill
|
|
231
|
+
remove_colony_skill = remove_apsimo_skill
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""Identity normalization service (v0.16.0).
|
|
2
|
+
|
|
3
|
+
Maps between the identifier formats Colony uses for a person:
|
|
4
|
+
contact-store CID, Neo4j Person node ID, display name, and platform
|
|
5
|
+
handles (email, phone, etc.).
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from apsimo.identity.resolver import (
|
|
9
|
+
IdentityResolver,
|
|
10
|
+
OwnerIdentityError,
|
|
11
|
+
get_identity_resolver,
|
|
12
|
+
get_owner_contact_id,
|
|
13
|
+
get_owner_name,
|
|
14
|
+
get_persona_name,
|
|
15
|
+
reset_identity_resolver,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
"IdentityResolver",
|
|
20
|
+
"OwnerIdentityError",
|
|
21
|
+
"get_identity_resolver",
|
|
22
|
+
"get_owner_contact_id",
|
|
23
|
+
"get_owner_name",
|
|
24
|
+
"get_persona_name",
|
|
25
|
+
"reset_identity_resolver",
|
|
26
|
+
]
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
"""ParticipantResolver -- per-message sender -> contact attribution.
|
|
2
|
+
|
|
3
|
+
The attribution chokepoint for relationship intelligence (docs/RELATIONSHIPS.md):
|
|
4
|
+
every synced turn that carries a ``sender`` resolves to a real contact here,
|
|
5
|
+
server-side, regardless of client caching. Unknown people become shadow
|
|
6
|
+
contacts so history accrues from first contact; machines resolve to the
|
|
7
|
+
reserved ``system`` sentinel and never touch relationship stores.
|
|
8
|
+
|
|
9
|
+
Resolution ladder (first hit wins):
|
|
10
|
+
1. exact / cross-gateway messaging handle (phones unify across sms/rcs/
|
|
11
|
+
whatsapp/signal/imessage via phone_key; emails normalize)
|
|
12
|
+
2. scoped display-name: propose a candidate association, without attributing
|
|
13
|
+
the sender to that person's identity or private history
|
|
14
|
+
3. shadow contact (tier unknown, interaction_allowed=false, provenance
|
|
15
|
+
recorded), when COLONY_IDENTITY_SHADOW_CONTACTS (default true)
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import logging
|
|
21
|
+
import os
|
|
22
|
+
from dataclasses import dataclass
|
|
23
|
+
from typing import Any, Optional
|
|
24
|
+
|
|
25
|
+
logger = logging.getLogger(__name__)
|
|
26
|
+
|
|
27
|
+
#: Reserved contact id for machine-origin turns. Comms may record it for ops
|
|
28
|
+
#: visibility; ToM/interactions/relationship surfaces must always exclude it.
|
|
29
|
+
SYSTEM_CONTACT_ID = "system"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def shadow_contacts_enabled() -> bool:
|
|
33
|
+
return os.environ.get(
|
|
34
|
+
"COLONY_IDENTITY_SHADOW_CONTACTS", "true").strip().lower() != "false"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def machine_channel_prefixes() -> tuple:
|
|
38
|
+
raw = os.environ.get("COLONY_IDENTITY_MACHINE_CHANNELS", "cron,api,internal")
|
|
39
|
+
return tuple(p.strip().lower() for p in raw.split(",") if p.strip())
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def is_machine_turn(channel_id: str, user_text: str, has_sender: bool) -> bool:
|
|
43
|
+
"""A turn is machine-origin when it has no human sender AND either rides
|
|
44
|
+
a machine channel or carries system-origin text markers. A turn WITH a
|
|
45
|
+
resolved human sender is never reclassified (a human talking on an api
|
|
46
|
+
channel is still a human)."""
|
|
47
|
+
if has_sender:
|
|
48
|
+
return False
|
|
49
|
+
ch = (channel_id or "").strip().lower()
|
|
50
|
+
for prefix in machine_channel_prefixes():
|
|
51
|
+
if ch == prefix or ch.startswith(prefix + ":"):
|
|
52
|
+
return True
|
|
53
|
+
try:
|
|
54
|
+
from apsimo.delivery.reachout_policy import is_system_origin
|
|
55
|
+
if is_system_origin(user_text):
|
|
56
|
+
return True
|
|
57
|
+
except ImportError:
|
|
58
|
+
pass
|
|
59
|
+
return False
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@dataclass
|
|
63
|
+
class Resolution:
|
|
64
|
+
contact_id: Optional[str]
|
|
65
|
+
method: str # handle | scoped_name | shadow | none
|
|
66
|
+
created: bool = False
|
|
67
|
+
proposal_filed: bool = False
|
|
68
|
+
candidate_contact_id: Optional[str] = None
|
|
69
|
+
proposal_id: Optional[str] = None
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class ParticipantResolver:
|
|
73
|
+
def __init__(self, contacts_store: Any) -> None:
|
|
74
|
+
self._store = contacts_store
|
|
75
|
+
|
|
76
|
+
async def resolve(self, *, platform: str, user_id: str,
|
|
77
|
+
display_name: str = "", group_id: str = "",
|
|
78
|
+
channel_id: str = "") -> Resolution:
|
|
79
|
+
"""Resolve a sender to a contact id (see module doc for the ladder)."""
|
|
80
|
+
platform = (platform or "").strip().lower()
|
|
81
|
+
user_id = (user_id or "").strip()
|
|
82
|
+
if not user_id:
|
|
83
|
+
return Resolution(None, "none")
|
|
84
|
+
|
|
85
|
+
# 0. Already-canonical contact id (e.g. the voice channel, where the
|
|
86
|
+
# speaker was resolved to a contact upstream and passed as user_id).
|
|
87
|
+
# Confirm it exists rather than minting a shadow for it.
|
|
88
|
+
if user_id.startswith("cid-"):
|
|
89
|
+
try:
|
|
90
|
+
if await self._store.get(user_id) is not None:
|
|
91
|
+
return Resolution(user_id, "contact_id")
|
|
92
|
+
except Exception:
|
|
93
|
+
pass
|
|
94
|
+
|
|
95
|
+
# 1. Handle match (exact + cross-gateway phone + email normalization).
|
|
96
|
+
try:
|
|
97
|
+
c = await self._store.resolve_messaging_handle(platform, user_id)
|
|
98
|
+
except Exception:
|
|
99
|
+
logger.debug("participant handle resolve failed", exc_info=True)
|
|
100
|
+
c = None
|
|
101
|
+
if c is not None:
|
|
102
|
+
return Resolution(c.contact_id, "handle")
|
|
103
|
+
|
|
104
|
+
# 2. A name can suggest a link; it cannot establish attribution.
|
|
105
|
+
candidate, proposal = None, None
|
|
106
|
+
if display_name and group_id:
|
|
107
|
+
match = await self._scoped_name_match(platform, group_id, display_name)
|
|
108
|
+
if match is not None:
|
|
109
|
+
proposal = await self._file_handle_proposal(
|
|
110
|
+
match, platform, user_id, display_name)
|
|
111
|
+
candidate = match
|
|
112
|
+
|
|
113
|
+
# 3. Shadow contact.
|
|
114
|
+
if not shadow_contacts_enabled():
|
|
115
|
+
return Resolution(None, "none", proposal_filed=bool(proposal),
|
|
116
|
+
candidate_contact_id=candidate, proposal_id=proposal)
|
|
117
|
+
try:
|
|
118
|
+
contact = await self._store.create(
|
|
119
|
+
display_name=display_name or user_id,
|
|
120
|
+
trust_tier="unknown",
|
|
121
|
+
interaction_allowed=False,
|
|
122
|
+
import_source="auto:sender",
|
|
123
|
+
notes=(f"Auto-created from first contact on {channel_id or platform}"
|
|
124
|
+
+ (f" (group {group_id})" if group_id else "")),
|
|
125
|
+
)
|
|
126
|
+
await self._store.add_handle(
|
|
127
|
+
contact.contact_id, platform, user_id,
|
|
128
|
+
is_primary=True, confidence=0.9, source="auto:sender")
|
|
129
|
+
logger.info("Shadow contact %s created for %s:%s (%r)",
|
|
130
|
+
contact.contact_id, platform, user_id,
|
|
131
|
+
display_name or "?")
|
|
132
|
+
return Resolution(contact.contact_id, "shadow", created=True,
|
|
133
|
+
proposal_filed=bool(proposal), candidate_contact_id=candidate,
|
|
134
|
+
proposal_id=proposal)
|
|
135
|
+
except Exception:
|
|
136
|
+
logger.warning("shadow contact creation failed for %s:%s",
|
|
137
|
+
platform, user_id, exc_info=True)
|
|
138
|
+
return Resolution(None, "none")
|
|
139
|
+
|
|
140
|
+
# -- rung 2 helpers ----------------------------------------------------
|
|
141
|
+
async def _scoped_name_match(self, platform: str, group_id: str,
|
|
142
|
+
display_name: str) -> Optional[str]:
|
|
143
|
+
"""The display name matches exactly ONE member of this group's scope."""
|
|
144
|
+
try:
|
|
145
|
+
scope = await self._store.get_scope(
|
|
146
|
+
platform=platform, external_id=str(group_id))
|
|
147
|
+
if scope is None:
|
|
148
|
+
return None
|
|
149
|
+
members = await self._store.scope_members(scope.scope_id)
|
|
150
|
+
except Exception:
|
|
151
|
+
return None
|
|
152
|
+
hits = []
|
|
153
|
+
want = display_name.strip().lower()
|
|
154
|
+
for m in members or []:
|
|
155
|
+
cid = getattr(m, "contact_id", None) or (
|
|
156
|
+
m.get("contact_id") if isinstance(m, dict) else None)
|
|
157
|
+
if not cid:
|
|
158
|
+
continue
|
|
159
|
+
try:
|
|
160
|
+
c = await self._store.get(cid)
|
|
161
|
+
except Exception:
|
|
162
|
+
continue
|
|
163
|
+
if c is None:
|
|
164
|
+
continue
|
|
165
|
+
names = {str(c.display_name or "").strip().lower(),
|
|
166
|
+
str(getattr(c, "given_name", "") or "").strip().lower()}
|
|
167
|
+
if want and (want in names
|
|
168
|
+
or any(n and want.split()[0] == n.split()[0]
|
|
169
|
+
for n in names if n)):
|
|
170
|
+
hits.append(c.contact_id)
|
|
171
|
+
return hits[0] if len(hits) == 1 else None
|
|
172
|
+
|
|
173
|
+
async def _file_handle_proposal(self, contact_id: str, platform: str,
|
|
174
|
+
user_id: str, display_name: str) -> Optional[str]:
|
|
175
|
+
"""Keep the hypothesis outside the handle index used for attribution."""
|
|
176
|
+
try:
|
|
177
|
+
result = await self._store.propose_handle_link(contact_id, platform, user_id)
|
|
178
|
+
return result['candidate_id'] if result['status'] == 'pending' else None
|
|
179
|
+
except Exception:
|
|
180
|
+
logger.debug("handle proposal failed", exc_info=True)
|
|
181
|
+
return None
|