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,15 @@
|
|
|
1
|
+
"""Turn middleware — host-agnostic cognitive steps that run around
|
|
2
|
+
each conversation turn.
|
|
3
|
+
|
|
4
|
+
These modules lift the Colony-distinctive cognition out of
|
|
5
|
+
``run_agent.py`` so it is reachable from any host (OpenClaw plugin,
|
|
6
|
+
future adapters, standalone). See ``docs/HOST_API.md`` for the
|
|
7
|
+
endpoints that expose them.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from apsimo.intelligence.turn_middleware.memory_sync import (
|
|
11
|
+
TurnSyncOutcome,
|
|
12
|
+
sync_turn_memory,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
__all__ = ["TurnSyncOutcome", "sync_turn_memory"]
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"""Post-turn memory sync — lifted from ``run_agent.py:_memory_sync``.
|
|
2
|
+
|
|
3
|
+
Host-agnostic. Accepts a plain dataclass describing what just happened
|
|
4
|
+
in the turn and routes the distilled signal into whichever continuity
|
|
5
|
+
store the host router has been wired with. Returns a
|
|
6
|
+
``TurnSyncOutcome`` describing what was touched; the caller is
|
|
7
|
+
responsible for emitting any host events.
|
|
8
|
+
|
|
9
|
+
Two sink shapes are supported via duck-typing so the endpoint works
|
|
10
|
+
whether the server wires the in-process
|
|
11
|
+
``colony.intelligence.components.session_continuity.SessionContinuity``
|
|
12
|
+
(expected when colony runs in-process — no HTTP loopback) or the
|
|
13
|
+
HTTP-client ``agent.memory_bridge.AgentMemoryBridge`` (useful for
|
|
14
|
+
standalone runs where the agent and API are in different processes).
|
|
15
|
+
|
|
16
|
+
Resolution order:
|
|
17
|
+
|
|
18
|
+
1. Bridge shape — ``is_available`` property + ``async
|
|
19
|
+
update_continuity(session_id, topics, entities, pending_tasks,
|
|
20
|
+
tools_used)``.
|
|
21
|
+
2. Continuity shape — ``async update_context(session_id, topics,
|
|
22
|
+
entities, pending_tasks)`` (note: ``tools_used`` is dropped because
|
|
23
|
+
``SessionContinuity`` does not persist it today; if it starts doing
|
|
24
|
+
so, extend the branch below.)
|
|
25
|
+
|
|
26
|
+
Background review (``_spawn_background_review`` in ``run_agent.py``)
|
|
27
|
+
is intentionally NOT moved here. It forks a full ``AIAgent`` and
|
|
28
|
+
recurses into ``run_conversation``, so it can only cleanly lift out of
|
|
29
|
+
``run_agent.py`` when the reasoning loop itself is extracted in
|
|
30
|
+
Stage B. OpenClaw owns reasoning today, so this is not blocking.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
from __future__ import annotations
|
|
34
|
+
|
|
35
|
+
import logging
|
|
36
|
+
from dataclasses import dataclass, field
|
|
37
|
+
from typing import Any, List, Optional
|
|
38
|
+
|
|
39
|
+
logger = logging.getLogger(__name__)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@dataclass
|
|
43
|
+
class TurnSyncOutcome:
|
|
44
|
+
"""Result of one ``sync_turn_memory`` call."""
|
|
45
|
+
|
|
46
|
+
continuity_updated: bool = False
|
|
47
|
+
skipped_reason: Optional[str] = None
|
|
48
|
+
errors: List[str] = field(default_factory=list)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
async def sync_turn_memory(
|
|
52
|
+
sink: Any,
|
|
53
|
+
session_id: str,
|
|
54
|
+
*,
|
|
55
|
+
topics: Optional[List[str]] = None,
|
|
56
|
+
entities: Optional[List[str]] = None,
|
|
57
|
+
pending_tasks: Optional[List[str]] = None,
|
|
58
|
+
tools_used: Optional[List[str]] = None,
|
|
59
|
+
) -> TurnSyncOutcome:
|
|
60
|
+
"""Update session continuity after a turn completes.
|
|
61
|
+
|
|
62
|
+
Never raises — unexpected errors are captured on the returned
|
|
63
|
+
outcome so the host stays up even if Colony's continuity store is
|
|
64
|
+
misbehaving.
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
outcome = TurnSyncOutcome()
|
|
68
|
+
|
|
69
|
+
if sink is None:
|
|
70
|
+
outcome.skipped_reason = "no_sink"
|
|
71
|
+
return outcome
|
|
72
|
+
|
|
73
|
+
if not session_id:
|
|
74
|
+
outcome.skipped_reason = "no_session_id"
|
|
75
|
+
return outcome
|
|
76
|
+
|
|
77
|
+
safe_topics = list(topics or [])
|
|
78
|
+
safe_entities = list(entities or [])
|
|
79
|
+
safe_pending = list(pending_tasks or [])
|
|
80
|
+
safe_tools = list(tools_used or [])
|
|
81
|
+
|
|
82
|
+
# Branch 1 — AgentMemoryBridge shape. ``is_available`` is a property
|
|
83
|
+
# that gates on circuit-breaker state and backend mode.
|
|
84
|
+
if hasattr(sink, "update_continuity"):
|
|
85
|
+
if not getattr(sink, "is_available", True):
|
|
86
|
+
outcome.skipped_reason = "sink_unavailable"
|
|
87
|
+
return outcome
|
|
88
|
+
try:
|
|
89
|
+
await sink.update_continuity(
|
|
90
|
+
session_id=session_id,
|
|
91
|
+
topics=safe_topics,
|
|
92
|
+
entities=safe_entities,
|
|
93
|
+
pending_tasks=safe_pending,
|
|
94
|
+
tools_used=safe_tools,
|
|
95
|
+
)
|
|
96
|
+
outcome.continuity_updated = True
|
|
97
|
+
except Exception as exc:
|
|
98
|
+
logger.debug("sync_turn_memory: update_continuity raised: %s", exc)
|
|
99
|
+
outcome.errors.append(f"update_continuity: {exc}")
|
|
100
|
+
return outcome
|
|
101
|
+
|
|
102
|
+
# Branch 2 — in-process SessionContinuity shape. Drops
|
|
103
|
+
# ``tools_used`` because ``update_context`` doesn't accept it today.
|
|
104
|
+
if hasattr(sink, "update_context"):
|
|
105
|
+
try:
|
|
106
|
+
await sink.update_context(
|
|
107
|
+
session_id=session_id,
|
|
108
|
+
topics=safe_topics or None,
|
|
109
|
+
entities=safe_entities or None,
|
|
110
|
+
pending_tasks=safe_pending or None,
|
|
111
|
+
)
|
|
112
|
+
outcome.continuity_updated = True
|
|
113
|
+
except Exception as exc:
|
|
114
|
+
logger.debug("sync_turn_memory: update_context raised: %s", exc)
|
|
115
|
+
outcome.errors.append(f"update_context: {exc}")
|
|
116
|
+
return outcome
|
|
117
|
+
|
|
118
|
+
outcome.skipped_reason = "sink_shape_unsupported"
|
|
119
|
+
return outcome
|
apsimo/mcp/__init__.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""Apsimo MCP Server — exposes cognitive substrate as MCP tools."""
|
|
2
|
+
|
|
3
|
+
import importlib.util
|
|
4
|
+
import logging
|
|
5
|
+
|
|
6
|
+
logger = logging.getLogger(__name__)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _fastmcp_available() -> bool:
|
|
10
|
+
"""True only if the symbol this package actually imports is present.
|
|
11
|
+
|
|
12
|
+
Checking for the top-level ``mcp`` package is not sufficient: mcp 2.x
|
|
13
|
+
ships the distribution but no longer provides ``mcp.server.fastmcp``.
|
|
14
|
+
Probing the parent alone made an incompatible SDK look available and
|
|
15
|
+
turned an optional dependency into an import-time crash.
|
|
16
|
+
"""
|
|
17
|
+
if importlib.util.find_spec("mcp") is None:
|
|
18
|
+
return False
|
|
19
|
+
try:
|
|
20
|
+
if importlib.util.find_spec("mcp.server.fastmcp") is None:
|
|
21
|
+
raise ModuleNotFoundError("mcp.server.fastmcp")
|
|
22
|
+
except (ImportError, AttributeError, ValueError):
|
|
23
|
+
logger.warning(
|
|
24
|
+
"mcp is installed but does not provide mcp.server.fastmcp "
|
|
25
|
+
"(mcp>=2 removed it). The Apsimo MCP server is UNAVAILABLE. "
|
|
26
|
+
"Install mcp[cli]>=1.0,<2 to enable it."
|
|
27
|
+
)
|
|
28
|
+
return False
|
|
29
|
+
return True
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
_MCP_SDK_AVAILABLE = _fastmcp_available()
|
|
33
|
+
|
|
34
|
+
if _MCP_SDK_AVAILABLE:
|
|
35
|
+
from apsimo.mcp.server import create_server, run_stdio, run_http
|
|
36
|
+
else:
|
|
37
|
+
# Optional dependency absent or incompatible. Callers must check for None
|
|
38
|
+
# rather than assume a server can be constructed.
|
|
39
|
+
create_server = run_stdio = run_http = None # type: ignore[assignment,misc]
|
|
40
|
+
|
|
41
|
+
__all__ = ["create_server", "run_stdio", "run_http", "_MCP_SDK_AVAILABLE"]
|
apsimo/mcp/__main__.py
ADDED
apsimo/mcp/config.py
ADDED
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
"""Apsimo MCP harness configuration.
|
|
2
|
+
|
|
3
|
+
Handles detecting installed harnesses and configuring them to use Apsimo's MCP server.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import json
|
|
7
|
+
import os
|
|
8
|
+
import shutil
|
|
9
|
+
import subprocess
|
|
10
|
+
import shlex
|
|
11
|
+
import tomllib
|
|
12
|
+
import re
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from typing import Any, Optional
|
|
15
|
+
|
|
16
|
+
try:
|
|
17
|
+
import yaml
|
|
18
|
+
except ImportError:
|
|
19
|
+
yaml = None # type: ignore[assignment]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# ---------------------------------------------------------------------------
|
|
23
|
+
# Harness definitions
|
|
24
|
+
# ---------------------------------------------------------------------------
|
|
25
|
+
|
|
26
|
+
HARNESS_DEFS = {
|
|
27
|
+
"claude-code": {
|
|
28
|
+
"display": "Claude Code",
|
|
29
|
+
"detect_cmds": ["claude"],
|
|
30
|
+
"config_path": "~/.claude.json",
|
|
31
|
+
"config_format": "json",
|
|
32
|
+
"mcp_key": "mcpServers", # Key for MCP servers in config
|
|
33
|
+
"source_tag": "claude-code",
|
|
34
|
+
},
|
|
35
|
+
"codex": {
|
|
36
|
+
"display": "Codex",
|
|
37
|
+
"detect_cmds": ["codex"],
|
|
38
|
+
"config_path": "~/.codex/config.toml",
|
|
39
|
+
"config_format": "toml",
|
|
40
|
+
"source_tag": "codex",
|
|
41
|
+
},
|
|
42
|
+
"crush": {
|
|
43
|
+
"display": "Crush",
|
|
44
|
+
"detect_cmds": ["crush"],
|
|
45
|
+
"config_path": "~/.crush.json",
|
|
46
|
+
"config_format": "json",
|
|
47
|
+
"mcp_key": "mcp",
|
|
48
|
+
"mcp_type": "stdio",
|
|
49
|
+
"source_tag": "crush",
|
|
50
|
+
},
|
|
51
|
+
"opencode": {
|
|
52
|
+
"display": "OpenCode",
|
|
53
|
+
"detect_cmds": ["opencode"],
|
|
54
|
+
"config_path": "~/.config/opencode/opencode.json",
|
|
55
|
+
"config_format": "json",
|
|
56
|
+
"mcp_key": "mcp", # OpenCode uses "mcp" not "mcpServers"
|
|
57
|
+
"mcp_type": "stdio", # OpenCode requires type field
|
|
58
|
+
"source_tag": "opencode",
|
|
59
|
+
},
|
|
60
|
+
"hermes": {
|
|
61
|
+
"display": "Hermes",
|
|
62
|
+
"detect_cmds": ["hermes"],
|
|
63
|
+
"config_path": "~/.hermes/config.yaml",
|
|
64
|
+
"config_format": "yaml",
|
|
65
|
+
"mcp_key": "mcp_servers",
|
|
66
|
+
"source_tag": "hermes",
|
|
67
|
+
},
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def detect_harnesses() -> dict[str, bool]:
|
|
72
|
+
"""Return {harness_id: is_installed} for all known harnesses."""
|
|
73
|
+
result = {}
|
|
74
|
+
for hid, hdef in HARNESS_DEFS.items():
|
|
75
|
+
installed = any(shutil.which(cmd) for cmd in hdef["detect_cmds"])
|
|
76
|
+
result[hid] = installed
|
|
77
|
+
return result
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
# ---------------------------------------------------------------------------
|
|
81
|
+
# Config writers
|
|
82
|
+
# ---------------------------------------------------------------------------
|
|
83
|
+
|
|
84
|
+
def _mcp_config(contact_id: str, source: str, include_type: bool = False,
|
|
85
|
+
sidecar_url: Optional[str] = None) -> dict[str, Any]:
|
|
86
|
+
"""New Apsimo config. Authentication is inherited, never a literal placeholder."""
|
|
87
|
+
from apsimo.environment import normalize_environment
|
|
88
|
+
env = normalize_environment()
|
|
89
|
+
url = sidecar_url or env.get("COLONY_SIDECAR_URL") or (
|
|
90
|
+
"http://127.0.0.1:" + env.get("COLONY_SIDECAR_PORT", "7777"))
|
|
91
|
+
custom = env.get("COLONY_MCP_COMMAND")
|
|
92
|
+
result = {
|
|
93
|
+
"command": custom or "apsimo",
|
|
94
|
+
"args": shlex.split(env.get("COLONY_MCP_ARGS", "")) if custom else ["mcp"],
|
|
95
|
+
"env": {"APSIMO_URL": url, "APSIMO_MCP_CONTACT_ID": contact_id,
|
|
96
|
+
"APSIMO_MCP_SOURCE": source},
|
|
97
|
+
}
|
|
98
|
+
if include_type:
|
|
99
|
+
result["type"] = "stdio"
|
|
100
|
+
return result
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _read_json(path: Path) -> dict:
|
|
104
|
+
# Do not replace unreadable configuration with an empty document.
|
|
105
|
+
return json.loads(path.read_text()) if path.exists() else {}
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _write_json(path: Path, data: dict) -> None:
|
|
109
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
110
|
+
path.write_text(json.dumps(data, indent=2) + "\n")
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _selected(servers: dict) -> dict:
|
|
114
|
+
old, new = servers.get("colony"), servers.get("apsimo")
|
|
115
|
+
if old is not None and new is not None and old != new:
|
|
116
|
+
raise ValueError("Both colony and apsimo MCP entries exist with different settings")
|
|
117
|
+
selected = new if new is not None else old
|
|
118
|
+
if selected is not None and not isinstance(selected, dict):
|
|
119
|
+
raise ValueError("The Apsimo MCP entry must be a configuration table")
|
|
120
|
+
return selected or {}
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _merged(existing: dict, desired: dict, sidecar_url: Optional[str]) -> dict:
|
|
124
|
+
"""Retain custom launch settings and literal credentials during the rename."""
|
|
125
|
+
from apsimo.environment import normalize_environment
|
|
126
|
+
current_env = existing.get("env", {})
|
|
127
|
+
normalized = normalize_environment(current_env)
|
|
128
|
+
env = {("APSIMO_" + k[7:] if k.startswith("COLONY_") else k): v
|
|
129
|
+
for k, v in normalized.items()}
|
|
130
|
+
# Old generated placeholders are not portable across harnesses. Inherit the
|
|
131
|
+
# real variable from the launching environment instead of passing a literal.
|
|
132
|
+
if env.get("APSIMO_API_KEY") in {"${COLONY_API_KEY}", "${APSIMO_API_KEY}"}:
|
|
133
|
+
env.pop("APSIMO_API_KEY")
|
|
134
|
+
selected_env = dict(desired["env"])
|
|
135
|
+
if (sidecar_url is None and not any(os.environ.get(k) for k in (
|
|
136
|
+
"APSIMO_SIDECAR_URL", "COLONY_SIDECAR_URL",
|
|
137
|
+
"APSIMO_SIDECAR_PORT", "COLONY_SIDECAR_PORT"))
|
|
138
|
+
and "APSIMO_URL" in env):
|
|
139
|
+
selected_env["APSIMO_URL"] = env["APSIMO_URL"]
|
|
140
|
+
env.update(selected_env)
|
|
141
|
+
result = {**existing, **desired, "env": env}
|
|
142
|
+
if (existing.get("command") not in {None, "colony", "apsimo"}
|
|
143
|
+
and not (os.environ.get("APSIMO_MCP_COMMAND") or os.environ.get("COLONY_MCP_COMMAND"))):
|
|
144
|
+
result["command"] = existing["command"]
|
|
145
|
+
result["args"] = existing.get("args", [])
|
|
146
|
+
return result
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def _update_servers(servers: dict, desired: dict, sidecar_url: Optional[str]) -> bool:
|
|
150
|
+
new = _merged(_selected(servers), desired, sidecar_url)
|
|
151
|
+
if servers.get("apsimo") == new and "colony" not in servers:
|
|
152
|
+
return False
|
|
153
|
+
servers.pop("colony", None)
|
|
154
|
+
servers["apsimo"] = new
|
|
155
|
+
return True
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def _add_to_json_config(hdef: dict, contact_id: str, source: str,
|
|
159
|
+
dry_run: bool = False, sidecar_url: Optional[str] = None) -> Optional[str]:
|
|
160
|
+
path = Path(hdef["config_path"]).expanduser()
|
|
161
|
+
data = _read_json(path)
|
|
162
|
+
servers = data.setdefault(hdef.get("mcp_key", "mcpServers"), {})
|
|
163
|
+
desired = _mcp_config(contact_id, source, hdef.get("mcp_type") == "stdio", sidecar_url)
|
|
164
|
+
if not _update_servers(servers, desired, sidecar_url):
|
|
165
|
+
return None
|
|
166
|
+
if not dry_run:
|
|
167
|
+
_write_json(path, data)
|
|
168
|
+
return " Configure one Apsimo MCP entry; preserve other servers and credentials"
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def _without_toml_entries(content: str) -> str:
|
|
172
|
+
"""Remove only our two server tables, preserving following tables verbatim."""
|
|
173
|
+
output, skipping = [], False
|
|
174
|
+
for line in content.splitlines(keepends=True):
|
|
175
|
+
stripped = line.strip()
|
|
176
|
+
if stripped.startswith("["):
|
|
177
|
+
match = re.match(r"^\[\s*mcp_servers\s*\.\s*(?:colony|apsimo)(?:\s*\.[^]]*)?\s*\]", stripped)
|
|
178
|
+
skipping = bool(match)
|
|
179
|
+
if not skipping:
|
|
180
|
+
output.append(line)
|
|
181
|
+
return "".join(output)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def _toml_block(config: dict) -> str:
|
|
185
|
+
# JSON string escaping is TOML basic-string escaping for our scalar values.
|
|
186
|
+
def key(name):
|
|
187
|
+
return name if re.fullmatch(r"[A-Za-z0-9_-]+", name) else json.dumps(name)
|
|
188
|
+
|
|
189
|
+
def value(item):
|
|
190
|
+
if isinstance(item, bool):
|
|
191
|
+
return "true" if item else "false"
|
|
192
|
+
if isinstance(item, dict):
|
|
193
|
+
return "{ " + ", ".join(key(k) + " = " + value(v) for k, v in item.items()) + " }"
|
|
194
|
+
if isinstance(item, list):
|
|
195
|
+
return "[" + ", ".join(value(v) for v in item) + "]"
|
|
196
|
+
return json.dumps(item, ensure_ascii=False)
|
|
197
|
+
return "\n[mcp_servers.apsimo]\n" + "".join(
|
|
198
|
+
key(k) + " = " + value(v) + "\n" for k, v in config.items())
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def _add_to_toml_config(config_path: str, contact_id: str, source: str,
|
|
202
|
+
dry_run: bool = False, sidecar_url: Optional[str] = None) -> Optional[str]:
|
|
203
|
+
path = Path(config_path).expanduser()
|
|
204
|
+
content = path.read_text() if path.exists() else ""
|
|
205
|
+
servers = tomllib.loads(content).get("mcp_servers", {})
|
|
206
|
+
desired = _mcp_config(contact_id, source, sidecar_url=sidecar_url)
|
|
207
|
+
if not _update_servers(servers, desired, sidecar_url):
|
|
208
|
+
return None
|
|
209
|
+
updated = _without_toml_entries(content).rstrip("\n") + _toml_block(servers["apsimo"])
|
|
210
|
+
parsed = tomllib.loads(updated)
|
|
211
|
+
if parsed.get("mcp_servers") != servers:
|
|
212
|
+
raise ValueError("Unsupported MCP TOML table layout; configuration was not modified")
|
|
213
|
+
if not dry_run:
|
|
214
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
215
|
+
path.write_text(updated)
|
|
216
|
+
return " Configure one Apsimo MCP entry; preserve other TOML tables and credentials"
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def _add_to_yaml_config(config_path: str, contact_id: str, source: str,
|
|
220
|
+
dry_run: bool = False, sidecar_url: Optional[str] = None) -> Optional[str]:
|
|
221
|
+
if yaml is None:
|
|
222
|
+
return " PyYAML not installed; run: pip install pyyaml"
|
|
223
|
+
path = Path(config_path).expanduser()
|
|
224
|
+
data = (yaml.safe_load(path.read_text()) or {}) if path.exists() else {}
|
|
225
|
+
desired = _mcp_config(contact_id, source, sidecar_url=sidecar_url)
|
|
226
|
+
if not _update_servers(data.setdefault("mcp_servers", {}), desired, sidecar_url):
|
|
227
|
+
return None
|
|
228
|
+
if not dry_run:
|
|
229
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
230
|
+
path.write_text(yaml.safe_dump(data, sort_keys=False))
|
|
231
|
+
return " Configure one Apsimo MCP entry; preserve other settings and credentials"
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def add_to_harness(harness_id: str, contact_id: str, dry_run: bool = False,
|
|
235
|
+
sidecar_url: Optional[str] = None) -> Optional[str]:
|
|
236
|
+
"""Configure a single Apsimo MCP server without mutating process settings."""
|
|
237
|
+
hdef = HARNESS_DEFS.get(harness_id)
|
|
238
|
+
if not hdef:
|
|
239
|
+
return f" Unknown harness: {harness_id}"
|
|
240
|
+
args = (contact_id, hdef["source_tag"], dry_run, sidecar_url)
|
|
241
|
+
if hdef["config_format"] == "json":
|
|
242
|
+
return _add_to_json_config(hdef, *args)
|
|
243
|
+
if hdef["config_format"] == "toml":
|
|
244
|
+
return _add_to_toml_config(hdef["config_path"], *args)
|
|
245
|
+
if hdef["config_format"] == "yaml":
|
|
246
|
+
return _add_to_yaml_config(hdef["config_path"], *args)
|
|
247
|
+
return None
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def remove_from_harness(harness_id: str, dry_run: bool = False) -> Optional[str]:
|
|
251
|
+
"""Remove either recognized MCP name, leaving unrelated settings intact."""
|
|
252
|
+
hdef = HARNESS_DEFS.get(harness_id)
|
|
253
|
+
if not hdef:
|
|
254
|
+
return f" Unknown harness: {harness_id}"
|
|
255
|
+
path = Path(hdef["config_path"]).expanduser()
|
|
256
|
+
if not path.exists():
|
|
257
|
+
return None
|
|
258
|
+
fmt = hdef["config_format"]
|
|
259
|
+
if fmt == "toml":
|
|
260
|
+
content = path.read_text()
|
|
261
|
+
data = tomllib.loads(content)
|
|
262
|
+
key = "mcp_servers"
|
|
263
|
+
elif fmt == "yaml":
|
|
264
|
+
if yaml is None:
|
|
265
|
+
return " PyYAML not installed"
|
|
266
|
+
data = yaml.safe_load(path.read_text()) or {}
|
|
267
|
+
key = "mcp_servers"
|
|
268
|
+
else:
|
|
269
|
+
data = _read_json(path)
|
|
270
|
+
key = hdef.get("mcp_key", "mcpServers")
|
|
271
|
+
servers = data.get(key, {})
|
|
272
|
+
if not any(name in servers for name in ("colony", "apsimo")):
|
|
273
|
+
return None
|
|
274
|
+
for name in ("colony", "apsimo"):
|
|
275
|
+
servers.pop(name, None)
|
|
276
|
+
if fmt == "toml":
|
|
277
|
+
updated = _without_toml_entries(content)
|
|
278
|
+
if tomllib.loads(updated).get(key, {}) != servers:
|
|
279
|
+
raise ValueError("Unsupported MCP TOML table layout; configuration was not modified")
|
|
280
|
+
if not dry_run:
|
|
281
|
+
if fmt == "toml":
|
|
282
|
+
path.write_text(updated)
|
|
283
|
+
elif fmt == "yaml":
|
|
284
|
+
path.write_text(yaml.safe_dump(data, sort_keys=False))
|
|
285
|
+
else:
|
|
286
|
+
_write_json(path, data)
|
|
287
|
+
return f" Removed Apsimo MCP integration from {hdef['display']} config"
|