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,635 @@
|
|
|
1
|
+
"""Persisted registry for self-built tools (Mind M1).
|
|
2
|
+
|
|
3
|
+
SQLite metadata plus an on-disk tool directory per tool (source, manifest,
|
|
4
|
+
test). Deliberately independent of the runtime `skills/` executor registry
|
|
5
|
+
(which serves initiative executors and is load-bearing elsewhere); this one
|
|
6
|
+
owns the LLM-callable-tool artifact lifecycle only.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import hashlib
|
|
12
|
+
import json
|
|
13
|
+
import logging
|
|
14
|
+
import os
|
|
15
|
+
import re
|
|
16
|
+
import sqlite3
|
|
17
|
+
import threading
|
|
18
|
+
import time
|
|
19
|
+
import uuid
|
|
20
|
+
from dataclasses import asdict, dataclass, field
|
|
21
|
+
from typing import Any, Dict, List, Optional
|
|
22
|
+
|
|
23
|
+
from apsimo.toolsmith.authority import (
|
|
24
|
+
GraduationAuthorityError,
|
|
25
|
+
GraduationAuthorityV1,
|
|
26
|
+
)
|
|
27
|
+
from apsimo.toolsmith.integrity import (
|
|
28
|
+
PURE_CAPABILITY_MANIFEST,
|
|
29
|
+
artifact_digest as compute_artifact_digest,
|
|
30
|
+
digest_json,
|
|
31
|
+
)
|
|
32
|
+
from apsimo.tools.definitions import STATIC_TOOL_NAMES
|
|
33
|
+
|
|
34
|
+
logger = logging.getLogger(__name__)
|
|
35
|
+
|
|
36
|
+
_NAME_RE = re.compile(r"^[a-z][a-z0-9_]{2,48}$")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class ToolStatus:
|
|
40
|
+
DRAFT = "draft" # written, not yet verified
|
|
41
|
+
VERIFIED = "verified" # passed sandbox replay, not yet advertised
|
|
42
|
+
SHADOW = "shadow" # advertised as shadow (simulated, journaled)
|
|
43
|
+
LIVE = "live" # advertised and executed for real
|
|
44
|
+
RETIRED = "retired" # demoted (unused or failing)
|
|
45
|
+
REJECTED = "rejected" # failed verification or owner-rejected
|
|
46
|
+
|
|
47
|
+
ALL = (DRAFT, VERIFIED, SHADOW, LIVE, RETIRED, REJECTED)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@dataclass
|
|
51
|
+
class Tool:
|
|
52
|
+
tool_id: str
|
|
53
|
+
name: str
|
|
54
|
+
description: str
|
|
55
|
+
status: str
|
|
56
|
+
source_code: str
|
|
57
|
+
input_schema: Dict[str, Any] = field(default_factory=dict)
|
|
58
|
+
checksum_sha256: str = ""
|
|
59
|
+
candidate_digest: str = ""
|
|
60
|
+
artifact_digest: str = ""
|
|
61
|
+
capability_manifest: Dict[str, Any] = field(
|
|
62
|
+
default_factory=lambda: dict(PURE_CAPABILITY_MANIFEST))
|
|
63
|
+
origin_kind: str = "mined" # mined | requested
|
|
64
|
+
evidence: List[str] = field(default_factory=list)
|
|
65
|
+
test_source: str = ""
|
|
66
|
+
verify_detail: Dict[str, Any] = field(default_factory=dict)
|
|
67
|
+
invocations: int = 0
|
|
68
|
+
failures: int = 0
|
|
69
|
+
shadow_runs: int = 0
|
|
70
|
+
last_used_at: Optional[float] = None
|
|
71
|
+
created_at: float = 0.0
|
|
72
|
+
updated_at: float = 0.0
|
|
73
|
+
|
|
74
|
+
def public(self) -> Dict[str, Any]:
|
|
75
|
+
d = asdict(self)
|
|
76
|
+
d.pop("source_code", None)
|
|
77
|
+
d.pop("test_source", None)
|
|
78
|
+
return d
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class ToolRegistry:
|
|
82
|
+
def __init__(self, db_path: str, library_root: str) -> None:
|
|
83
|
+
self._conn = sqlite3.connect(db_path, check_same_thread=False)
|
|
84
|
+
self._conn.row_factory = sqlite3.Row
|
|
85
|
+
self._conn.execute("PRAGMA journal_mode=WAL")
|
|
86
|
+
self._lock = threading.Lock()
|
|
87
|
+
self._library = library_root
|
|
88
|
+
os.makedirs(self._library, exist_ok=True)
|
|
89
|
+
self._conn.executescript(
|
|
90
|
+
"""
|
|
91
|
+
CREATE TABLE IF NOT EXISTS tools (
|
|
92
|
+
tool_id TEXT PRIMARY KEY,
|
|
93
|
+
name TEXT NOT NULL,
|
|
94
|
+
description TEXT NOT NULL,
|
|
95
|
+
status TEXT NOT NULL,
|
|
96
|
+
source_code TEXT NOT NULL,
|
|
97
|
+
input_schema TEXT,
|
|
98
|
+
checksum_sha256 TEXT,
|
|
99
|
+
candidate_digest TEXT,
|
|
100
|
+
artifact_digest TEXT,
|
|
101
|
+
capability_manifest TEXT,
|
|
102
|
+
origin_kind TEXT,
|
|
103
|
+
evidence TEXT,
|
|
104
|
+
test_source TEXT,
|
|
105
|
+
verify_detail TEXT,
|
|
106
|
+
invocations INTEGER DEFAULT 0,
|
|
107
|
+
failures INTEGER DEFAULT 0,
|
|
108
|
+
shadow_runs INTEGER DEFAULT 0,
|
|
109
|
+
last_used_at REAL,
|
|
110
|
+
created_at REAL NOT NULL,
|
|
111
|
+
updated_at REAL NOT NULL
|
|
112
|
+
);
|
|
113
|
+
CREATE INDEX IF NOT EXISTS idx_tools_status ON tools(status);
|
|
114
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_tools_name ON tools(name);
|
|
115
|
+
CREATE TABLE IF NOT EXISTS toolsmith_shadow_comparisons (
|
|
116
|
+
comparison_id TEXT PRIMARY KEY,
|
|
117
|
+
tool_id TEXT NOT NULL,
|
|
118
|
+
capture_id TEXT NOT NULL,
|
|
119
|
+
capture_source TEXT NOT NULL,
|
|
120
|
+
principal_id TEXT NOT NULL,
|
|
121
|
+
artifact_digest TEXT NOT NULL,
|
|
122
|
+
input_digest TEXT NOT NULL,
|
|
123
|
+
incumbent_output_digest TEXT NOT NULL,
|
|
124
|
+
candidate_output_digest TEXT NOT NULL,
|
|
125
|
+
repeat_output_digest TEXT NOT NULL,
|
|
126
|
+
deterministic INTEGER NOT NULL,
|
|
127
|
+
matched INTEGER NOT NULL,
|
|
128
|
+
success INTEGER NOT NULL,
|
|
129
|
+
created_at REAL NOT NULL,
|
|
130
|
+
UNIQUE(tool_id, capture_id)
|
|
131
|
+
);
|
|
132
|
+
CREATE INDEX IF NOT EXISTS idx_toolsmith_shadow_tool
|
|
133
|
+
ON toolsmith_shadow_comparisons(tool_id, created_at);
|
|
134
|
+
CREATE TABLE IF NOT EXISTS toolsmith_graduations (
|
|
135
|
+
authority_id TEXT PRIMARY KEY,
|
|
136
|
+
decision_id TEXT NOT NULL UNIQUE,
|
|
137
|
+
tool_id TEXT NOT NULL,
|
|
138
|
+
candidate_digest TEXT NOT NULL,
|
|
139
|
+
artifact_digest TEXT NOT NULL,
|
|
140
|
+
authority_digest TEXT NOT NULL,
|
|
141
|
+
principal_id TEXT NOT NULL,
|
|
142
|
+
owner_person_id TEXT NOT NULL,
|
|
143
|
+
issued_at TEXT NOT NULL,
|
|
144
|
+
expires_at TEXT NOT NULL,
|
|
145
|
+
max_uses INTEGER NOT NULL,
|
|
146
|
+
status TEXT NOT NULL,
|
|
147
|
+
created_at REAL NOT NULL
|
|
148
|
+
);
|
|
149
|
+
CREATE INDEX IF NOT EXISTS idx_toolsmith_graduations_tool
|
|
150
|
+
ON toolsmith_graduations(tool_id, created_at);
|
|
151
|
+
"""
|
|
152
|
+
)
|
|
153
|
+
self._ensure_column("tools", "candidate_digest", "TEXT")
|
|
154
|
+
self._ensure_column("tools", "artifact_digest", "TEXT")
|
|
155
|
+
self._ensure_column("tools", "capability_manifest", "TEXT")
|
|
156
|
+
self._backfill_integrity_digests()
|
|
157
|
+
self._conn.commit()
|
|
158
|
+
|
|
159
|
+
# -- helpers ----------------------------------------------------------
|
|
160
|
+
@staticmethod
|
|
161
|
+
def valid_name(name: str) -> bool:
|
|
162
|
+
return bool(_NAME_RE.match(name or ""))
|
|
163
|
+
|
|
164
|
+
def tool_dir(self, tool_id: str) -> str:
|
|
165
|
+
return os.path.join(self._library, tool_id)
|
|
166
|
+
|
|
167
|
+
def _ensure_column(self, table: str, column: str, declaration: str) -> None:
|
|
168
|
+
columns = {
|
|
169
|
+
str(row["name"])
|
|
170
|
+
for row in self._conn.execute(f"PRAGMA table_info({table})")
|
|
171
|
+
}
|
|
172
|
+
if column not in columns:
|
|
173
|
+
self._conn.execute(
|
|
174
|
+
f"ALTER TABLE {table} ADD COLUMN {column} {declaration}"
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
@staticmethod
|
|
178
|
+
def _artifact_digest_for(tool: Tool) -> str:
|
|
179
|
+
return compute_artifact_digest(
|
|
180
|
+
name=tool.name,
|
|
181
|
+
description=tool.description,
|
|
182
|
+
source_code=tool.source_code,
|
|
183
|
+
input_schema=tool.input_schema,
|
|
184
|
+
test_source=tool.test_source,
|
|
185
|
+
origin_kind=tool.origin_kind,
|
|
186
|
+
evidence=tool.evidence,
|
|
187
|
+
candidate_digest_value=tool.candidate_digest,
|
|
188
|
+
capability_manifest=tool.capability_manifest,
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
def _backfill_integrity_digests(self) -> None:
|
|
192
|
+
rows = self._conn.execute("SELECT * FROM tools").fetchall()
|
|
193
|
+
for row in rows:
|
|
194
|
+
tool = self._row_to_tool(row)
|
|
195
|
+
candidate = tool.candidate_digest or digest_json({
|
|
196
|
+
"legacy_tool_id": tool.tool_id,
|
|
197
|
+
"origin_kind": tool.origin_kind,
|
|
198
|
+
"evidence": tool.evidence,
|
|
199
|
+
})
|
|
200
|
+
tool.candidate_digest = candidate
|
|
201
|
+
# Only fill fields introduced by this migration. Once an
|
|
202
|
+
# artifact digest exists, never silently recompute/bless changed
|
|
203
|
+
# code during startup; artifact_intact() must expose the mismatch.
|
|
204
|
+
artifact = row["artifact_digest"] or self._artifact_digest_for(tool)
|
|
205
|
+
manifest = row["capability_manifest"] or json.dumps(
|
|
206
|
+
PURE_CAPABILITY_MANIFEST, sort_keys=True)
|
|
207
|
+
if (
|
|
208
|
+
not row["candidate_digest"]
|
|
209
|
+
or not row["artifact_digest"]
|
|
210
|
+
or not row["capability_manifest"]
|
|
211
|
+
):
|
|
212
|
+
self._conn.execute(
|
|
213
|
+
"UPDATE tools SET candidate_digest=?, artifact_digest=?,"
|
|
214
|
+
" capability_manifest=? "
|
|
215
|
+
"WHERE tool_id=?",
|
|
216
|
+
(candidate, artifact, manifest, tool.tool_id),
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
def _row_to_tool(self, r: sqlite3.Row) -> Tool:
|
|
220
|
+
return Tool(
|
|
221
|
+
tool_id=r["tool_id"], name=r["name"], description=r["description"],
|
|
222
|
+
status=r["status"], source_code=r["source_code"],
|
|
223
|
+
input_schema=json.loads(r["input_schema"] or "{}"),
|
|
224
|
+
checksum_sha256=r["checksum_sha256"] or "",
|
|
225
|
+
candidate_digest=r["candidate_digest"] or "",
|
|
226
|
+
artifact_digest=r["artifact_digest"] or "",
|
|
227
|
+
capability_manifest=json.loads(
|
|
228
|
+
r["capability_manifest"] or json.dumps(PURE_CAPABILITY_MANIFEST)),
|
|
229
|
+
origin_kind=r["origin_kind"] or "mined",
|
|
230
|
+
evidence=json.loads(r["evidence"] or "[]"),
|
|
231
|
+
test_source=r["test_source"] or "",
|
|
232
|
+
verify_detail=json.loads(r["verify_detail"] or "{}"),
|
|
233
|
+
invocations=r["invocations"] or 0, failures=r["failures"] or 0,
|
|
234
|
+
shadow_runs=r["shadow_runs"] or 0, last_used_at=r["last_used_at"],
|
|
235
|
+
created_at=r["created_at"], updated_at=r["updated_at"])
|
|
236
|
+
|
|
237
|
+
# -- writes -----------------------------------------------------------
|
|
238
|
+
def create_draft(self, *, name: str, description: str, source_code: str,
|
|
239
|
+
input_schema: Dict[str, Any], test_source: str,
|
|
240
|
+
origin_kind: str = "mined",
|
|
241
|
+
evidence: Optional[List[str]] = None,
|
|
242
|
+
candidate_digest: str = "") -> Optional[Tool]:
|
|
243
|
+
if not self.valid_name(name):
|
|
244
|
+
logger.warning("toolsmith: invalid tool name %r", name)
|
|
245
|
+
return None
|
|
246
|
+
if name in STATIC_TOOL_NAMES:
|
|
247
|
+
logger.warning("toolsmith: reserved first-party tool name %r", name)
|
|
248
|
+
return None
|
|
249
|
+
if self.get_by_name(name) is not None:
|
|
250
|
+
logger.info("toolsmith: tool named %r already exists", name)
|
|
251
|
+
return None
|
|
252
|
+
tool_id = f"tool-{uuid.uuid4().hex[:12]}"
|
|
253
|
+
now = time.time()
|
|
254
|
+
checksum = hashlib.sha256(source_code.encode()).hexdigest()
|
|
255
|
+
provenance_digest = candidate_digest or digest_json({
|
|
256
|
+
"origin_kind": origin_kind,
|
|
257
|
+
"evidence": evidence or [],
|
|
258
|
+
"name": name,
|
|
259
|
+
})
|
|
260
|
+
tool = Tool(
|
|
261
|
+
tool_id=tool_id, name=name, description=description,
|
|
262
|
+
status=ToolStatus.DRAFT, source_code=source_code,
|
|
263
|
+
input_schema=input_schema or {}, checksum_sha256=checksum,
|
|
264
|
+
candidate_digest=provenance_digest,
|
|
265
|
+
capability_manifest=dict(PURE_CAPABILITY_MANIFEST),
|
|
266
|
+
origin_kind=origin_kind, evidence=evidence or [],
|
|
267
|
+
test_source=test_source, created_at=now, updated_at=now)
|
|
268
|
+
tool.artifact_digest = self._artifact_digest_for(tool)
|
|
269
|
+
self._persist_files(tool)
|
|
270
|
+
with self._lock:
|
|
271
|
+
self._conn.execute(
|
|
272
|
+
"INSERT INTO tools (tool_id,name,description,status,"
|
|
273
|
+
"source_code,input_schema,checksum_sha256,candidate_digest,"
|
|
274
|
+
"artifact_digest,capability_manifest,origin_kind,evidence,"
|
|
275
|
+
"test_source,verify_detail,created_at,updated_at) "
|
|
276
|
+
"VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",
|
|
277
|
+
(tool_id, name, description, ToolStatus.DRAFT, source_code,
|
|
278
|
+
json.dumps(input_schema or {}), checksum, provenance_digest,
|
|
279
|
+
tool.artifact_digest,
|
|
280
|
+
json.dumps(tool.capability_manifest, sort_keys=True),
|
|
281
|
+
origin_kind, json.dumps(evidence or []), test_source, "{}",
|
|
282
|
+
now, now))
|
|
283
|
+
self._conn.commit()
|
|
284
|
+
return tool
|
|
285
|
+
|
|
286
|
+
def _persist_files(self, tool: Tool) -> None:
|
|
287
|
+
d = self.tool_dir(tool.tool_id)
|
|
288
|
+
os.makedirs(d, exist_ok=True)
|
|
289
|
+
with open(os.path.join(d, "tool.py"), "w") as f:
|
|
290
|
+
f.write(tool.source_code)
|
|
291
|
+
with open(os.path.join(d, "test_tool.py"), "w") as f:
|
|
292
|
+
f.write(tool.test_source)
|
|
293
|
+
with open(os.path.join(d, "manifest.json"), "w") as f:
|
|
294
|
+
json.dump({"tool_id": tool.tool_id, "name": tool.name,
|
|
295
|
+
"description": tool.description,
|
|
296
|
+
"input_schema": tool.input_schema,
|
|
297
|
+
"checksum_sha256": tool.checksum_sha256,
|
|
298
|
+
"candidate_digest": tool.candidate_digest,
|
|
299
|
+
"artifact_digest": tool.artifact_digest,
|
|
300
|
+
"capability_manifest": tool.capability_manifest,
|
|
301
|
+
"origin_kind": tool.origin_kind}, f, indent=2)
|
|
302
|
+
|
|
303
|
+
def set_status(self, tool_id: str, status: str, *,
|
|
304
|
+
verify_detail: Optional[Dict[str, Any]] = None) -> bool:
|
|
305
|
+
if status not in ToolStatus.ALL:
|
|
306
|
+
return False
|
|
307
|
+
# LIVE is a capability publication, not a generic lifecycle edit. It
|
|
308
|
+
# must be committed atomically with a one-shot authority receipt.
|
|
309
|
+
if status == ToolStatus.LIVE:
|
|
310
|
+
return False
|
|
311
|
+
with self._lock:
|
|
312
|
+
if verify_detail is not None:
|
|
313
|
+
self._conn.execute(
|
|
314
|
+
"UPDATE tools SET status=?, verify_detail=?, updated_at=?"
|
|
315
|
+
" WHERE tool_id=?",
|
|
316
|
+
(status, json.dumps(verify_detail), time.time(), tool_id))
|
|
317
|
+
else:
|
|
318
|
+
self._conn.execute(
|
|
319
|
+
"UPDATE tools SET status=?, updated_at=? WHERE tool_id=?",
|
|
320
|
+
(status, time.time(), tool_id))
|
|
321
|
+
self._conn.commit()
|
|
322
|
+
return True
|
|
323
|
+
|
|
324
|
+
def artifact_intact(self, tool: Tool) -> bool:
|
|
325
|
+
return bool(
|
|
326
|
+
tool.artifact_digest
|
|
327
|
+
and tool.artifact_digest == self._artifact_digest_for(tool)
|
|
328
|
+
)
|
|
329
|
+
|
|
330
|
+
def record_shadow_comparison(
|
|
331
|
+
self,
|
|
332
|
+
*,
|
|
333
|
+
tool: Tool,
|
|
334
|
+
capture_id: str,
|
|
335
|
+
capture_source: str,
|
|
336
|
+
principal_id: str,
|
|
337
|
+
input_digest: str,
|
|
338
|
+
incumbent_output_digest: str,
|
|
339
|
+
candidate_output_digest: str,
|
|
340
|
+
repeat_output_digest: str,
|
|
341
|
+
deterministic: bool,
|
|
342
|
+
matched: bool,
|
|
343
|
+
) -> Dict[str, Any]:
|
|
344
|
+
"""Persist one immutable same-input comparison exactly once."""
|
|
345
|
+
|
|
346
|
+
if tool.status != ToolStatus.SHADOW:
|
|
347
|
+
raise ValueError("tool is not in shadow state")
|
|
348
|
+
if not self.artifact_intact(tool):
|
|
349
|
+
raise ValueError("tool artifact digest mismatch")
|
|
350
|
+
success = bool(deterministic and matched)
|
|
351
|
+
payload = {
|
|
352
|
+
"tool_id": tool.tool_id,
|
|
353
|
+
"capture_id": capture_id,
|
|
354
|
+
"capture_source": capture_source,
|
|
355
|
+
"principal_id": principal_id,
|
|
356
|
+
"artifact_digest": tool.artifact_digest,
|
|
357
|
+
"input_digest": input_digest,
|
|
358
|
+
"incumbent_output_digest": incumbent_output_digest,
|
|
359
|
+
"candidate_output_digest": candidate_output_digest,
|
|
360
|
+
"repeat_output_digest": repeat_output_digest,
|
|
361
|
+
"deterministic": bool(deterministic),
|
|
362
|
+
"matched": bool(matched),
|
|
363
|
+
"success": success,
|
|
364
|
+
}
|
|
365
|
+
comparison_id = "cmp_" + digest_json(payload)
|
|
366
|
+
now = time.time()
|
|
367
|
+
with self._lock:
|
|
368
|
+
self._conn.execute("BEGIN IMMEDIATE")
|
|
369
|
+
try:
|
|
370
|
+
current_row = self._conn.execute(
|
|
371
|
+
"SELECT * FROM tools WHERE tool_id=?", (tool.tool_id,)
|
|
372
|
+
).fetchone()
|
|
373
|
+
if current_row is None:
|
|
374
|
+
raise ValueError("tool was not found")
|
|
375
|
+
current = self._row_to_tool(current_row)
|
|
376
|
+
if current.status != ToolStatus.SHADOW:
|
|
377
|
+
raise ValueError("tool is not in shadow state")
|
|
378
|
+
if (
|
|
379
|
+
current.artifact_digest != tool.artifact_digest
|
|
380
|
+
or not self.artifact_intact(current)
|
|
381
|
+
):
|
|
382
|
+
raise ValueError("tool artifact digest mismatch")
|
|
383
|
+
existing = self._conn.execute(
|
|
384
|
+
"SELECT * FROM toolsmith_shadow_comparisons "
|
|
385
|
+
"WHERE tool_id=? AND capture_id=?",
|
|
386
|
+
(tool.tool_id, capture_id),
|
|
387
|
+
).fetchone()
|
|
388
|
+
if existing is not None:
|
|
389
|
+
if existing["comparison_id"] != comparison_id:
|
|
390
|
+
raise ValueError(
|
|
391
|
+
"shadow capture replayed with conflicting content"
|
|
392
|
+
)
|
|
393
|
+
self._conn.commit()
|
|
394
|
+
return {
|
|
395
|
+
**payload,
|
|
396
|
+
"comparison_id": comparison_id,
|
|
397
|
+
"replayed": True,
|
|
398
|
+
}
|
|
399
|
+
self._conn.execute(
|
|
400
|
+
"INSERT INTO toolsmith_shadow_comparisons ("
|
|
401
|
+
"comparison_id,tool_id,capture_id,capture_source,principal_id,"
|
|
402
|
+
"artifact_digest,input_digest,incumbent_output_digest,"
|
|
403
|
+
"candidate_output_digest,repeat_output_digest,deterministic,"
|
|
404
|
+
"matched,success,created_at) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)",
|
|
405
|
+
(
|
|
406
|
+
comparison_id,
|
|
407
|
+
tool.tool_id,
|
|
408
|
+
capture_id,
|
|
409
|
+
capture_source,
|
|
410
|
+
principal_id,
|
|
411
|
+
tool.artifact_digest,
|
|
412
|
+
input_digest,
|
|
413
|
+
incumbent_output_digest,
|
|
414
|
+
candidate_output_digest,
|
|
415
|
+
repeat_output_digest,
|
|
416
|
+
int(deterministic),
|
|
417
|
+
int(matched),
|
|
418
|
+
int(success),
|
|
419
|
+
now,
|
|
420
|
+
),
|
|
421
|
+
)
|
|
422
|
+
self._conn.execute(
|
|
423
|
+
"UPDATE tools SET shadow_runs=shadow_runs+1,"
|
|
424
|
+
" failures=failures+?, last_used_at=?, updated_at=?"
|
|
425
|
+
" WHERE tool_id=?",
|
|
426
|
+
(0 if success else 1, now, now, tool.tool_id),
|
|
427
|
+
)
|
|
428
|
+
self._conn.commit()
|
|
429
|
+
except Exception:
|
|
430
|
+
self._conn.rollback()
|
|
431
|
+
raise
|
|
432
|
+
return {**payload, "comparison_id": comparison_id, "replayed": False}
|
|
433
|
+
|
|
434
|
+
def graduate_with_authority(
|
|
435
|
+
self,
|
|
436
|
+
authority: GraduationAuthorityV1,
|
|
437
|
+
*,
|
|
438
|
+
shadow_min: int,
|
|
439
|
+
) -> Dict[str, Any]:
|
|
440
|
+
"""Atomically publish one exact artifact and consume one authority."""
|
|
441
|
+
|
|
442
|
+
authority.assert_current()
|
|
443
|
+
authority_digest = authority.authority_digest
|
|
444
|
+
with self._lock:
|
|
445
|
+
self._conn.execute("BEGIN IMMEDIATE")
|
|
446
|
+
try:
|
|
447
|
+
prior = self._conn.execute(
|
|
448
|
+
"SELECT * FROM toolsmith_graduations WHERE authority_id=? "
|
|
449
|
+
"OR decision_id=?",
|
|
450
|
+
(authority.authority_id, authority.decision_id),
|
|
451
|
+
).fetchone()
|
|
452
|
+
if prior is not None:
|
|
453
|
+
if (
|
|
454
|
+
prior["authority_digest"] != authority_digest
|
|
455
|
+
or prior["tool_id"] != authority.tool_id
|
|
456
|
+
):
|
|
457
|
+
raise GraduationAuthorityError(
|
|
458
|
+
"authority_replay",
|
|
459
|
+
"authority_id or decision_id was used for another decision",
|
|
460
|
+
)
|
|
461
|
+
self._conn.commit()
|
|
462
|
+
return {
|
|
463
|
+
"graduated": authority.tool_id,
|
|
464
|
+
"authority_id": authority.authority_id,
|
|
465
|
+
"authority_digest": authority_digest,
|
|
466
|
+
"replayed": True,
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
row = self._conn.execute(
|
|
470
|
+
"SELECT * FROM tools WHERE tool_id=?", (authority.tool_id,)
|
|
471
|
+
).fetchone()
|
|
472
|
+
if row is None:
|
|
473
|
+
raise GraduationAuthorityError(
|
|
474
|
+
"tool_not_found", "tool was not found"
|
|
475
|
+
)
|
|
476
|
+
tool = self._row_to_tool(row)
|
|
477
|
+
if tool.name in STATIC_TOOL_NAMES:
|
|
478
|
+
raise GraduationAuthorityError(
|
|
479
|
+
"tool_name_reserved",
|
|
480
|
+
"tool name collides with a first-party capability",
|
|
481
|
+
)
|
|
482
|
+
if tool.status != ToolStatus.SHADOW:
|
|
483
|
+
raise GraduationAuthorityError(
|
|
484
|
+
"tool_not_shadow", "tool is not in shadow state"
|
|
485
|
+
)
|
|
486
|
+
if not self.artifact_intact(tool):
|
|
487
|
+
raise GraduationAuthorityError(
|
|
488
|
+
"artifact_integrity_failure", "tool artifact digest mismatch"
|
|
489
|
+
)
|
|
490
|
+
if tool.candidate_digest != authority.candidate_digest:
|
|
491
|
+
raise GraduationAuthorityError(
|
|
492
|
+
"candidate_digest_mismatch", "candidate digest changed"
|
|
493
|
+
)
|
|
494
|
+
if tool.artifact_digest != authority.artifact_digest:
|
|
495
|
+
raise GraduationAuthorityError(
|
|
496
|
+
"artifact_digest_mismatch", "artifact digest changed"
|
|
497
|
+
)
|
|
498
|
+
clean_row = self._conn.execute(
|
|
499
|
+
"SELECT COUNT(*) AS n FROM toolsmith_shadow_comparisons "
|
|
500
|
+
"WHERE tool_id=? AND success=1",
|
|
501
|
+
(tool.tool_id,),
|
|
502
|
+
).fetchone()
|
|
503
|
+
clean_comparisons = int(clean_row["n"] if clean_row else 0)
|
|
504
|
+
if clean_comparisons < shadow_min or tool.failures:
|
|
505
|
+
raise GraduationAuthorityError(
|
|
506
|
+
"shadow_evidence_insufficient",
|
|
507
|
+
"tool lacks the required clean same-input comparisons",
|
|
508
|
+
)
|
|
509
|
+
now = time.time()
|
|
510
|
+
self._conn.execute(
|
|
511
|
+
"INSERT INTO toolsmith_graduations (authority_id,decision_id,"
|
|
512
|
+
"tool_id,candidate_digest,artifact_digest,authority_digest,"
|
|
513
|
+
"principal_id,owner_person_id,issued_at,expires_at,max_uses,"
|
|
514
|
+
"status,created_at) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)",
|
|
515
|
+
(
|
|
516
|
+
authority.authority_id,
|
|
517
|
+
authority.decision_id,
|
|
518
|
+
authority.tool_id,
|
|
519
|
+
authority.candidate_digest,
|
|
520
|
+
authority.artifact_digest,
|
|
521
|
+
authority_digest,
|
|
522
|
+
authority.principal_id,
|
|
523
|
+
authority.owner_person_id,
|
|
524
|
+
authority.issued_at,
|
|
525
|
+
authority.expires_at,
|
|
526
|
+
authority.max_uses,
|
|
527
|
+
"consumed",
|
|
528
|
+
now,
|
|
529
|
+
),
|
|
530
|
+
)
|
|
531
|
+
changed = self._conn.execute(
|
|
532
|
+
"UPDATE tools SET status=?, updated_at=? "
|
|
533
|
+
"WHERE tool_id=? AND status=?",
|
|
534
|
+
(ToolStatus.LIVE, now, tool.tool_id, ToolStatus.SHADOW),
|
|
535
|
+
).rowcount
|
|
536
|
+
if changed != 1:
|
|
537
|
+
raise GraduationAuthorityError(
|
|
538
|
+
"graduation_conflict", "tool state changed during graduation"
|
|
539
|
+
)
|
|
540
|
+
self._conn.commit()
|
|
541
|
+
except Exception:
|
|
542
|
+
self._conn.rollback()
|
|
543
|
+
raise
|
|
544
|
+
return {
|
|
545
|
+
"graduated": authority.tool_id,
|
|
546
|
+
"authority_id": authority.authority_id,
|
|
547
|
+
"authority_digest": authority_digest,
|
|
548
|
+
"replayed": False,
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
def audit_projection(self, tool_id: str) -> Dict[str, Any]:
|
|
552
|
+
"""Return digest-only comparison and publication receipts."""
|
|
553
|
+
|
|
554
|
+
with self._lock:
|
|
555
|
+
comparisons = self._conn.execute(
|
|
556
|
+
"SELECT comparison_id,capture_id,capture_source,principal_id,"
|
|
557
|
+
"artifact_digest,input_digest,incumbent_output_digest,"
|
|
558
|
+
"candidate_output_digest,repeat_output_digest,deterministic,"
|
|
559
|
+
"matched,success,created_at FROM toolsmith_shadow_comparisons "
|
|
560
|
+
"WHERE tool_id=? ORDER BY created_at ASC",
|
|
561
|
+
(tool_id,),
|
|
562
|
+
).fetchall()
|
|
563
|
+
graduations = self._conn.execute(
|
|
564
|
+
"SELECT authority_id,decision_id,candidate_digest,artifact_digest,"
|
|
565
|
+
"authority_digest,principal_id,owner_person_id,issued_at,expires_at,"
|
|
566
|
+
"max_uses,status,created_at FROM toolsmith_graduations "
|
|
567
|
+
"WHERE tool_id=? ORDER BY created_at ASC",
|
|
568
|
+
(tool_id,),
|
|
569
|
+
).fetchall()
|
|
570
|
+
return {
|
|
571
|
+
"shadow_comparisons": [dict(row) for row in comparisons],
|
|
572
|
+
"graduations": [dict(row) for row in graduations],
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
def clean_comparison_count(self, tool_id: str) -> int:
|
|
576
|
+
"""Count only receipt-backed successful comparisons, never old counters."""
|
|
577
|
+
|
|
578
|
+
with self._lock:
|
|
579
|
+
row = self._conn.execute(
|
|
580
|
+
"SELECT COUNT(*) AS n FROM toolsmith_shadow_comparisons "
|
|
581
|
+
"WHERE tool_id=? AND success=1",
|
|
582
|
+
(tool_id,),
|
|
583
|
+
).fetchone()
|
|
584
|
+
return int(row["n"] if row is not None else 0)
|
|
585
|
+
|
|
586
|
+
def record_invocation(self, tool_id: str, *, success: bool,
|
|
587
|
+
shadow: bool = False) -> None:
|
|
588
|
+
with self._lock:
|
|
589
|
+
if shadow:
|
|
590
|
+
self._conn.execute(
|
|
591
|
+
"UPDATE tools SET shadow_runs=shadow_runs+1,"
|
|
592
|
+
" failures=failures+?, last_used_at=?, updated_at=?"
|
|
593
|
+
" WHERE tool_id=?",
|
|
594
|
+
(0 if success else 1, time.time(), time.time(), tool_id))
|
|
595
|
+
else:
|
|
596
|
+
self._conn.execute(
|
|
597
|
+
"UPDATE tools SET invocations=invocations+1,"
|
|
598
|
+
" failures=failures+?, last_used_at=?, updated_at=?"
|
|
599
|
+
" WHERE tool_id=?",
|
|
600
|
+
(0 if success else 1, time.time(), time.time(), tool_id))
|
|
601
|
+
self._conn.commit()
|
|
602
|
+
|
|
603
|
+
# -- reads ------------------------------------------------------------
|
|
604
|
+
def get(self, tool_id: str) -> Optional[Tool]:
|
|
605
|
+
with self._lock:
|
|
606
|
+
r = self._conn.execute(
|
|
607
|
+
"SELECT * FROM tools WHERE tool_id=?", (tool_id,)).fetchone()
|
|
608
|
+
return self._row_to_tool(r) if r else None
|
|
609
|
+
|
|
610
|
+
def get_by_name(self, name: str) -> Optional[Tool]:
|
|
611
|
+
with self._lock:
|
|
612
|
+
r = self._conn.execute(
|
|
613
|
+
"SELECT * FROM tools WHERE name=?", (name,)).fetchone()
|
|
614
|
+
return self._row_to_tool(r) if r else None
|
|
615
|
+
|
|
616
|
+
def list(self, status: Optional[str] = None,
|
|
617
|
+
limit: int = 200) -> List[Tool]:
|
|
618
|
+
q = "SELECT * FROM tools"
|
|
619
|
+
params: List[Any] = []
|
|
620
|
+
if status:
|
|
621
|
+
q += " WHERE status=?"
|
|
622
|
+
params.append(status)
|
|
623
|
+
q += " ORDER BY updated_at DESC LIMIT ?"
|
|
624
|
+
params.append(limit)
|
|
625
|
+
with self._lock:
|
|
626
|
+
rows = self._conn.execute(q, params).fetchall()
|
|
627
|
+
return [self._row_to_tool(r) for r in rows]
|
|
628
|
+
|
|
629
|
+
def advertised(self) -> List[Tool]:
|
|
630
|
+
"""Tools the reasoning loop should see: shadow + live."""
|
|
631
|
+
with self._lock:
|
|
632
|
+
rows = self._conn.execute(
|
|
633
|
+
"SELECT * FROM tools WHERE status IN (?,?)",
|
|
634
|
+
(ToolStatus.SHADOW, ToolStatus.LIVE)).fetchall()
|
|
635
|
+
return [self._row_to_tool(r) for r in rows]
|
apsimo/turns/__init__.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""Stable turn-ingestion primitives."""
|
|
2
|
+
|
|
3
|
+
from .idempotency import (
|
|
4
|
+
Reservation,
|
|
5
|
+
ReservationOutcome,
|
|
6
|
+
TurnIdempotencyLedger,
|
|
7
|
+
canonical_turn_digest,
|
|
8
|
+
get_turn_idempotency_ledger,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"Reservation",
|
|
13
|
+
"ReservationOutcome",
|
|
14
|
+
"TurnIdempotencyLedger",
|
|
15
|
+
"canonical_turn_digest",
|
|
16
|
+
"get_turn_idempotency_ledger",
|
|
17
|
+
]
|