agentengine-sdk-python 0.8.5__py3-none-any.whl → 0.8.6__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.
- {agentengine_sdk_python-0.8.5.dist-info → agentengine_sdk_python-0.8.6.dist-info}/METADATA +12 -8
- {agentengine_sdk_python-0.8.5.dist-info → agentengine_sdk_python-0.8.6.dist-info}/RECORD +823 -388
- ksadk/_async_iterators.py +79 -0
- ksadk/_build_provenance.json +1 -1
- ksadk/_model_anthropic.py +49 -0
- ksadk/_model_input.py +86 -0
- ksadk/_model_json.py +40 -0
- ksadk/_model_reasoning.py +17 -0
- ksadk/_model_refusal.py +20 -0
- ksadk/_model_sse.py +163 -0
- ksadk/a2a/__init__.py +157 -167
- ksadk/a2a/_invocation_state.py +86 -0
- ksadk/a2a/_space_client_events.py +33 -22
- ksadk/a2a/adk.py +194 -0
- ksadk/a2a/adk_invocation_context.py +242 -0
- ksadk/a2a/adk_runtime.py +126 -0
- ksadk/a2a/adk_tools.py +319 -0
- ksadk/a2a/bootstrap.py +149 -23
- ksadk/a2a/build_validation.py +147 -0
- ksadk/a2a/card.py +23 -15
- ksadk/a2a/codex.py +160 -0
- ksadk/a2a/codex_checkpoint.py +111 -0
- ksadk/a2a/codex_recovery.py +96 -0
- ksadk/a2a/codex_recovery_events.py +123 -0
- ksadk/a2a/codex_runtime.py +198 -0
- ksadk/a2a/codex_session_checkpoint.py +162 -0
- ksadk/a2a/composition.py +88 -0
- ksadk/a2a/context_store.py +118 -19
- ksadk/a2a/control_plane.py +108 -106
- ksadk/a2a/event_adapter.py +92 -193
- ksadk/a2a/executor.py +257 -85
- ksadk/a2a/exposure.py +106 -0
- ksadk/a2a/external_transport.py +3 -4
- ksadk/a2a/identity.py +106 -12
- ksadk/a2a/ids.py +14 -3
- ksadk/a2a/inbound_kernel.py +133 -0
- ksadk/a2a/interaction_wire.py +76 -0
- ksadk/a2a/invocation_checkpoint.py +274 -0
- ksadk/a2a/invocation_policy.py +87 -0
- ksadk/a2a/invocation_publication.py +210 -0
- ksadk/a2a/invocation_service.py +972 -0
- ksadk/a2a/invocation_types.py +459 -0
- ksadk/a2a/kernel_task_adapter.py +259 -0
- ksadk/a2a/langgraph.py +20 -7
- ksadk/a2a/langgraph_invocation_context.py +136 -0
- ksadk/a2a/langgraph_managed.py +150 -0
- ksadk/a2a/langgraph_recovery.py +32 -0
- ksadk/a2a/langgraph_runtime.py +89 -0
- ksadk/a2a/langgraph_tool_runtime.py +132 -0
- ksadk/a2a/langgraph_tools.py +63 -0
- ksadk/a2a/message_digest.py +40 -0
- ksadk/a2a/platform.py +389 -0
- ksadk/a2a/policy_refs.py +13 -0
- ksadk/a2a/presentation.py +402 -0
- ksadk/a2a/product_kernel_facts.py +177 -0
- ksadk/a2a/product_key.py +99 -0
- ksadk/a2a/protocol_errors.py +116 -0
- ksadk/a2a/result_text.py +49 -0
- ksadk/a2a/resume_store.py +93 -3
- ksadk/a2a/routes.py +6 -1
- ksadk/a2a/runtime_approval.py +198 -0
- ksadk/a2a/runtime_input.py +151 -0
- ksadk/a2a/server.py +16 -7
- ksadk/a2a/space_client.py +101 -16
- ksadk/a2a/task_adapter.py +19 -5
- ksadk/a2a/task_event_dispatcher.py +39 -2
- ksadk/a2a/task_event_outbox.py +215 -0
- ksadk/a2a/task_event_outbox_owned.py +130 -0
- ksadk/a2a/task_store.py +37 -0
- ksadk/a2a/tool_activity.py +176 -0
- ksadk/a2a/workload_tokens.py +134 -0
- ksadk/api/client.py +32 -79
- ksadk/api/sse_stream.py +96 -0
- ksadk/builders/code_builder.py +83 -106
- ksadk/builders/container_builder.py +11 -29
- ksadk/builders/framework_requirements.py +2 -0
- ksadk/builders/managed_runtime_builder.py +12 -0
- ksadk/builders/source_provenance.py +87 -0
- ksadk/cli/cmd_a2a.py +43 -0
- ksadk/cli/cmd_build.py +9 -0
- ksadk/cli/cmd_deploy.py +27 -2
- ksadk/cli/cmd_invoke.py +45 -92
- ksadk/cli/cmd_studio.py +5 -1
- ksadk/cli/cmd_web.py +2 -0
- ksadk/cli/hosted_invoke.py +206 -0
- ksadk/cli/invoke_content.py +60 -0
- ksadk/cli/runtime_bootstrap.py +27 -5
- ksadk/codex/_expert_request_reader.py +48 -0
- ksadk/codex/a2a_tools.py +140 -0
- ksadk/codex/async_approval.py +169 -0
- ksadk/codex/async_client.py +938 -0
- ksadk/codex/async_proxy.py +216 -0
- ksadk/codex/base_runtime.py +25 -0
- ksadk/codex/client.py +3 -1048
- ksadk/codex/client_payloads.py +85 -0
- ksadk/codex/rollout_checkpoint.py +264 -0
- ksadk/codex/runtime.py +290 -54
- ksadk/common/auth.py +2 -2
- ksadk/common/cloud_headers.py +36 -0
- ksadk/common/diagnostics.py +34 -0
- ksadk/common/kop_client.py +2 -1
- ksadk/configs/env_registry.py +106 -127
- ksadk/configs/env_registry_a2a.py +173 -0
- ksadk/configs/env_registry_studio.py +53 -5
- ksadk/configs/global_config.py +1 -0
- ksadk/configs/settings.py +6 -0
- ksadk/context_engine/assembler.py +9 -0
- ksadk/context_engine/contributors.py +80 -17
- ksadk/context_engine/instruction_authority.py +18 -0
- ksadk/conversations/attachments.py +53 -15
- ksadk/conversations/contracts.py +28 -9
- ksadk/conversations/message_projection.py +30 -125
- ksadk/conversations/message_projection_blocks.py +137 -0
- ksadk/conversations/model_context.py +11 -3
- ksadk/conversations/projector.py +63 -0
- ksadk/conversations/runtime_compaction.py +7 -2
- ksadk/conversations/runtime_input.py +16 -4
- ksadk/conversations/runtime_payloads.py +4 -0
- ksadk/conversations/runtime_persistence.py +192 -24
- ksadk/conversations/runtime_preparation.py +85 -6
- ksadk/conversations/runtime_projection.py +217 -0
- ksadk/conversations/runtime_resume.py +141 -70
- ksadk/conversations/tool_receipt_resume.py +387 -0
- ksadk/conversations/tool_receipts.py +32 -0
- ksadk/deployment/env_forward.py +5 -0
- ksadk/deployment/manager.py +15 -6
- ksadk/deployment/providers/serverless.py +76 -136
- ksadk/deployment/providers/serverless_payloads.py +122 -0
- ksadk/evaluation/agent_eval_client.py +2 -1
- ksadk/evaluation/contracts.py +78 -3
- ksadk/evaluation/evaluators.py +166 -1
- ksadk/evaluation/evidence.py +271 -4
- ksadk/evaluation/local_adapter.py +76 -12
- ksadk/evaluation/studio_build_adapter.py +73 -9
- ksadk/events/adapters/_a2a_checkpoint.py +151 -0
- ksadk/events/adapters/_a2a_snapshot.py +18 -14
- ksadk/events/adapters/_a2a_support.py +15 -3
- ksadk/events/adapters/_a2a_tool_activity.py +123 -0
- ksadk/events/adapters/_codex_errors.py +49 -0
- ksadk/events/adapters/_langgraph_support.py +12 -1
- ksadk/events/adapters/a2a.py +54 -26
- ksadk/events/adapters/adk.py +188 -30
- ksadk/events/adapters/codex.py +33 -7
- ksadk/events/adapters/langgraph.py +16 -6
- ksadk/events/canonical.py +5 -0
- ksadk/events/canonical_store.py +200 -4
- ksadk/events/reducer.py +82 -0
- ksadk/events/run_authority.py +176 -0
- ksadk/harness/__init__.py +4 -0
- ksadk/harness/artifact_adapters/__init__.py +1 -0
- ksadk/harness/artifact_adapters/native_page_budget.py +172 -0
- ksadk/harness/artifact_adapters/native_search.py +249 -0
- ksadk/harness/artifact_adapters/native_search_budget.py +134 -0
- ksadk/harness/artifact_adapters/native_store.py +115 -0
- ksadk/harness/artifact_adapters/native_tool.py +222 -0
- ksadk/harness/artifact_store.py +26 -9
- ksadk/harness/browser_adapters/__init__.py +6 -0
- ksadk/harness/browser_adapters/native_browser_tool.py +92 -0
- ksadk/harness/browser_adapters/native_dom_snapshot.py +187 -0
- ksadk/harness/browser_adapters/native_playwright.py +953 -0
- ksadk/harness/browser_adapters/native_vision.py +295 -0
- ksadk/harness/capability_adapters/__init__.py +1 -0
- ksadk/harness/capability_adapters/native_dsh.py +289 -0
- ksadk/harness/capability_runtime.py +104 -9
- ksadk/harness/compaction_record.py +31 -0
- ksadk/harness/compiler.py +3 -3
- ksadk/harness/config.py +28 -5
- ksadk/harness/conformance/contract.py +9 -11
- ksadk/harness/context_engine.py +18 -33
- ksadk/harness/context_sections.py +397 -0
- ksadk/harness/diagnostic_adapters/__init__.py +1 -0
- ksadk/harness/diagnostic_adapters/contracts.py +70 -0
- ksadk/harness/diagnostic_adapters/lsp_rpc.py +210 -0
- ksadk/harness/diagnostic_adapters/native_lsp.py +286 -0
- ksadk/harness/engine/completion_guard.py +16 -1
- ksadk/harness/engine/context_pipeline.py +864 -92
- ksadk/harness/engine/graph_builder.py +123 -630
- ksadk/harness/engine/hooked_reasoner.py +380 -0
- ksadk/harness/engine/langgraph.py +126 -8
- ksadk/harness/engine/mcp_disclosure.py +65 -114
- ksadk/harness/engine/phases/__init__.py +6 -0
- ksadk/harness/engine/phases/context.py +48 -0
- ksadk/harness/engine/phases/interaction.py +31 -0
- ksadk/harness/engine/phases/reason.py +552 -0
- ksadk/harness/engine/phases/tools.py +638 -0
- ksadk/harness/engine/policy_runtime.py +21 -10
- ksadk/harness/engine/postgres_checkpointer.py +325 -2
- ksadk/harness/engine/run_control_bridge.py +21 -1
- ksadk/harness/engine/run_types.py +10 -0
- ksadk/harness/engine/skill_disclosure.py +98 -12
- ksadk/harness/engine/steering.py +177 -0
- ksadk/harness/engine/subagents.py +18 -1
- ksadk/harness/engine/tool_dispatch.py +112 -7
- ksadk/harness/events.py +39 -2
- ksadk/harness/execution_policy.py +10 -0
- ksadk/harness/hooks.py +259 -0
- ksadk/harness/loop/reason.py +281 -36
- ksadk/harness/loop/tools.py +157 -12
- ksadk/harness/managed_runtime.py +254 -94
- ksadk/harness/mcp_disclosure_contract.py +85 -0
- ksadk/harness/mcp_result_policy.py +44 -0
- ksadk/harness/mcp_runtime.py +33 -0
- ksadk/harness/micro_compact.py +69 -0
- ksadk/harness/model_adapters/__init__.py +1 -0
- ksadk/harness/model_adapters/native_mcp_media.py +100 -0
- ksadk/harness/model_adapters/native_mcp_result.py +36 -0
- ksadk/harness/model_adapters/native_openai.py +703 -0
- ksadk/harness/model_capability.py +223 -8
- ksadk/harness/model_provider.py +214 -4
- ksadk/harness/multimodal.py +57 -0
- ksadk/harness/native/__init__.py +1 -0
- ksadk/harness/native/activation_contract.py +25 -0
- ksadk/harness/native/activation_recovery.py +29 -0
- ksadk/harness/native/adapters.py +291 -0
- ksadk/harness/native/artifact_contract.py +14 -0
- ksadk/harness/native/artifact_partial_read_capacity.py +171 -0
- ksadk/harness/native/artifact_read_capacity.py +182 -0
- ksadk/harness/native/artifact_read_contract.py +27 -0
- ksadk/harness/native/artifact_runtime.py +408 -0
- ksadk/harness/native/artifact_search_capacity.py +206 -0
- ksadk/harness/native/artifact_search_contract.py +40 -0
- ksadk/harness/native/artifact_single_read_capacity.py +170 -0
- ksadk/harness/native/browser_contract.py +229 -0
- ksadk/harness/native/browser_recovery.py +183 -0
- ksadk/harness/native/capabilities.py +204 -0
- ksadk/harness/native/child_approval.py +60 -0
- ksadk/harness/native/child_budget.py +136 -0
- ksadk/harness/native/child_cancellation.py +80 -0
- ksadk/harness/native/child_input.py +98 -0
- ksadk/harness/native/child_input_runtime.py +59 -0
- ksadk/harness/native/child_interruption.py +81 -0
- ksadk/harness/native/child_lifecycle.py +246 -0
- ksadk/harness/native/child_lifecycle_contract.py +91 -0
- ksadk/harness/native/child_notifications.py +75 -0
- ksadk/harness/native/child_promotion.py +66 -0
- ksadk/harness/native/command_runtime.py +99 -0
- ksadk/harness/native/completion.py +302 -0
- ksadk/harness/native/completion_text.py +116 -0
- ksadk/harness/native/context.py +358 -0
- ksadk/harness/native/context_artifact_page.py +75 -0
- ksadk/harness/native/context_compaction_projection.py +131 -0
- ksadk/harness/native/context_contributions.py +320 -0
- ksadk/harness/native/context_contributor_contract.py +133 -0
- ksadk/harness/native/context_driver.py +820 -0
- ksadk/harness/native/context_fresh_tools.py +49 -0
- ksadk/harness/native/context_history.py +272 -0
- ksadk/harness/native/context_microcompact.py +61 -0
- ksadk/harness/native/context_partial_summary.py +58 -0
- ksadk/harness/native/context_partition.py +492 -0
- ksadk/harness/native/context_pruning.py +275 -0
- ksadk/harness/native/context_reduction.py +60 -0
- ksadk/harness/native/context_replay.py +148 -0
- ksadk/harness/native/context_request_projection.py +526 -0
- ksadk/harness/native/context_runtime.py +976 -0
- ksadk/harness/native/context_runtime_contracts.py +409 -0
- ksadk/harness/native/context_search.py +112 -0
- ksadk/harness/native/context_source_fragments.py +188 -0
- ksadk/harness/native/context_source_progress.py +119 -0
- ksadk/harness/native/context_summary_planning.py +788 -0
- ksadk/harness/native/context_summary_recovery.py +105 -0
- ksadk/harness/native/delivery_review.py +331 -0
- ksadk/harness/native/delivery_review_contract.py +54 -0
- ksadk/harness/native/effect_admission.py +82 -0
- ksadk/harness/native/effect_control.py +318 -0
- ksadk/harness/native/effect_receipts.py +729 -0
- ksadk/harness/native/effect_runtime.py +303 -0
- ksadk/harness/native/effective_configuration.py +28 -0
- ksadk/harness/native/engine.py +945 -0
- ksadk/harness/native/final_output.py +34 -0
- ksadk/harness/native/final_submission.py +58 -0
- ksadk/harness/native/finalization_workspace.py +96 -0
- ksadk/harness/native/fork.py +151 -0
- ksadk/harness/native/guidance.py +91 -0
- ksadk/harness/native/history.py +381 -0
- ksadk/harness/native/hook_executor.py +205 -0
- ksadk/harness/native/hook_runtime_contract.py +282 -0
- ksadk/harness/native/interaction_observation.py +117 -0
- ksadk/harness/native/lease.py +103 -0
- ksadk/harness/native/machine.py +974 -0
- ksadk/harness/native/mcp_result_governance.py +156 -0
- ksadk/harness/native/model.py +570 -0
- ksadk/harness/native/model_feedback.py +63 -0
- ksadk/harness/native/model_input.py +30 -0
- ksadk/harness/native/model_recovery_contract.py +35 -0
- ksadk/harness/native/model_replay.py +128 -0
- ksadk/harness/native/model_request.py +133 -0
- ksadk/harness/native/model_runtime.py +196 -0
- ksadk/harness/native/model_takeover.py +419 -0
- ksadk/harness/native/model_tool_batch.py +40 -0
- ksadk/harness/native/observed_progress.py +46 -0
- ksadk/harness/native/operator_guidance.py +201 -0
- ksadk/harness/native/output_contract.py +80 -0
- ksadk/harness/native/output_recovery.py +24 -0
- ksadk/harness/native/ports.py +749 -0
- ksadk/harness/native/precompact_hook_runtime.py +171 -0
- ksadk/harness/native/replay_context.py +149 -0
- ksadk/harness/native/request_media.py +112 -0
- ksadk/harness/native/restore_contract.py +16 -0
- ksadk/harness/native/run_clock.py +120 -0
- ksadk/harness/native/state.py +436 -0
- ksadk/harness/native/steering.py +170 -0
- ksadk/harness/native/stop_hook_runtime.py +151 -0
- ksadk/harness/native/studio_admission.py +29 -0
- ksadk/harness/native/subagents.py +86 -0
- ksadk/harness/native/task_policy.py +99 -0
- ksadk/harness/native/task_progress.py +86 -0
- ksadk/harness/native/terminal_verification.py +43 -0
- ksadk/harness/native/tool_argument_feedback.py +178 -0
- ksadk/harness/native/tool_budget.py +253 -0
- ksadk/harness/native/tool_error_streak.py +89 -0
- ksadk/harness/native/tool_hook_runtime.py +343 -0
- ksadk/harness/native/tool_input_validation.py +58 -0
- ksadk/harness/native/tool_interruption.py +96 -0
- ksadk/harness/native/tool_observation.py +259 -0
- ksadk/harness/native/tool_progress.py +223 -0
- ksadk/harness/native/tool_recovery.py +25 -0
- ksadk/harness/native/tool_scheduling.py +33 -0
- ksadk/harness/native/tool_schema.py +126 -0
- ksadk/harness/native/transitions.py +151 -0
- ksadk/harness/native/turn_control.py +98 -0
- ksadk/harness/native/user_document.py +98 -0
- ksadk/harness/native/user_document_capture.py +49 -0
- ksadk/harness/native/user_input.py +87 -0
- ksadk/harness/native/verification.py +432 -0
- ksadk/harness/native/verification_binding.py +40 -0
- ksadk/harness/native/verification_runtime.py +233 -0
- ksadk/harness/native/verification_workspace.py +67 -0
- ksadk/harness/native_driver_failure.py +123 -0
- ksadk/harness/native_facade.py +977 -0
- ksadk/harness/native_factory.py +244 -0
- ksadk/harness/native_projection.py +503 -0
- ksadk/harness/native_runtime.py +621 -0
- ksadk/harness/native_shadow.py +250 -0
- ksadk/harness/native_shadow_inputs.py +146 -0
- ksadk/harness/progress_adapters/__init__.py +5 -0
- ksadk/harness/progress_adapters/native_todo.py +75 -0
- ksadk/harness/prompt_cache.py +65 -3
- ksadk/harness/public_activity.py +59 -0
- ksadk/harness/public_output.py +30 -0
- ksadk/harness/public_summary.py +36 -0
- ksadk/harness/reasoner.py +331 -22
- ksadk/harness/rpc.py +200 -0
- ksadk/harness/run_control.py +107 -2
- ksadk/harness/runtime.py +11 -2
- ksadk/harness/runtime_capabilities.py +71 -1
- ksadk/harness/sandbox_backend.py +203 -0
- ksadk/harness/sdk.py +153 -0
- ksadk/harness/session_store.py +370 -24
- ksadk/harness/skill_adapters/__init__.py +1 -0
- ksadk/harness/skill_adapters/native_bundle.py +148 -0
- ksadk/harness/skill_adapters/native_disclosure.py +306 -0
- ksadk/harness/skill_intent.py +47 -0
- ksadk/harness/skill_runtime.py +74 -16
- ksadk/harness/spec.py +20 -10
- ksadk/harness/state.py +2 -0
- ksadk/harness/stores/__init__.py +14 -0
- ksadk/harness/stores/native_command_settlement.py +65 -0
- ksadk/harness/stores/native_contract.py +311 -0
- ksadk/harness/stores/native_memory.py +326 -0
- ksadk/harness/stores/native_memory_archive.py +70 -0
- ksadk/harness/stores/native_migration_cli.py +91 -0
- ksadk/harness/stores/native_outbox.py +56 -0
- ksadk/harness/stores/native_sqlite.py +782 -0
- ksadk/harness/stores/native_sqlite_audit.py +29 -0
- ksadk/harness/stores/native_sqlite_migration.py +252 -0
- ksadk/harness/subagent.py +12 -2
- ksadk/harness/subagent_adapters/checkpoint_recovery.py +120 -0
- ksadk/harness/subagent_adapters/native_background_child.py +803 -0
- ksadk/harness/subagent_adapters/native_child.py +819 -0
- ksadk/harness/subagent_adapters/native_child_approval.py +128 -0
- ksadk/harness/subagent_adapters/native_child_continuation.py +51 -0
- ksadk/harness/subagent_adapters/native_child_result.py +392 -0
- ksadk/harness/subagent_adapters/prepared_recovery.py +120 -0
- ksadk/harness/tool_display.py +75 -0
- ksadk/harness/tool_display_contract.py +134 -0
- ksadk/harness/tool_output.py +141 -0
- ksadk/harness/tool_policy.py +3 -0
- ksadk/harness/tool_result_policy.py +216 -0
- ksadk/harness/tools.py +52 -0
- ksadk/harness/workspace_adapters/__init__.py +6 -0
- ksadk/harness/workspace_adapters/native_directory.py +180 -0
- ksadk/harness/workspace_adapters/native_git.py +208 -0
- ksadk/harness/workspace_adapters/native_isolation.py +350 -0
- ksadk/interaction/providers/langgraph.py +8 -1
- ksadk/kernel/bootstrap.py +144 -160
- ksadk/kernel/contract_fingerprints.py +45 -1
- ksadk/kernel/contracts.py +25 -0
- ksadk/kernel/control.py +153 -33
- ksadk/kernel/execution_grants.py +209 -2
- ksadk/kernel/execution_grants_memory.py +127 -8
- ksadk/kernel/execution_grants_postgres.py +188 -10
- ksadk/kernel/execution_grants_sqlite.py +235 -9
- ksadk/kernel/execution_host_ingress.py +1071 -0
- ksadk/kernel/history_projection.py +120 -0
- ksadk/kernel/ingress.py +117 -204
- ksadk/kernel/ingress_bootstrap.py +268 -0
- ksadk/kernel/legacy_history.py +179 -0
- ksadk/kernel/memory_store.py +38 -48
- ksadk/kernel/memory_store_projection.py +63 -0
- ksadk/kernel/postgres_event_log.py +181 -0
- ksadk/kernel/postgres_store.py +167 -198
- ksadk/kernel/readiness.py +125 -0
- ksadk/kernel/recovery.py +89 -3
- ksadk/kernel/recovery_projection.py +124 -0
- ksadk/kernel/runtime_checkpoint.py +34 -0
- ksadk/kernel/sql/001_agent_kernel.sql +5 -0
- ksadk/kernel/sqlite_schema.py +100 -0
- ksadk/kernel/sqlite_store.py +147 -216
- ksadk/kernel/store.py +47 -15
- ksadk/kernel/teams_artifact_tools.py +392 -0
- ksadk/kernel/teams_effects.py +851 -0
- ksadk/kernel/teams_execution_context.py +408 -0
- ksadk/kernel/teams_host_http.py +237 -0
- ksadk/kernel/teams_material_transport.py +245 -0
- ksadk/kernel/teams_materials.py +318 -0
- ksadk/kernel/teams_runtime_bootstrap.py +604 -0
- ksadk/kernel/teams_tool_runtime.py +273 -0
- ksadk/kernel/teams_workspace.py +259 -0
- ksadk/kernel/worker.py +323 -332
- ksadk/kernel/worker_controls.py +454 -0
- ksadk/kernel/worker_identity.py +27 -0
- ksadk/kernel/worker_interactions.py +197 -0
- ksadk/knowledge_base/client.py +15 -1
- ksadk/managed_runtime.py +29 -2
- ksadk/memory/adk/backends/http_ltm_backend.py +2 -1
- ksadk/memory/adk/backends/sdk_ltm_backend.py +15 -1
- ksadk/memory/adk/bounded_database_session_service.py +232 -0
- ksadk/memory/adk/resilient_session_service.py +115 -15
- ksadk/memory/adk/short_term_memory.py +8 -9
- ksadk/memory/extraction.py +120 -7
- ksadk/memory/providers/local_candidates.py +183 -0
- ksadk/memory/providers/local_sqlite.py +252 -78
- ksadk/memory/resolved_policy.py +12 -7
- ksadk/model_gateway/__init__.py +29 -0
- ksadk/model_gateway/client.py +253 -0
- ksadk/model_gateway/events.py +113 -0
- ksadk/model_gateway/matrix.py +297 -0
- ksadk/model_proxy/compat.py +670 -0
- ksadk/model_proxy/server.py +10 -7
- ksadk/model_proxy/transform.py +37 -9
- ksadk/plugins/bridges/dsh.py +18 -20
- ksadk/plugins/bridges/dsh_profile_manifest.py +36 -0
- ksadk/plugins/builtin_resources.py +199 -0
- ksadk/plugins/builtins.py +380 -186
- ksadk/plugins/context_contributor.py +15 -4
- ksadk/plugins/dsh_dependency_policy.py +41 -0
- ksadk/plugins/dsh_toolchain.py +24 -35
- ksadk/plugins/execution_host.py +8 -0
- ksadk/plugins/host.py +73 -5
- ksadk/plugins/kernel_adapter.py +44 -1
- ksadk/plugins/memory_provider.py +180 -0
- ksadk/plugins/providers/_process_group.py +50 -0
- ksadk/plugins/providers/bundles/dsh-knowledge/index.mjs +24 -2
- ksadk/plugins/providers/bundles/dsh-knowledge/package.json +1 -1
- ksadk/plugins/providers/bundles/dsh-memory/package.json +1 -1
- ksadk/plugins/providers/bundles/dsh-skill-center/package.json +1 -1
- ksadk/plugins/providers/bundles/dsh-teams-tools/package.json +1 -1
- ksadk/plugins/providers/bundles/ksadk-dsh-capability-host/package.json +1 -1
- ksadk/plugins/providers/codex.py +42 -38
- ksadk/plugins/providers/codex_native.py +20 -0
- ksadk/plugins/providers/codex_skills.py +46 -0
- ksadk/plugins/providers/dsh_capabilities.py +16 -171
- ksadk/plugins/providers/dsh_connector_lease.py +225 -0
- ksadk/plugins/providers/dsh_mcp.py +163 -1
- ksadk/plugins/providers/harness.py +247 -34
- ksadk/plugins/providers/harness_context.py +61 -0
- ksadk/plugins/providers/harness_contributions.py +241 -0
- ksadk/plugins/providers/harness_delegation.py +91 -14
- ksadk/plugins/providers/harness_hooks.py +121 -0
- ksadk/plugins/providers/harness_managed.py +269 -175
- ksadk/plugins/providers/harness_tools.py +165 -36
- ksadk/plugins/providers/legacy_catalog.py +1 -1
- ksadk/plugins/providers/mcp_projection.py +100 -3
- ksadk/plugins/providers/platform_resources.py +16 -6
- ksadk/plugins/teams/application.py +3 -143
- ksadk/plugins/teams/build_artifacts.py +460 -0
- ksadk/plugins/teams/cloud_contract_fingerprints.py +22 -0
- ksadk/plugins/teams/cloud_contracts.py +928 -0
- ksadk/plugins/teams/cloud_permits.py +250 -0
- ksadk/plugins/teams/domain.py +201 -148
- ksadk/plugins/teams/effect_contracts.py +255 -0
- ksadk/plugins/teams/execution_projection.py +255 -0
- ksadk/plugins/teams/leader_decisions.py +106 -0
- ksadk/plugins/teams/postgres_store.py +299 -85
- ksadk/plugins/teams/release_contracts.py +60 -0
- ksadk/plugins/teams/runtime.py +116 -40
- ksadk/plugins/teams/store.py +214 -6
- ksadk/plugins/teams/task_decisions.py +21 -8
- ksadk/plugins/teams/tool_service.py +152 -0
- ksadk/plugins/teams/transport.py +8 -3
- ksadk/resource_runtime/skill_runtime.py +3 -0
- ksadk/resource_runtime/socket_server.py +11 -0
- ksadk/resource_runtime/worker.py +28 -4
- ksadk/runners/_adk_a2a_stream.py +122 -0
- ksadk/runners/_adk_canonical_stream.py +474 -0
- ksadk/runners/_langgraph_canonical_streams.py +527 -0
- ksadk/runners/_langgraph_runner_streams.py +6 -400
- ksadk/runners/_langgraph_tool_projection.py +86 -0
- ksadk/runners/_langgraph_tools_transformer.py +27 -0
- ksadk/runners/adk_runner.py +195 -290
- ksadk/runners/base_runner.py +6 -5
- ksadk/runners/langgraph_runner.py +82 -86
- ksadk/runners/utils/loader.py +28 -14
- ksadk/runtime/_runner_adapter/a2ui_surface.py +145 -0
- ksadk/runtime/_runner_adapter/gateway_approvals.py +93 -0
- ksadk/runtime/_runner_adapter/stream_mapping.py +83 -128
- ksadk/runtime/adapter.py +40 -3
- ksadk/runtime/conversation_execution.py +4 -2
- ksadk/runtime/executor.py +97 -7
- ksadk/runtime/factory.py +13 -6
- ksadk/runtime/framework_adapters.py +184 -10
- ksadk/runtime/hosted_finalizer.py +4 -6
- ksadk/runtime/launch.py +2 -0
- ksadk/runtime/runner_adapter.py +123 -13
- ksadk/runtime_context.py +2 -0
- ksadk/sandbox/backends/e2b.py +30 -4
- ksadk/sandbox/backends/local_process.py +10 -0
- ksadk/sandbox/e2b_connection.py +9 -1
- ksadk/sandbox/local_controls.py +32 -2
- ksadk/sandbox/local_launcher.py +34 -0
- ksadk/server/factory.py +178 -25
- ksadk/server/history_deadline.py +158 -0
- ksadk/server/history_page_cache.py +129 -0
- ksadk/server/routes/checkpoint_audit.py +88 -0
- ksadk/server/routes/checkpoint_listing.py +294 -0
- ksadk/server/routes/checkpoint_resolution.py +73 -20
- ksadk/server/routes/control.py +51 -3
- ksadk/server/routes/display_projection.py +154 -0
- ksadk/server/routes/kernel_ingress.py +17 -2
- ksadk/server/routes/misc.py +102 -12
- ksadk/server/routes/models.py +25 -1
- ksadk/server/routes/openai_compat.py +6 -0
- ksadk/server/routes/projection.py +159 -216
- ksadk/server/routes/run.py +6 -2
- ksadk/server/routes/sessions.py +301 -342
- ksadk/server/routes/streaming.py +68 -15
- ksadk/server/routes/tool_receipt_claim_routes.py +107 -0
- ksadk/server/routes/workspace.py +5 -2
- ksadk/server/static/assets/{ArtifactsPanel-C1YoaKhx.js → ArtifactsPanel-CILaW-fC.js} +1 -1
- ksadk/server/static/assets/{CodeBlock-DHuAWfr5.js → CodeBlock-BVrNVtJC.js} +1 -1
- ksadk/server/static/assets/{MathMessageMarkdown-cvdrBwYr.js → MathMessageMarkdown-Dp7c4wWD.js} +1 -1
- ksadk/server/static/assets/{MermaidBlock-DeBkbJvI.js → MermaidBlock-Dxop8Rd7.js} +6 -6
- ksadk/server/static/assets/{NativeTerminalPanel-oJj44_sV.js → NativeTerminalPanel-Cjjz8B30.js} +1 -1
- ksadk/server/static/assets/{abnfDiagram-VCTEODGH-Dhfv25o2.js → abnfDiagram-VCTEODGH-ckWDmOzw.js} +1 -1
- ksadk/server/static/assets/{arc-C35w-pn1.js → arc-DqxJede-.js} +1 -1
- ksadk/server/static/assets/architecture-7GRP2DOG-pn_DuLy6.js +1 -0
- ksadk/server/static/assets/{architectureDiagram-5GKGNRK7-FoMY_u6D.js → architectureDiagram-5GKGNRK7-SEQA4bVi.js} +1 -1
- ksadk/server/static/assets/{blockDiagram-NRAW4CY4-zD4Rt7oE.js → blockDiagram-NRAW4CY4-pM0B-6NJ.js} +1 -1
- ksadk/server/static/assets/{c4Diagram-UCG6FXSJ-BCtGGkhN.js → c4Diagram-UCG6FXSJ-m2tuCNVq.js} +1 -1
- ksadk/server/static/assets/channel-fySsnkft.js +1 -0
- ksadk/server/static/assets/chunk-2Q5K7J3B-BheGxKRQ.js +1 -0
- ksadk/server/static/assets/{chunk-5VM5RSS4-C2Ms13_k.js → chunk-5VM5RSS4-GJHDr9Ww.js} +1 -1
- ksadk/server/static/assets/{chunk-F27PBJKO-BoTj-qgx.js → chunk-F27PBJKO-CbAN45AV.js} +1 -1
- ksadk/server/static/assets/{chunk-G27WJ6UU-DOMyyjHH.js → chunk-G27WJ6UU-D5ngbbzF.js} +1 -1
- ksadk/server/static/assets/{chunk-JWPE2WC7-DbJJqDli.js → chunk-JWPE2WC7-Cc5IwuYC.js} +1 -1
- ksadk/server/static/assets/{chunk-LCL6LL3I-CLuRCt_W.js → chunk-LCL6LL3I-CprgDDdJ.js} +1 -1
- ksadk/server/static/assets/{chunk-POPQ4Y6H-D0Dz847e.js → chunk-POPQ4Y6H-CDZhSy82.js} +1 -1
- ksadk/server/static/assets/{chunk-RHFEMEQ7-BWSRzEcd.js → chunk-RHFEMEQ7-CZchUNro.js} +1 -1
- ksadk/server/static/assets/{chunk-SVP7TREG-CPoHqkCY.js → chunk-SVP7TREG-BVruqk6T.js} +1 -1
- ksadk/server/static/assets/{chunk-XXDRQBXY-Bx1jS-X-.js → chunk-XXDRQBXY-4ld9SxPN.js} +1 -1
- ksadk/server/static/assets/classDiagram-DTDB5LWJ-DSfLtNYm.js +1 -0
- ksadk/server/static/assets/classDiagram-v2-JRS7N3AN-DSfLtNYm.js +1 -0
- ksadk/server/static/assets/{cose-bilkent-JH36ORCC-LzyWm4m0.js → cose-bilkent-JH36ORCC-Dmyyk_Vp.js} +1 -1
- ksadk/server/static/assets/cynefin-OW5HDTMX-PFWIGtvA.js +1 -0
- ksadk/server/static/assets/{cynefinDiagram-5FMLGOSQ-BlW_bHED.js → cynefinDiagram-5FMLGOSQ-BIyq3EJb.js} +1 -1
- ksadk/server/static/assets/{dagre-3AP2YEHR-Bu_S86_i.js → dagre-3AP2YEHR-eBw08Qdk.js} +1 -1
- ksadk/server/static/assets/{dagre-B7nCawed.js → dagre-DfvnEf45.js} +1 -1
- ksadk/server/static/assets/{diagram-S7CK7UJ4-W_Zow2Km.js → diagram-S7CK7UJ4-CUSrE3Mg.js} +1 -1
- ksadk/server/static/assets/{diagram-UQ7AKVKN-CLIKAHON.js → diagram-UQ7AKVKN-Dq5Zw2gy.js} +1 -1
- ksadk/server/static/assets/{diagram-VSXAHHWV-Dv6_QAlN.js → diagram-VSXAHHWV-DyUHfNwT.js} +1 -1
- ksadk/server/static/assets/{diagram-VX7I27RA-BWAWRDtZ.js → diagram-VX7I27RA-CREmM89O.js} +1 -1
- ksadk/server/static/assets/{diagram-Z3DM3KII-XA_rWMss.js → diagram-Z3DM3KII-Dw9zhHUK.js} +1 -1
- ksadk/server/static/assets/{dist-CGt_UGeO.js → dist-0uJ4tdx6.js} +1 -1
- ksadk/server/static/assets/{dist-C0J5_3qM.js → dist-AEv-vXf-.js} +1 -1
- ksadk/server/static/assets/{dist-CIpWk_5f.js → dist-B49GzZDb.js} +1 -1
- ksadk/server/static/assets/{dist-B3bA05-e.js → dist-B8Myiet9.js} +1 -1
- ksadk/server/static/assets/{dist-DWfdmz9s.js → dist-BBsYarrI.js} +1 -1
- ksadk/server/static/assets/{dist-BLpRy_Yx.js → dist-BQMrUwdI.js} +1 -1
- ksadk/server/static/assets/{dist-CIRMvUM4.js → dist-BX_-FENW.js} +1 -1
- ksadk/server/static/assets/{dist-BdNFAney.js → dist-CD4L6ymH.js} +1 -1
- ksadk/server/static/assets/{dist-n3n6t_FO.js → dist-CHpW1BHj.js} +1 -1
- ksadk/server/static/assets/{dist-DppE_r-D.js → dist-CVPau3tn.js} +1 -1
- ksadk/server/static/assets/{dist-qe8xjesy.js → dist-C_qdxcSH.js} +1 -1
- ksadk/server/static/assets/{dist-2vL2sAdF.js → dist-Co68Apq4.js} +1 -1
- ksadk/server/static/assets/{dist-1nXCdsej.js → dist-Cp2PaOMY.js} +1 -1
- ksadk/server/static/assets/{dist-DNKuMzWc.js → dist-CuR5Bgme.js} +1 -1
- ksadk/server/static/assets/{dist-t6MzE0jU.js → dist-DDSyLEOX.js} +1 -1
- ksadk/server/static/assets/{dist-CPyieXEf.js → dist-DUxO0R99.js} +1 -1
- ksadk/server/static/assets/{dist-DyG6Ya-z.js → dist-QzbGrED_.js} +1 -1
- ksadk/server/static/assets/{dist-0X_j2Rcc.js → dist-bupkyEBZ.js} +1 -1
- ksadk/server/static/assets/{dist-mqD5TZup.js → dist-cxTGfdqx.js} +1 -1
- ksadk/server/static/assets/{dist-M-SrdhhZ.js → dist-hL5p1til.js} +1 -1
- ksadk/server/static/assets/{dist-D-92S-Fd.js → dist-lOy23y43.js} +1 -1
- ksadk/server/static/assets/{dist-BO_UwatH.js → dist-pq-nD33R.js} +1 -1
- ksadk/server/static/assets/{ebnfDiagram-PWID7BFC-BfCzF1Xi.js → ebnfDiagram-PWID7BFC-BS5LdNoR.js} +1 -1
- ksadk/server/static/assets/{erDiagram-SSCWMZ5O-Dpz9_0zn.js → erDiagram-SSCWMZ5O-Bv1QtiY-.js} +1 -1
- ksadk/server/static/assets/eventmodeling-NTZA5JFV-DsM4upQX.js +1 -0
- ksadk/server/static/assets/flowDiagram-A5DVABFB-D5NA16k9.js +1 -0
- ksadk/server/static/assets/{ganttDiagram-EL5Y4UJY-DDaG1f4_.js → ganttDiagram-EL5Y4UJY-musi-e0G.js} +1 -1
- ksadk/server/static/assets/gitGraph-4MIJSDKK-DUCVz-ih.js +1 -0
- ksadk/server/static/assets/{gitGraphDiagram-WWUBYQGX-DcxzRwYC.js → gitGraphDiagram-WWUBYQGX-DH37EGkG.js} +1 -1
- ksadk/server/static/assets/index-CZdkAvsv.js +244 -0
- ksadk/server/static/assets/index-CvXdHgF3.css +2 -0
- ksadk/server/static/assets/info-A6RAGUB7-rw0ZNHZp.js +1 -0
- ksadk/server/static/assets/{infoDiagram-RXCK75RN-CYMdn6lQ.js → infoDiagram-RXCK75RN-C1kEtv2y.js} +1 -1
- ksadk/server/static/assets/{ishikawaDiagram-5VMMS53U-CLHFAD6c.js → ishikawaDiagram-5VMMS53U-BCNPyMka.js} +1 -1
- ksadk/server/static/assets/{journeyDiagram-EYS64GPL-BHSKQGAl.js → journeyDiagram-EYS64GPL--cDbKnjP.js} +1 -1
- ksadk/server/static/assets/{kanban-definition-3QL26DDD-D5W3tjTT.js → kanban-definition-3QL26DDD-R1rAVw2f.js} +1 -1
- ksadk/server/static/assets/{katex-mtjOi4KM.js → katex-BT-X-V7U.js} +1 -1
- ksadk/server/static/assets/{linear-BETqsxE_.js → linear-DTR631iG.js} +1 -1
- ksadk/server/static/assets/{mermaid-parser.core-DPWrvs90.js → mermaid-parser.core-DfFzJhX8.js} +2 -2
- ksadk/server/static/assets/{mindmap-definition-FBJOCRG2-CJG2mfBP.js → mindmap-definition-FBJOCRG2-D4by_rmk.js} +1 -1
- ksadk/server/static/assets/packet-AYTQ26CC-Ca4Hvki9.js +1 -0
- ksadk/server/static/assets/{pegDiagram-XKGWAZYB-GjAPNaqn.js → pegDiagram-XKGWAZYB-xWXPEh0c.js} +1 -1
- ksadk/server/static/assets/pie-WAS4IAKB-hwiz6fbw.js +1 -0
- ksadk/server/static/assets/{pieDiagram-E7YTZNPT-ToIewrqr.js → pieDiagram-E7YTZNPT-BudBDlCI.js} +1 -1
- ksadk/server/static/assets/{quadrantDiagram-AXDQQJYC-4d78KiEY.js → quadrantDiagram-AXDQQJYC-DXZ0PCJF.js} +1 -1
- ksadk/server/static/assets/radar-RG4KPBEZ-BR1oZXLM.js +1 -0
- ksadk/server/static/assets/railroad-74A4TZTK-DfgEISfX.js +1 -0
- ksadk/server/static/assets/railroad-abnf-HS5TGJTU-CWOC3rdk.js +1 -0
- ksadk/server/static/assets/railroad-ebnf-LZEXJU2U-CiRvHCLY.js +1 -0
- ksadk/server/static/assets/railroad-peg-WCYAUIDC-DSDDyJ2f.js +1 -0
- ksadk/server/static/assets/{railroadDiagram-O6MQD6OU-DKzx4xfv.js → railroadDiagram-O6MQD6OU-BdAYRYB8.js} +1 -1
- ksadk/server/static/assets/{requirementDiagram-EFPCY7ZU-CJYljxQS.js → requirementDiagram-EFPCY7ZU-CBBmaL70.js} +1 -1
- ksadk/server/static/assets/{sankeyDiagram-P5KCCOFB-DLU2fV1w.js → sankeyDiagram-P5KCCOFB-Doc3rdwp.js} +1 -1
- ksadk/server/static/assets/{sequenceDiagram-WJ2MYXX4-CVqysrA9.js → sequenceDiagram-WJ2MYXX4-0J8R8hWW.js} +1 -1
- ksadk/server/static/assets/{sizeCapture-X5ZJPWSS-BUxfmTHc.js → sizeCapture-X5ZJPWSS-CfFLlWsK.js} +1 -1
- ksadk/server/static/assets/{stateDiagram-HBIQ2CUA-CNFgYI9E.js → stateDiagram-HBIQ2CUA-jBwkjtTy.js} +1 -1
- ksadk/server/static/assets/stateDiagram-v2-4QOOHH4V-Cw1fPb5w.js +1 -0
- ksadk/server/static/assets/{swimlanes-XN3QIQJK-B6qCMe8N.js → swimlanes-XN3QIQJK-B0WxTQf8.js} +1 -1
- ksadk/server/static/assets/swimlanesDiagram-VK2B7HYN-Cc3JMBAF.js +8 -0
- ksadk/server/static/assets/{timeline-definition-24CTP7MA-CMD3x_U_.js → timeline-definition-24CTP7MA-BfCT_75A.js} +1 -1
- ksadk/server/static/assets/treeView-Q6P3EWNA-C05SJHxo.js +1 -0
- ksadk/server/static/assets/treemap-WGGIJYW6-BqNLAhc3.js +1 -0
- ksadk/server/static/assets/{vennDiagram-4TSXK5OY-BqtUqvXM.js → vennDiagram-4TSXK5OY-9gX1VMDC.js} +1 -1
- ksadk/server/static/assets/wardley-WFR3VGLG-DokNktqB.js +1 -0
- ksadk/server/static/assets/{wardleyDiagram-VM6X3IG4-B5KfDjI-.js → wardleyDiagram-VM6X3IG4-sEs_BP6O.js} +1 -1
- ksadk/server/static/assets/{xychartDiagram-S5SC5T6Z-BaUlSZrn.js → xychartDiagram-S5SC5T6Z-Ll4jvUN9.js} +1 -1
- ksadk/server/static/index.html +2 -2
- ksadk/sessions/__init__.py +8 -1
- ksadk/sessions/_checkpoint_resume_audit.py +44 -0
- ksadk/sessions/_in_memory_query.py +112 -0
- ksadk/sessions/_local_service_sync.py +157 -156
- ksadk/sessions/_local_state_sync.py +152 -0
- ksadk/sessions/_local_tables.py +2 -0
- ksadk/sessions/_postgres_checkpoint_read.py +323 -0
- ksadk/sessions/_postgres_checkpoint_stats.py +330 -0
- ksadk/sessions/_postgres_history_read.py +678 -0
- ksadk/sessions/_postgres_schema.py +44 -2
- ksadk/sessions/_postgres_tables.py +2 -0
- ksadk/sessions/_postgres_tool_receipts.py +169 -0
- ksadk/sessions/_resilient_read.py +630 -0
- ksadk/sessions/_tool_receipt_sync.py +119 -0
- ksadk/sessions/agent_identity.py +16 -0
- ksadk/sessions/base.py +278 -0
- ksadk/sessions/db_budget.py +332 -0
- ksadk/sessions/errors.py +12 -0
- ksadk/sessions/history_budget.py +36 -0
- ksadk/sessions/history_identity.py +27 -0
- ksadk/sessions/in_memory.py +376 -113
- ksadk/sessions/local_service.py +345 -0
- ksadk/sessions/postgres_service.py +515 -549
- ksadk/sessions/resilience.py +21 -14
- ksadk/sessions/resilient.py +611 -504
- ksadk/skills/loader.py +44 -2
- ksadk/skills/runtime/backends/e2b.py +17 -14
- ksadk/skills/service_client.py +6 -1
- ksadk/studio/a2a_bindings.py +407 -0
- ksadk/studio/a2a_orchestration.py +178 -0
- ksadk/studio/active_run_output.py +92 -0
- ksadk/studio/activity_records.py +542 -0
- ksadk/studio/agent_configuration.py +173 -0
- ksadk/studio/api.py +219 -389
- ksadk/studio/api_a2a_routes.py +93 -0
- ksadk/studio/api_catalog_routes.py +13 -0
- ksadk/studio/api_cloud_chat_routes.py +382 -0
- ksadk/studio/api_contracts.py +23 -0
- ksadk/studio/attachment_store.py +14 -2
- ksadk/studio/build_resolution.py +117 -0
- ksadk/studio/build_singleflight.py +37 -0
- ksadk/studio/builder.py +20 -2
- ksadk/studio/capabilities.py +8 -3
- ksadk/studio/cloud.py +83 -377
- ksadk/studio/cloud_gateways.py +424 -0
- ksadk/studio/cloud_shared_web.py +7 -0
- ksadk/studio/codex_agent_service.py +17 -0
- ksadk/studio/codex_manifest.py +5 -1
- ksadk/studio/codex_provider_build.py +65 -0
- ksadk/studio/compiler.py +102 -6
- ksadk/studio/contract_base.py +19 -0
- ksadk/studio/contracts.py +231 -201
- ksadk/studio/dsh_application.py +58 -12
- ksadk/studio/dsh_capability_service.py +17 -171
- ksadk/studio/dsh_mcp_transport.py +191 -0
- ksadk/studio/event_store.py +17 -0
- ksadk/studio/execution_host.py +84 -11
- ksadk/studio/framework_run.py +18 -0
- ksadk/studio/interaction_submission.py +265 -0
- ksadk/studio/kernel_registry.py +12 -1
- ksadk/studio/lifecycle_status.py +43 -0
- ksadk/studio/model_buffered_wire.py +227 -0
- ksadk/studio/model_chat_tools.py +143 -0
- ksadk/studio/model_client.py +1241 -761
- ksadk/studio/model_content_stream.py +129 -0
- ksadk/studio/model_credentials.py +196 -0
- ksadk/studio/model_profile_service.py +65 -31
- ksadk/studio/model_request_projection.py +284 -0
- ksadk/studio/model_responses_content.py +296 -0
- ksadk/studio/model_usage.py +109 -0
- ksadk/studio/model_wire_failure.py +110 -0
- ksadk/studio/model_wire_identity.py +138 -0
- ksadk/studio/model_wire_snapshot.py +45 -0
- ksadk/studio/model_wire_tools.py +283 -0
- ksadk/studio/native_acceptance.py +68 -0
- ksadk/studio/native_approval.py +111 -0
- ksadk/studio/native_browser_port.py +861 -0
- ksadk/studio/native_child_mcp.py +87 -0
- ksadk/studio/native_child_publication.py +68 -0
- ksadk/studio/native_child_skills.py +72 -0
- ksadk/studio/native_child_workspace.py +176 -0
- ksadk/studio/native_command_policy.py +53 -0
- ksadk/studio/native_context_port.py +326 -0
- ksadk/studio/native_context_registration.py +54 -0
- ksadk/studio/native_document_input.py +88 -0
- ksadk/studio/native_final_shape.py +852 -0
- ksadk/studio/native_gateway_stream.py +69 -0
- ksadk/studio/native_hook_port.py +145 -0
- ksadk/studio/native_hook_registration.py +70 -0
- ksadk/studio/native_inventory_disclosure.py +223 -0
- ksadk/studio/native_kernel_admission.py +84 -0
- ksadk/studio/native_language_diagnostics.py +320 -0
- ksadk/studio/native_mcp_disclosure.py +198 -0
- ksadk/studio/native_mcp_tool_port.py +219 -0
- ksadk/studio/native_memory_acceptance.py +362 -0
- ksadk/studio/native_memory_context.py +266 -0
- ksadk/studio/native_memory_flush.py +174 -0
- ksadk/studio/native_memory_sources.py +80 -0
- ksadk/studio/native_memory_tool_port.py +360 -0
- ksadk/studio/native_memory_turn.py +183 -0
- ksadk/studio/native_model_port.py +765 -0
- ksadk/studio/native_model_recovery.py +126 -0
- ksadk/studio/native_options.py +75 -0
- ksadk/studio/native_output_fields.py +58 -0
- ksadk/studio/native_planning.py +156 -0
- ksadk/studio/native_product.py +988 -0
- ksadk/studio/native_read_budget.py +426 -0
- ksadk/studio/native_recovery.py +171 -0
- ksadk/studio/native_scope.py +35 -0
- ksadk/studio/native_session_context_port.py +141 -0
- ksadk/studio/native_session_engine.py +14 -0
- ksadk/studio/native_skill_source.py +74 -0
- ksadk/studio/native_subagent_port.py +700 -0
- ksadk/studio/native_terminal_recovery.py +95 -0
- ksadk/studio/native_test_action.py +610 -0
- ksadk/studio/native_tool_port.py +477 -0
- ksadk/studio/native_user_input.py +60 -0
- ksadk/studio/native_verification_presentation.py +65 -0
- ksadk/studio/native_workspace_diagnostics.py +183 -0
- ksadk/studio/native_workspace_port.py +43 -0
- ksadk/studio/operations.py +34 -9
- ksadk/studio/plugin_composition.py +26 -10
- ksadk/studio/plugin_kernel_adapter.py +75 -1
- ksadk/studio/plugin_native_activation.py +163 -0
- ksadk/studio/plugin_runtime.py +639 -119
- ksadk/studio/plugin_runtime_admission.py +57 -0
- ksadk/studio/presentation.py +92 -0
- ksadk/studio/public_output_store.py +69 -0
- ksadk/studio/resource_authority.py +11 -3
- ksadk/studio/resource_catalog.py +8 -12
- ksadk/studio/run_activity.py +338 -385
- ksadk/studio/run_controls.py +185 -0
- ksadk/studio/run_service.py +908 -319
- ksadk/studio/run_submission_receipts.py +68 -0
- ksadk/studio/runtime_catalog.py +23 -4
- ksadk/studio/runtime_prewarm.py +22 -0
- ksadk/studio/scheduler_assistant.py +4 -1
- ksadk/studio/service.py +356 -282
- ksadk/studio/service_settings.py +229 -0
- ksadk/studio/shared_web.py +393 -169
- ksadk/studio/shared_web_response_events.py +180 -0
- ksadk/studio/shared_web_run_state.py +133 -0
- ksadk/studio/skill_discovery.py +305 -3
- ksadk/studio/static/assets/ChannelsPage-xX5HlMId.js +8 -0
- ksadk/studio/static/assets/{CodeBlock-DMIf4h33-UIqo-BPW.js → CodeBlock-DMIf4h33-CkufcPYo.js} +3 -3
- ksadk/studio/static/assets/{MathMessageMarkdown-BesSeCYl-OhNwH4K9.js → MathMessageMarkdown-BesSeCYl-DYG1Wzbx.js} +1 -1
- ksadk/studio/static/assets/{MessageMarkdown-CwMTvgFj-g0I-I4dD.js → MessageMarkdown-CwMTvgFj-BeyLyQHu.js} +2 -2
- ksadk/studio/static/assets/TeamsPage-CCaaGaVp.js +3 -0
- ksadk/studio/static/assets/index-C7DNDik2.css +2 -0
- ksadk/studio/static/assets/index-Cy68gTlH.js +265 -0
- ksadk/studio/static/index.html +3 -3
- ksadk/studio/teams_catalog.py +33 -0
- ksadk/studio/teams_installation.py +4 -1
- ksadk/studio/teams_node.py +4 -0
- ksadk/studio/teams_node_factory.py +558 -0
- ksadk/studio/teams_node_runner.py +3 -0
- ksadk/studio/teams_node_v1.py +935 -0
- ksadk/studio/teams_remote.py +259 -37
- ksadk/studio/templates.py +1 -0
- ksadk/studio/workspace_plugins.py +3 -1
- ksadk/tools/a2a_policy.py +5 -0
- ksadk/tools/artifact_observation.py +53 -0
- ksadk/tools/captured_source_artifact.py +68 -0
- ksadk/tools/schema.py +34 -0
- ksadk/toolsets/__init__.py +82 -88
- ksadk/toolsets/a2a.py +89 -89
- ksadk/toolsets/sandbox.py +377 -6
- ksadk/toolsets/skill_management.py +315 -0
- ksadk/toolsets/tool_selection.py +94 -0
- ksadk/toolsets/workspace.py +342 -324
- ksadk/toolsets/workspace_editing.py +291 -0
- ksadk/toolsets/workspace_model_observation.py +104 -0
- ksadk/toolsets/workspace_mutation.py +75 -0
- ksadk/toolsets/workspace_read_budget.py +216 -0
- ksadk/toolsets/workspace_search.py +192 -0
- ksadk/toolsets/workspace_search_process.py +129 -0
- ksadk/toolsets/workspace_state.py +6 -0
- ksadk/version.py +1 -1
- ksadk_runtime_common/http_headers.py +18 -0
- ksadk_runtime_common/workspace_files/router.py +2 -1
- ksadk/server/static/assets/architecture-7GRP2DOG-BCsa26zi.js +0 -1
- ksadk/server/static/assets/channel-_KDifNys.js +0 -1
- ksadk/server/static/assets/chunk-2Q5K7J3B-HdVkJ9F0.js +0 -1
- ksadk/server/static/assets/classDiagram-DTDB5LWJ-BVCW3ZqZ.js +0 -1
- ksadk/server/static/assets/classDiagram-v2-JRS7N3AN-BVCW3ZqZ.js +0 -1
- ksadk/server/static/assets/cynefin-OW5HDTMX-CNXjFctL.js +0 -1
- ksadk/server/static/assets/eventmodeling-NTZA5JFV-B5-8RH9c.js +0 -1
- ksadk/server/static/assets/flowDiagram-A5DVABFB-BppFxEKw.js +0 -1
- ksadk/server/static/assets/gitGraph-4MIJSDKK-B-AhYj4C.js +0 -1
- ksadk/server/static/assets/index-BMd2mucF.css +0 -2
- ksadk/server/static/assets/index-D5qyyhTH.js +0 -244
- ksadk/server/static/assets/info-A6RAGUB7-0uiKrC35.js +0 -1
- ksadk/server/static/assets/packet-AYTQ26CC-BVTmZ_cw.js +0 -1
- ksadk/server/static/assets/pie-WAS4IAKB-DyfxBU7V.js +0 -1
- ksadk/server/static/assets/radar-RG4KPBEZ-FFontjL2.js +0 -1
- ksadk/server/static/assets/railroad-74A4TZTK-D0Uidzen.js +0 -1
- ksadk/server/static/assets/railroad-abnf-HS5TGJTU-BOo_9T2h.js +0 -1
- ksadk/server/static/assets/railroad-ebnf-LZEXJU2U-D6-BjW7Z.js +0 -1
- ksadk/server/static/assets/railroad-peg-WCYAUIDC-CXx__yMm.js +0 -1
- ksadk/server/static/assets/stateDiagram-v2-4QOOHH4V-BXAFScz8.js +0 -1
- ksadk/server/static/assets/swimlanesDiagram-VK2B7HYN-uIQ2lJKH.js +0 -8
- ksadk/server/static/assets/treeView-Q6P3EWNA-CK5GgucZ.js +0 -1
- ksadk/server/static/assets/treemap-WGGIJYW6-Bq1u1SpG.js +0 -1
- ksadk/server/static/assets/wardley-WFR3VGLG-QAt06uEz.js +0 -1
- ksadk/studio/static/assets/ChannelsPage-D_PGmKVq.js +0 -8
- ksadk/studio/static/assets/TeamsPage-DqX4HWKQ.js +0 -3
- ksadk/studio/static/assets/index-CfEnms6Y.css +0 -2
- ksadk/studio/static/assets/index-Dp61rOWZ.js +0 -0
- {agentengine_sdk_python-0.8.5.dist-info → agentengine_sdk_python-0.8.6.dist-info}/WHEEL +0 -0
- {agentengine_sdk_python-0.8.5.dist-info → agentengine_sdk_python-0.8.6.dist-info}/entry_points.txt +0 -0
- {agentengine_sdk_python-0.8.5.dist-info → agentengine_sdk_python-0.8.6.dist-info}/licenses/LICENSE +0 -0
- {agentengine_sdk_python-0.8.5.dist-info → agentengine_sdk_python-0.8.6.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
"""Workspace edit validation, read-state reconciliation and bounded diff evidence.
|
|
2
|
+
|
|
3
|
+
Filesystem authorization and tool entry points remain in ``workspace``.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import difflib
|
|
9
|
+
import hashlib
|
|
10
|
+
from dataclasses import replace
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
from ksadk.tools.result_budget import budget_tool_output, default_tool_result_budget
|
|
15
|
+
from ksadk.toolsets.workspace_mutation import publish_workspace_bytes
|
|
16
|
+
from ksadk.toolsets.workspace_state import WorkspaceReadState, record_read_state
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _dict_result(value: Any, *, tool_name: str) -> dict[str, Any]:
|
|
20
|
+
if not isinstance(value, dict):
|
|
21
|
+
raise TypeError(f"{tool_name} returned {type(value).__name__}, expected dict")
|
|
22
|
+
return dict(value)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _read_workspace_text(target: Path) -> tuple[str | None, dict[str, Any] | None]:
|
|
26
|
+
if not target.is_file():
|
|
27
|
+
return None, {"ok": False, "error_message": "workspace file not found"}
|
|
28
|
+
if target.stat().st_size > 2 * 1024 * 1024:
|
|
29
|
+
return None, {"ok": False, "error_message": "file is larger than 2MB"}
|
|
30
|
+
try:
|
|
31
|
+
# Editing must retain the original separators and UTF-8 BOM. The
|
|
32
|
+
# model's line-oriented read view is not the file's canonical bytes.
|
|
33
|
+
with target.open("rb") as source:
|
|
34
|
+
payload = source.read(2 * 1024 * 1024 + 1)
|
|
35
|
+
if len(payload) > 2 * 1024 * 1024:
|
|
36
|
+
return None, {"ok": False, "error_message": "file is larger than 2MB"}
|
|
37
|
+
return payload.decode("utf-8"), None
|
|
38
|
+
except UnicodeDecodeError:
|
|
39
|
+
return None, {"ok": False, "error_message": "file is not utf-8 text"}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _validate_read_state(
|
|
43
|
+
target: Path,
|
|
44
|
+
relative: str,
|
|
45
|
+
*,
|
|
46
|
+
state: WorkspaceReadState | None,
|
|
47
|
+
content: str | None = None,
|
|
48
|
+
) -> dict[str, Any] | None:
|
|
49
|
+
if (
|
|
50
|
+
state is None
|
|
51
|
+
or state.canonical_path != str(target.resolve())
|
|
52
|
+
or not state.content_sha256
|
|
53
|
+
):
|
|
54
|
+
return {
|
|
55
|
+
"ok": False,
|
|
56
|
+
"path": relative,
|
|
57
|
+
"error_type": "file_not_read",
|
|
58
|
+
"error_message": "file must be read before editing",
|
|
59
|
+
"suggested_action": f"Call read_workspace_file(path={relative!r}) before editing.",
|
|
60
|
+
}
|
|
61
|
+
stat = target.stat() if target.exists() else None
|
|
62
|
+
if (
|
|
63
|
+
stat is None
|
|
64
|
+
or stat.st_mtime_ns != state.mtime_ns
|
|
65
|
+
or stat.st_size != state.size_bytes
|
|
66
|
+
or (content is not None
|
|
67
|
+
and hashlib.sha256(content.encode("utf-8")).hexdigest() != state.content_sha256)
|
|
68
|
+
):
|
|
69
|
+
return {
|
|
70
|
+
"ok": False,
|
|
71
|
+
"path": relative,
|
|
72
|
+
"error_type": "file_modified_since_read",
|
|
73
|
+
"error_message": "workspace file changed since last read",
|
|
74
|
+
"suggested_action": (
|
|
75
|
+
f"Re-read the file with read_workspace_file(path={relative!r}) and retry."
|
|
76
|
+
),
|
|
77
|
+
}
|
|
78
|
+
return None
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _commit_workspace_edit(
|
|
82
|
+
target: Path, relative: str, updated: str, *, state: WorkspaceReadState
|
|
83
|
+
) -> tuple[Any | None, dict[str, Any] | None]:
|
|
84
|
+
"""Recheck the pinned source immediately before writing, never a new read state.
|
|
85
|
+
|
|
86
|
+
This detects intervening changes during matching/diff generation. It is
|
|
87
|
+
not a filesystem CAS against unrelated writers after the final check.
|
|
88
|
+
"""
|
|
89
|
+
current, error = _read_workspace_text(target)
|
|
90
|
+
if error is not None:
|
|
91
|
+
return None, {**error, "path": relative}
|
|
92
|
+
if current is None:
|
|
93
|
+
return None, {"ok": False, "path": relative, "error_message": "workspace file read failed"}
|
|
94
|
+
error = _validate_read_state(target, relative, state=state, content=current)
|
|
95
|
+
if error is not None:
|
|
96
|
+
return None, error
|
|
97
|
+
publish_workspace_bytes(target, updated.encode("utf-8"))
|
|
98
|
+
stat = target.stat()
|
|
99
|
+
_refresh_read_state(relative, stat, updated, target=target)
|
|
100
|
+
return stat, None
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _apply_single_edit(
|
|
104
|
+
text: str,
|
|
105
|
+
old_text: Any,
|
|
106
|
+
new_text: Any,
|
|
107
|
+
*,
|
|
108
|
+
expected_replacements: int = 1,
|
|
109
|
+
replace_all: bool = False,
|
|
110
|
+
relative: str,
|
|
111
|
+
) -> dict[str, Any]:
|
|
112
|
+
request_error = _validate_single_edit_request(old_text, relative=relative)
|
|
113
|
+
if request_error is not None:
|
|
114
|
+
return request_error
|
|
115
|
+
# Match LF/CRLF snippets against the file's convention without converting
|
|
116
|
+
# any unrelated bytes. In particular, never normalize the whole file.
|
|
117
|
+
ending = "\r\n" if "\r\n" in text else "\n"
|
|
118
|
+
old = str(old_text).replace("\r\n", "\n").replace("\n", ending)
|
|
119
|
+
replacement = str(new_text or "").replace("\r\n", "\n").replace("\n", ending)
|
|
120
|
+
expected = max(1, int(expected_replacements or 1))
|
|
121
|
+
actual_old, used_quote_normalization = _find_actual_string(text, old)
|
|
122
|
+
matches = _match_previews(text, actual_old or old) if actual_old else []
|
|
123
|
+
occurrences = text.count(actual_old) if actual_old else 0
|
|
124
|
+
if occurrences == 0:
|
|
125
|
+
return {
|
|
126
|
+
"ok": False,
|
|
127
|
+
"path": relative,
|
|
128
|
+
"error_type": "snippet_not_found",
|
|
129
|
+
"error_message": "old_text was not found in the workspace file",
|
|
130
|
+
"occurrences": 0,
|
|
131
|
+
"nearby_candidates": _nearby_candidates(text, old),
|
|
132
|
+
"suggested_action": (
|
|
133
|
+
"Use one nearby candidate as old_text, or call read_workspace_file "
|
|
134
|
+
"with line numbers and retry."
|
|
135
|
+
),
|
|
136
|
+
}
|
|
137
|
+
if actual_old is None:
|
|
138
|
+
return {
|
|
139
|
+
"ok": False,
|
|
140
|
+
"path": relative,
|
|
141
|
+
"error_type": "snippet_not_found",
|
|
142
|
+
"error_message": "old_text was not found in the workspace file",
|
|
143
|
+
}
|
|
144
|
+
if not replace_all and occurrences != expected:
|
|
145
|
+
return {
|
|
146
|
+
"ok": False,
|
|
147
|
+
"path": relative,
|
|
148
|
+
"error_type": "ambiguous_edit",
|
|
149
|
+
"error_message": f"old_text matched {occurrences} occurrences, expected {expected}",
|
|
150
|
+
"occurrences": occurrences,
|
|
151
|
+
"expected_replacements": expected,
|
|
152
|
+
"matches": matches,
|
|
153
|
+
"suggested_action": (
|
|
154
|
+
"Provide a larger unique old_text snippet, set expected_replacements, "
|
|
155
|
+
"or use replace_all=true."
|
|
156
|
+
),
|
|
157
|
+
}
|
|
158
|
+
replacements = occurrences if replace_all else expected
|
|
159
|
+
return {
|
|
160
|
+
"ok": True,
|
|
161
|
+
"updated": text.replace(actual_old, replacement, replacements),
|
|
162
|
+
"replacements": replacements,
|
|
163
|
+
"used_quote_normalization": used_quote_normalization,
|
|
164
|
+
"matches": matches,
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def _validate_single_edit_request(old_text: Any, *, relative: str) -> dict[str, Any] | None:
|
|
169
|
+
if not str(old_text or ""):
|
|
170
|
+
return {
|
|
171
|
+
"ok": False,
|
|
172
|
+
"path": relative,
|
|
173
|
+
"error_type": "missing_old_text",
|
|
174
|
+
"error_message": "old_text is required",
|
|
175
|
+
}
|
|
176
|
+
return None
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def _match_previews(text: str, needle: str, limit: int = 20) -> list[dict[str, Any]]:
|
|
180
|
+
if not needle:
|
|
181
|
+
return []
|
|
182
|
+
matches: list[dict[str, Any]] = []
|
|
183
|
+
for line_no, line in enumerate(text.splitlines(), start=1):
|
|
184
|
+
if needle in line:
|
|
185
|
+
matches.append({"line": line_no, "text": line.strip(), "snippet": line})
|
|
186
|
+
if len(matches) >= limit:
|
|
187
|
+
break
|
|
188
|
+
return matches
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def _nearby_candidates(text: str, old: str, limit: int = 5) -> list[dict[str, Any]]:
|
|
192
|
+
old_norm = _normalize_quotes(old).strip()
|
|
193
|
+
candidates: list[dict[str, Any]] = []
|
|
194
|
+
for line_no, line in enumerate(text.splitlines(), start=1):
|
|
195
|
+
ratio = difflib.SequenceMatcher(None, old_norm, _normalize_quotes(line).strip()).ratio()
|
|
196
|
+
if old_norm and (
|
|
197
|
+
old_norm[: max(1, min(4, len(old_norm)))] in _normalize_quotes(line) or ratio > 0.45
|
|
198
|
+
):
|
|
199
|
+
candidates.append(
|
|
200
|
+
{"line": line_no, "text": line.strip(), "snippet": line, "score": round(ratio, 3)}
|
|
201
|
+
)
|
|
202
|
+
candidates.sort(key=lambda item: (-float(item["score"]), int(item["line"])))
|
|
203
|
+
return candidates[:limit]
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def _build_diff(relative: str, before: str, after: str) -> str:
|
|
207
|
+
return "".join(
|
|
208
|
+
difflib.unified_diff(
|
|
209
|
+
before.splitlines(keepends=True),
|
|
210
|
+
after.splitlines(keepends=True),
|
|
211
|
+
fromfile=f"{relative} before",
|
|
212
|
+
tofile=f"{relative} after",
|
|
213
|
+
)
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def _budget_diff(tool_name: str, diff: str, relative: str) -> dict[str, Any]:
|
|
218
|
+
# The model supplied the edit, so echoing a whole-file diff back into its
|
|
219
|
+
# next context wastes the same budget it needs to verify and repair. Keep
|
|
220
|
+
# the full diff in a distinct audit file and return a bounded preview.
|
|
221
|
+
default = default_tool_result_budget()
|
|
222
|
+
budget = replace(
|
|
223
|
+
default,
|
|
224
|
+
max_chars=min(default.max_chars, 4096),
|
|
225
|
+
preview_chars=min(default.preview_chars, 2048),
|
|
226
|
+
persist_threshold_chars=min(default.persist_threshold_chars, 4096),
|
|
227
|
+
)
|
|
228
|
+
diff_digest = hashlib.sha256(diff.encode("utf-8")).hexdigest()
|
|
229
|
+
path_digest = hashlib.sha256(relative.encode("utf-8")).hexdigest()[:12]
|
|
230
|
+
result = _dict_result(
|
|
231
|
+
budget_tool_output(
|
|
232
|
+
tool_name=tool_name,
|
|
233
|
+
field_name="diff",
|
|
234
|
+
value=diff,
|
|
235
|
+
metadata={"tool_use_id": f"{tool_name}_{path_digest}_{diff_digest[:16]}"},
|
|
236
|
+
budget=budget,
|
|
237
|
+
),
|
|
238
|
+
tool_name=tool_name,
|
|
239
|
+
)
|
|
240
|
+
result["diff_sha256"] = diff_digest
|
|
241
|
+
result["diff_line_count"] = diff.count("\n")
|
|
242
|
+
if result["truncated"]:
|
|
243
|
+
result["suggested_action"] = (
|
|
244
|
+
"Edit succeeded. Read the affected file and run relevant checks; "
|
|
245
|
+
"the full diff is retained at the persisted path for audit."
|
|
246
|
+
)
|
|
247
|
+
return result
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def _refresh_read_state(relative: str, stat: Any, text: str, *, target: Path) -> None:
|
|
251
|
+
lines = text.splitlines()
|
|
252
|
+
record_read_state(
|
|
253
|
+
WorkspaceReadState(
|
|
254
|
+
path=relative,
|
|
255
|
+
mtime_ns=stat.st_mtime_ns,
|
|
256
|
+
size_bytes=stat.st_size,
|
|
257
|
+
start_line=1,
|
|
258
|
+
end_line=len(lines),
|
|
259
|
+
total_lines=len(lines),
|
|
260
|
+
partial=False,
|
|
261
|
+
canonical_path=str(target.resolve()),
|
|
262
|
+
content_sha256=hashlib.sha256(text.encode("utf-8")).hexdigest(),
|
|
263
|
+
)
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def _find_actual_string(text: str, old: str) -> tuple[str | None, bool]:
|
|
268
|
+
if old in text:
|
|
269
|
+
return old, False
|
|
270
|
+
normalized_old = _normalize_quotes(old)
|
|
271
|
+
smart_quote_map: dict[str, str | int | None] = {'"': "“", "'": "‘"}
|
|
272
|
+
for candidate in {normalized_old, old.translate(str.maketrans(smart_quote_map))}:
|
|
273
|
+
if candidate and candidate in text:
|
|
274
|
+
return candidate, True
|
|
275
|
+
normalized_text = _normalize_quotes(text)
|
|
276
|
+
index = normalized_text.find(normalized_old)
|
|
277
|
+
if index < 0:
|
|
278
|
+
return None, False
|
|
279
|
+
return text[index : index + len(old)], True
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def _normalize_quotes(value: str) -> str:
|
|
283
|
+
quote_map: dict[str, str | int | None] = {
|
|
284
|
+
"“": '"',
|
|
285
|
+
"”": '"',
|
|
286
|
+
"„": '"',
|
|
287
|
+
"‘": "'",
|
|
288
|
+
"’": "'",
|
|
289
|
+
"‚": "'",
|
|
290
|
+
}
|
|
291
|
+
return value.translate(str.maketrans(quote_map))
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""Source-reader model view; the full SDK result remains the audit record.
|
|
2
|
+
|
|
3
|
+
Only host-selected builtin readers use this projection. Source text, visible
|
|
4
|
+
range, omissions, continuation and overflow locators stay intact. Internal
|
|
5
|
+
accounting and duplicate display counters need not compete with the source
|
|
6
|
+
for the model's window. Unknown fields are preserved, never silently dropped.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import json
|
|
12
|
+
from collections.abc import Mapping
|
|
13
|
+
|
|
14
|
+
WORKSPACE_SOURCE_MODEL_VERSION = "workspace-source-model/v1"
|
|
15
|
+
_AUDIT_FIELDS = frozenset(
|
|
16
|
+
{"size", "size_bytes", "mtime_ns", "source_output_budget", "original_chars", "preview_chars"}
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def source_model_reply(result: object) -> str | None:
|
|
21
|
+
"""Return a detached, structured view of a recognized successful page.
|
|
22
|
+
|
|
23
|
+
No file access, source extraction, interpretation or acceptance occurs.
|
|
24
|
+
Malformed/failed results keep the existing raw projection instead.
|
|
25
|
+
"""
|
|
26
|
+
if (
|
|
27
|
+
not isinstance(result, Mapping)
|
|
28
|
+
or result.get("ok") is not True
|
|
29
|
+
or not isinstance(result.get("path"), str)
|
|
30
|
+
or not result["path"]
|
|
31
|
+
or not isinstance(result.get("content"), str)
|
|
32
|
+
):
|
|
33
|
+
return None
|
|
34
|
+
start, end, total, count = (
|
|
35
|
+
result.get(key) for key in ("start_line", "end_line", "total_lines", "line_count")
|
|
36
|
+
)
|
|
37
|
+
carrier = result.get("requested_source")
|
|
38
|
+
if result.get("artifact_only") is True:
|
|
39
|
+
if (
|
|
40
|
+
not isinstance(carrier, Mapping) or carrier.get("path") != result["path"]
|
|
41
|
+
or not isinstance(carrier.get("content"), str) or not carrier["content"]
|
|
42
|
+
or not isinstance(carrier.get("read_range"), dict)
|
|
43
|
+
or type(start) is not int or type(total) is not int or not 1 <= start <= total
|
|
44
|
+
or type(count) is not int or count != 0
|
|
45
|
+
or end is not None or result.get("read_range") is not None
|
|
46
|
+
or result["content"] != "" or result.get("truncated") is not True
|
|
47
|
+
or result.get("partial") is not True
|
|
48
|
+
):
|
|
49
|
+
return None
|
|
50
|
+
requested = carrier["read_range"]
|
|
51
|
+
if (
|
|
52
|
+
set(requested) != {"start", "end"}
|
|
53
|
+
or any(type(v) is not int for v in requested.values())
|
|
54
|
+
or requested["start"] != start or not start <= requested["end"] <= total
|
|
55
|
+
):
|
|
56
|
+
return None
|
|
57
|
+
return json.dumps({"status": "ok", "content": {
|
|
58
|
+
"ok": True, "path": result["path"], "source_state": "unread",
|
|
59
|
+
"source_read_range": requested,
|
|
60
|
+
}}, ensure_ascii=False, separators=(",", ":"))
|
|
61
|
+
if any(type(value) is not int for value in (start, total, count)):
|
|
62
|
+
return None
|
|
63
|
+
if start < 1 or total < 0 or count < 0:
|
|
64
|
+
return None
|
|
65
|
+
read_range = result.get("read_range")
|
|
66
|
+
if count:
|
|
67
|
+
if (
|
|
68
|
+
type(end) is not int
|
|
69
|
+
or end != start + count - 1
|
|
70
|
+
or end > total
|
|
71
|
+
or not isinstance(read_range, dict)
|
|
72
|
+
or any(type(read_range.get(key)) is not int for key in ("start", "end"))
|
|
73
|
+
or read_range != {"start": start, "end": end}
|
|
74
|
+
):
|
|
75
|
+
return None
|
|
76
|
+
elif total:
|
|
77
|
+
if (
|
|
78
|
+
start > total
|
|
79
|
+
or end is not None
|
|
80
|
+
or read_range is not None
|
|
81
|
+
or result.get("partial_line") != start
|
|
82
|
+
or result.get("truncated") is not True
|
|
83
|
+
or not result["content"]
|
|
84
|
+
):
|
|
85
|
+
return None
|
|
86
|
+
elif end != 0 or result["content"] or start != 1 or read_range != {"start": 1, "end": 0}:
|
|
87
|
+
return None
|
|
88
|
+
if any(type(result.get(key)) is not bool for key in ("partial", "truncated")):
|
|
89
|
+
return None
|
|
90
|
+
hidden = _AUDIT_FIELDS
|
|
91
|
+
if "requested_source" in result:
|
|
92
|
+
# The opt-in carrier crosses the host boundary, but never the model
|
|
93
|
+
# window. SDK filesystem locators cannot grant Native artifact access.
|
|
94
|
+
hidden = hidden | {"requested_source", "persisted"}
|
|
95
|
+
view = {k: v for k, v in result.items() if k not in hidden}
|
|
96
|
+
if "requested_source" in result:
|
|
97
|
+
view["suggested_action"] = (
|
|
98
|
+
"Inspect the complete requested source through the host artifact reference."
|
|
99
|
+
)
|
|
100
|
+
return json.dumps(
|
|
101
|
+
{"status": "ok", "content": view},
|
|
102
|
+
ensure_ascii=False,
|
|
103
|
+
separators=(",", ":"),
|
|
104
|
+
)
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""Local file mutation primitives; neither a Run ledger nor a distributed lock.
|
|
2
|
+
|
|
3
|
+
SDK callers in one process serialize by canonical path. Builtin workers retain
|
|
4
|
+
their host's SQLite serialization. Unrelated external writers do not obey these
|
|
5
|
+
locks: atomic publication prevents partial files, not arbitrary filesystem CAS.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import os
|
|
11
|
+
import secrets
|
|
12
|
+
import stat
|
|
13
|
+
from contextlib import ExitStack, contextmanager
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from threading import Lock, RLock
|
|
16
|
+
from weakref import WeakValueDictionary
|
|
17
|
+
|
|
18
|
+
_LOCKS: WeakValueDictionary[str, RLock] = WeakValueDictionary()
|
|
19
|
+
_LOCKS_GUARD = Lock()
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@contextmanager
|
|
23
|
+
def locked_workspace_paths(paths):
|
|
24
|
+
"""Acquire a deterministic set of local path locks, including nested calls."""
|
|
25
|
+
keys = sorted({os.path.normcase(str(Path(path).resolve())) for path in paths})
|
|
26
|
+
# Hold strong references before acquiring any lock. Otherwise an unused
|
|
27
|
+
# entry could disappear while another caller prepares the same set.
|
|
28
|
+
with _LOCKS_GUARD:
|
|
29
|
+
locks = []
|
|
30
|
+
for key in keys:
|
|
31
|
+
lock = _LOCKS.get(key)
|
|
32
|
+
if lock is None:
|
|
33
|
+
lock = RLock()
|
|
34
|
+
_LOCKS[key] = lock
|
|
35
|
+
locks.append(lock)
|
|
36
|
+
with ExitStack() as stack:
|
|
37
|
+
for lock in locks:
|
|
38
|
+
stack.enter_context(lock)
|
|
39
|
+
yield
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def publish_workspace_bytes(target: Path, payload: bytes, *, create_only: bool = False) -> None:
|
|
43
|
+
"""Publish a complete sibling file; never fall back to truncating the target.
|
|
44
|
+
|
|
45
|
+
The caller must hold its local mutation lock and validate the observed
|
|
46
|
+
revision. Create-only publication cannot replace a racing new file. A
|
|
47
|
+
failed replace leaves the original content and read-state unchanged.
|
|
48
|
+
"""
|
|
49
|
+
target.parent.mkdir(parents=True, exist_ok=True)
|
|
50
|
+
mode = None
|
|
51
|
+
try:
|
|
52
|
+
info = target.lstat()
|
|
53
|
+
except FileNotFoundError:
|
|
54
|
+
pass
|
|
55
|
+
else:
|
|
56
|
+
if not stat.S_ISREG(info.st_mode):
|
|
57
|
+
raise ValueError("workspace mutation requires a regular file")
|
|
58
|
+
mode = stat.S_IMODE(info.st_mode)
|
|
59
|
+
temporary = target.parent / f".{target.name}.ksadk-{secrets.token_hex(12)}"
|
|
60
|
+
# Preserve ordinary new-file permissions (0666 filtered by the process
|
|
61
|
+
# umask) without reading/changing the process-wide umask in other threads.
|
|
62
|
+
descriptor = os.open(temporary, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o666)
|
|
63
|
+
try:
|
|
64
|
+
with os.fdopen(descriptor, "wb") as stream:
|
|
65
|
+
stream.write(payload)
|
|
66
|
+
if mode is not None:
|
|
67
|
+
os.chmod(temporary, mode)
|
|
68
|
+
stream.flush()
|
|
69
|
+
os.fsync(stream.fileno())
|
|
70
|
+
if create_only:
|
|
71
|
+
os.link(temporary, target)
|
|
72
|
+
else:
|
|
73
|
+
os.replace(temporary, target)
|
|
74
|
+
finally:
|
|
75
|
+
temporary.unlink(missing_ok=True)
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
"""Optional host budget for a source page, independent of Agent frameworks."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import math
|
|
7
|
+
from contextlib import contextmanager
|
|
8
|
+
from contextvars import ContextVar
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from typing import Callable
|
|
11
|
+
|
|
12
|
+
from ksadk.conversations.model_context import estimate_text_tokens
|
|
13
|
+
from ksadk.tools.artifact_observation import (
|
|
14
|
+
ARCHIVE_MODEL_POLICY,
|
|
15
|
+
ARCHIVE_SEARCH_MODEL_POLICY,
|
|
16
|
+
ARCHIVE_URI_MAX_CHARS,
|
|
17
|
+
CANONICAL_ARCHIVE_MODEL_POLICY,
|
|
18
|
+
OBSERVED_ARCHIVE_MODEL_POLICY,
|
|
19
|
+
SOURCE_ARCHIVE_MODEL_POLICY,
|
|
20
|
+
archived_observation,
|
|
21
|
+
)
|
|
22
|
+
from ksadk.toolsets.workspace_model_observation import (
|
|
23
|
+
WORKSPACE_SOURCE_MODEL_VERSION,
|
|
24
|
+
source_model_reply,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
SOURCE_COUNTER_ID = "studio-cjk-ascii-estimate-125/v1"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def estimated_source_units(text: str) -> int:
|
|
31
|
+
return max(1, math.ceil(estimate_text_tokens(text) * 1.25))
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass(frozen=True)
|
|
35
|
+
class SourceReadBudget:
|
|
36
|
+
max_units: int
|
|
37
|
+
count_units: Callable[[str], int]
|
|
38
|
+
counter_id: str
|
|
39
|
+
max_result_units: int | None = None
|
|
40
|
+
result_projection: str | None = None
|
|
41
|
+
retain_requested_source: bool = False
|
|
42
|
+
artifact_only_fallback: bool = False
|
|
43
|
+
archive_model_policy: str = ARCHIVE_MODEL_POLICY
|
|
44
|
+
|
|
45
|
+
def __post_init__(self):
|
|
46
|
+
if type(self.max_units) is not int or self.max_units < 1:
|
|
47
|
+
raise ValueError("source_read_budget_unavailable")
|
|
48
|
+
if not callable(self.count_units) or not self.counter_id.strip():
|
|
49
|
+
raise ValueError("source_read_counter_unavailable")
|
|
50
|
+
if self.max_result_units is not None and (
|
|
51
|
+
type(self.max_result_units) is not int or self.max_result_units < 1
|
|
52
|
+
):
|
|
53
|
+
raise ValueError("source_read_budget_unavailable")
|
|
54
|
+
if self.result_projection not in (None, WORKSPACE_SOURCE_MODEL_VERSION):
|
|
55
|
+
raise ValueError("source_read_projection_unavailable")
|
|
56
|
+
if type(self.retain_requested_source) is not bool or (
|
|
57
|
+
self.retain_requested_source
|
|
58
|
+
and self.result_projection != WORKSPACE_SOURCE_MODEL_VERSION
|
|
59
|
+
):
|
|
60
|
+
raise ValueError("source_read_archive_policy_invalid")
|
|
61
|
+
if type(self.artifact_only_fallback) is not bool or (
|
|
62
|
+
self.artifact_only_fallback and not self.retain_requested_source
|
|
63
|
+
):
|
|
64
|
+
raise ValueError("source_read_archive_policy_invalid")
|
|
65
|
+
if self.archive_model_policy not in {
|
|
66
|
+
ARCHIVE_MODEL_POLICY, ARCHIVE_SEARCH_MODEL_POLICY, CANONICAL_ARCHIVE_MODEL_POLICY,
|
|
67
|
+
OBSERVED_ARCHIVE_MODEL_POLICY, SOURCE_ARCHIVE_MODEL_POLICY,
|
|
68
|
+
} or (
|
|
69
|
+
self.archive_model_policy in {
|
|
70
|
+
ARCHIVE_SEARCH_MODEL_POLICY, CANONICAL_ARCHIVE_MODEL_POLICY,
|
|
71
|
+
OBSERVED_ARCHIVE_MODEL_POLICY, SOURCE_ARCHIVE_MODEL_POLICY,
|
|
72
|
+
}
|
|
73
|
+
and not (self.retain_requested_source and self.artifact_only_fallback)
|
|
74
|
+
):
|
|
75
|
+
raise ValueError("source_read_archive_policy_invalid")
|
|
76
|
+
|
|
77
|
+
def character_limit(self, text: str, requested_limit: int) -> int:
|
|
78
|
+
def fits(length):
|
|
79
|
+
fragment = text[:length]
|
|
80
|
+
cost = self.count_units(fragment)
|
|
81
|
+
if type(cost) is not int or cost < 0 or (fragment and cost == 0):
|
|
82
|
+
raise ValueError("source_read_counter_invalid")
|
|
83
|
+
return cost <= self.max_units
|
|
84
|
+
|
|
85
|
+
high = min(len(text), requested_limit)
|
|
86
|
+
if fits(high):
|
|
87
|
+
return max(1, high)
|
|
88
|
+
low = 0
|
|
89
|
+
while low < high:
|
|
90
|
+
middle = (low + high + 1) // 2
|
|
91
|
+
if fits(middle):
|
|
92
|
+
low = middle
|
|
93
|
+
else:
|
|
94
|
+
high = middle - 1
|
|
95
|
+
if not low:
|
|
96
|
+
raise ValueError("source_read_budget_unavailable")
|
|
97
|
+
return low
|
|
98
|
+
|
|
99
|
+
def result_fits(self, result: dict) -> bool:
|
|
100
|
+
if self.max_result_units is None:
|
|
101
|
+
return True
|
|
102
|
+
# A JSON reply is quoted inside the model tool-message text. Charge
|
|
103
|
+
# that escaping as well as the page metadata, not just source text.
|
|
104
|
+
text = (
|
|
105
|
+
source_model_reply(result)
|
|
106
|
+
if self.result_projection == WORKSPACE_SOURCE_MODEL_VERSION
|
|
107
|
+
else None
|
|
108
|
+
)
|
|
109
|
+
if text is None:
|
|
110
|
+
text = json.dumps({"status": "ok", "content": result}, ensure_ascii=False)
|
|
111
|
+
if self.retain_requested_source:
|
|
112
|
+
# The URI is issued only after persistence. Reserve the maximum
|
|
113
|
+
# declared locator here, including its complete model framing.
|
|
114
|
+
text = archived_observation(
|
|
115
|
+
text,
|
|
116
|
+
"artifact://" + "a" * (ARCHIVE_URI_MAX_CHARS - 11),
|
|
117
|
+
policy=self.archive_model_policy,
|
|
118
|
+
)
|
|
119
|
+
cost = self.count_units(json.dumps(text, ensure_ascii=False))
|
|
120
|
+
if type(cost) is not int or cost <= 0:
|
|
121
|
+
raise ValueError("source_read_counter_invalid")
|
|
122
|
+
return cost <= self.max_result_units
|
|
123
|
+
|
|
124
|
+
def transport(self) -> dict:
|
|
125
|
+
"""Only a versioned host counter can cross an isolated SDK boundary."""
|
|
126
|
+
if self.counter_id != SOURCE_COUNTER_ID:
|
|
127
|
+
raise ValueError("source_read_counter_transport_unavailable")
|
|
128
|
+
result = {
|
|
129
|
+
"version": "source-read-budget/v2"
|
|
130
|
+
if self.result_projection is None
|
|
131
|
+
else "source-read-budget/v3",
|
|
132
|
+
"counter_id": self.counter_id,
|
|
133
|
+
"max_units": self.max_units,
|
|
134
|
+
"max_result_units": self.max_result_units,
|
|
135
|
+
}
|
|
136
|
+
if self.result_projection is not None:
|
|
137
|
+
result["result_projection"] = self.result_projection
|
|
138
|
+
if self.retain_requested_source:
|
|
139
|
+
result.update(version="source-read-budget/v4", retain_requested_source=True)
|
|
140
|
+
if self.artifact_only_fallback:
|
|
141
|
+
result.update(version="source-read-budget/v5", artifact_only_fallback=True)
|
|
142
|
+
if self.archive_model_policy == ARCHIVE_SEARCH_MODEL_POLICY:
|
|
143
|
+
result.update(
|
|
144
|
+
version="source-read-budget/v6", archive_model_policy=self.archive_model_policy
|
|
145
|
+
)
|
|
146
|
+
if self.archive_model_policy == CANONICAL_ARCHIVE_MODEL_POLICY:
|
|
147
|
+
result.update(
|
|
148
|
+
version="source-read-budget/v7", archive_model_policy=self.archive_model_policy
|
|
149
|
+
)
|
|
150
|
+
versions = {
|
|
151
|
+
OBSERVED_ARCHIVE_MODEL_POLICY: "source-read-budget/v8",
|
|
152
|
+
SOURCE_ARCHIVE_MODEL_POLICY: "source-read-budget/v9",
|
|
153
|
+
}
|
|
154
|
+
if self.archive_model_policy in versions:
|
|
155
|
+
result.update(version=versions[self.archive_model_policy],
|
|
156
|
+
archive_model_policy=self.archive_model_policy)
|
|
157
|
+
return result
|
|
158
|
+
|
|
159
|
+
@classmethod
|
|
160
|
+
def from_transport(cls, value: object) -> SourceReadBudget | None:
|
|
161
|
+
if value is None:
|
|
162
|
+
return None
|
|
163
|
+
if not isinstance(value, dict):
|
|
164
|
+
raise ValueError("source_read_budget_transport_invalid")
|
|
165
|
+
version = value.get("version")
|
|
166
|
+
known = {f"source-read-budget/v{i}": i for i in range(2, 10)}
|
|
167
|
+
rank = known.get(version) if isinstance(version, str) else None
|
|
168
|
+
if rank is None:
|
|
169
|
+
raise ValueError("source_read_budget_transport_invalid")
|
|
170
|
+
expected = {"version", "counter_id", "max_units", "max_result_units"}
|
|
171
|
+
if rank >= 3:
|
|
172
|
+
expected.add("result_projection")
|
|
173
|
+
if rank >= 4:
|
|
174
|
+
expected.add("retain_requested_source")
|
|
175
|
+
if rank >= 5:
|
|
176
|
+
expected.add("artifact_only_fallback")
|
|
177
|
+
if rank >= 6:
|
|
178
|
+
expected.add("archive_model_policy")
|
|
179
|
+
policies = {
|
|
180
|
+
6: ARCHIVE_SEARCH_MODEL_POLICY, 7: CANONICAL_ARCHIVE_MODEL_POLICY,
|
|
181
|
+
8: OBSERVED_ARCHIVE_MODEL_POLICY, 9: SOURCE_ARCHIVE_MODEL_POLICY,
|
|
182
|
+
}
|
|
183
|
+
if (
|
|
184
|
+
set(value) != expected or value["counter_id"] != SOURCE_COUNTER_ID
|
|
185
|
+
or (rank >= 3 and value["result_projection"] != WORKSPACE_SOURCE_MODEL_VERSION)
|
|
186
|
+
or (rank >= 4 and value["retain_requested_source"] is not True)
|
|
187
|
+
or (rank >= 5 and value["artifact_only_fallback"] is not True)
|
|
188
|
+
or (rank >= 6 and value["archive_model_policy"] != policies[rank])
|
|
189
|
+
):
|
|
190
|
+
raise ValueError("source_read_budget_transport_invalid")
|
|
191
|
+
return cls(
|
|
192
|
+
value["max_units"],
|
|
193
|
+
estimated_source_units,
|
|
194
|
+
SOURCE_COUNTER_ID,
|
|
195
|
+
value["max_result_units"],
|
|
196
|
+
value.get("result_projection"),
|
|
197
|
+
value.get("retain_requested_source", False),
|
|
198
|
+
value.get("artifact_only_fallback", False),
|
|
199
|
+
value.get("archive_model_policy", ARCHIVE_MODEL_POLICY),
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
_READ_BUDGET: ContextVar[SourceReadBudget | None] = ContextVar("source_read_budget", default=None)
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
@contextmanager
|
|
207
|
+
def source_read_budget_scope(budget: SourceReadBudget | None):
|
|
208
|
+
token = _READ_BUDGET.set(budget)
|
|
209
|
+
try:
|
|
210
|
+
yield
|
|
211
|
+
finally:
|
|
212
|
+
_READ_BUDGET.reset(token)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def current_source_read_budget() -> SourceReadBudget | None:
|
|
216
|
+
return _READ_BUDGET.get()
|