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,214 @@
|
|
|
1
|
+
"""Inline per-turn introspection.
|
|
2
|
+
|
|
3
|
+
After a turn is synced, judge it with a local LLM and record any owed follow-up
|
|
4
|
+
directly — durable commitments and, above all, IMMEDIATE OWED DELIVERABLES ("text me
|
|
5
|
+
the result") — without depending on an external cognition subagent. This is the
|
|
6
|
+
sidecar-side counterpart to the event-based ``cognition.requested`` path: that path
|
|
7
|
+
emits an event for a host plugin to consume, which not every deployment wires up; this
|
|
8
|
+
path runs the judgment in-process against any OpenAI-compatible chat endpoint.
|
|
9
|
+
|
|
10
|
+
Disabled by default. Activated with ``COLONY_INTROSPECT_ENABLED=true`` and a configured
|
|
11
|
+
endpoint; deployment-agnostic (point ``COLONY_INTROSPECT_BASE_URL`` at a local model):
|
|
12
|
+
|
|
13
|
+
COLONY_INTROSPECT_ENABLED "true"|"false" (default false)
|
|
14
|
+
COLONY_INTROSPECT_BASE_URL OpenAI-compatible base, e.g. http://127.0.0.1:8000/v1
|
|
15
|
+
COLONY_INTROSPECT_MODEL model id the endpoint serves (required to run)
|
|
16
|
+
COLONY_INTROSPECT_API_KEY bearer token, blank for a local model
|
|
17
|
+
COLONY_INTROSPECT_TIMEOUT seconds (default 30)
|
|
18
|
+
COLONY_INTROSPECT_MAX_TOKENS response cap (default 400)
|
|
19
|
+
|
|
20
|
+
Because an inline call cannot use tools, it uses a focused JSON-only prompt and creates
|
|
21
|
+
the commitments itself.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
|
|
26
|
+
import json
|
|
27
|
+
import logging
|
|
28
|
+
import os
|
|
29
|
+
from typing import Any, Dict, List, Optional
|
|
30
|
+
|
|
31
|
+
import httpx
|
|
32
|
+
|
|
33
|
+
logger = logging.getLogger(__name__)
|
|
34
|
+
|
|
35
|
+
# Purpose-built, JSON-only prompt (the tool-calling COGNITION_SYSTEM_PROMPT is too hedged and
|
|
36
|
+
# mentions tools, which makes a no-think local model return nothing). Self-contained.
|
|
37
|
+
_SYSTEM_PROMPT = (
|
|
38
|
+
"You audit ONE finished assistant turn and extract any follow-up worth recording, as STRICT JSON.\n"
|
|
39
|
+
"You get what the person SAID and what the assistant REPLIED. Decide only from the literal words.\n\n"
|
|
40
|
+
"Record an item only when the turn clearly contains one of:\n"
|
|
41
|
+
"1. A DURABLE COMMITMENT — an explicit promise, obligation, or reminder to do something later "
|
|
42
|
+
"(\"remind me to X\", \"I'll get back to you on X\", \"follow up on X by Friday\", \"we need to fix X\").\n"
|
|
43
|
+
"2. An IMMEDIATE OWED DELIVERABLE — the person asked to be SENT something through a channel the reply "
|
|
44
|
+
"did NOT satisfy (email it, text a DIFFERENT number, send it to someone else, send it later), AND the "
|
|
45
|
+
"actual content to send is present in the exchange. IMPORTANT: in a chat the assistant's reply already "
|
|
46
|
+
"IS a message to the person, so a plain \"text me\"/\"message me\" is ALREADY satisfied — do NOT record "
|
|
47
|
+
"that; only record a deliverable for a genuinely different channel, recipient, or time.\n\n"
|
|
48
|
+
"Do NOT record small talk, questions, hypotheticals, vague intentions, or anything the reply already "
|
|
49
|
+
"fully handled. Fewer items beats wrong items.\n\n"
|
|
50
|
+
"Output ONLY a JSON array, nothing else (no prose, no markdown, no code fence). Empty array [] when "
|
|
51
|
+
"nothing qualifies. Each element:\n"
|
|
52
|
+
'{"description": string, "due_at": ISO-8601-UTC string or null, "priority": integer 0-100, '
|
|
53
|
+
'"source_type": "cognition" | "introspection", "metadata": null or '
|
|
54
|
+
'{"kind":"deliverable","content":"<exact text to send, ready as-is>","channel_hint":"sms"|"dm"|"email"}}\n'
|
|
55
|
+
"Use \"introspection\" + the deliverable metadata (due_at about two minutes from now) for case 2; "
|
|
56
|
+
"\"cognition\" + metadata null for case 1.\n\n"
|
|
57
|
+
"Examples:\n"
|
|
58
|
+
"They said: Remind me to call the dentist Friday at 9am. | Assistant replied: Got it.\n"
|
|
59
|
+
'[{"description":"Remind them to call the dentist Friday 9am","due_at":"2026-06-26T13:00:00+00:00",'
|
|
60
|
+
'"priority":70,"source_type":"cognition","metadata":null}]\n'
|
|
61
|
+
"They said: Email me the Q3 revenue number. | Assistant replied: Q3 revenue was 4.2 million.\n"
|
|
62
|
+
'[{"description":"Email them the Q3 revenue","due_at":"2026-06-21T21:40:00+00:00","priority":80,'
|
|
63
|
+
'"source_type":"introspection","metadata":{"kind":"deliverable","content":"Q3 revenue was 4.2 million.",'
|
|
64
|
+
'"channel_hint":"email"}}]\n'
|
|
65
|
+
"They said: What's the weather? | Assistant replied: 72 and sunny.\n"
|
|
66
|
+
"[]\n"
|
|
67
|
+
"They said: Text me that. | Assistant replied: The address is 5 Main St.\n"
|
|
68
|
+
"[] (a plain text-me in chat is already satisfied by the reply)")
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _build_user_prompt(user_message: str, assistant_message: str,
|
|
72
|
+
conversation_text: str, existing: list,
|
|
73
|
+
rejections: Optional[list] = None) -> str:
|
|
74
|
+
parts = []
|
|
75
|
+
if conversation_text:
|
|
76
|
+
parts.append(f"Recent conversation:\n{conversation_text}\n")
|
|
77
|
+
parts.append("This turn, verbatim:\n"
|
|
78
|
+
f" They said: {user_message}\n"
|
|
79
|
+
f" Assistant replied: {assistant_message}\n")
|
|
80
|
+
if existing:
|
|
81
|
+
parts.append("\nAlready-recorded OPEN items for this person (do not duplicate; "
|
|
82
|
+
"mentioning an open item again is NOT a new item):")
|
|
83
|
+
for c in existing[:6]:
|
|
84
|
+
parts.append(f"\n- {(c.get('description') or '?')[:80]}")
|
|
85
|
+
if rejections:
|
|
86
|
+
parts.append("\n\nRecently REJECTED items (judged invalid or duplicate — do NOT "
|
|
87
|
+
"record these or anything similar again):")
|
|
88
|
+
for r in rejections[:6]:
|
|
89
|
+
parts.append(f"\n- {(r.get('description') or '?')[:80]} "
|
|
90
|
+
f"[{r.get('outcome') or 'rejected'}]")
|
|
91
|
+
return "".join(parts)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def introspect_enabled() -> bool:
|
|
95
|
+
from apsimo.util.autonomy_preset import resolve_bool
|
|
96
|
+
return resolve_bool("COLONY_INTROSPECT_ENABLED", False)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _config() -> Dict[str, Any]:
|
|
100
|
+
return {
|
|
101
|
+
"base_url": os.environ.get("COLONY_INTROSPECT_BASE_URL", "http://127.0.0.1:8000/v1").rstrip("/"),
|
|
102
|
+
"model": os.environ.get("COLONY_INTROSPECT_MODEL", "").strip(),
|
|
103
|
+
"api_key": os.environ.get("COLONY_INTROSPECT_API_KEY", "").strip(),
|
|
104
|
+
"timeout": float(os.environ.get("COLONY_INTROSPECT_TIMEOUT", "30")),
|
|
105
|
+
"max_tokens": int(os.environ.get("COLONY_INTROSPECT_MAX_TOKENS", "400")),
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _parse_json_array(text: str) -> List[Dict[str, Any]]:
|
|
112
|
+
"""Pull the first JSON array out of the model's reply; [] on anything malformed."""
|
|
113
|
+
if not text:
|
|
114
|
+
return []
|
|
115
|
+
try:
|
|
116
|
+
chunk = text[text.index("["): text.rindex("]") + 1]
|
|
117
|
+
data = json.loads(chunk)
|
|
118
|
+
return [x for x in data if isinstance(x, dict)] if isinstance(data, list) else []
|
|
119
|
+
except Exception:
|
|
120
|
+
return []
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
async def run_turn_introspection(
|
|
124
|
+
*,
|
|
125
|
+
user_message: str,
|
|
126
|
+
assistant_message: str,
|
|
127
|
+
conversation_text: str,
|
|
128
|
+
person_id: str,
|
|
129
|
+
existing_commitments: Optional[List[Dict[str, Any]]],
|
|
130
|
+
commitment_store: Any,
|
|
131
|
+
recent_rejections: Optional[List[Dict[str, Any]]] = None,
|
|
132
|
+
) -> Dict[str, Any]:
|
|
133
|
+
"""Judge one completed turn and record any owed follow-ups as commitments.
|
|
134
|
+
|
|
135
|
+
Best-effort and exception-safe: a failure here never affects the turn. Returns a small
|
|
136
|
+
dict describing what it did, mostly for tests/logging.
|
|
137
|
+
|
|
138
|
+
Dedup is enforced in code, not just in the prompt: a candidate matching an
|
|
139
|
+
existing OPEN item, or a recently REJECTED one, is skipped. Without this,
|
|
140
|
+
every conversational mention of an overdue item re-records it and resolving
|
|
141
|
+
one copy leaves another — the "resolve does nothing" loop.
|
|
142
|
+
"""
|
|
143
|
+
cfg = _config()
|
|
144
|
+
if not cfg["model"] or commitment_store is None:
|
|
145
|
+
return {"ok": False, "reason": "introspection not configured"}
|
|
146
|
+
if not (user_message or assistant_message or conversation_text):
|
|
147
|
+
return {"ok": False, "reason": "empty turn"}
|
|
148
|
+
|
|
149
|
+
user_prompt = _build_user_prompt(
|
|
150
|
+
user_message or "", assistant_message or "", conversation_text or "",
|
|
151
|
+
existing_commitments or [], recent_rejections or [])
|
|
152
|
+
|
|
153
|
+
try:
|
|
154
|
+
async with httpx.AsyncClient(timeout=cfg["timeout"]) as client:
|
|
155
|
+
resp = await client.post(
|
|
156
|
+
cfg["base_url"] + "/chat/completions",
|
|
157
|
+
headers={"Authorization": f"Bearer {cfg['api_key'] or 'x'}",
|
|
158
|
+
"Content-Type": "application/json"},
|
|
159
|
+
json={
|
|
160
|
+
"model": cfg["model"], "temperature": 0.1, "max_tokens": cfg["max_tokens"],
|
|
161
|
+
"messages": [{"role": "system", "content": _SYSTEM_PROMPT},
|
|
162
|
+
{"role": "user", "content": user_prompt}],
|
|
163
|
+
},
|
|
164
|
+
)
|
|
165
|
+
resp.raise_for_status()
|
|
166
|
+
content = (resp.json()["choices"][0]["message"]["content"] or "").strip()
|
|
167
|
+
except Exception as exc:
|
|
168
|
+
logger.warning("introspection LLM call failed: %s", exc)
|
|
169
|
+
return {"ok": False, "reason": str(exc)}
|
|
170
|
+
|
|
171
|
+
items = _parse_json_array(content)
|
|
172
|
+
created: List[str] = []
|
|
173
|
+
skipped_duplicates = 0
|
|
174
|
+
from apsimo.commitments.store import _normalize_desc, _similar_desc
|
|
175
|
+
_known = [_normalize_desc(c.get("description") or "")
|
|
176
|
+
for c in (existing_commitments or [])]
|
|
177
|
+
_known += [_normalize_desc(r.get("description") or "")
|
|
178
|
+
for r in (recent_rejections or [])]
|
|
179
|
+
_known = [k for k in _known if k]
|
|
180
|
+
for it in items:
|
|
181
|
+
desc = str(it.get("description") or "").strip()
|
|
182
|
+
if not desc:
|
|
183
|
+
continue
|
|
184
|
+
norm = _normalize_desc(desc)
|
|
185
|
+
if any(_similar_desc(norm, k) for k in _known):
|
|
186
|
+
skipped_duplicates += 1
|
|
187
|
+
continue
|
|
188
|
+
try:
|
|
189
|
+
row = commitment_store.create(
|
|
190
|
+
person_id=person_id,
|
|
191
|
+
description=desc[:1000],
|
|
192
|
+
dedupe=True,
|
|
193
|
+
due_at=(it.get("due_at") or None),
|
|
194
|
+
priority=int(it.get("priority") or 60),
|
|
195
|
+
source_type=(it.get("source_type") or "introspection"),
|
|
196
|
+
source_context="inline turn introspection",
|
|
197
|
+
metadata=(it.get("metadata") if isinstance(it.get("metadata"), dict) else None),
|
|
198
|
+
)
|
|
199
|
+
if row.get("deduped"):
|
|
200
|
+
skipped_duplicates += 1
|
|
201
|
+
else:
|
|
202
|
+
created.append(row.get("id"))
|
|
203
|
+
_known.append(norm) # a later candidate in the same batch can also dupe this one
|
|
204
|
+
except Exception as exc:
|
|
205
|
+
# e.g. a non-future due_at is rejected by the store — skip that one, keep going.
|
|
206
|
+
logger.debug("introspection commitment skipped: %s", exc)
|
|
207
|
+
|
|
208
|
+
if created:
|
|
209
|
+
logger.info("introspection recorded %d commitment(s) for %s", len(created), person_id)
|
|
210
|
+
if skipped_duplicates:
|
|
211
|
+
logger.info("introspection skipped %d duplicate candidate(s) for %s",
|
|
212
|
+
skipped_duplicates, person_id)
|
|
213
|
+
return {"ok": True, "created": created, "candidates": len(items),
|
|
214
|
+
"skipped_duplicates": skipped_duplicates}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
"""Cognition prompt templates for Colony's background thinking.
|
|
2
|
+
|
|
3
|
+
The cognition prompt tells the subagent what Colony can do and how to
|
|
4
|
+
think about the context it receives. It is deliberately conservative:
|
|
5
|
+
false negatives are preferred over false positives.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from typing import Any, Dict, Optional
|
|
11
|
+
|
|
12
|
+
from apsimo.cognition.charter import build_system_prompt
|
|
13
|
+
|
|
14
|
+
# The detailed observation guide: worked positive/negative examples for the
|
|
15
|
+
# observer role. Injected as <context> on top of the shared charter, which
|
|
16
|
+
# carries identity, doctrine, and the conservatism rules.
|
|
17
|
+
_GUIDE = """\
|
|
18
|
+
You have access to Apsimo's API through HTTP endpoints running on the same \
|
|
19
|
+
host. Use the tools available to you to take action when appropriate.
|
|
20
|
+
|
|
21
|
+
## What to Look For
|
|
22
|
+
|
|
23
|
+
### 1. COMMITMENTS
|
|
24
|
+
Any explicit promise, follow-up, or obligation expressed in conversation.
|
|
25
|
+
|
|
26
|
+
Examples of commitments:
|
|
27
|
+
- "I'll check on that tomorrow" → commitment with due date tomorrow
|
|
28
|
+
- "Let me get back to you about X" → commitment, no specific deadline
|
|
29
|
+
- "Remind me to call Sarah" → commitment with implicit deadline
|
|
30
|
+
- "I should follow up on the PR review" → commitment, self-directed
|
|
31
|
+
- "We need to fix that bug by Friday" → commitment with deadline
|
|
32
|
+
|
|
33
|
+
NOT commitments (do not record):
|
|
34
|
+
- Casual future tense without obligation ("I might look into it")
|
|
35
|
+
- Hypotheticals ("What if we tried X?")
|
|
36
|
+
- General intentions without specificity ("I want to be better about Y")
|
|
37
|
+
- Questions about future events ("When is the meeting?")
|
|
38
|
+
|
|
39
|
+
When recording a commitment:
|
|
40
|
+
- Use the person who made the promise as person_id
|
|
41
|
+
- Include the specific due date if mentioned, otherwise leave null
|
|
42
|
+
- Set source_type to "cognition"
|
|
43
|
+
- Set priority: urgent/critical promises = 70-90, casual = 40-60
|
|
44
|
+
|
|
45
|
+
### 2. IMMEDIATE OWED DELIVERABLES
|
|
46
|
+
Something the person asked to be DELIVERED to them in THIS just-finished exchange that
|
|
47
|
+
was NOT actually done in it. The classic case: they asked you to send/text/email/show them
|
|
48
|
+
the result, a link, a file, a summary, or a number, and the answer was only spoken or never
|
|
49
|
+
sent. This is distinct from a commitment: it is owed NOW, not on a future date.
|
|
50
|
+
|
|
51
|
+
Examples of immediate owed deliverables:
|
|
52
|
+
- "text me the findings" / "send me that summary" / "shoot me his number" → deliverable, send now
|
|
53
|
+
- "email me the link" → deliverable via email
|
|
54
|
+
- "send me the chart you described" → deliverable
|
|
55
|
+
|
|
56
|
+
NOT deliverables (do not record):
|
|
57
|
+
- The assistant ALREADY sent/did it in this exchange (check the assistant's message first)
|
|
58
|
+
- The person only wanted to KNOW or be TOLD something (answering aloud already satisfied it)
|
|
59
|
+
- A vague or future "send it over sometime" (that is a commitment, not an immediate deliverable)
|
|
60
|
+
|
|
61
|
+
When recording an immediate owed deliverable, use commitment_create with:
|
|
62
|
+
- person_id = the person who is owed it
|
|
63
|
+
- source_type = "introspection"
|
|
64
|
+
- due_at = about two minutes from now (it is owed now; the store rejects a past due_at)
|
|
65
|
+
- priority = 75-90 (they are waiting on it)
|
|
66
|
+
- description = "Deliver to <person>: <short label>"
|
|
67
|
+
- metadata = {"kind": "deliverable", "content": "<the exact, self-contained thing to send,
|
|
68
|
+
ready to send as-is, drawn from this exchange>", "channel_hint": "sms" | "dm" | "email"
|
|
69
|
+
as they asked}
|
|
70
|
+
The "content" must be the real material to send, not a description of it. If you cannot
|
|
71
|
+
recover the actual content from this exchange, do not record a deliverable.
|
|
72
|
+
|
|
73
|
+
### 3. When NOT to Act
|
|
74
|
+
- Casual small talk with no actionable content
|
|
75
|
+
- Information exchange with no commitments or owed deliverables
|
|
76
|
+
- Jokes, humor, or social pleasantries
|
|
77
|
+
- Already-recorded commitments (check before creating duplicates)
|
|
78
|
+
- Anything the assistant already handled in this same exchange
|
|
79
|
+
|
|
80
|
+
## Critical Rules
|
|
81
|
+
|
|
82
|
+
1. **Fewer actions are better than wrong actions.** If you are unsure whether \
|
|
83
|
+
something is a commitment, do not record it.
|
|
84
|
+
2. **Do not create duplicate commitments.** Check existing commitments before \
|
|
85
|
+
creating new ones.
|
|
86
|
+
3. **Be specific in descriptions.** "Check DGX cluster status by Friday" is \
|
|
87
|
+
better than "follow up on something."
|
|
88
|
+
4. **Only record commitments when there is clear intent.** Maybes and mights \
|
|
89
|
+
are not commitments.
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
COGNITION_SYSTEM_PROMPT = build_system_prompt("observer", extra=_GUIDE)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def build_cognition_prompt(
|
|
96
|
+
trigger_type: str,
|
|
97
|
+
context: Dict[str, Any],
|
|
98
|
+
existing_commitments: Optional[list] = None,
|
|
99
|
+
) -> str:
|
|
100
|
+
"""Build the user prompt for a cognition trigger.
|
|
101
|
+
|
|
102
|
+
Args:
|
|
103
|
+
trigger_type: Type of trigger (turn_sync, signal_ingest, anomaly, manual).
|
|
104
|
+
context: Trigger-specific context (conversation text, person_id, etc.).
|
|
105
|
+
existing_commitments: Current pending commitments for context.
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
Formatted prompt string for the subagent.
|
|
109
|
+
"""
|
|
110
|
+
parts = [f"Trigger type: {trigger_type}\n"]
|
|
111
|
+
|
|
112
|
+
if trigger_type == "turn_sync":
|
|
113
|
+
conversation_text = context.get("conversation_text", "")
|
|
114
|
+
person_id = context.get("person_id", "unknown")
|
|
115
|
+
parts.append(f"Contact: {person_id}\n")
|
|
116
|
+
parts.append(f"Recent conversation:\n{conversation_text}\n")
|
|
117
|
+
# Verbatim last turn (when available) so an owed deliverable and whether the
|
|
118
|
+
# assistant ALREADY did it can both be judged from the actual words.
|
|
119
|
+
user_msg = context.get("user_message", "")
|
|
120
|
+
asst_msg = context.get("assistant_message", "")
|
|
121
|
+
if user_msg or asst_msg:
|
|
122
|
+
parts.append(
|
|
123
|
+
"\nLast turn, verbatim (use this to tell if the person asked to be "
|
|
124
|
+
"sent something and whether the assistant already did it):\n"
|
|
125
|
+
f" They said: {user_msg}\n"
|
|
126
|
+
f" Assistant replied: {asst_msg}\n"
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
elif trigger_type == "signal_ingest":
|
|
130
|
+
signal_type = context.get("signal_type", "unknown")
|
|
131
|
+
signal_data = context.get("signal_data", {})
|
|
132
|
+
parts.append(f"Signal type: {signal_type}\n")
|
|
133
|
+
parts.append(f"Signal data: {signal_data}\n")
|
|
134
|
+
|
|
135
|
+
elif trigger_type == "anomaly":
|
|
136
|
+
anomaly_description = context.get("description", "Unknown anomaly")
|
|
137
|
+
parts.append(f"Anomaly detected: {anomaly_description}\n")
|
|
138
|
+
|
|
139
|
+
elif trigger_type == "manual":
|
|
140
|
+
manual_prompt = context.get("prompt", "")
|
|
141
|
+
parts.append(f"Manual cognition request: {manual_prompt}\n")
|
|
142
|
+
|
|
143
|
+
# Include existing commitments so the subagent can avoid duplicates
|
|
144
|
+
if existing_commitments:
|
|
145
|
+
parts.append("\nExisting pending commitments for this contact:\n")
|
|
146
|
+
for c in existing_commitments[:5]:
|
|
147
|
+
due = f" (due {c.get('due_at', 'no deadline')[:10]})" if c.get("due_at") else ""
|
|
148
|
+
parts.append(f"- {c.get('description', '?')}{due}\n")
|
|
149
|
+
|
|
150
|
+
return "".join(parts)
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"""Versioned deployment contract for the P3 cognition spine.
|
|
2
|
+
|
|
3
|
+
The feature flags used by the older cognition slices are independent. That
|
|
4
|
+
is useful for canaries, but it also made unsafe combinations representable --
|
|
5
|
+
for example a live P3 consuming concerns manufactured by a shadow reducer.
|
|
6
|
+
This module turns the flags into one small, inspectable lattice. It does not
|
|
7
|
+
enable any producer and it never widens a requested mode.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from dataclasses import asdict, dataclass
|
|
13
|
+
import hashlib
|
|
14
|
+
import json
|
|
15
|
+
from typing import Any, Mapping, Optional
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
_MODES = {"off", "shadow", "live"}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _mode(value: Any) -> str:
|
|
22
|
+
normalized = str(value or "off").strip().lower()
|
|
23
|
+
return normalized if normalized in _MODES else "off"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _digest(value: Mapping[str, Any]) -> str:
|
|
27
|
+
encoded = json.dumps(
|
|
28
|
+
dict(value), sort_keys=True, separators=(",", ":"), ensure_ascii=True,
|
|
29
|
+
).encode("utf-8")
|
|
30
|
+
return hashlib.sha256(encoded).hexdigest()
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclass(frozen=True)
|
|
34
|
+
class CognitionRuntimeContractV1:
|
|
35
|
+
"""Attached/effective mode composition and its immutable revision."""
|
|
36
|
+
|
|
37
|
+
requested_mode: str
|
|
38
|
+
effective_mode: str
|
|
39
|
+
workspace_mode: str
|
|
40
|
+
event_concern_mode: str
|
|
41
|
+
drive_governance_mode: str
|
|
42
|
+
charter_revision_id: Optional[str]
|
|
43
|
+
blockers: tuple[str, ...]
|
|
44
|
+
revision: str
|
|
45
|
+
schema: str = "CognitionRuntimeContractV1"
|
|
46
|
+
version: int = 1
|
|
47
|
+
|
|
48
|
+
@classmethod
|
|
49
|
+
def compose(
|
|
50
|
+
cls,
|
|
51
|
+
*,
|
|
52
|
+
requested_mode: str,
|
|
53
|
+
workspace_mode: str,
|
|
54
|
+
event_concern_mode: str,
|
|
55
|
+
drive_governance_mode: str,
|
|
56
|
+
charter_revision_id: Optional[str] = None,
|
|
57
|
+
charter_store_attached: bool = False,
|
|
58
|
+
attachment_blockers: tuple[str, ...] = (),
|
|
59
|
+
) -> "CognitionRuntimeContractV1":
|
|
60
|
+
requested = _mode(requested_mode)
|
|
61
|
+
workspace = _mode(workspace_mode)
|
|
62
|
+
events = _mode(event_concern_mode)
|
|
63
|
+
drives = _mode(drive_governance_mode)
|
|
64
|
+
blockers = list(dict.fromkeys(str(item) for item in attachment_blockers))
|
|
65
|
+
|
|
66
|
+
if requested == "live":
|
|
67
|
+
if workspace != "live":
|
|
68
|
+
blockers.append(f"workspace_mode_{workspace}_cannot_feed_live_p3")
|
|
69
|
+
# Event provenance is checked on the exact Concern. Holding the
|
|
70
|
+
# whole runtime because an optional shadow reducer is attached
|
|
71
|
+
# would also stop separately-produced live concerns. Likewise P7
|
|
72
|
+
# shadow is an observer and must not change P3 admission.
|
|
73
|
+
if drives == "live":
|
|
74
|
+
if not charter_store_attached:
|
|
75
|
+
blockers.append("live_charter_store_not_attached")
|
|
76
|
+
elif not charter_revision_id:
|
|
77
|
+
blockers.append("active_owner_ratified_charter_required")
|
|
78
|
+
|
|
79
|
+
effective = requested
|
|
80
|
+
if requested != "off" and blockers:
|
|
81
|
+
effective = "held"
|
|
82
|
+
authority = {
|
|
83
|
+
"schema": "CognitionRuntimeContractV1",
|
|
84
|
+
"version": 1,
|
|
85
|
+
"requested_mode": requested,
|
|
86
|
+
"effective_mode": effective,
|
|
87
|
+
"workspace_mode": workspace,
|
|
88
|
+
"event_concern_mode": events,
|
|
89
|
+
"drive_governance_mode": drives,
|
|
90
|
+
"charter_revision_id": charter_revision_id,
|
|
91
|
+
"blockers": sorted(set(blockers)),
|
|
92
|
+
}
|
|
93
|
+
return cls(
|
|
94
|
+
requested_mode=requested,
|
|
95
|
+
effective_mode=effective,
|
|
96
|
+
workspace_mode=workspace,
|
|
97
|
+
event_concern_mode=events,
|
|
98
|
+
drive_governance_mode=drives,
|
|
99
|
+
charter_revision_id=charter_revision_id,
|
|
100
|
+
blockers=tuple(authority["blockers"]),
|
|
101
|
+
revision=f"cognition-runtime:{_digest(authority)[:24]}",
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
def payload(self) -> dict[str, Any]:
|
|
105
|
+
return asdict(self)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
__all__ = ["CognitionRuntimeContractV1"]
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"""Cognition trigger — spawns an OpenClaw subagent for background thinking.
|
|
2
|
+
|
|
3
|
+
Colony does not run its own LLM. It sends cognition requests to OpenClaw
|
|
4
|
+
via the subagent spawn API, which handles model routing, concurrency,
|
|
5
|
+
and token budgeting.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import logging
|
|
11
|
+
import os
|
|
12
|
+
import time
|
|
13
|
+
from typing import Any, Dict, Optional
|
|
14
|
+
|
|
15
|
+
from apsimo.cognition.prompt import COGNITION_SYSTEM_PROMPT, build_cognition_prompt
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
# Module-level throttle state
|
|
20
|
+
_last_trigger_time: float = 0.0
|
|
21
|
+
_last_trigger_session: Optional[str] = None
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _cognition_enabled() -> bool:
|
|
25
|
+
from apsimo.util.autonomy_preset import resolve_bool
|
|
26
|
+
return resolve_bool("COLONY_COGNITION_ENABLED", False)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _cognition_model() -> Optional[str]:
|
|
30
|
+
return os.environ.get("COLONY_COGNITION_MODEL") or None
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _throttle_seconds() -> int:
|
|
34
|
+
try:
|
|
35
|
+
return int(os.environ.get("COLONY_COGNITION_THROTTLE_SECONDS", "30"))
|
|
36
|
+
except ValueError:
|
|
37
|
+
return 30
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _update_throttle(now: float, session_id: str) -> None:
|
|
41
|
+
global _last_trigger_time, _last_trigger_session
|
|
42
|
+
_last_trigger_time = now
|
|
43
|
+
_last_trigger_session = session_id
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
async def trigger_cognition(
|
|
47
|
+
trigger_type: str,
|
|
48
|
+
context: Dict[str, Any],
|
|
49
|
+
priority: str = "normal",
|
|
50
|
+
) -> Dict[str, Any]:
|
|
51
|
+
"""Fire a cognition trigger by spawning an OpenClaw subagent.
|
|
52
|
+
|
|
53
|
+
This is called from the API endpoint. It builds the cognition prompt,
|
|
54
|
+
checks throttling, and delegates to OpenClaw's sessions_spawn.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
trigger_type: turn_sync, signal_ingest, anomaly, or manual.
|
|
58
|
+
context: Trigger-specific context data.
|
|
59
|
+
priority: high, normal, or low.
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
Dict with accepted status and optional throttle info.
|
|
63
|
+
"""
|
|
64
|
+
if not _cognition_enabled():
|
|
65
|
+
return {
|
|
66
|
+
"accepted": False,
|
|
67
|
+
"message": "Cognition substrate is disabled",
|
|
68
|
+
"throttle_seconds": None,
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
model = _cognition_model()
|
|
72
|
+
if not model:
|
|
73
|
+
return {
|
|
74
|
+
"accepted": False,
|
|
75
|
+
"message": "COLONY_COGNITION_MODEL not configured",
|
|
76
|
+
"throttle_seconds": None,
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
# Throttle check (skip for high priority)
|
|
80
|
+
throttle_secs = _throttle_seconds()
|
|
81
|
+
now = time.time()
|
|
82
|
+
if priority != "high" and (now - _last_trigger_time) < throttle_secs:
|
|
83
|
+
remaining = int(throttle_secs - (now - _last_trigger_time))
|
|
84
|
+
return {
|
|
85
|
+
"accepted": True,
|
|
86
|
+
"message": f"Throttled — queued for {remaining}s",
|
|
87
|
+
"throttle_seconds": remaining,
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
# Get existing commitments for context (avoid duplicates)
|
|
91
|
+
existing = []
|
|
92
|
+
person_id = context.get("person_id")
|
|
93
|
+
if person_id:
|
|
94
|
+
try:
|
|
95
|
+
from apsimo.api.routers.host import _commitment_store
|
|
96
|
+
if _commitment_store is not None:
|
|
97
|
+
existing = _commitment_store.get_pending_for_person(person_id)
|
|
98
|
+
except Exception:
|
|
99
|
+
logger.debug("Failed to fetch existing commitments for cognition", exc_info=True)
|
|
100
|
+
|
|
101
|
+
# Build the prompt
|
|
102
|
+
user_prompt = build_cognition_prompt(
|
|
103
|
+
trigger_type=trigger_type,
|
|
104
|
+
context=context,
|
|
105
|
+
existing_commitments=existing,
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
# Update throttle timestamp
|
|
109
|
+
session_id = context.get("session_id", "")
|
|
110
|
+
_update_throttle(now, session_id)
|
|
111
|
+
|
|
112
|
+
# Emit cognition.requested event for the plugin to pick up
|
|
113
|
+
_emit_cognition_event(trigger_type, user_prompt, model, priority)
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
"accepted": True,
|
|
117
|
+
"message": "Cognition trigger fired",
|
|
118
|
+
"throttle_seconds": None,
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def _emit_cognition_event(
|
|
123
|
+
trigger_type: str,
|
|
124
|
+
prompt: str,
|
|
125
|
+
model: str,
|
|
126
|
+
priority: str,
|
|
127
|
+
) -> None:
|
|
128
|
+
"""Emit a cognition.requested event carrying a full spawn spec.
|
|
129
|
+
|
|
130
|
+
This is an OPTIONAL integration point: no shipped consumer spawns a
|
|
131
|
+
dedicated cognition session from it today (the hermes plugin only caches
|
|
132
|
+
events as context blurbs). The working per-turn judgment path is the
|
|
133
|
+
inline introspection in cognition/introspection.py; a deployment that
|
|
134
|
+
wants a real tool-restricted cognition session can subscribe to this
|
|
135
|
+
event and honor system_prompt/model/tools_allow. See docs/KNOWN-GAPS.md.
|
|
136
|
+
"""
|
|
137
|
+
try:
|
|
138
|
+
from apsimo.events.broadcaster import emit
|
|
139
|
+
emit("cognition.requested", {
|
|
140
|
+
"trigger_type": trigger_type,
|
|
141
|
+
"system_prompt": COGNITION_SYSTEM_PROMPT,
|
|
142
|
+
"user_prompt": prompt,
|
|
143
|
+
"model": model,
|
|
144
|
+
"priority": priority,
|
|
145
|
+
# Names match the registered agent tools so a consumer can pass
|
|
146
|
+
# this straight through as an allowlist.
|
|
147
|
+
"tools_allow": [
|
|
148
|
+
"colony_create_commitment",
|
|
149
|
+
"colony_list_commitments",
|
|
150
|
+
"colony_resolve_commitment",
|
|
151
|
+
],
|
|
152
|
+
})
|
|
153
|
+
except Exception:
|
|
154
|
+
logger.debug("Failed to emit cognition.requested event", exc_info=True)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""Commitment tracking for Colony.
|
|
2
|
+
|
|
3
|
+
Records, tracks, and surfaces promises made during conversations.
|
|
4
|
+
Commitments are created by the cognition substrate (automatic extraction),
|
|
5
|
+
the autonomy loop, or manual API calls.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from apsimo.commitments.store import (
|
|
9
|
+
CommitmentResolutionSchemaError,
|
|
10
|
+
CommitmentStore,
|
|
11
|
+
RESOLUTION_RECOVERY_CAPABILITY,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"CommitmentResolutionSchemaError",
|
|
16
|
+
"CommitmentStore",
|
|
17
|
+
"RESOLUTION_RECOVERY_CAPABILITY",
|
|
18
|
+
]
|