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,506 @@
|
|
|
1
|
+
"""ToolExecutor — dispatch tool calls and collect results.
|
|
2
|
+
|
|
3
|
+
The executor is pluggable: hosts can register tool handlers, or the
|
|
4
|
+
default executor can be used (which returns "not implemented" for
|
|
5
|
+
unknown tools, letting the host handle them client-side).
|
|
6
|
+
|
|
7
|
+
Colony-native tools are defined in tools/definitions.py and handlers
|
|
8
|
+
are in tools/handlers.py. These tools provide direct access to
|
|
9
|
+
Colony's intelligence systems (memory, goals, relationships, etc.)
|
|
10
|
+
without going through the host plugin.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import inspect
|
|
16
|
+
import json
|
|
17
|
+
import logging
|
|
18
|
+
import uuid
|
|
19
|
+
from collections.abc import Mapping
|
|
20
|
+
from typing import TYPE_CHECKING, Any, Callable, Coroutine
|
|
21
|
+
|
|
22
|
+
if TYPE_CHECKING:
|
|
23
|
+
from apsimo.autonomy.registry import SubsystemRegistry
|
|
24
|
+
|
|
25
|
+
from apsimo.tools.definitions import STATIC_TOOL_NAMES, get_tool_definitions
|
|
26
|
+
from apsimo.tools.handlers import TOOL_HANDLERS
|
|
27
|
+
from apsimo.reasoning.tool_policy import (
|
|
28
|
+
ToolActorPolicy,
|
|
29
|
+
ToolEffect,
|
|
30
|
+
action_target,
|
|
31
|
+
actor_allows_effect,
|
|
32
|
+
classify_tool,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
logger = logging.getLogger(__name__)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
# Type alias for tool handler functions
|
|
39
|
+
ToolHandler = Callable[..., Coroutine[Any, Any, str]]
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class ToolRegistryError(RuntimeError):
|
|
43
|
+
"""A dynamic tool surface could not be resolved without ambiguity."""
|
|
44
|
+
|
|
45
|
+
def __init__(self, code: str, message: str) -> None:
|
|
46
|
+
super().__init__(message)
|
|
47
|
+
self.code = code
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class ToolExecutor:
|
|
51
|
+
"""Dispatch tool calls and collect results.
|
|
52
|
+
|
|
53
|
+
Parameters
|
|
54
|
+
----------
|
|
55
|
+
handlers :
|
|
56
|
+
Optional mapping of tool name → async handler function.
|
|
57
|
+
Handlers receive the tool's arguments dict and must return
|
|
58
|
+
a string result.
|
|
59
|
+
registry :
|
|
60
|
+
Optional SubsystemRegistry for Colony-native tools. When provided,
|
|
61
|
+
Colony tools are automatically available.
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
def __init__(
|
|
65
|
+
self,
|
|
66
|
+
handlers: dict[str, ToolHandler] | None = None,
|
|
67
|
+
registry: SubsystemRegistry | None = None,
|
|
68
|
+
graph_client = None,
|
|
69
|
+
) -> None:
|
|
70
|
+
self._handlers: dict[str, ToolHandler] = handlers or {}
|
|
71
|
+
self._registry = registry
|
|
72
|
+
self._graph = graph_client
|
|
73
|
+
# Dynamic tools (e.g. toolsmith-built): a provider returning
|
|
74
|
+
# {name: (openai_definition, async_handler)} consulted at call time
|
|
75
|
+
# so newly-graduated tools appear without re-instantiation.
|
|
76
|
+
self._dynamic_provider = None
|
|
77
|
+
self._directive_manager = None
|
|
78
|
+
self._boundary_required = False
|
|
79
|
+
|
|
80
|
+
# Auto-register Colony-native tool handlers if registry is provided
|
|
81
|
+
if registry is not None:
|
|
82
|
+
for name, handler in TOOL_HANDLERS.items():
|
|
83
|
+
if name not in self._handlers:
|
|
84
|
+
# Wrap handler to inject registry
|
|
85
|
+
self._handlers[name] = lambda args, h=handler, r=registry: h(args, r)
|
|
86
|
+
|
|
87
|
+
def register(self, name: str, handler: ToolHandler) -> None:
|
|
88
|
+
"""Register a tool handler."""
|
|
89
|
+
self._handlers[name] = handler
|
|
90
|
+
|
|
91
|
+
def register_native_tools(self, search_orchestrator=None, sandbox_dir: str = "") -> None:
|
|
92
|
+
"""Register Colony-native tools that run inside the sidecar.
|
|
93
|
+
|
|
94
|
+
Tool classes expose ``.execute(args) -> dict``; the executor's
|
|
95
|
+
handler contract is ``(args) -> Awaitable[str|dict]``, so we bind
|
|
96
|
+
the bound-method ``tool.execute`` directly — registering the bare
|
|
97
|
+
instance would fail because the tool classes are not callable.
|
|
98
|
+
"""
|
|
99
|
+
try:
|
|
100
|
+
from apsimo.reasoning.native_tools.calculate import CalculateTool
|
|
101
|
+
self.register("calculate", CalculateTool().execute)
|
|
102
|
+
except Exception as exc:
|
|
103
|
+
logger.warning("register calculate tool failed: %s", exc)
|
|
104
|
+
|
|
105
|
+
if search_orchestrator and search_orchestrator.has_providers:
|
|
106
|
+
try:
|
|
107
|
+
from apsimo.reasoning.native_tools.web_search import WebSearchTool
|
|
108
|
+
ws_tool = WebSearchTool(search_orchestrator)
|
|
109
|
+
self.register("web_search", ws_tool.execute)
|
|
110
|
+
except Exception as exc:
|
|
111
|
+
logger.warning("register web_search tool failed: %s", exc)
|
|
112
|
+
|
|
113
|
+
if sandbox_dir:
|
|
114
|
+
try:
|
|
115
|
+
from apsimo.reasoning.native_tools.file_ops import (
|
|
116
|
+
ReadFileTool, WriteFileTool, ListDirectoryTool,
|
|
117
|
+
)
|
|
118
|
+
self.register("read_file", ReadFileTool(sandbox_dir).execute)
|
|
119
|
+
self.register("write_file", WriteFileTool(sandbox_dir).execute)
|
|
120
|
+
self.register("list_directory", ListDirectoryTool(sandbox_dir).execute)
|
|
121
|
+
except Exception as exc:
|
|
122
|
+
logger.warning("register file_ops tools failed: %s", exc)
|
|
123
|
+
|
|
124
|
+
def unregister(self, name: str) -> None:
|
|
125
|
+
"""Remove a tool handler."""
|
|
126
|
+
self._handlers.pop(name, None)
|
|
127
|
+
|
|
128
|
+
def set_dynamic_provider(self, provider) -> None:
|
|
129
|
+
"""Register a provider callable returning a dict
|
|
130
|
+
{name: (openai_definition, async_handler)} of runtime tools
|
|
131
|
+
(e.g. toolsmith-graduated tools). Consulted on every turn."""
|
|
132
|
+
self._dynamic_provider = provider
|
|
133
|
+
|
|
134
|
+
def configure_execution_policy(
|
|
135
|
+
self,
|
|
136
|
+
*,
|
|
137
|
+
directive_manager,
|
|
138
|
+
boundary_required: bool = True,
|
|
139
|
+
) -> None:
|
|
140
|
+
"""Install the standing-boundary dependency for all executions.
|
|
141
|
+
|
|
142
|
+
Server startup uses ``boundary_required=True``. Tests and legacy
|
|
143
|
+
in-process embeddings that never configure a manager retain their
|
|
144
|
+
historical behavior; a configured live server does not silently run a
|
|
145
|
+
private/mutating tool when the boundary dependency is absent.
|
|
146
|
+
"""
|
|
147
|
+
|
|
148
|
+
self._directive_manager = directive_manager
|
|
149
|
+
self._boundary_required = bool(boundary_required)
|
|
150
|
+
|
|
151
|
+
def _dynamic_tools(self) -> dict[str, Any]:
|
|
152
|
+
if self._dynamic_provider is None:
|
|
153
|
+
return {}
|
|
154
|
+
try:
|
|
155
|
+
supplied = self._dynamic_provider()
|
|
156
|
+
except Exception as exc:
|
|
157
|
+
logger.warning("dynamic tool provider failed: %s", exc)
|
|
158
|
+
raise ToolRegistryError(
|
|
159
|
+
"tool_registry_unavailable",
|
|
160
|
+
"dynamic tool provider is unavailable",
|
|
161
|
+
) from exc
|
|
162
|
+
if supplied is None:
|
|
163
|
+
return {}
|
|
164
|
+
if not isinstance(supplied, Mapping):
|
|
165
|
+
raise ToolRegistryError(
|
|
166
|
+
"tool_registry_malformed",
|
|
167
|
+
"dynamic tool provider returned a malformed registry",
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
dynamic = dict(supplied)
|
|
171
|
+
names = set()
|
|
172
|
+
for name in dynamic:
|
|
173
|
+
if (
|
|
174
|
+
not isinstance(name, str)
|
|
175
|
+
or not name
|
|
176
|
+
or name.strip() != name
|
|
177
|
+
):
|
|
178
|
+
raise ToolRegistryError(
|
|
179
|
+
"tool_registry_malformed",
|
|
180
|
+
"dynamic tool provider returned an invalid name",
|
|
181
|
+
)
|
|
182
|
+
names.add(name)
|
|
183
|
+
|
|
184
|
+
collisions = names.intersection(
|
|
185
|
+
set(STATIC_TOOL_NAMES).union(self._handlers)
|
|
186
|
+
)
|
|
187
|
+
if collisions:
|
|
188
|
+
listed = ", ".join(sorted(collisions))
|
|
189
|
+
logger.error("dynamic/static tool name collision: %s", listed)
|
|
190
|
+
raise ToolRegistryError(
|
|
191
|
+
"tool_name_collision",
|
|
192
|
+
f"dynamic tool name collides with first-party tool: {listed}",
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
validated: dict[str, Any] = {}
|
|
196
|
+
for name, value in dynamic.items():
|
|
197
|
+
if not isinstance(value, (tuple, list)) or len(value) != 2:
|
|
198
|
+
raise ToolRegistryError(
|
|
199
|
+
"tool_registry_malformed",
|
|
200
|
+
f"dynamic tool '{name}' has a malformed provider entry",
|
|
201
|
+
)
|
|
202
|
+
definition, handler = value
|
|
203
|
+
if (
|
|
204
|
+
self._definition_name(definition) != name
|
|
205
|
+
or not inspect.iscoroutinefunction(handler)
|
|
206
|
+
):
|
|
207
|
+
raise ToolRegistryError(
|
|
208
|
+
"tool_registry_malformed",
|
|
209
|
+
f"dynamic tool '{name}' has invalid definition provenance",
|
|
210
|
+
)
|
|
211
|
+
validated[name] = (definition, handler)
|
|
212
|
+
return validated
|
|
213
|
+
|
|
214
|
+
@staticmethod
|
|
215
|
+
def _definition_name(definition: object) -> str | None:
|
|
216
|
+
if not isinstance(definition, Mapping):
|
|
217
|
+
return None
|
|
218
|
+
if definition.get("type") == "function":
|
|
219
|
+
function = definition.get("function")
|
|
220
|
+
if not isinstance(function, Mapping):
|
|
221
|
+
return None
|
|
222
|
+
name = function.get("name")
|
|
223
|
+
else:
|
|
224
|
+
name = definition.get("name")
|
|
225
|
+
return name if isinstance(name, str) and name else None
|
|
226
|
+
|
|
227
|
+
def _resolved_handler(
|
|
228
|
+
self,
|
|
229
|
+
name: str,
|
|
230
|
+
dynamic: Mapping[str, Any],
|
|
231
|
+
) -> tuple[ToolHandler | None, ToolEffect | None, str | None]:
|
|
232
|
+
"""Resolve executable and provenance before any authority decision."""
|
|
233
|
+
|
|
234
|
+
handler = self._handlers.get(name)
|
|
235
|
+
if handler is not None:
|
|
236
|
+
return handler, classify_tool(name), "static"
|
|
237
|
+
supplied = dynamic.get(name)
|
|
238
|
+
if supplied is not None:
|
|
239
|
+
# Dynamic capabilities are always mutations. Their human-selected
|
|
240
|
+
# names are never an authorization type declaration.
|
|
241
|
+
return supplied[1], ToolEffect.MUTATION, "dynamic"
|
|
242
|
+
return None, None, None
|
|
243
|
+
|
|
244
|
+
def available_names(self) -> list[str]:
|
|
245
|
+
"""Return a stable, de-duplicated snapshot of executable tool names."""
|
|
246
|
+
|
|
247
|
+
dynamic = self._dynamic_tools()
|
|
248
|
+
return list(dict.fromkeys((
|
|
249
|
+
*self._handlers.keys(),
|
|
250
|
+
*dynamic.keys(),
|
|
251
|
+
)))
|
|
252
|
+
|
|
253
|
+
def filter_names_for_actor(
|
|
254
|
+
self,
|
|
255
|
+
names: list[str] | tuple[str, ...] | set[str] | frozenset[str] | None,
|
|
256
|
+
actor_policy: ToolActorPolicy | None,
|
|
257
|
+
) -> list[str]:
|
|
258
|
+
"""Filter one registry snapshot using resolved handler provenance."""
|
|
259
|
+
|
|
260
|
+
dynamic = self._dynamic_tools()
|
|
261
|
+
requested = (
|
|
262
|
+
(*self._handlers.keys(), *dynamic.keys())
|
|
263
|
+
if names is None else names
|
|
264
|
+
)
|
|
265
|
+
allowed = []
|
|
266
|
+
for name in dict.fromkeys(requested):
|
|
267
|
+
handler, effect, _provenance = self._resolved_handler(name, dynamic)
|
|
268
|
+
if (
|
|
269
|
+
handler is not None
|
|
270
|
+
and effect is not None
|
|
271
|
+
and actor_allows_effect(effect, actor_policy)
|
|
272
|
+
):
|
|
273
|
+
allowed.append(name)
|
|
274
|
+
return allowed
|
|
275
|
+
|
|
276
|
+
def _boundary_verdict(
|
|
277
|
+
self,
|
|
278
|
+
name: str,
|
|
279
|
+
arguments: dict[str, Any],
|
|
280
|
+
effect: ToolEffect,
|
|
281
|
+
) -> tuple[bool, str | None, str]:
|
|
282
|
+
"""Return ``(allowed, error_code, reason)`` for standing boundaries."""
|
|
283
|
+
|
|
284
|
+
manager = self._directive_manager
|
|
285
|
+
if manager is None:
|
|
286
|
+
# Keep general information tools usable during a boundary-store
|
|
287
|
+
# outage. Private reads and mutations fail closed on a configured
|
|
288
|
+
# server because they can expose or alter durable owner state.
|
|
289
|
+
if not self._boundary_required or effect is ToolEffect.PUBLIC_READ:
|
|
290
|
+
return True, None, "boundary_not_required"
|
|
291
|
+
return False, "tool_boundary_unavailable", "directive manager unavailable"
|
|
292
|
+
|
|
293
|
+
try:
|
|
294
|
+
from apsimo.directives import Action
|
|
295
|
+
|
|
296
|
+
verdict = manager.check(Action(
|
|
297
|
+
kind=(
|
|
298
|
+
"read"
|
|
299
|
+
if effect in {ToolEffect.PUBLIC_READ, ToolEffect.PRIVATE_READ}
|
|
300
|
+
else "execute_tool"
|
|
301
|
+
),
|
|
302
|
+
text=f"reasoning tool {name}",
|
|
303
|
+
target=action_target(arguments),
|
|
304
|
+
tool_name=name,
|
|
305
|
+
args=dict(arguments),
|
|
306
|
+
high_risk=(effect is ToolEffect.MUTATION),
|
|
307
|
+
))
|
|
308
|
+
except Exception as exc:
|
|
309
|
+
logger.warning("ToolExecutor: boundary check failed for '%s': %s", name, exc)
|
|
310
|
+
if effect is ToolEffect.PUBLIC_READ:
|
|
311
|
+
return True, None, "public_read_boundary_unavailable"
|
|
312
|
+
return False, "tool_boundary_unavailable", "directive check unavailable"
|
|
313
|
+
|
|
314
|
+
allowed = getattr(verdict, "allowed", None)
|
|
315
|
+
if not isinstance(allowed, bool):
|
|
316
|
+
logger.warning("ToolExecutor: malformed boundary verdict for '%s'", name)
|
|
317
|
+
if effect is ToolEffect.PUBLIC_READ:
|
|
318
|
+
return True, None, "public_read_boundary_malformed"
|
|
319
|
+
return False, "tool_boundary_unavailable", "malformed directive verdict"
|
|
320
|
+
if not allowed:
|
|
321
|
+
return (
|
|
322
|
+
False,
|
|
323
|
+
"tool_boundary_denied",
|
|
324
|
+
str(getattr(verdict, "reason", "standing owner boundary") or
|
|
325
|
+
"standing owner boundary"),
|
|
326
|
+
)
|
|
327
|
+
return True, None, str(getattr(verdict, "reason", "ok") or "ok")
|
|
328
|
+
|
|
329
|
+
@staticmethod
|
|
330
|
+
def _error_result(
|
|
331
|
+
tool_call_id: str,
|
|
332
|
+
*,
|
|
333
|
+
code: str,
|
|
334
|
+
message: str,
|
|
335
|
+
tool_name: str,
|
|
336
|
+
) -> dict[str, Any]:
|
|
337
|
+
return {
|
|
338
|
+
"tool_call_id": tool_call_id,
|
|
339
|
+
"content": json.dumps({
|
|
340
|
+
"error": code,
|
|
341
|
+
"message": message,
|
|
342
|
+
"tool_name": tool_name,
|
|
343
|
+
}),
|
|
344
|
+
"executed": False,
|
|
345
|
+
"error": code,
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
def get_definitions(self, available_tools: list[str] | None = None) -> list[dict[str, Any]]:
|
|
349
|
+
"""Build OpenAI-format tool definitions for the LLM call.
|
|
350
|
+
|
|
351
|
+
Returns Colony-native tool definitions that can be executed server-side.
|
|
352
|
+
These are in addition to any host-side tools passed through the ReasoningLoop.
|
|
353
|
+
|
|
354
|
+
Parameters
|
|
355
|
+
----------
|
|
356
|
+
available_tools :
|
|
357
|
+
Optional filter for specific tool names to include. When None,
|
|
358
|
+
defaults to the set of registered handlers.
|
|
359
|
+
|
|
360
|
+
Returns
|
|
361
|
+
-------
|
|
362
|
+
List of OpenAI-format tool definitions.
|
|
363
|
+
"""
|
|
364
|
+
dynamic = self._dynamic_tools()
|
|
365
|
+
explicit_filter = available_tools is not None
|
|
366
|
+
names = list(dict.fromkeys(
|
|
367
|
+
available_tools if explicit_filter else self._handlers.keys()
|
|
368
|
+
))
|
|
369
|
+
definitions = get_tool_definitions(tool_names=names)
|
|
370
|
+
defs = []
|
|
371
|
+
seen = set()
|
|
372
|
+
for definition in definitions:
|
|
373
|
+
name = self._definition_name(definition)
|
|
374
|
+
if name is None:
|
|
375
|
+
raise ToolRegistryError(
|
|
376
|
+
"tool_registry_malformed",
|
|
377
|
+
"first-party tool definition is malformed",
|
|
378
|
+
)
|
|
379
|
+
# The static catalog is authoritative. Keep one stable definition
|
|
380
|
+
# if a future composition accidentally repeats a shipped entry.
|
|
381
|
+
if name not in seen:
|
|
382
|
+
defs.append(definition)
|
|
383
|
+
seen.add(name)
|
|
384
|
+
for name, (definition, _handler) in dynamic.items():
|
|
385
|
+
# a filter must name a dynamic tool to include it; unfiltered
|
|
386
|
+
# turns see all graduated tools
|
|
387
|
+
if definition and (not explicit_filter or name in names):
|
|
388
|
+
if name in seen:
|
|
389
|
+
raise ToolRegistryError(
|
|
390
|
+
"tool_name_collision",
|
|
391
|
+
f"model tool definition collision for '{name}'",
|
|
392
|
+
)
|
|
393
|
+
defs.append(definition)
|
|
394
|
+
seen.add(name)
|
|
395
|
+
return defs
|
|
396
|
+
|
|
397
|
+
async def execute_batch(
|
|
398
|
+
self,
|
|
399
|
+
tool_calls: list[dict[str, Any]],
|
|
400
|
+
*,
|
|
401
|
+
session_id: str = "",
|
|
402
|
+
allowed_tools: set[str] | frozenset[str] | None = None,
|
|
403
|
+
actor_policy: ToolActorPolicy | None = None,
|
|
404
|
+
) -> list[dict[str, Any]]:
|
|
405
|
+
"""Execute a batch of tool calls and return results.
|
|
406
|
+
|
|
407
|
+
Each result dict has:
|
|
408
|
+
- tool_call_id: the ID from the original tool call
|
|
409
|
+
- content: the string result
|
|
410
|
+
|
|
411
|
+
Unknown tools return a "not implemented" result rather than
|
|
412
|
+
raising, so the LLM can see the failure and adjust.
|
|
413
|
+
"""
|
|
414
|
+
results = []
|
|
415
|
+
try:
|
|
416
|
+
dynamic = self._dynamic_tools()
|
|
417
|
+
except ToolRegistryError as exc:
|
|
418
|
+
for tc in tool_calls:
|
|
419
|
+
tc_id = tc.get("id", str(uuid.uuid4()))
|
|
420
|
+
name = str(tc.get("name", "unknown"))
|
|
421
|
+
results.append(self._error_result(
|
|
422
|
+
tc_id,
|
|
423
|
+
code=exc.code,
|
|
424
|
+
message=str(exc),
|
|
425
|
+
tool_name=name,
|
|
426
|
+
))
|
|
427
|
+
return results
|
|
428
|
+
|
|
429
|
+
for tc in tool_calls:
|
|
430
|
+
tc_id = tc.get("id", str(uuid.uuid4()))
|
|
431
|
+
name = tc.get("name", "unknown")
|
|
432
|
+
arguments = tc.get("arguments", {})
|
|
433
|
+
if not isinstance(arguments, dict):
|
|
434
|
+
arguments = {}
|
|
435
|
+
|
|
436
|
+
handler, effect, _provenance = self._resolved_handler(name, dynamic)
|
|
437
|
+
if handler is None or effect is None:
|
|
438
|
+
logger.debug("ToolExecutor: no handler for '%s' — returning error", name)
|
|
439
|
+
results.append({
|
|
440
|
+
"tool_call_id": tc_id,
|
|
441
|
+
"content": json.dumps({
|
|
442
|
+
"error": True,
|
|
443
|
+
"message": f"Tool '{name}' is not available. Try a different approach.",
|
|
444
|
+
"available_tools": list(self._handlers.keys())
|
|
445
|
+
+ list(dynamic.keys()),
|
|
446
|
+
}),
|
|
447
|
+
"executed": False,
|
|
448
|
+
"error": "tool_unavailable",
|
|
449
|
+
})
|
|
450
|
+
continue
|
|
451
|
+
|
|
452
|
+
if allowed_tools is not None and name not in allowed_tools:
|
|
453
|
+
results.append(self._error_result(
|
|
454
|
+
tc_id,
|
|
455
|
+
code="tool_not_authorized",
|
|
456
|
+
message=f"Tool '{name}' was not authorized for this turn.",
|
|
457
|
+
tool_name=name,
|
|
458
|
+
))
|
|
459
|
+
continue
|
|
460
|
+
|
|
461
|
+
if not actor_allows_effect(effect, actor_policy):
|
|
462
|
+
results.append(self._error_result(
|
|
463
|
+
tc_id,
|
|
464
|
+
code="tool_authority_denied",
|
|
465
|
+
message=f"Tool '{name}' exceeds caller authority.",
|
|
466
|
+
tool_name=name,
|
|
467
|
+
))
|
|
468
|
+
continue
|
|
469
|
+
|
|
470
|
+
boundary_allowed, boundary_error, boundary_reason = (
|
|
471
|
+
self._boundary_verdict(name, arguments, effect)
|
|
472
|
+
)
|
|
473
|
+
if not boundary_allowed:
|
|
474
|
+
assert boundary_error is not None
|
|
475
|
+
results.append(self._error_result(
|
|
476
|
+
tc_id,
|
|
477
|
+
code=boundary_error,
|
|
478
|
+
message=boundary_reason,
|
|
479
|
+
tool_name=name,
|
|
480
|
+
))
|
|
481
|
+
continue
|
|
482
|
+
|
|
483
|
+
try:
|
|
484
|
+
result = await handler(arguments)
|
|
485
|
+
if isinstance(result, (dict, list)):
|
|
486
|
+
content = json.dumps(result)
|
|
487
|
+
else:
|
|
488
|
+
content = str(result)
|
|
489
|
+
results.append({
|
|
490
|
+
"tool_call_id": tc_id,
|
|
491
|
+
"content": content,
|
|
492
|
+
"executed": True,
|
|
493
|
+
})
|
|
494
|
+
except Exception as exc:
|
|
495
|
+
logger.error("ToolExecutor: handler '%s' failed: %s", name, exc)
|
|
496
|
+
results.append({
|
|
497
|
+
"tool_call_id": tc_id,
|
|
498
|
+
"content": json.dumps({
|
|
499
|
+
"error": f"Tool '{name}' execution failed: {exc}",
|
|
500
|
+
"tool_name": name,
|
|
501
|
+
}),
|
|
502
|
+
"executed": False,
|
|
503
|
+
"error": "tool_execution_failed",
|
|
504
|
+
})
|
|
505
|
+
|
|
506
|
+
return results
|