power-loop 7.3.1__tar.gz → 9.0.0__tar.gz
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.
- {power_loop-7.3.1 → power_loop-9.0.0}/PKG-INFO +47 -4
- {power_loop-7.3.1 → power_loop-9.0.0}/README.md +43 -2
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/__init__.py +16 -2
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/_vendor/llm_client/llm_factory.py +2 -1
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/_vendor/llm_client/multimodal.py +3 -3
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/agent/sink.py +33 -10
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/agent/stateful_loop.py +164 -75
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/agent/system_prompt.py +50 -55
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/agent/types.py +6 -1
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/contracts/context_policy.py +3 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/contracts/errors.py +10 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/contracts/event_payloads.py +42 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/contracts/events.py +4 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/contracts/tools.py +34 -2
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/contrib/mcp.py +29 -8
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/core/pass_turn.py +13 -5
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/core/pipeline.py +274 -114
- power_loop-9.0.0/power_loop/runtime/_tagged_processes.py +129 -0
- power_loop-9.0.0/power_loop/runtime/abort_outcomes.py +481 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/runtime/background.py +1 -1
- power_loop-9.0.0/power_loop/runtime/budget_contracts.py +279 -0
- power_loop-9.0.0/power_loop/runtime/budget_revisions.py +221 -0
- power_loop-9.0.0/power_loop/runtime/budget_scopes.py +207 -0
- power_loop-9.0.0/power_loop/runtime/completion.py +250 -0
- power_loop-9.0.0/power_loop/runtime/completion_windows.py +119 -0
- power_loop-9.0.0/power_loop/runtime/context_window_probe.py +112 -0
- power_loop-9.0.0/power_loop/runtime/controller_handoff.py +241 -0
- power_loop-9.0.0/power_loop/runtime/controller_recovery.py +397 -0
- power_loop-9.0.0/power_loop/runtime/controller_retirement.py +127 -0
- power_loop-9.0.0/power_loop/runtime/controller_task_exit.py +181 -0
- power_loop-9.0.0/power_loop/runtime/delegation.py +605 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/runtime/exec_backend.py +7 -0
- power_loop-9.0.0/power_loop/runtime/execution_membership.py +235 -0
- power_loop-9.0.0/power_loop/runtime/execution_roles.py +84 -0
- power_loop-9.0.0/power_loop/runtime/executor_lifecycle.py +384 -0
- power_loop-9.0.0/power_loop/runtime/executor_registry.py +364 -0
- power_loop-9.0.0/power_loop/runtime/finalization.py +516 -0
- power_loop-9.0.0/power_loop/runtime/fold_calls.py +7 -0
- power_loop-9.0.0/power_loop/runtime/host_send_steps.py +213 -0
- power_loop-9.0.0/power_loop/runtime/input_dispositions.py +180 -0
- power_loop-9.0.0/power_loop/runtime/invocation.py +24 -0
- power_loop-9.0.0/power_loop/runtime/jobs.py +1028 -0
- power_loop-9.0.0/power_loop/runtime/managed_claim.py +116 -0
- power_loop-9.0.0/power_loop/runtime/managed_completion.py +310 -0
- power_loop-9.0.0/power_loop/runtime/managed_delivery.py +405 -0
- power_loop-9.0.0/power_loop/runtime/managed_effects.py +1401 -0
- power_loop-9.0.0/power_loop/runtime/message_ref.py +51 -0
- power_loop-9.0.0/power_loop/runtime/model_attempt_query.py +83 -0
- power_loop-9.0.0/power_loop/runtime/model_attempts.py +692 -0
- power_loop-9.0.0/power_loop/runtime/model_budgets.py +1019 -0
- power_loop-9.0.0/power_loop/runtime/model_calls.py +165 -0
- power_loop-9.0.0/power_loop/runtime/model_exit.py +72 -0
- power_loop-9.0.0/power_loop/runtime/native_returns.py +298 -0
- power_loop-9.0.0/power_loop/runtime/nested_tools.py +188 -0
- power_loop-9.0.0/power_loop/runtime/node_inputs.py +240 -0
- power_loop-9.0.0/power_loop/runtime/node_resume.py +330 -0
- power_loop-9.0.0/power_loop/runtime/nudges.py +155 -0
- power_loop-9.0.0/power_loop/runtime/original_owner_review.py +312 -0
- power_loop-9.0.0/power_loop/runtime/owner_waits.py +556 -0
- power_loop-9.0.0/power_loop/runtime/pending_calls.py +429 -0
- power_loop-9.0.0/power_loop/runtime/pending_native.py +170 -0
- power_loop-9.0.0/power_loop/runtime/pending_owner.py +97 -0
- power_loop-9.0.0/power_loop/runtime/pricing.py +201 -0
- power_loop-9.0.0/power_loop/runtime/process_control.py +194 -0
- power_loop-9.0.0/power_loop/runtime/processing.py +379 -0
- power_loop-9.0.0/power_loop/runtime/recall.py +237 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/runtime/representation.py +7 -2
- power_loop-9.0.0/power_loop/runtime/response_capture.py +157 -0
- power_loop-9.0.0/power_loop/runtime/result_payloads.py +36 -0
- power_loop-9.0.0/power_loop/runtime/result_proof.py +14 -0
- power_loop-9.0.0/power_loop/runtime/result_revisions.py +41 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/runtime/retry.py +2 -0
- power_loop-9.0.0/power_loop/runtime/review_tools.py +53 -0
- power_loop-9.0.0/power_loop/runtime/root_context.py +178 -0
- power_loop-9.0.0/power_loop/runtime/root_control.py +163 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/runtime/runtime_state.py +8 -2
- power_loop-9.0.0/power_loop/runtime/send_receipt.py +143 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/runtime/session_store.py +15 -12
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/runtime/skills.py +4 -2
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/runtime/spec.py +121 -39
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/runtime/store/backends/sqlite.py +101 -45
- power_loop-9.0.0/power_loop/runtime/store/context_cache.py +41 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/runtime/store/dialect.py +6 -3
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/runtime/store/factory.py +12 -3
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/runtime/store/schema.py +17 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/runtime/store/store.py +181 -39
- power_loop-9.0.0/power_loop/runtime/store/task_schema.py +453 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/runtime/store/types.py +2 -0
- power_loop-9.0.0/power_loop/runtime/subagent_output.py +45 -0
- power_loop-9.0.0/power_loop/runtime/subagent_resume.py +254 -0
- power_loop-9.0.0/power_loop/runtime/task_control.py +284 -0
- power_loop-9.0.0/power_loop/runtime/task_events.py +249 -0
- power_loop-9.0.0/power_loop/runtime/task_history.py +390 -0
- power_loop-9.0.0/power_loop/runtime/task_inspection.py +170 -0
- power_loop-9.0.0/power_loop/runtime/task_query.py +298 -0
- power_loop-9.0.0/power_loop/runtime/task_runtime.py +797 -0
- power_loop-9.0.0/power_loop/runtime/task_sessions.py +137 -0
- power_loop-9.0.0/power_loop/runtime/timer_backend.py +60 -0
- power_loop-9.0.0/power_loop/runtime/timer_waits.py +339 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/runtime/timers.py +9 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/runtime/tool_archive.py +41 -43
- power_loop-9.0.0/power_loop/runtime/tool_attempts.py +407 -0
- power_loop-9.0.0/power_loop/runtime/tool_billing.py +141 -0
- power_loop-9.0.0/power_loop/runtime/tool_call_abort.py +174 -0
- power_loop-9.0.0/power_loop/runtime/tool_call_plans.py +428 -0
- power_loop-9.0.0/power_loop/runtime/tool_context.py +274 -0
- power_loop-9.0.0/power_loop/runtime/tool_contracts.py +312 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/runtime/tool_truncation.py +1 -2
- power_loop-9.0.0/power_loop/runtime/wait_decisions.py +228 -0
- power_loop-9.0.0/power_loop/runtime/wait_dispositions.py +205 -0
- power_loop-9.0.0/power_loop/runtime/wait_effects.py +299 -0
- power_loop-9.0.0/power_loop/runtime/wait_notifications.py +240 -0
- power_loop-9.0.0/power_loop/runtime/wait_reviews.py +193 -0
- power_loop-9.0.0/power_loop/runtime/wait_terminal.py +271 -0
- power_loop-9.0.0/power_loop/runtime/workflow_controller.py +196 -0
- power_loop-9.0.0/power_loop/runtime/workflow_profile.py +164 -0
- power_loop-9.0.0/power_loop/runtime/workflow_resources.py +279 -0
- power_loop-9.0.0/power_loop/runtime/workflow_restore.py +249 -0
- power_loop-9.0.0/power_loop/runtime/workflow_restore_execution.py +367 -0
- power_loop-9.0.0/power_loop/runtime/workflow_restore_resources.py +168 -0
- power_loop-9.0.0/power_loop/runtime/workflow_restore_runner.py +246 -0
- power_loop-9.0.0/power_loop/runtime/workflow_results.py +304 -0
- power_loop-9.0.0/power_loop/runtime/workflow_resume.py +262 -0
- power_loop-9.0.0/power_loop/runtime/workflow_tasks.py +488 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/tools/__init__.py +5 -2
- power_loop-9.0.0/power_loop/tools/background_contract.py +71 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/tools/blackboard.py +2 -1
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/tools/default_manifest.py +68 -36
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/tools/default_tools.py +310 -123
- power_loop-9.0.0/power_loop/tools/jobs.py +238 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/tools/registry.py +76 -46
- power_loop-9.0.0/power_loop/tools/shell_contract.py +46 -0
- power_loop-9.0.0/power_loop/tools/skill_contract.py +22 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/tools/spawn_agent.py +94 -30
- power_loop-9.0.0/power_loop/tools/wait_handoff.py +81 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/workflow/__init__.py +4 -0
- power_loop-9.0.0/power_loop/workflow/_process_guard.py +81 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/workflow/admission.py +7 -1
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/workflow/api.py +22 -1
- power_loop-9.0.0/power_loop/workflow/continuation.py +167 -0
- power_loop-9.0.0/power_loop/workflow/control.py +149 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/workflow/engine.py +346 -38
- power_loop-9.0.0/power_loop/workflow/facade.py +527 -0
- power_loop-9.0.0/power_loop/workflow/legacy_history.py +555 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/workflow/result.py +6 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/workflow/resume.py +18 -44
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/workflow/runner.py +302 -22
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/workflow/spec.py +11 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/workflow/subprocess_executor.py +73 -74
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/workflow/tool.py +57 -140
- power_loop-9.0.0/power_loop/workflow/yaml.py +1513 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop.egg-info/PKG-INFO +47 -4
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop.egg-info/SOURCES.txt +100 -2
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop.egg-info/requires.txt +3 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/pyproject.toml +3 -2
- power_loop-7.3.1/power_loop/runtime/context_worker.py +0 -121
- power_loop-7.3.1/power_loop/runtime/store/context_cache.py +0 -223
- power_loop-7.3.1/power_loop/runtime/tool_context.py +0 -416
- {power_loop-7.3.1 → power_loop-9.0.0}/LICENSE +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/_vendor/__init__.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/_vendor/llm_client/__init__.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/_vendor/llm_client/anthropic_factory.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/_vendor/llm_client/attempt_scope.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/_vendor/llm_client/capabilities.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/_vendor/llm_client/interface.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/_vendor/llm_client/llm_tooling.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/_vendor/llm_client/llm_utils.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/_vendor/llm_client/proxy_fallback.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/agent/__init__.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/agent/follow_up.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/contracts/__init__.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/contracts/handlers.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/contracts/hook_contexts.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/contracts/hooks.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/contracts/messages.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/contracts/protocols.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/contrib/__init__.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/contrib/_redact.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/contrib/jsonl_sink.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/contrib/logging_sink.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/contrib/metrics_sink.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/contrib/otel_sink.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/core/agent_context.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/core/events.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/core/hooks.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/core/phase.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/core/runner.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/core/state.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/py.typed +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/runtime/bash_capture.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/runtime/blackboard.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/runtime/budget.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/runtime/cancellation.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/runtime/capability_probe.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/runtime/compact.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/runtime/env.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/runtime/fold.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/runtime/fold_adapter.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/runtime/history_projector.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/runtime/history_sanitize.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/runtime/human_input.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/runtime/image_recall.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/runtime/lineage.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/runtime/memory.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/runtime/notes.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/runtime/provider.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/runtime/steer.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/runtime/stop_policy.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/runtime/store/__init__.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/runtime/store/backends/__init__.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/runtime/store/backends/mysql.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/runtime/store/backends/postgres.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/runtime/store/capabilities.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/runtime/store/db.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/runtime/structured.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/runtime/stub_provider.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/runtime/todos.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/runtime/usage_estimate.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/tools/command_policy.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/tools/command_rules.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/workflow/introspect.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/workflow/journal.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop/workflow/worker.py +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop.egg-info/dependency_links.txt +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/power_loop.egg-info/top_level.txt +0 -0
- {power_loop-7.3.1 → power_loop-9.0.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: power-loop
|
|
3
|
-
Version:
|
|
3
|
+
Version: 9.0.0
|
|
4
4
|
Summary: Embeddable agent execution kernel — LLM loop, hooks, events, tools, dynamic sub-agents.
|
|
5
5
|
Author-email: zhangran <zhangran24@126.com>
|
|
6
6
|
License: MIT
|
|
@@ -18,7 +18,7 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.11
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.12
|
|
20
20
|
Classifier: Topic :: Software Development :: Libraries
|
|
21
|
-
Requires-Python: >=3.
|
|
21
|
+
Requires-Python: >=3.11
|
|
22
22
|
Description-Content-Type: text/markdown
|
|
23
23
|
License-File: LICENSE
|
|
24
24
|
Provides-Extra: anthropic
|
|
@@ -27,6 +27,8 @@ Provides-Extra: openai
|
|
|
27
27
|
Requires-Dist: openai>=1.52.0; extra == "openai"
|
|
28
28
|
Provides-Extra: skills
|
|
29
29
|
Requires-Dist: pyyaml>=6.0; extra == "skills"
|
|
30
|
+
Provides-Extra: workflow
|
|
31
|
+
Requires-Dist: pyyaml>=6.0; extra == "workflow"
|
|
30
32
|
Provides-Extra: pdf
|
|
31
33
|
Requires-Dist: pypdf>=5.3.0; extra == "pdf"
|
|
32
34
|
Provides-Extra: images
|
|
@@ -303,7 +305,8 @@ Every event carries a process-wide `seq` and a monotonic clock, so streams total
|
|
|
303
305
|
from power_loop.contrib.mcp import StdioMCPClient, register_mcp_tools # power-loop[mcp]
|
|
304
306
|
|
|
305
307
|
client = await StdioMCPClient("npx", ["-y", "@modelcontextprotocol/server-filesystem", "/data"]).connect()
|
|
306
|
-
|
|
308
|
+
from power_loop.runtime.pricing import Free
|
|
309
|
+
await register_mcp_tools(registry, client, pricing=Free("local_filesystem"), prefix="fs.") # MCP tools → power-loop ToolDefinitions
|
|
307
310
|
```
|
|
308
311
|
|
|
309
312
|
The seam is a tiny `MCPToolSource` Protocol, so the `mcp` SDK is optional and any client works.
|
|
@@ -342,7 +345,7 @@ POWER_LOOP_API_KEY=sk-...
|
|
|
342
345
|
POWER_LOOP_MODEL=gpt-4o-mini
|
|
343
346
|
```
|
|
344
347
|
|
|
345
|
-
Python 3.
|
|
348
|
+
Python 3.11+. See [Getting Started](docs/en/getting-started.md). Optional extras: `postgres`, `mysql`, `skills`, `pdf`, `prometheus`, `statsd`, `otel`, `mcp`.
|
|
346
349
|
|
|
347
350
|
---
|
|
348
351
|
|
|
@@ -375,3 +378,43 @@ power-loop **orchestrates; it does not, by itself, isolate.** The built-in `bash
|
|
|
375
378
|
- **Used by:** DeepTalk — the agent runtime for a 1-on-1 relationship-IM product's in-conversation agents. *(Using it in production? PR a line here.)*
|
|
376
379
|
- **Develop:** `pip install -e ".[dev]"` · `ruff check .` · `pytest -q --no-real` (drop `--no-real` for the live-LLM suite; set a `POWER_LOOP_TEST_PG_DSN` / `POWER_LOOP_TEST_MYSQL_DSN` to run the server-backend conformance suites).
|
|
377
380
|
- [Docs](docs/en/index.md) · [Architecture](docs/en/architecture.md) · [Storage backends](docs/en/user-guide/storage-backends.md) · [Changelog](CHANGELOG.md) · [Contributing](CONTRIBUTING.md) · [Security](SECURITY.md) · [License](LICENSE)
|
|
381
|
+
|
|
382
|
+
Trusted host tools declare roles, admission and background behavior through the
|
|
383
|
+
[RuntimeTool interface](docs/runtime-tool-contracts.md); names do not grant capabilities.
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
### Explicit tool pricing
|
|
387
|
+
|
|
388
|
+
Every registered tool definition must provide `pricing`, including local and
|
|
389
|
+
control tools: `ToolDefinition(name="local", description="...", pricing=Free("local"))`.
|
|
390
|
+
Import `Free` or implement `PricingSpec` from `power_loop.runtime.pricing`.
|
|
391
|
+
Registration rejects a missing declaration. The rule version is retained with the
|
|
392
|
+
registration and pending call identity; changing it requires a new registration,
|
|
393
|
+
and cannot silently reprice a previously planned call. Pricing metadata is not
|
|
394
|
+
sent to the model as part of the tool schema.
|
|
395
|
+
|
|
396
|
+
MCP registration requires either one explicit server pricing rule or a mapping
|
|
397
|
+
from remote tool names to rules. Missing entries reject the batch before any tool
|
|
398
|
+
is registered. An external service is not assumed free.
|
|
399
|
+
|
|
400
|
+
Native callbacks can return `ToolResult(content, billing_receipt=receipt)`; only
|
|
401
|
+
`content` is shown to the model. Async callbacks can instead
|
|
402
|
+
`await record_billing_receipt(receipt)` immediately after receiving provider billing
|
|
403
|
+
facts, before subsequent response processing might fail. Both helpers are
|
|
404
|
+
provisional top-level exports; `BillingReceipt` is in `power_loop.runtime.pricing`.
|
|
405
|
+
The awaited capture is bound to the original callback task and persisted call,
|
|
406
|
+
and repeating the same receipt is idempotent. A different receipt is rejected.
|
|
407
|
+
Receipt capture requires the native callback checkpoint path; it does not yet
|
|
408
|
+
support managed recovery adapters or legacy invocation. Persisting a receipt
|
|
409
|
+
does not acknowledge successful execution or settle a charge. Host budget and
|
|
410
|
+
settlement wiring is still required before paid tools can be enabled.
|
|
411
|
+
|
|
412
|
+
An ordinary native callback can invoke a priced internal tool with the provisional
|
|
413
|
+
`await invoke_priced_tool(definition, arguments, handler)` export. Each invocation
|
|
414
|
+
gets a separate budget attempt linked to its original source callback. Its price
|
|
415
|
+
must already be bound, and provider facts use the same `record_billing_receipt` API.
|
|
416
|
+
Internal concurrent tasks are retained and joined before the source callback ends.
|
|
417
|
+
After the exact charge is locally durable, API delivery failure leaves a pending
|
|
418
|
+
budget submission and preserves the real tool result. Reconciliation never calls
|
|
419
|
+
the handler again or reads a replacement price. Managed recovery sources without
|
|
420
|
+
an ordinary callback checkpoint still need a separate adapter.
|
|
@@ -227,7 +227,8 @@ Every event carries a process-wide `seq` and a monotonic clock, so streams total
|
|
|
227
227
|
from power_loop.contrib.mcp import StdioMCPClient, register_mcp_tools # power-loop[mcp]
|
|
228
228
|
|
|
229
229
|
client = await StdioMCPClient("npx", ["-y", "@modelcontextprotocol/server-filesystem", "/data"]).connect()
|
|
230
|
-
|
|
230
|
+
from power_loop.runtime.pricing import Free
|
|
231
|
+
await register_mcp_tools(registry, client, pricing=Free("local_filesystem"), prefix="fs.") # MCP tools → power-loop ToolDefinitions
|
|
231
232
|
```
|
|
232
233
|
|
|
233
234
|
The seam is a tiny `MCPToolSource` Protocol, so the `mcp` SDK is optional and any client works.
|
|
@@ -266,7 +267,7 @@ POWER_LOOP_API_KEY=sk-...
|
|
|
266
267
|
POWER_LOOP_MODEL=gpt-4o-mini
|
|
267
268
|
```
|
|
268
269
|
|
|
269
|
-
Python 3.
|
|
270
|
+
Python 3.11+. See [Getting Started](docs/en/getting-started.md). Optional extras: `postgres`, `mysql`, `skills`, `pdf`, `prometheus`, `statsd`, `otel`, `mcp`.
|
|
270
271
|
|
|
271
272
|
---
|
|
272
273
|
|
|
@@ -299,3 +300,43 @@ power-loop **orchestrates; it does not, by itself, isolate.** The built-in `bash
|
|
|
299
300
|
- **Used by:** DeepTalk — the agent runtime for a 1-on-1 relationship-IM product's in-conversation agents. *(Using it in production? PR a line here.)*
|
|
300
301
|
- **Develop:** `pip install -e ".[dev]"` · `ruff check .` · `pytest -q --no-real` (drop `--no-real` for the live-LLM suite; set a `POWER_LOOP_TEST_PG_DSN` / `POWER_LOOP_TEST_MYSQL_DSN` to run the server-backend conformance suites).
|
|
301
302
|
- [Docs](docs/en/index.md) · [Architecture](docs/en/architecture.md) · [Storage backends](docs/en/user-guide/storage-backends.md) · [Changelog](CHANGELOG.md) · [Contributing](CONTRIBUTING.md) · [Security](SECURITY.md) · [License](LICENSE)
|
|
303
|
+
|
|
304
|
+
Trusted host tools declare roles, admission and background behavior through the
|
|
305
|
+
[RuntimeTool interface](docs/runtime-tool-contracts.md); names do not grant capabilities.
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
### Explicit tool pricing
|
|
309
|
+
|
|
310
|
+
Every registered tool definition must provide `pricing`, including local and
|
|
311
|
+
control tools: `ToolDefinition(name="local", description="...", pricing=Free("local"))`.
|
|
312
|
+
Import `Free` or implement `PricingSpec` from `power_loop.runtime.pricing`.
|
|
313
|
+
Registration rejects a missing declaration. The rule version is retained with the
|
|
314
|
+
registration and pending call identity; changing it requires a new registration,
|
|
315
|
+
and cannot silently reprice a previously planned call. Pricing metadata is not
|
|
316
|
+
sent to the model as part of the tool schema.
|
|
317
|
+
|
|
318
|
+
MCP registration requires either one explicit server pricing rule or a mapping
|
|
319
|
+
from remote tool names to rules. Missing entries reject the batch before any tool
|
|
320
|
+
is registered. An external service is not assumed free.
|
|
321
|
+
|
|
322
|
+
Native callbacks can return `ToolResult(content, billing_receipt=receipt)`; only
|
|
323
|
+
`content` is shown to the model. Async callbacks can instead
|
|
324
|
+
`await record_billing_receipt(receipt)` immediately after receiving provider billing
|
|
325
|
+
facts, before subsequent response processing might fail. Both helpers are
|
|
326
|
+
provisional top-level exports; `BillingReceipt` is in `power_loop.runtime.pricing`.
|
|
327
|
+
The awaited capture is bound to the original callback task and persisted call,
|
|
328
|
+
and repeating the same receipt is idempotent. A different receipt is rejected.
|
|
329
|
+
Receipt capture requires the native callback checkpoint path; it does not yet
|
|
330
|
+
support managed recovery adapters or legacy invocation. Persisting a receipt
|
|
331
|
+
does not acknowledge successful execution or settle a charge. Host budget and
|
|
332
|
+
settlement wiring is still required before paid tools can be enabled.
|
|
333
|
+
|
|
334
|
+
An ordinary native callback can invoke a priced internal tool with the provisional
|
|
335
|
+
`await invoke_priced_tool(definition, arguments, handler)` export. Each invocation
|
|
336
|
+
gets a separate budget attempt linked to its original source callback. Its price
|
|
337
|
+
must already be bound, and provider facts use the same `record_billing_receipt` API.
|
|
338
|
+
Internal concurrent tasks are retained and joined before the source callback ends.
|
|
339
|
+
After the exact charge is locally durable, API delivery failure leaves a pending
|
|
340
|
+
budget submission and preserves the real tool result. Reconciliation never calls
|
|
341
|
+
the handler again or reads a replacement price. Managed recovery sources without
|
|
342
|
+
an ordinary callback checkpoint still need a separate adapter.
|
|
@@ -15,7 +15,7 @@ Stability tiers
|
|
|
15
15
|
无版本承诺,可随时变更或删除。
|
|
16
16
|
"""
|
|
17
17
|
|
|
18
|
-
__version__ = "
|
|
18
|
+
__version__ = "9.0.0"
|
|
19
19
|
|
|
20
20
|
# Public LLM contract (SDK-free) re-exported so callers (e.g. writing llm.* hooks or
|
|
21
21
|
# a custom LLMService) don't reach into the internal vendored transport package (H3.4).
|
|
@@ -76,6 +76,7 @@ from power_loop.contracts.event_payloads import (
|
|
|
76
76
|
LlmCallCompletedPayload,
|
|
77
77
|
LlmCallStartedPayload,
|
|
78
78
|
LlmDegradedPayload,
|
|
79
|
+
LlmResponseReceivedPayload,
|
|
79
80
|
LlmRetryAttemptedPayload,
|
|
80
81
|
LoopCancelledPayload,
|
|
81
82
|
MemoryFailedPayload,
|
|
@@ -97,6 +98,7 @@ from power_loop.contracts.event_payloads import (
|
|
|
97
98
|
SubagentTaskStartPayload,
|
|
98
99
|
SubagentTextPayload,
|
|
99
100
|
SystemLogPayload,
|
|
101
|
+
TaskSupervisionClosedPayload,
|
|
100
102
|
TimerFiredPayload,
|
|
101
103
|
TodoUpdatedPayload,
|
|
102
104
|
ToolCallCompletedPayload,
|
|
@@ -136,7 +138,7 @@ from power_loop.contracts.protocols import (
|
|
|
136
138
|
HookManagerProtocol,
|
|
137
139
|
ToolArgsValidator,
|
|
138
140
|
)
|
|
139
|
-
from power_loop.contracts.tools import ToolDefinition, validate_tool_args
|
|
141
|
+
from power_loop.contracts.tools import ToolDefinition, ToolResult, validate_tool_args
|
|
140
142
|
from power_loop.core.agent_context import get_effective_tools
|
|
141
143
|
from power_loop.core.events import AgentEventBus
|
|
142
144
|
from power_loop.core.hooks import AgentHooks
|
|
@@ -174,6 +176,7 @@ from power_loop.runtime.memory import (
|
|
|
174
176
|
MemorySnapshot,
|
|
175
177
|
tag_as_memory,
|
|
176
178
|
)
|
|
179
|
+
from power_loop.runtime.nested_tools import invoke_priced_tool
|
|
177
180
|
from power_loop.runtime.notes import (
|
|
178
181
|
DEFAULT_NOTES_POLICY,
|
|
179
182
|
NoteMemory,
|
|
@@ -239,6 +242,7 @@ from power_loop.runtime.structured import (
|
|
|
239
242
|
parse_structured,
|
|
240
243
|
)
|
|
241
244
|
from power_loop.runtime.timers import TimerDelivery, TimerRunner
|
|
245
|
+
from power_loop.runtime.tool_billing import record_billing_receipt, tool_billing_active, tool_billing_managed
|
|
242
246
|
from power_loop.runtime.tool_context import ToolContextConfig
|
|
243
247
|
from power_loop.tools import (
|
|
244
248
|
DEFAULT_TOOL_HANDLERS,
|
|
@@ -254,6 +258,7 @@ from power_loop.tools.default_manifest import (
|
|
|
254
258
|
TOOL_PRESETS,
|
|
255
259
|
get_tool_definitions,
|
|
256
260
|
)
|
|
261
|
+
from power_loop.tools.jobs import JOBS_DEFINITION, register_jobs_tools
|
|
257
262
|
from power_loop.tools.registry import AsyncToolInSyncContext
|
|
258
263
|
from power_loop.tools.spawn_agent import (
|
|
259
264
|
SPAWN_AGENT_DEFINITION,
|
|
@@ -397,6 +402,7 @@ __all__ = [
|
|
|
397
402
|
"request_user_input",
|
|
398
403
|
"LlmCallStartedPayload",
|
|
399
404
|
"LlmCallCompletedPayload",
|
|
405
|
+
"LlmResponseReceivedPayload",
|
|
400
406
|
"LlmRetryAttemptedPayload",
|
|
401
407
|
"SteerInterruptedPayload",
|
|
402
408
|
"LlmDegradedPayload",
|
|
@@ -489,6 +495,7 @@ __all__ = [
|
|
|
489
495
|
"SubagentTextPayload",
|
|
490
496
|
"SubagentLimitPayload",
|
|
491
497
|
"SubagentCompletedPayload",
|
|
498
|
+
"TaskSupervisionClosedPayload",
|
|
492
499
|
"EventHandler",
|
|
493
500
|
"HookHandler",
|
|
494
501
|
"ToolHandler",
|
|
@@ -499,6 +506,11 @@ __all__ = [
|
|
|
499
506
|
"ChildRunGuard",
|
|
500
507
|
"get_effective_tools",
|
|
501
508
|
"ToolDefinition",
|
|
509
|
+
"ToolResult",
|
|
510
|
+
"record_billing_receipt",
|
|
511
|
+
"tool_billing_managed",
|
|
512
|
+
"tool_billing_active",
|
|
513
|
+
"invoke_priced_tool",
|
|
502
514
|
"validate_tool_args",
|
|
503
515
|
"ToolRegistry",
|
|
504
516
|
"AsyncToolInSyncContext",
|
|
@@ -523,6 +535,8 @@ __all__ = [
|
|
|
523
535
|
"build_explore_subagent_system_prompt",
|
|
524
536
|
"format_tool_catalog",
|
|
525
537
|
"register_spawn_agent",
|
|
538
|
+
"register_jobs_tools",
|
|
539
|
+
"JOBS_DEFINITION",
|
|
526
540
|
"SPAWN_AGENT_DEFINITION",
|
|
527
541
|
"LOAD_SKILL_DEFINITION",
|
|
528
542
|
"SkillLoader",
|
|
@@ -634,7 +634,8 @@ class OpenAICompatibleChatLLMService(LLMService):
|
|
|
634
634
|
current_request = request
|
|
635
635
|
restart_count = 0
|
|
636
636
|
max_restarts = max(0, int(self._cfg.stream_max_restarts or 0))
|
|
637
|
-
|
|
637
|
+
from .attempt_scope import single_attempt
|
|
638
|
+
resume_enabled = bool(self._cfg.stream_resume_on_error) and not single_attempt.get()
|
|
638
639
|
|
|
639
640
|
def _as_dict(obj: Any) -> Any:
|
|
640
641
|
if obj is None:
|
|
@@ -27,7 +27,7 @@ class AttachmentRef:
|
|
|
27
27
|
mime_type: str
|
|
28
28
|
kind: str
|
|
29
29
|
#: 宿主给的「怎么把这张图找回来」坐标,原样带进蒸馏与降级文案(DeepTalk 放
|
|
30
|
-
#:
|
|
30
|
+
#: 宿主提供的不透明资源引用)。空字符串 = 没有。
|
|
31
31
|
ref: str = ""
|
|
32
32
|
|
|
33
33
|
|
|
@@ -192,7 +192,7 @@ def _render_image_attachment(ref: AttachmentRef, path: Path, capabilities: Model
|
|
|
192
192
|
为什么降级不等于回到老毛病:老实现塞的是一句含糊的
|
|
193
193
|
"The current model does not support image input",混在附件描述里,模型读过去照样按
|
|
194
194
|
「我看过这张图」的语气编答案。这里的占位必须做到两件事——**说清楚模型没有看到**,并且
|
|
195
|
-
**给出把图找回来的坐标**(``ref
|
|
195
|
+
**给出把图找回来的坐标**(``ref``,由宿主解释和回取原件)。
|
|
196
196
|
调用方想要「发图给看不了图的模型就报错」,用 ``capabilities.require_image_input()`` 自查。
|
|
197
197
|
"""
|
|
198
198
|
if capabilities.supports_image_input is True:
|
|
@@ -226,7 +226,7 @@ def _render_image_attachment(ref: AttachmentRef, path: Path, capabilities: Model
|
|
|
226
226
|
)
|
|
227
227
|
text = (
|
|
228
228
|
f"[图片 {describe_attachment_ref(ref)}——**当前模型看不了图片,你没有看到它的内容**。"
|
|
229
|
-
"
|
|
229
|
+
"不要凭空描述这张图;需要时由可看图的工具或模型按上面的坐标回取原图。]"
|
|
230
230
|
)
|
|
231
231
|
return PreparedAttachment(
|
|
232
232
|
ref=ref,
|
|
@@ -17,6 +17,8 @@ import logging
|
|
|
17
17
|
from typing import Any, Protocol, runtime_checkable
|
|
18
18
|
|
|
19
19
|
from power_loop.agent.types import LoopMessage
|
|
20
|
+
from power_loop.contracts.errors import InboxClaimChanged
|
|
21
|
+
from power_loop.core.agent_context import get_current_loop
|
|
20
22
|
from power_loop.runtime.store.store import SessionStore
|
|
21
23
|
from power_loop.runtime.store.types import (
|
|
22
24
|
CONTENT_ENCODING_JSON,
|
|
@@ -149,6 +151,11 @@ class SQLiteSink:
|
|
|
149
151
|
|
|
150
152
|
# ── messages ───────────────────────────────────────────────
|
|
151
153
|
|
|
154
|
+
@property
|
|
155
|
+
def persisted_assistant_seq(self) -> int | None:
|
|
156
|
+
"""Actual DB seq, never the index of an in-memory/projected history row."""
|
|
157
|
+
return self._assistant_seq
|
|
158
|
+
|
|
152
159
|
async def on_round_started(self, round_index: int) -> None:
|
|
153
160
|
await self.store.set_round_index(self.session_id, round_index)
|
|
154
161
|
|
|
@@ -171,6 +178,8 @@ class SQLiteSink:
|
|
|
171
178
|
)
|
|
172
179
|
self._history_seqs.append(seq)
|
|
173
180
|
self._history_ord.append(seq)
|
|
181
|
+
from power_loop.runtime.task_runtime import bind_tool_receipt
|
|
182
|
+
await bind_tool_receipt(self.store, self.session_id, self._assistant_seq, tool_call_id, seq)
|
|
174
183
|
# Auto-resolve pending: when the matching tool message lands,
|
|
175
184
|
# drop it from the unresolved set and clear pending once empty.
|
|
176
185
|
if tool_call_id and tool_call_id in self._unresolved:
|
|
@@ -204,6 +213,9 @@ class SQLiteSink:
|
|
|
204
213
|
if role == "assistant":
|
|
205
214
|
tool_calls = message.get("tool_calls")
|
|
206
215
|
text, structured = _encode_content(message.get("content"))
|
|
216
|
+
from power_loop.runtime.task_runtime import atomic_pending_required
|
|
217
|
+
options = ({"pending_tool_calls": True}
|
|
218
|
+
if tool_calls and atomic_pending_required(self.store, self.session_id) else {})
|
|
207
219
|
seq = await self.store.append_message(
|
|
208
220
|
self.session_id,
|
|
209
221
|
role="assistant",
|
|
@@ -213,6 +225,7 @@ class SQLiteSink:
|
|
|
213
225
|
meta=_meta_with_content_encoding(message.get("meta"), structured=structured),
|
|
214
226
|
send_index=message.get("send_index"),
|
|
215
227
|
hook_injected=message.get("hook_injected"),
|
|
228
|
+
**options,
|
|
216
229
|
)
|
|
217
230
|
self._history_seqs.append(seq)
|
|
218
231
|
self._history_ord.append(seq)
|
|
@@ -227,16 +240,26 @@ class SQLiteSink:
|
|
|
227
240
|
# Which inbox items this row carries (dedupe/audit; design/124 §6).
|
|
228
241
|
meta = {**(meta or {}), "inbox": {k: inbox[k] for k in ("item_ids", "kinds")
|
|
229
242
|
if k in inbox}}
|
|
230
|
-
|
|
231
|
-
self.
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
243
|
+
try:
|
|
244
|
+
seq = await self.store.append_message(
|
|
245
|
+
self.session_id,
|
|
246
|
+
role=str(role or "user"),
|
|
247
|
+
content=text,
|
|
248
|
+
name=message.get("name"),
|
|
249
|
+
round_index=round_index,
|
|
250
|
+
meta=meta,
|
|
251
|
+
send_index=message.get("send_index"),
|
|
252
|
+
inbox=inbox,
|
|
253
|
+
)
|
|
254
|
+
except InboxClaimChanged:
|
|
255
|
+
# The batch was rolled back. Release only its surviving claim rows;
|
|
256
|
+
# the pipeline will render a fresh batch, never reuse the old text.
|
|
257
|
+
await self.store.inbox_release(self.session_id, inbox["claim_token"])
|
|
258
|
+
pending = (await self.store.inbox_counts(self.session_id))["pending"]
|
|
259
|
+
loop = get_current_loop()
|
|
260
|
+
if pending and loop is not None:
|
|
261
|
+
loop._note_committed_inbox(self.session_id)
|
|
262
|
+
raise
|
|
240
263
|
self._history_seqs.append(seq)
|
|
241
264
|
self._history_ord.append(seq)
|
|
242
265
|
|