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,49 @@
|
|
|
1
|
+
"""Web search native tool."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
from typing import Any, Dict
|
|
7
|
+
|
|
8
|
+
logger = logging.getLogger(__name__)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class WebSearchTool:
|
|
12
|
+
"""Web search via Colony's SearchOrchestrator."""
|
|
13
|
+
|
|
14
|
+
def __init__(self, search_orchestrator=None):
|
|
15
|
+
self._orchestrator = search_orchestrator
|
|
16
|
+
|
|
17
|
+
@property
|
|
18
|
+
def name(self) -> str:
|
|
19
|
+
return "web_search"
|
|
20
|
+
|
|
21
|
+
@property
|
|
22
|
+
def schema(self) -> dict:
|
|
23
|
+
return {
|
|
24
|
+
"type": "object",
|
|
25
|
+
"properties": {
|
|
26
|
+
"query": {"type": "string", "description": "Search query"},
|
|
27
|
+
"max_results": {"type": "integer", "description": "Max results (default 5)", "default": 5},
|
|
28
|
+
},
|
|
29
|
+
"required": ["query"],
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async def execute(self, args: Dict[str, Any]) -> Dict[str, Any]:
|
|
33
|
+
if not self._orchestrator or not self._orchestrator.has_providers:
|
|
34
|
+
return {"error": True, "message": "Web search not configured"}
|
|
35
|
+
|
|
36
|
+
query = args.get("query", "")
|
|
37
|
+
max_results = args.get("max_results", 5)
|
|
38
|
+
|
|
39
|
+
try:
|
|
40
|
+
results = await self._orchestrator.search(query, max_results)
|
|
41
|
+
return {
|
|
42
|
+
"results": [
|
|
43
|
+
{"title": r.title, "url": r.url, "snippet": r.snippet, "source": r.source}
|
|
44
|
+
for r in results
|
|
45
|
+
],
|
|
46
|
+
"count": len(results),
|
|
47
|
+
}
|
|
48
|
+
except Exception as e:
|
|
49
|
+
return {"error": True, "message": str(e)}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
"""Typed authorization policy for sidecar-resident reasoning tools.
|
|
2
|
+
|
|
3
|
+
Model tool definitions are an ergonomics hint, not an enforcement boundary: a
|
|
4
|
+
model (or a direct API caller) can still name a tool that was not advertised.
|
|
5
|
+
This module gives every execution path the same small, conservative effect
|
|
6
|
+
classification and actor policy. Unknown and dynamically supplied tools are
|
|
7
|
+
mutating until explicitly classified.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from dataclasses import dataclass
|
|
13
|
+
from enum import Enum
|
|
14
|
+
from typing import Any, Mapping
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ToolEffect(str, Enum):
|
|
18
|
+
"""The authority needed to execute a tool."""
|
|
19
|
+
|
|
20
|
+
PUBLIC_READ = "public_read"
|
|
21
|
+
PRIVATE_READ = "private_read"
|
|
22
|
+
MUTATION = "mutation"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass(frozen=True)
|
|
26
|
+
class ToolActorPolicy:
|
|
27
|
+
"""Server-derived capabilities for one reasoning turn or direct call.
|
|
28
|
+
|
|
29
|
+
``None`` remains the compatibility representation for trusted in-process
|
|
30
|
+
callers. HTTP handlers must always supply a concrete policy while P8 is
|
|
31
|
+
attached; request-body identities never populate these fields.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
principal_id: str
|
|
35
|
+
viewer_person_id: str
|
|
36
|
+
allow_private_read: bool = False
|
|
37
|
+
allow_mutation: bool = False
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
# Pure/general information tools that do not expose Colony's private stores.
|
|
41
|
+
_PUBLIC_READ_TOOLS = frozenset({
|
|
42
|
+
"calculate",
|
|
43
|
+
"web_search",
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
# Tools that can expose owner, contact, repository, memory, or self-state.
|
|
48
|
+
# They are read-only, but are intentionally not guest-callable because most
|
|
49
|
+
# legacy handlers do not yet seal selectors in their argument dictionaries.
|
|
50
|
+
_PRIVATE_READ_TOOLS = frozenset({
|
|
51
|
+
"action_journal",
|
|
52
|
+
"belief_conflicts",
|
|
53
|
+
"colony_discover_connections",
|
|
54
|
+
"colony_get_briefing",
|
|
55
|
+
"colony_get_relationship",
|
|
56
|
+
"colony_list_boundaries",
|
|
57
|
+
"colony_list_goals",
|
|
58
|
+
"colony_memory_search",
|
|
59
|
+
"colony_query_entities",
|
|
60
|
+
"colony_recent_boundary_blocks",
|
|
61
|
+
"list_directory",
|
|
62
|
+
"list_projects",
|
|
63
|
+
"pending_contact_proposals",
|
|
64
|
+
"project_status",
|
|
65
|
+
"read_file",
|
|
66
|
+
"recall_skills",
|
|
67
|
+
"relationship_brief",
|
|
68
|
+
"repo_list_files",
|
|
69
|
+
"repo_read_file",
|
|
70
|
+
"repo_search",
|
|
71
|
+
"sandbox_status",
|
|
72
|
+
"self_status",
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
# Known state-changing tools. Unknown/dynamic tools also default to mutation.
|
|
77
|
+
_MUTATION_TOOLS = frozenset({
|
|
78
|
+
"abandon_project",
|
|
79
|
+
"colony_flag_boundary_concern",
|
|
80
|
+
"colony_initiative_feedback",
|
|
81
|
+
"colony_record_insight",
|
|
82
|
+
"colony_start_research",
|
|
83
|
+
"colony_task_complete",
|
|
84
|
+
"colony_task_dismiss",
|
|
85
|
+
"colony_task_snooze",
|
|
86
|
+
"create_project",
|
|
87
|
+
"link_contact",
|
|
88
|
+
"merge_contacts",
|
|
89
|
+
"sandbox_run",
|
|
90
|
+
"write_file",
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def classify_tool(name: str) -> ToolEffect:
|
|
95
|
+
"""Return the tool's conservative effect class.
|
|
96
|
+
|
|
97
|
+
The explicit mutation set documents current first-party tools. The final
|
|
98
|
+
branch is deliberately mutation: newly graduated/toolsmith tools cannot
|
|
99
|
+
silently inherit read authority merely because they are new.
|
|
100
|
+
"""
|
|
101
|
+
|
|
102
|
+
normalized = str(name or "").strip()
|
|
103
|
+
if normalized in _PUBLIC_READ_TOOLS:
|
|
104
|
+
return ToolEffect.PUBLIC_READ
|
|
105
|
+
if normalized in _PRIVATE_READ_TOOLS:
|
|
106
|
+
return ToolEffect.PRIVATE_READ
|
|
107
|
+
if normalized in _MUTATION_TOOLS:
|
|
108
|
+
return ToolEffect.MUTATION
|
|
109
|
+
return ToolEffect.MUTATION
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def actor_allows_tool(
|
|
113
|
+
name: str,
|
|
114
|
+
actor: ToolActorPolicy | None,
|
|
115
|
+
) -> bool:
|
|
116
|
+
"""Whether actor capabilities permit this tool before boundary checks."""
|
|
117
|
+
|
|
118
|
+
return actor_allows_effect(classify_tool(name), actor)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def actor_allows_effect(
|
|
122
|
+
effect: ToolEffect,
|
|
123
|
+
actor: ToolActorPolicy | None,
|
|
124
|
+
) -> bool:
|
|
125
|
+
"""Whether actor capabilities permit an already-resolved tool effect.
|
|
126
|
+
|
|
127
|
+
Callers with handler provenance must use this form. In particular, every
|
|
128
|
+
dynamic handler resolves to ``MUTATION`` even if its chosen name resembles
|
|
129
|
+
a shipped public or private read.
|
|
130
|
+
"""
|
|
131
|
+
|
|
132
|
+
if actor is None: # trusted in-process compatibility caller
|
|
133
|
+
return True
|
|
134
|
+
if effect is ToolEffect.PUBLIC_READ:
|
|
135
|
+
return True
|
|
136
|
+
if effect is ToolEffect.PRIVATE_READ:
|
|
137
|
+
return actor.allow_private_read
|
|
138
|
+
return actor.allow_mutation
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def filter_tools_for_actor(
|
|
142
|
+
names: list[str] | tuple[str, ...] | set[str] | frozenset[str],
|
|
143
|
+
actor: ToolActorPolicy | None,
|
|
144
|
+
) -> list[str]:
|
|
145
|
+
"""Preserve caller order while removing actor-inaccessible tool names."""
|
|
146
|
+
|
|
147
|
+
return [name for name in names if actor_allows_tool(name, actor)]
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def action_target(arguments: Mapping[str, Any]) -> str:
|
|
151
|
+
"""Extract a useful human/resource subject for DirectiveGuard matching."""
|
|
152
|
+
|
|
153
|
+
for key in (
|
|
154
|
+
"target",
|
|
155
|
+
"target_path",
|
|
156
|
+
"path",
|
|
157
|
+
"repo",
|
|
158
|
+
"url",
|
|
159
|
+
"endpoint",
|
|
160
|
+
"who",
|
|
161
|
+
"contact_id",
|
|
162
|
+
"person_id",
|
|
163
|
+
"entity_id",
|
|
164
|
+
"topic",
|
|
165
|
+
"query",
|
|
166
|
+
"purpose",
|
|
167
|
+
):
|
|
168
|
+
value = arguments.get(key)
|
|
169
|
+
if isinstance(value, (str, int, float)) and str(value).strip():
|
|
170
|
+
return str(value).strip()
|
|
171
|
+
return ""
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
__all__ = [
|
|
175
|
+
"ToolActorPolicy",
|
|
176
|
+
"ToolEffect",
|
|
177
|
+
"action_target",
|
|
178
|
+
"actor_allows_effect",
|
|
179
|
+
"actor_allows_tool",
|
|
180
|
+
"classify_tool",
|
|
181
|
+
"filter_tools_for_actor",
|
|
182
|
+
]
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"""Regex-based secret redaction for logs and tool output.
|
|
2
|
+
|
|
3
|
+
Applies pattern matching to mask API keys, tokens, and credentials
|
|
4
|
+
before they reach log files, verbose output, or gateway logs.
|
|
5
|
+
|
|
6
|
+
Short tokens (< 18 chars) are fully masked. Longer tokens preserve
|
|
7
|
+
the first 6 and last 4 characters for debuggability.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import logging
|
|
11
|
+
import os
|
|
12
|
+
import re
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
# Known API key prefixes -- match the prefix + contiguous token chars
|
|
17
|
+
_PREFIX_PATTERNS = [
|
|
18
|
+
r"sk-[A-Za-z0-9_-]{10,}", # OpenAI / OpenRouter / Anthropic (sk-ant-*)
|
|
19
|
+
r"ghp_[A-Za-z0-9]{10,}", # GitHub PAT (classic)
|
|
20
|
+
r"github_pat_[A-Za-z0-9_]{10,}", # GitHub PAT (fine-grained)
|
|
21
|
+
r"xox[baprs]-[A-Za-z0-9-]{10,}", # Slack tokens
|
|
22
|
+
r"AIza[A-Za-z0-9_-]{30,}", # Google API keys
|
|
23
|
+
r"pplx-[A-Za-z0-9]{10,}", # Perplexity
|
|
24
|
+
r"fal_[A-Za-z0-9_-]{10,}", # Fal.ai
|
|
25
|
+
r"fc-[A-Za-z0-9]{10,}", # Firecrawl
|
|
26
|
+
r"bb_live_[A-Za-z0-9_-]{10,}", # BrowserBase
|
|
27
|
+
r"gAAAA[A-Za-z0-9_=-]{20,}", # Codex encrypted tokens
|
|
28
|
+
r"AKIA[A-Z0-9]{16}", # AWS Access Key ID
|
|
29
|
+
r"sk_live_[A-Za-z0-9]{10,}", # Stripe secret key (live)
|
|
30
|
+
r"sk_test_[A-Za-z0-9]{10,}", # Stripe secret key (test)
|
|
31
|
+
r"rk_live_[A-Za-z0-9]{10,}", # Stripe restricted key
|
|
32
|
+
r"SG\.[A-Za-z0-9_-]{10,}", # SendGrid API key
|
|
33
|
+
r"hf_[A-Za-z0-9]{10,}", # HuggingFace token
|
|
34
|
+
r"r8_[A-Za-z0-9]{10,}", # Replicate API token
|
|
35
|
+
r"npm_[A-Za-z0-9]{10,}", # npm access token
|
|
36
|
+
r"pypi-[A-Za-z0-9_-]{10,}", # PyPI API token
|
|
37
|
+
r"dop_v1_[A-Za-z0-9]{10,}", # DigitalOcean PAT
|
|
38
|
+
r"doo_v1_[A-Za-z0-9]{10,}", # DigitalOcean OAuth
|
|
39
|
+
r"am_[A-Za-z0-9_-]{10,}", # AgentMail API key
|
|
40
|
+
r"sk_[A-Za-z0-9_]{10,}", # ElevenLabs TTS key (sk_ underscore, not sk- dash)
|
|
41
|
+
r"tvly-[A-Za-z0-9]{10,}", # Tavily search API key
|
|
42
|
+
r"exa_[A-Za-z0-9]{10,}", # Exa search API key
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
# ENV assignment patterns: KEY=value where KEY contains a secret-like name
|
|
46
|
+
_SECRET_ENV_NAMES = r"(?:API_?KEY|TOKEN|SECRET|PASSWORD|PASSWD|CREDENTIAL|AUTH)"
|
|
47
|
+
_ENV_ASSIGN_RE = re.compile(
|
|
48
|
+
rf"([A-Z_]*{_SECRET_ENV_NAMES}[A-Z_]*)\s*=\s*(['\"]?)(\S+)\2",
|
|
49
|
+
re.IGNORECASE,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
# JSON field patterns: "apiKey": "value", "token": "value", etc.
|
|
53
|
+
_JSON_KEY_NAMES = r"(?:api_?[Kk]ey|token|secret|password|access_token|refresh_token|auth_token|bearer|secret_value|raw_secret|secret_input|key_material)"
|
|
54
|
+
_JSON_FIELD_RE = re.compile(
|
|
55
|
+
rf'("{_JSON_KEY_NAMES}")\s*:\s*"([^"]+)"',
|
|
56
|
+
re.IGNORECASE,
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
# Authorization headers
|
|
60
|
+
_AUTH_HEADER_RE = re.compile(
|
|
61
|
+
r"(Authorization:\s*Bearer\s+)(\S+)",
|
|
62
|
+
re.IGNORECASE,
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
# Telegram bot tokens: bot<digits>:<token> or <digits>:<token>,
|
|
66
|
+
# where token part is restricted to [-A-Za-z0-9_] and length >= 30
|
|
67
|
+
_TELEGRAM_RE = re.compile(
|
|
68
|
+
r"(bot)?(\d{8,}):([-A-Za-z0-9_]{30,})",
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
# Private key blocks: -----BEGIN RSA PRIVATE KEY----- ... -----END RSA PRIVATE KEY-----
|
|
72
|
+
_PRIVATE_KEY_RE = re.compile(
|
|
73
|
+
r"-----BEGIN[A-Z ]*PRIVATE KEY-----[\s\S]*?-----END[A-Z ]*PRIVATE KEY-----"
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
# Database connection strings: protocol://user:PASSWORD@host
|
|
77
|
+
# Catches postgres, mysql, mongodb, redis, amqp URLs and redacts the password
|
|
78
|
+
_DB_CONNSTR_RE = re.compile(
|
|
79
|
+
r"((?:postgres(?:ql)?|mysql|mongodb(?:\+srv)?|redis|amqp)://[^:]+:)([^@]+)(@)",
|
|
80
|
+
re.IGNORECASE,
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
# E.164 phone numbers: +<country><number>, 7-15 digits
|
|
84
|
+
# Negative lookahead prevents matching hex strings or identifiers
|
|
85
|
+
_SIGNAL_PHONE_RE = re.compile(r"(\+[1-9]\d{6,14})(?![A-Za-z0-9])")
|
|
86
|
+
|
|
87
|
+
# Compile known prefix patterns into one alternation
|
|
88
|
+
_PREFIX_RE = re.compile(
|
|
89
|
+
r"(?<![A-Za-z0-9_-])(" + "|".join(_PREFIX_PATTERNS) + r")(?![A-Za-z0-9_-])"
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _mask_token(token: str) -> str:
|
|
94
|
+
"""Mask a token, preserving prefix for long tokens."""
|
|
95
|
+
if len(token) < 18:
|
|
96
|
+
return "***"
|
|
97
|
+
return f"{token[:6]}...{token[-4:]}"
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def redact_sensitive_text(text: str) -> str:
|
|
101
|
+
"""Apply all redaction patterns to a block of text.
|
|
102
|
+
|
|
103
|
+
Safe to call on any string -- non-matching text passes through unchanged.
|
|
104
|
+
Disabled when security.redact_secrets is false in config.yaml.
|
|
105
|
+
"""
|
|
106
|
+
if not text:
|
|
107
|
+
return text
|
|
108
|
+
# Redaction bypass is only honoured when COLONY_ENV=development to prevent
|
|
109
|
+
# accidental disablement in production via .env files or container env overrides.
|
|
110
|
+
if os.getenv("COLONY_REDACT_SECRETS", "").lower() in ("0", "false", "no", "off"):
|
|
111
|
+
colony_env = os.getenv("COLONY_ENV", "production").lower()
|
|
112
|
+
if colony_env != "development":
|
|
113
|
+
# Silently ignore the bypass flag in non-development environments.
|
|
114
|
+
pass
|
|
115
|
+
else:
|
|
116
|
+
import logging as _log
|
|
117
|
+
_log.getLogger(__name__).warning(
|
|
118
|
+
"COLONY_REDACT_SECRETS bypass is active — log redaction disabled. "
|
|
119
|
+
"This must NOT be set in production."
|
|
120
|
+
)
|
|
121
|
+
return text
|
|
122
|
+
|
|
123
|
+
# Known prefixes (sk-, ghp_, etc.)
|
|
124
|
+
text = _PREFIX_RE.sub(lambda m: _mask_token(m.group(1)), text)
|
|
125
|
+
|
|
126
|
+
# ENV assignments: OPENAI_API_KEY=sk-abc...
|
|
127
|
+
def _redact_env(m):
|
|
128
|
+
name, quote, value = m.group(1), m.group(2), m.group(3)
|
|
129
|
+
return f"{name}={quote}{_mask_token(value)}{quote}"
|
|
130
|
+
text = _ENV_ASSIGN_RE.sub(_redact_env, text)
|
|
131
|
+
|
|
132
|
+
# JSON fields: "apiKey": "value"
|
|
133
|
+
def _redact_json(m):
|
|
134
|
+
key, value = m.group(1), m.group(2)
|
|
135
|
+
return f'{key}: "{_mask_token(value)}"'
|
|
136
|
+
text = _JSON_FIELD_RE.sub(_redact_json, text)
|
|
137
|
+
|
|
138
|
+
# Authorization headers
|
|
139
|
+
text = _AUTH_HEADER_RE.sub(
|
|
140
|
+
lambda m: m.group(1) + _mask_token(m.group(2)),
|
|
141
|
+
text,
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
# Telegram bot tokens
|
|
145
|
+
def _redact_telegram(m):
|
|
146
|
+
prefix = m.group(1) or ""
|
|
147
|
+
digits = m.group(2)
|
|
148
|
+
return f"{prefix}{digits}:***"
|
|
149
|
+
text = _TELEGRAM_RE.sub(_redact_telegram, text)
|
|
150
|
+
|
|
151
|
+
# Private key blocks
|
|
152
|
+
text = _PRIVATE_KEY_RE.sub("[REDACTED PRIVATE KEY]", text)
|
|
153
|
+
|
|
154
|
+
# Database connection string passwords
|
|
155
|
+
text = _DB_CONNSTR_RE.sub(lambda m: f"{m.group(1)}***{m.group(3)}", text)
|
|
156
|
+
|
|
157
|
+
# E.164 phone numbers (Signal, WhatsApp)
|
|
158
|
+
def _redact_phone(m):
|
|
159
|
+
phone = m.group(1)
|
|
160
|
+
if len(phone) <= 8:
|
|
161
|
+
return phone[:2] + "****" + phone[-2:]
|
|
162
|
+
return phone[:4] + "****" + phone[-4:]
|
|
163
|
+
text = _SIGNAL_PHONE_RE.sub(_redact_phone, text)
|
|
164
|
+
|
|
165
|
+
return text
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
class RedactingFormatter(logging.Formatter):
|
|
169
|
+
"""Log formatter that redacts secrets from all log messages."""
|
|
170
|
+
|
|
171
|
+
def __init__(self, fmt=None, datefmt=None, style='%', **kwargs):
|
|
172
|
+
super().__init__(fmt, datefmt, style, **kwargs)
|
|
173
|
+
|
|
174
|
+
def format(self, record: logging.LogRecord) -> str:
|
|
175
|
+
original = super().format(record)
|
|
176
|
+
return redact_sensitive_text(original)
|
apsimo/repos/__init__.py
ADDED
apsimo/repos/mirrors.py
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
"""Read-only local repo mirrors for Colony's own research (best-of-B).
|
|
2
|
+
|
|
3
|
+
Owner-designated repos (env/config only, never hardcoded) are cloned/pulled
|
|
4
|
+
into a local mirror directory. Colony gets low-latency read access -- list,
|
|
5
|
+
read, search -- with NO write credentials anywhere. Every access is
|
|
6
|
+
boundary-gated: a standing "leave X alone" blocks even reads of X.
|
|
7
|
+
|
|
8
|
+
Config: COLONY_REPO_MIRRORS = "name=url[|alias1|alias2][,name2=url2...]"
|
|
9
|
+
Mirror dir: <state_dir>/repo-mirrors/<name>
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import fnmatch
|
|
15
|
+
import logging
|
|
16
|
+
import os
|
|
17
|
+
import subprocess
|
|
18
|
+
import time
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
from typing import Any, Dict, List, Optional
|
|
21
|
+
|
|
22
|
+
logger = logging.getLogger(__name__)
|
|
23
|
+
|
|
24
|
+
_MAX_READ_BYTES = 64 * 1024
|
|
25
|
+
_SKIP_DIRS = {".git", "node_modules", "__pycache__", ".venv", "venv", "dist", "build"}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def parse_mirror_config(raw: str) -> Dict[str, Dict[str, str]]:
|
|
29
|
+
"""Parse COLONY_REPO_MIRRORS into {name: {url, aliases}}."""
|
|
30
|
+
out: Dict[str, Dict[str, str]] = {}
|
|
31
|
+
for part in (raw or "").split(","):
|
|
32
|
+
part = part.strip()
|
|
33
|
+
if not part or "=" not in part:
|
|
34
|
+
continue
|
|
35
|
+
name, rest = part.split("=", 1)
|
|
36
|
+
pieces = rest.split("|")
|
|
37
|
+
url = pieces[0].strip()
|
|
38
|
+
aliases = "|".join(a.strip() for a in pieces[1:] if a.strip())
|
|
39
|
+
if name.strip() and url:
|
|
40
|
+
out[name.strip()] = {"url": url, "aliases": aliases}
|
|
41
|
+
return out
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class RepoMirrorManager:
|
|
45
|
+
def __init__(
|
|
46
|
+
self,
|
|
47
|
+
mirror_dir: str,
|
|
48
|
+
config: Optional[Dict[str, Dict[str, str]]] = None,
|
|
49
|
+
directive_manager: Any = None,
|
|
50
|
+
) -> None:
|
|
51
|
+
self._dir = Path(mirror_dir)
|
|
52
|
+
self._config = config if config is not None else parse_mirror_config(
|
|
53
|
+
os.environ.get("COLONY_REPO_MIRRORS", ""))
|
|
54
|
+
self._directives = directive_manager
|
|
55
|
+
self._last_refresh: Dict[str, float] = {}
|
|
56
|
+
|
|
57
|
+
def configured(self) -> Dict[str, Dict[str, str]]:
|
|
58
|
+
return dict(self._config)
|
|
59
|
+
|
|
60
|
+
def path_for(self, name: str) -> Optional[str]:
|
|
61
|
+
if name not in self._config:
|
|
62
|
+
return None
|
|
63
|
+
p = self._dir / name
|
|
64
|
+
return str(p) if (p / ".git").exists() or (p / "HEAD").exists() else None
|
|
65
|
+
|
|
66
|
+
# -- boundary gate --------------------------------------------------
|
|
67
|
+
def _boundary_ok(self, name: str, extra: str = "") -> Any:
|
|
68
|
+
if self._directives is None:
|
|
69
|
+
return None
|
|
70
|
+
try:
|
|
71
|
+
from apsimo.directives import Action
|
|
72
|
+
verdict = self._directives.check(Action(
|
|
73
|
+
kind="repo_read", text=f"{name} {extra}", target=name))
|
|
74
|
+
return verdict if not verdict.allowed else None
|
|
75
|
+
except Exception:
|
|
76
|
+
return None
|
|
77
|
+
|
|
78
|
+
# -- sync -------------------------------------------------------------
|
|
79
|
+
def refresh(self, name: str, min_interval_secs: float = 300.0) -> Dict[str, Any]:
|
|
80
|
+
"""Clone or pull one mirror (read-only; no credentials added)."""
|
|
81
|
+
info = self._config.get(name)
|
|
82
|
+
if info is None:
|
|
83
|
+
return {"ok": False, "reason": "unknown_repo"}
|
|
84
|
+
blocked = self._boundary_ok(name, "refresh")
|
|
85
|
+
if blocked is not None:
|
|
86
|
+
return {"ok": False, "reason": blocked.reason}
|
|
87
|
+
now = time.time()
|
|
88
|
+
if now - self._last_refresh.get(name, 0) < min_interval_secs and self.path_for(name):
|
|
89
|
+
return {"ok": True, "action": "fresh"}
|
|
90
|
+
self._dir.mkdir(parents=True, exist_ok=True)
|
|
91
|
+
dest = self._dir / name
|
|
92
|
+
try:
|
|
93
|
+
if (dest / ".git").exists():
|
|
94
|
+
subprocess.run(["git", "-C", str(dest), "fetch", "--all", "--prune"],
|
|
95
|
+
capture_output=True, text=True, timeout=120, check=True)
|
|
96
|
+
subprocess.run(["git", "-C", str(dest), "reset", "--hard", "@{upstream}"],
|
|
97
|
+
capture_output=True, text=True, timeout=60, check=False)
|
|
98
|
+
action = "pulled"
|
|
99
|
+
else:
|
|
100
|
+
subprocess.run(["git", "clone", info["url"], str(dest)],
|
|
101
|
+
capture_output=True, text=True, timeout=300, check=True)
|
|
102
|
+
action = "cloned"
|
|
103
|
+
self._last_refresh[name] = now
|
|
104
|
+
return {"ok": True, "action": action}
|
|
105
|
+
except subprocess.CalledProcessError as exc:
|
|
106
|
+
err = (exc.stderr or "")[:200]
|
|
107
|
+
logger.warning("mirror refresh failed for %s: %s", name, err)
|
|
108
|
+
return {"ok": False, "reason": err}
|
|
109
|
+
except Exception as exc:
|
|
110
|
+
return {"ok": False, "reason": str(exc)[:200]}
|
|
111
|
+
|
|
112
|
+
def refresh_all(self) -> Dict[str, Any]:
|
|
113
|
+
return {name: self.refresh(name) for name in self._config}
|
|
114
|
+
|
|
115
|
+
# -- read tools ---------------------------------------------------------
|
|
116
|
+
def list_files(self, name: str, subpath: str = "", limit: int = 200) -> Dict[str, Any]:
|
|
117
|
+
blocked = self._boundary_ok(name, subpath)
|
|
118
|
+
if blocked is not None:
|
|
119
|
+
return {"error": blocked.reason, "status": "boundary_refused"}
|
|
120
|
+
root = self.path_for(name)
|
|
121
|
+
if root is None:
|
|
122
|
+
return {"error": f"repo {name!r} not mirrored", "status": "unavailable"}
|
|
123
|
+
base = Path(root) / subpath.lstrip("/")
|
|
124
|
+
base_r = base.resolve()
|
|
125
|
+
if not str(base_r).startswith(str(Path(root).resolve())):
|
|
126
|
+
return {"error": "path escapes repo", "status": "error"}
|
|
127
|
+
if not base_r.exists():
|
|
128
|
+
return {"error": f"no such path {subpath!r}", "status": "not_found"}
|
|
129
|
+
files: List[str] = []
|
|
130
|
+
for p in sorted(base_r.rglob("*")):
|
|
131
|
+
rel_parts = p.relative_to(root).parts
|
|
132
|
+
if any(part in _SKIP_DIRS for part in rel_parts):
|
|
133
|
+
continue
|
|
134
|
+
if p.is_file():
|
|
135
|
+
files.append(str(p.relative_to(root)))
|
|
136
|
+
if len(files) >= limit:
|
|
137
|
+
break
|
|
138
|
+
return {"repo": name, "path": subpath or "/", "count": len(files), "files": files}
|
|
139
|
+
|
|
140
|
+
def read_file(self, name: str, path: str, max_bytes: int = _MAX_READ_BYTES) -> Dict[str, Any]:
|
|
141
|
+
blocked = self._boundary_ok(name, path)
|
|
142
|
+
if blocked is not None:
|
|
143
|
+
return {"error": blocked.reason, "status": "boundary_refused"}
|
|
144
|
+
root = self.path_for(name)
|
|
145
|
+
if root is None:
|
|
146
|
+
return {"error": f"repo {name!r} not mirrored", "status": "unavailable"}
|
|
147
|
+
f = (Path(root) / path.lstrip("/")).resolve()
|
|
148
|
+
if not str(f).startswith(str(Path(root).resolve())):
|
|
149
|
+
return {"error": "path escapes repo", "status": "error"}
|
|
150
|
+
if not f.is_file():
|
|
151
|
+
return {"error": f"no such file {path!r}", "status": "not_found"}
|
|
152
|
+
try:
|
|
153
|
+
data = f.read_bytes()[: max(1024, min(max_bytes, _MAX_READ_BYTES))]
|
|
154
|
+
return {"repo": name, "path": path, "size": f.stat().st_size,
|
|
155
|
+
"content": data.decode("utf-8", errors="replace")}
|
|
156
|
+
except Exception as exc:
|
|
157
|
+
return {"error": str(exc)[:200], "status": "error"}
|
|
158
|
+
|
|
159
|
+
def search(self, name: str, query: str, glob: str = "",
|
|
160
|
+
max_results: int = 40) -> Dict[str, Any]:
|
|
161
|
+
blocked = self._boundary_ok(name, query)
|
|
162
|
+
if blocked is not None:
|
|
163
|
+
return {"error": blocked.reason, "status": "boundary_refused"}
|
|
164
|
+
root = self.path_for(name)
|
|
165
|
+
if root is None:
|
|
166
|
+
return {"error": f"repo {name!r} not mirrored", "status": "unavailable"}
|
|
167
|
+
if not query:
|
|
168
|
+
return {"error": "query required", "status": "error"}
|
|
169
|
+
try:
|
|
170
|
+
args = ["git", "-C", root, "grep", "-In", "--max-depth", "-1", "-e", query]
|
|
171
|
+
if glob:
|
|
172
|
+
args += ["--", glob]
|
|
173
|
+
out = subprocess.run(args, capture_output=True, text=True, timeout=30)
|
|
174
|
+
lines = [ln for ln in out.stdout.splitlines() if ln.strip()][:max_results]
|
|
175
|
+
return {"repo": name, "query": query, "count": len(lines),
|
|
176
|
+
"matches": lines}
|
|
177
|
+
except Exception as exc:
|
|
178
|
+
return {"error": str(exc)[:200], "status": "error"}
|
|
179
|
+
|
|
180
|
+
# -- world-model feed -----------------------------------------------------
|
|
181
|
+
async def register_entities(self, world_store: Any) -> int:
|
|
182
|
+
"""Upsert each owner-designated repo as a Project entity (deterministic,
|
|
183
|
+
config-driven; not extraction guesswork)."""
|
|
184
|
+
if world_store is None:
|
|
185
|
+
return 0
|
|
186
|
+
n = 0
|
|
187
|
+
try:
|
|
188
|
+
from apsimo.world_model.entities import ProjectEntity
|
|
189
|
+
from apsimo.world_model.sqlite.backend import _generate_id
|
|
190
|
+
for name, info in self._config.items():
|
|
191
|
+
existing = await world_store.get_entity_by_external_id("repo_name", name)
|
|
192
|
+
if existing is not None:
|
|
193
|
+
continue
|
|
194
|
+
ent = ProjectEntity(
|
|
195
|
+
id=_generate_id("we"), name=name, entity_type="project",
|
|
196
|
+
confidence=0.95,
|
|
197
|
+
external_ids={"repo_name": name},
|
|
198
|
+
properties={"kind": "repo", "url": info.get("url", "")},
|
|
199
|
+
)
|
|
200
|
+
await world_store.upsert_entity(ent)
|
|
201
|
+
n += 1
|
|
202
|
+
except Exception:
|
|
203
|
+
logger.debug("repo entity registration failed", exc_info=True)
|
|
204
|
+
return n
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""Colony Research-to-Artifact Pipeline (RAP).
|
|
2
|
+
|
|
3
|
+
A unified, autonomy-capable pipeline that takes a research goal and produces
|
|
4
|
+
a polished, gated deliverable with minimal human intervention.
|
|
5
|
+
|
|
6
|
+
Six pipeline stages:
|
|
7
|
+
1. DECOMPOSE — break goal into sub-tasks
|
|
8
|
+
2. GATHER — collect evidence from web, graph, documents, email
|
|
9
|
+
3. SYNTHESIZE — cross-reference and extract insights
|
|
10
|
+
4. OUTLINE — build structured artifact outline
|
|
11
|
+
5. PRODUCE — render artifact in requested format
|
|
12
|
+
6. REVIEW — quality and safety gate
|
|
13
|
+
|
|
14
|
+
Usage::
|
|
15
|
+
|
|
16
|
+
from apsimo.research import ResearchPipeline, ArtifactFormat
|
|
17
|
+
|
|
18
|
+
pipeline = ResearchPipeline()
|
|
19
|
+
result = await pipeline.run(
|
|
20
|
+
goal="Research competitor pricing and produce a report",
|
|
21
|
+
format=ArtifactFormat.MARKDOWN,
|
|
22
|
+
)
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from apsimo.research.artifact import ArtifactFormat, Artifact
|
|
26
|
+
from apsimo.research.gatherer import EvidenceItem, EvidencePackage, SourceType
|
|
27
|
+
from apsimo.research.synthesizer import SynthesisReport
|
|
28
|
+
from apsimo.research.pipeline import ResearchPipeline, PipelineRun, PipelineStage, PipelineStatus
|
|
29
|
+
|
|
30
|
+
__all__ = [
|
|
31
|
+
"ResearchPipeline",
|
|
32
|
+
"PipelineRun",
|
|
33
|
+
"PipelineStage",
|
|
34
|
+
"PipelineStatus",
|
|
35
|
+
"ArtifactFormat",
|
|
36
|
+
"Artifact",
|
|
37
|
+
"EvidenceItem",
|
|
38
|
+
"EvidencePackage",
|
|
39
|
+
"SourceType",
|
|
40
|
+
"SynthesisReport",
|
|
41
|
+
]
|