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
|
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
4
|
import logging
|
|
5
|
+
from dataclasses import dataclass
|
|
5
6
|
from typing import Any, Optional
|
|
6
7
|
|
|
7
8
|
from ksadk.compat.adk_compat import (
|
|
@@ -12,11 +13,22 @@ from ksadk.compat.adk_compat import (
|
|
|
12
13
|
ListSessionsResponse,
|
|
13
14
|
Session,
|
|
14
15
|
)
|
|
16
|
+
from ksadk.sessions.db_budget import DatabaseOperationDeadline, session_db_budget
|
|
15
17
|
from ksadk.sessions.resilience import is_session_backend_failure
|
|
16
18
|
|
|
17
19
|
logger = logging.getLogger(__name__)
|
|
18
20
|
|
|
19
21
|
|
|
22
|
+
@dataclass(frozen=True)
|
|
23
|
+
class ADKEventCommitUnknown:
|
|
24
|
+
"""Fail-open live event whose durable COMMIT cannot be confirmed."""
|
|
25
|
+
|
|
26
|
+
app_name: str
|
|
27
|
+
user_id: str
|
|
28
|
+
session_id: str
|
|
29
|
+
event_id: str
|
|
30
|
+
|
|
31
|
+
|
|
20
32
|
class ResilientADKSessionService(BaseSessionService):
|
|
21
33
|
"""Run ADK sessions locally while mirroring them to durable storage."""
|
|
22
34
|
|
|
@@ -26,6 +38,11 @@ class ResilientADKSessionService(BaseSessionService):
|
|
|
26
38
|
self.primary = primary
|
|
27
39
|
self.live = InMemorySessionService()
|
|
28
40
|
self._primary_sessions: dict[tuple[str, str, str], Session] = {}
|
|
41
|
+
# A failed write has an unknown commit outcome. Never replay it or
|
|
42
|
+
# hydrate stale durable state over the live session after recovery.
|
|
43
|
+
self._dirty_sessions: set[tuple[str, str, str]] = set()
|
|
44
|
+
self._deleted_sessions: set[tuple[str, str, str]] = set()
|
|
45
|
+
self.unknown_event_commits: set[ADKEventCommitUnknown] = set()
|
|
29
46
|
self._primary_enabled = True
|
|
30
47
|
self._probe_task: asyncio.Task[None] | None = None
|
|
31
48
|
|
|
@@ -92,7 +109,42 @@ class ResilientADKSessionService(BaseSessionService):
|
|
|
92
109
|
def _key(app_name: str, user_id: str, session_id: str) -> tuple[str, str, str]:
|
|
93
110
|
return app_name, user_id, session_id
|
|
94
111
|
|
|
112
|
+
def _write_deadline(self) -> DatabaseOperationDeadline:
|
|
113
|
+
return DatabaseOperationDeadline(
|
|
114
|
+
getattr(self.primary, "db_budget", None) or session_db_budget()
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
async def reconcile_event_commit(
|
|
118
|
+
self, *, app_name: str, user_id: str, session_id: str, event_id: str
|
|
119
|
+
) -> str:
|
|
120
|
+
"""Read back a stable ADK event ID after a write's outcome is unknown.
|
|
121
|
+
|
|
122
|
+
Absence is still ``unknown`` because an in-flight commit may finish
|
|
123
|
+
after this read. This method never replays an event or clears dirty
|
|
124
|
+
local state.
|
|
125
|
+
"""
|
|
126
|
+
try:
|
|
127
|
+
durable = await self.primary.get_session(
|
|
128
|
+
app_name=app_name, user_id=user_id, session_id=session_id
|
|
129
|
+
)
|
|
130
|
+
except Exception:
|
|
131
|
+
return "unknown"
|
|
132
|
+
if durable and any(event.id == event_id for event in durable.events):
|
|
133
|
+
self.unknown_event_commits.discard(
|
|
134
|
+
ADKEventCommitUnknown(app_name, user_id, session_id, event_id)
|
|
135
|
+
)
|
|
136
|
+
return "committed"
|
|
137
|
+
return "unknown"
|
|
138
|
+
|
|
95
139
|
async def _hydrate(self, durable: Session) -> Session:
|
|
140
|
+
key = self._key(durable.app_name, durable.user_id, durable.id)
|
|
141
|
+
if key in self._dirty_sessions:
|
|
142
|
+
existing = await self.live.get_session(
|
|
143
|
+
app_name=durable.app_name, user_id=durable.user_id, session_id=durable.id
|
|
144
|
+
)
|
|
145
|
+
if existing is None:
|
|
146
|
+
raise RuntimeError("Dirty ADK session has no live state")
|
|
147
|
+
return existing
|
|
96
148
|
existing = await self.live.get_session(
|
|
97
149
|
app_name=durable.app_name,
|
|
98
150
|
user_id=durable.user_id,
|
|
@@ -128,12 +180,15 @@ class ResilientADKSessionService(BaseSessionService):
|
|
|
128
180
|
session_id: Optional[str] = None,
|
|
129
181
|
) -> Session:
|
|
130
182
|
if session_id:
|
|
183
|
+
key = self._key(app_name, user_id, session_id)
|
|
131
184
|
existing = await self.live.get_session(
|
|
132
185
|
app_name=app_name,
|
|
133
186
|
user_id=user_id,
|
|
134
187
|
session_id=session_id,
|
|
135
188
|
)
|
|
136
|
-
if self.
|
|
189
|
+
if key in self._dirty_sessions and existing is not None:
|
|
190
|
+
return existing
|
|
191
|
+
if self._primary_enabled and key not in self._dirty_sessions:
|
|
137
192
|
try:
|
|
138
193
|
durable = await self.primary.get_session(
|
|
139
194
|
app_name=app_name,
|
|
@@ -154,7 +209,9 @@ class ResilientADKSessionService(BaseSessionService):
|
|
|
154
209
|
state=state,
|
|
155
210
|
session_id=session_id,
|
|
156
211
|
)
|
|
157
|
-
|
|
212
|
+
key = self._key(app_name, user_id, live.id)
|
|
213
|
+
self._deleted_sessions.discard(key)
|
|
214
|
+
if self._primary_enabled and key not in self._dirty_sessions:
|
|
158
215
|
try:
|
|
159
216
|
durable = await self.primary.create_session(
|
|
160
217
|
app_name=app_name,
|
|
@@ -164,8 +221,11 @@ class ResilientADKSessionService(BaseSessionService):
|
|
|
164
221
|
)
|
|
165
222
|
self._primary_sessions[self._key(app_name, user_id, live.id)] = durable
|
|
166
223
|
except Exception as exc:
|
|
224
|
+
self._dirty_sessions.add(key)
|
|
167
225
|
if not self._handle_primary_failure(exc):
|
|
168
226
|
raise
|
|
227
|
+
else:
|
|
228
|
+
self._dirty_sessions.add(key)
|
|
169
229
|
return live
|
|
170
230
|
|
|
171
231
|
async def get_session(
|
|
@@ -176,13 +236,16 @@ class ResilientADKSessionService(BaseSessionService):
|
|
|
176
236
|
session_id: str,
|
|
177
237
|
config: Optional[GetSessionConfig] = None,
|
|
178
238
|
) -> Optional[Session]:
|
|
239
|
+
key = self._key(app_name, user_id, session_id)
|
|
179
240
|
live = await self.live.get_session(
|
|
180
241
|
app_name=app_name,
|
|
181
242
|
user_id=user_id,
|
|
182
243
|
session_id=session_id,
|
|
183
244
|
config=config,
|
|
184
245
|
)
|
|
185
|
-
if
|
|
246
|
+
if key in self._deleted_sessions:
|
|
247
|
+
return None
|
|
248
|
+
if not self._primary_enabled or key in self._dirty_sessions:
|
|
186
249
|
return live
|
|
187
250
|
try:
|
|
188
251
|
durable = await self.primary.get_session(
|
|
@@ -207,6 +270,9 @@ class ResilientADKSessionService(BaseSessionService):
|
|
|
207
270
|
try:
|
|
208
271
|
durable = await self.primary.list_sessions(app_name=app_name, user_id=user_id)
|
|
209
272
|
for session in durable.sessions:
|
|
273
|
+
key = self._key(session.app_name, session.user_id, session.id)
|
|
274
|
+
if key in self._dirty_sessions:
|
|
275
|
+
continue
|
|
210
276
|
await self._hydrate(session)
|
|
211
277
|
except Exception as exc:
|
|
212
278
|
if not self._handle_primary_failure(exc):
|
|
@@ -214,8 +280,11 @@ class ResilientADKSessionService(BaseSessionService):
|
|
|
214
280
|
return await self.live.list_sessions(app_name=app_name, user_id=user_id)
|
|
215
281
|
|
|
216
282
|
async def delete_session(self, *, app_name: str, user_id: str, session_id: str) -> None:
|
|
283
|
+
key = self._key(app_name, user_id, session_id)
|
|
217
284
|
await self.live.delete_session(app_name=app_name, user_id=user_id, session_id=session_id)
|
|
218
|
-
self._primary_sessions.pop(
|
|
285
|
+
self._primary_sessions.pop(key, None)
|
|
286
|
+
self._dirty_sessions.add(key)
|
|
287
|
+
self._deleted_sessions.add(key)
|
|
219
288
|
if self._primary_enabled:
|
|
220
289
|
try:
|
|
221
290
|
await self.primary.delete_session(
|
|
@@ -223,33 +292,64 @@ class ResilientADKSessionService(BaseSessionService):
|
|
|
223
292
|
user_id=user_id,
|
|
224
293
|
session_id=session_id,
|
|
225
294
|
)
|
|
295
|
+
self._dirty_sessions.discard(key)
|
|
296
|
+
self._deleted_sessions.discard(key)
|
|
226
297
|
except Exception as exc:
|
|
227
298
|
if not self._handle_primary_failure(exc):
|
|
228
299
|
raise
|
|
229
300
|
|
|
230
301
|
async def append_event(self, session: Session, event: Event) -> Event:
|
|
231
302
|
stored = await self.live.append_event(session, event)
|
|
232
|
-
|
|
233
|
-
return stored
|
|
303
|
+
deadline = self._write_deadline()
|
|
234
304
|
key = self._key(session.app_name, session.user_id, session.id)
|
|
305
|
+
if not self._primary_enabled or key in self._dirty_sessions:
|
|
306
|
+
self._dirty_sessions.add(key)
|
|
307
|
+
return stored
|
|
235
308
|
durable_session = self._primary_sessions.get(key)
|
|
236
309
|
try:
|
|
237
310
|
if durable_session is None:
|
|
238
|
-
durable_session = await
|
|
239
|
-
|
|
240
|
-
user_id=session.user_id,
|
|
241
|
-
session_id=session.id,
|
|
242
|
-
)
|
|
243
|
-
if durable_session is None:
|
|
244
|
-
durable_session = await self.primary.create_session(
|
|
311
|
+
durable_session = await deadline.run(
|
|
312
|
+
self.primary.get_session(
|
|
245
313
|
app_name=session.app_name,
|
|
246
314
|
user_id=session.user_id,
|
|
247
|
-
state=session.state,
|
|
248
315
|
session_id=session.id,
|
|
249
316
|
)
|
|
317
|
+
)
|
|
318
|
+
if durable_session is None:
|
|
319
|
+
durable_session = await deadline.run(
|
|
320
|
+
self.primary.create_session(
|
|
321
|
+
app_name=session.app_name,
|
|
322
|
+
user_id=session.user_id,
|
|
323
|
+
state=session.state,
|
|
324
|
+
session_id=session.id,
|
|
325
|
+
)
|
|
326
|
+
)
|
|
250
327
|
self._primary_sessions[key] = durable_session
|
|
251
|
-
await self.primary.append_event(durable_session, event)
|
|
328
|
+
await deadline.run(self.primary.append_event(durable_session, event))
|
|
252
329
|
except Exception as exc:
|
|
330
|
+
if is_session_backend_failure(exc):
|
|
331
|
+
try:
|
|
332
|
+
outcome = await deadline.run(
|
|
333
|
+
self.reconcile_event_commit(
|
|
334
|
+
app_name=session.app_name,
|
|
335
|
+
user_id=session.user_id,
|
|
336
|
+
session_id=session.id,
|
|
337
|
+
event_id=event.id,
|
|
338
|
+
)
|
|
339
|
+
)
|
|
340
|
+
except Exception:
|
|
341
|
+
outcome = "unknown"
|
|
342
|
+
if outcome == "committed":
|
|
343
|
+
# A lost acknowledgement is not a failed write. Reload the
|
|
344
|
+
# exact durable revision before the next append.
|
|
345
|
+
self._primary_sessions.pop(key, None)
|
|
346
|
+
return stored
|
|
347
|
+
self.unknown_event_commits.add(ADKEventCommitUnknown(*key, event.id))
|
|
348
|
+
logger.warning(
|
|
349
|
+
"ADK event commit outcome unknown; retaining dirty live state",
|
|
350
|
+
extra={"session_backend_reason_code": "ADK_EVENT_COMMIT_UNKNOWN"},
|
|
351
|
+
)
|
|
352
|
+
self._dirty_sessions.add(key)
|
|
253
353
|
if not self._handle_primary_failure(exc):
|
|
254
354
|
raise
|
|
255
355
|
return stored
|
|
@@ -153,18 +153,17 @@ class ShortTermMemory(BaseModel):
|
|
|
153
153
|
try:
|
|
154
154
|
from ksadk.compat.adk_compat import DatabaseSessionService
|
|
155
155
|
from ksadk.memory.adk.resilient_session_service import ResilientADKSessionService
|
|
156
|
-
from ksadk.sessions.resilience import session_backend_timeout_seconds
|
|
157
|
-
|
|
158
|
-
service_kwargs = {}
|
|
159
156
|
if normalized_db_url.startswith(("postgresql://", "postgresql+asyncpg://")):
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
157
|
+
from ksadk.memory.adk.bounded_database_session_service import (
|
|
158
|
+
BoundedADKDatabaseSessionService,
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
primary = BoundedADKDatabaseSessionService(db_url=normalized_db_url)
|
|
162
|
+
else:
|
|
163
|
+
primary = DatabaseSessionService(db_url=normalized_db_url)
|
|
165
164
|
|
|
166
165
|
self._session_service = ResilientADKSessionService(
|
|
167
|
-
|
|
166
|
+
primary
|
|
168
167
|
)
|
|
169
168
|
logger.info("ShortTermMemory: using DatabaseSessionService")
|
|
170
169
|
except ImportError:
|
ksadk/memory/extraction.py
CHANGED
|
@@ -12,8 +12,12 @@
|
|
|
12
12
|
|
|
13
13
|
from __future__ import annotations
|
|
14
14
|
|
|
15
|
+
import hashlib
|
|
16
|
+
import json
|
|
15
17
|
import re
|
|
16
18
|
import uuid
|
|
19
|
+
from dataclasses import replace
|
|
20
|
+
from types import SimpleNamespace
|
|
17
21
|
from typing import Sequence
|
|
18
22
|
|
|
19
23
|
from ksadk.memory.models import MemoryCandidate, MemoryScope
|
|
@@ -25,6 +29,23 @@ _EXPLICIT_PATTERNS: tuple[re.Pattern[str], ...] = (
|
|
|
25
29
|
re.compile(r"(?i)remember\s+(?:that\s+)?(.+)", re.IGNORECASE),
|
|
26
30
|
re.compile(r"(?i)请记[::]?\s*(.+)"),
|
|
27
31
|
)
|
|
32
|
+
# A declared conversation/turn horizon belongs to Session history, not the
|
|
33
|
+
# cross-session Memory provider. Inspect the memory-bearing first statement:
|
|
34
|
+
# a later "For this turn reply ACK" only scopes the answer format.
|
|
35
|
+
_SESSION_RETENTION = re.compile(
|
|
36
|
+
r"\b(?:for|during|within|in)\s+(?:only\s+)?(?:this|the\s+current)\s+"
|
|
37
|
+
r"(?:conversation|chat|session|turn)\b"
|
|
38
|
+
r"|(?:本次|这次|此次|当前|这个|本)(?:会话|对话|聊天)"
|
|
39
|
+
r"|(?:本轮|这轮)",
|
|
40
|
+
re.IGNORECASE,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _session_only_memory(text: str) -> bool:
|
|
45
|
+
statement = re.split(r"[。!?!?\r\n]|\.(?=\s|$)", text.strip(), maxsplit=1)[0]
|
|
46
|
+
return _SESSION_RETENTION.search(statement) is not None
|
|
47
|
+
|
|
48
|
+
|
|
28
49
|
# 明确纠正同一偏好槽位。首期只覆盖语义边界清晰的动作型偏好,避免把
|
|
29
50
|
# “喜欢音乐”和“喜欢运动”等无关事实误判为冲突。
|
|
30
51
|
_PREFERENCE_CORRECTION = re.compile(
|
|
@@ -95,17 +116,44 @@ def derive_profile_slot_key(content: str) -> str:
|
|
|
95
116
|
return f"profile.preference.{labels[action]}"
|
|
96
117
|
|
|
97
118
|
|
|
119
|
+
def is_explicit_memory_instruction(text: str) -> bool:
|
|
120
|
+
"""Recognize a complete durable instruction, not a quoted substring.
|
|
121
|
+
|
|
122
|
+
Candidate extraction intentionally searches larger transcripts. A host
|
|
123
|
+
granting an immediate explicit write needs the narrower whole-input test.
|
|
124
|
+
Unsupported wording can use the host's exact-call consent path; this is
|
|
125
|
+
not a general semantic intent classifier.
|
|
126
|
+
"""
|
|
127
|
+
text = text.strip()
|
|
128
|
+
if _session_only_memory(text):
|
|
129
|
+
return False
|
|
130
|
+
if any(pattern.fullmatch(text) for pattern in _EXPLICIT_PATTERNS):
|
|
131
|
+
return True
|
|
132
|
+
if _PREFERENCE_CORRECTION.fullmatch(text) or _GENERAL_CORRECTION.fullmatch(text):
|
|
133
|
+
return True
|
|
134
|
+
hobby = _HOBBY_DECLARATION.fullmatch(text)
|
|
135
|
+
return bool(hobby and hobby.group("correction"))
|
|
136
|
+
|
|
137
|
+
|
|
98
138
|
def propose_memory_candidates(
|
|
99
139
|
events: Sequence[any], # type: ignore[name-defined]
|
|
100
140
|
*,
|
|
101
141
|
scope: MemoryScope = "user",
|
|
102
142
|
scope_id: str = "",
|
|
143
|
+
max_content_chars: int | None = 1000,
|
|
103
144
|
) -> list[MemoryCandidate]:
|
|
104
145
|
"""从待压缩事件提取记忆候选(方案 §9.2)。
|
|
105
146
|
|
|
106
147
|
纯确定性、无 LLM。返回候选列表交 Coordinator flush;Policy 决定 commit/reject。
|
|
107
148
|
"""
|
|
108
149
|
candidates: list[MemoryCandidate] = []
|
|
150
|
+
if max_content_chars is not None and (
|
|
151
|
+
type(max_content_chars) is not int or max_content_chars < 1
|
|
152
|
+
):
|
|
153
|
+
raise ValueError("memory candidate limit must be a positive integer or None")
|
|
154
|
+
|
|
155
|
+
def bounded(content):
|
|
156
|
+
return content if max_content_chars is None else content[:max_content_chars]
|
|
109
157
|
if not events:
|
|
110
158
|
return candidates
|
|
111
159
|
for event in events:
|
|
@@ -119,6 +167,8 @@ def propose_memory_candidates(
|
|
|
119
167
|
|
|
120
168
|
# 1. 用户显式记忆意图
|
|
121
169
|
if author == "user" or event_type == "user_message":
|
|
170
|
+
if _session_only_memory(text):
|
|
171
|
+
continue
|
|
122
172
|
hobby = _HOBBY_DECLARATION.search(text)
|
|
123
173
|
if hobby and hobby.group("correction"):
|
|
124
174
|
new_value = hobby.group("value").strip().strip("。.,, ")
|
|
@@ -131,7 +181,7 @@ def propose_memory_candidates(
|
|
|
131
181
|
memory_type="profile",
|
|
132
182
|
scope=scope,
|
|
133
183
|
scope_id=scope_id,
|
|
134
|
-
content=content
|
|
184
|
+
content=bounded(content),
|
|
135
185
|
confidence=0.95,
|
|
136
186
|
importance=0.9,
|
|
137
187
|
source_event_ids=[event_id],
|
|
@@ -153,7 +203,7 @@ def propose_memory_candidates(
|
|
|
153
203
|
memory_type="profile",
|
|
154
204
|
scope=scope,
|
|
155
205
|
scope_id=scope_id,
|
|
156
|
-
content=content
|
|
206
|
+
content=bounded(content),
|
|
157
207
|
confidence=0.9,
|
|
158
208
|
importance=0.8,
|
|
159
209
|
source_event_ids=[event_id],
|
|
@@ -175,7 +225,7 @@ def propose_memory_candidates(
|
|
|
175
225
|
memory_type="profile",
|
|
176
226
|
scope=scope,
|
|
177
227
|
scope_id=scope_id,
|
|
178
|
-
content=content
|
|
228
|
+
content=bounded(content),
|
|
179
229
|
confidence=0.95,
|
|
180
230
|
importance=0.9,
|
|
181
231
|
source_event_ids=[event_id],
|
|
@@ -197,7 +247,7 @@ def propose_memory_candidates(
|
|
|
197
247
|
memory_type="profile",
|
|
198
248
|
scope=scope,
|
|
199
249
|
scope_id=scope_id,
|
|
200
|
-
content=content
|
|
250
|
+
content=bounded(content),
|
|
201
251
|
confidence=0.9,
|
|
202
252
|
importance=0.8,
|
|
203
253
|
source_event_ids=[event_id],
|
|
@@ -221,7 +271,7 @@ def propose_memory_candidates(
|
|
|
221
271
|
memory_type="profile",
|
|
222
272
|
scope=scope,
|
|
223
273
|
scope_id=scope_id,
|
|
224
|
-
content=content
|
|
274
|
+
content=bounded(content),
|
|
225
275
|
confidence=0.75,
|
|
226
276
|
importance=0.65,
|
|
227
277
|
source_event_ids=[event_id],
|
|
@@ -242,7 +292,7 @@ def propose_memory_candidates(
|
|
|
242
292
|
memory_type="fact",
|
|
243
293
|
scope=scope,
|
|
244
294
|
scope_id=scope_id,
|
|
245
|
-
content=text
|
|
295
|
+
content=bounded(text),
|
|
246
296
|
confidence=0.7,
|
|
247
297
|
importance=0.6,
|
|
248
298
|
source_event_ids=[event_id],
|
|
@@ -253,6 +303,65 @@ def propose_memory_candidates(
|
|
|
253
303
|
return candidates
|
|
254
304
|
|
|
255
305
|
|
|
306
|
+
def explicit_memory_target(text: str) -> tuple[str, str] | None:
|
|
307
|
+
"""Decode one whole explicit goal without truncating its requested fact.
|
|
308
|
+
|
|
309
|
+
This is a pure intent interpretation. The synthetic extraction input never
|
|
310
|
+
supplies persisted provenance or write authorization; those remain the
|
|
311
|
+
host's actual goal, approval and Run event identities.
|
|
312
|
+
"""
|
|
313
|
+
if not is_explicit_memory_instruction(text):
|
|
314
|
+
return None
|
|
315
|
+
candidates = propose_memory_candidates(
|
|
316
|
+
(SimpleNamespace(text=text, author="user", event_type="user_message"),),
|
|
317
|
+
max_content_chars=None,
|
|
318
|
+
)
|
|
319
|
+
explicit = [candidate for candidate in candidates
|
|
320
|
+
if candidate.reason.startswith("explicit_user")]
|
|
321
|
+
if len(explicit) != 1:
|
|
322
|
+
return None
|
|
323
|
+
return explicit[0].operation, explicit[0].content
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
NATIVE_USER_EXTRACTION_POLICY = "native-original-user-profile/v2"
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
def native_user_memory_candidate(
|
|
330
|
+
text: str, *, source_event_id: str, scope: MemoryScope, scope_id: str,
|
|
331
|
+
) -> MemoryCandidate | None:
|
|
332
|
+
"""Interpret a complete original user statement, never a transcript substring.
|
|
333
|
+
|
|
334
|
+
The host must supply canonical user input and its durable source identity.
|
|
335
|
+
Confidence describes known user authorship, not the truth of a preference
|
|
336
|
+
or permission to persist it. The existing Policy still requires independent
|
|
337
|
+
observations for implicit declarations. No model/summary/tool text is admitted.
|
|
338
|
+
"""
|
|
339
|
+
text = text.strip()
|
|
340
|
+
explicit = is_explicit_memory_instruction(text)
|
|
341
|
+
implicit = any(pattern.fullmatch(text) for pattern in _IMPLICIT_PREFERENCE_PATTERNS)
|
|
342
|
+
hobby = _HOBBY_DECLARATION.fullmatch(text)
|
|
343
|
+
if not source_event_id.strip() or not (explicit or implicit or hobby):
|
|
344
|
+
return None
|
|
345
|
+
proposed = propose_memory_candidates(
|
|
346
|
+
(SimpleNamespace(text=text, author="user", event_type="user_message"),),
|
|
347
|
+
scope=scope, scope_id=scope_id, max_content_chars=None,
|
|
348
|
+
)
|
|
349
|
+
if not proposed and hobby and not hobby.group("correction"):
|
|
350
|
+
proposed = [MemoryCandidate(
|
|
351
|
+
candidate_id="pending", operation="add", memory_type="profile", scope=scope,
|
|
352
|
+
scope_id=scope_id, content=f"我的爱好是{hobby.group('value').strip()}",
|
|
353
|
+
confidence=1, importance=0.65, source_event_ids=[source_event_id],
|
|
354
|
+
reason="implicit_user_preference", slot_key="profile.preference.hobby",
|
|
355
|
+
)]
|
|
356
|
+
if len(proposed) != 1 or (not explicit and proposed[0].reason != "implicit_user_preference"):
|
|
357
|
+
return None
|
|
358
|
+
value = proposed[0]
|
|
359
|
+
identity = json.dumps((NATIVE_USER_EXTRACTION_POLICY, scope, scope_id, source_event_id,
|
|
360
|
+
value.operation, value.content), ensure_ascii=False)
|
|
361
|
+
return replace(value, candidate_id="candidate_" + hashlib.sha256(identity.encode()).hexdigest(),
|
|
362
|
+
confidence=1, source_event_ids=[source_event_id])
|
|
363
|
+
|
|
364
|
+
|
|
256
365
|
class MemoryExtractor:
|
|
257
366
|
"""方案 §9.2 的 ``MemoryExtractor.propose()`` 接口封装。"""
|
|
258
367
|
|
|
@@ -264,4 +373,8 @@ class MemoryExtractor:
|
|
|
264
373
|
return propose_memory_candidates(events, scope=self._scope, scope_id=self._scope_id)
|
|
265
374
|
|
|
266
375
|
|
|
267
|
-
__all__ = [
|
|
376
|
+
__all__ = [
|
|
377
|
+
"MemoryExtractor", "derive_profile_slot_key", "explicit_memory_target",
|
|
378
|
+
"is_explicit_memory_instruction", "propose_memory_candidates",
|
|
379
|
+
"native_user_memory_candidate", "NATIVE_USER_EXTRACTION_POLICY",
|
|
380
|
+
]
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
"""Local candidate consumption under the owning Provider's writer transaction.
|
|
2
|
+
|
|
3
|
+
No candidate body is retained while pending/rejected. Original source markers
|
|
4
|
+
outlive a forget so replay cannot count old prose as new evidence or resurrect it.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import hashlib
|
|
10
|
+
import json
|
|
11
|
+
from dataclasses import replace
|
|
12
|
+
|
|
13
|
+
from ksadk.memory.extraction import NATIVE_USER_EXTRACTION_POLICY, derive_profile_slot_key
|
|
14
|
+
from ksadk.memory.models import MemoryRecord
|
|
15
|
+
from ksadk.memory.policy import MemoryPolicy, content_hash
|
|
16
|
+
|
|
17
|
+
from .local_sqlite import VersionConflict, _now_iso, _row_to_record
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def namespace(tenant_id, workspace_id, scope, scope_id, memory_type):
|
|
21
|
+
identity = (tenant_id, None if scope == "org" else workspace_id, scope, scope_id, memory_type)
|
|
22
|
+
return hashlib.sha256(json.dumps(identity, ensure_ascii=False).encode()).hexdigest()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def discard_slot_observations(conn, record):
|
|
26
|
+
key = namespace(record.tenant_id, record.workspace_id, record.scope,
|
|
27
|
+
record.scope_id, record.memory_type)
|
|
28
|
+
slot = str(record.metadata.get("slot_key") or derive_profile_slot_key(record.content))
|
|
29
|
+
conn.execute(
|
|
30
|
+
"DELETE FROM memory_candidate_observations WHERE namespace=? "
|
|
31
|
+
"AND (content_hash=? OR (slot_key<>'' AND slot_key=?))",
|
|
32
|
+
(key, record.content_hash, slot),
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def consume_explicit_write_horizon(conn, record, original, candidates):
|
|
37
|
+
"""A confirmed write consumes earlier eligible inputs in its actual slot.
|
|
38
|
+
|
|
39
|
+
This is part of the same writer transaction as the fact and receipt. Keep only
|
|
40
|
+
original identities/digests, and preserve unrelated slots for later extraction.
|
|
41
|
+
"""
|
|
42
|
+
targets = (original, record) if original is not None else (record,)
|
|
43
|
+
slots = {value for target in targets if (
|
|
44
|
+
value := target.metadata.get("slot_key") or derive_profile_slot_key(target.content)
|
|
45
|
+
)}
|
|
46
|
+
hashes = {target.content_hash for target in targets}
|
|
47
|
+
for candidate, source_digest in candidates:
|
|
48
|
+
if (candidate.scope, candidate.scope_id) != (record.scope, record.scope_id):
|
|
49
|
+
raise VersionConflict("candidate_write_horizon_scope_unbound")
|
|
50
|
+
if not (candidate.slot_key in slots or content_hash(candidate.content) in hashes):
|
|
51
|
+
continue
|
|
52
|
+
key = namespace(record.tenant_id, record.workspace_id, candidate.scope,
|
|
53
|
+
candidate.scope_id, candidate.memory_type)
|
|
54
|
+
for target in targets:
|
|
55
|
+
conn.execute(
|
|
56
|
+
"DELETE FROM memory_candidate_observations WHERE namespace=? "
|
|
57
|
+
"AND (content_hash=? OR (slot_key<>'' AND slot_key=?))",
|
|
58
|
+
(key, target.content_hash,
|
|
59
|
+
target.metadata.get("slot_key") or derive_profile_slot_key(target.content)),
|
|
60
|
+
)
|
|
61
|
+
source = candidate.source_event_ids[0]
|
|
62
|
+
previous = conn.execute(
|
|
63
|
+
"SELECT source_digest FROM memory_candidate_sources "
|
|
64
|
+
"WHERE namespace=? AND source_event_id=?", (key, source),
|
|
65
|
+
).fetchone()
|
|
66
|
+
if previous is not None and previous["source_digest"] != source_digest:
|
|
67
|
+
raise VersionConflict("candidate_source_changed")
|
|
68
|
+
conn.execute("INSERT OR IGNORE INTO memory_candidate_sources VALUES (?,?,?)",
|
|
69
|
+
(key, source, source_digest))
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def consume(provider, candidate, *, operation_key, request_digest, source_session_id,
|
|
73
|
+
source_digest, tenant_id, workspace_id):
|
|
74
|
+
# The caller holds the Provider lock and BEGIN IMMEDIATE. Fact, source,
|
|
75
|
+
# independent observations and receipt commit/roll back together.
|
|
76
|
+
prior = provider._mutation_receipt_locked(operation_key, request_digest)
|
|
77
|
+
if prior is not None:
|
|
78
|
+
return prior
|
|
79
|
+
if tenant_id != provider._tenant_id or len(candidate.source_event_ids) != 1:
|
|
80
|
+
raise VersionConflict("candidate_source_or_tenant_unbound")
|
|
81
|
+
conn, source = provider._conn, candidate.source_event_ids[0]
|
|
82
|
+
key = namespace(tenant_id, workspace_id, candidate.scope, candidate.scope_id,
|
|
83
|
+
candidate.memory_type)
|
|
84
|
+
fact_hash = content_hash(candidate.content)
|
|
85
|
+
fingerprint = hashlib.sha256(json.dumps(
|
|
86
|
+
(NATIVE_USER_EXTRACTION_POLICY, candidate.slot_key, fact_hash),
|
|
87
|
+
).encode()).hexdigest()
|
|
88
|
+
|
|
89
|
+
def seal(decision, *, reason, observations=0, record=None, action=None):
|
|
90
|
+
result = {"committed": decision == "committed", "decision": decision,
|
|
91
|
+
"code": reason, "observations": observations}
|
|
92
|
+
if record is not None:
|
|
93
|
+
result.update(memory_id=record.memory_id, version=record.version,
|
|
94
|
+
status=record.status, content_hash=record.content_hash,
|
|
95
|
+
action=action)
|
|
96
|
+
conn.execute("INSERT INTO memory_operation_receipts VALUES (?,?,?)",
|
|
97
|
+
(operation_key, request_digest, json.dumps(result, ensure_ascii=False)))
|
|
98
|
+
return result
|
|
99
|
+
|
|
100
|
+
policy = MemoryPolicy()
|
|
101
|
+
early = policy.evaluate(candidate)
|
|
102
|
+
if early.decision == "reject":
|
|
103
|
+
return seal("rejected", reason=early.reason)
|
|
104
|
+
if len(candidate.content) > provider.capabilities().max_record_chars:
|
|
105
|
+
return seal("rejected", reason="candidate_content_over_budget")
|
|
106
|
+
original = conn.execute(
|
|
107
|
+
"SELECT source_digest FROM memory_candidate_sources "
|
|
108
|
+
"WHERE namespace=? AND source_event_id=?",
|
|
109
|
+
(key, source),
|
|
110
|
+
).fetchone()
|
|
111
|
+
if original is not None:
|
|
112
|
+
if original["source_digest"] != source_digest:
|
|
113
|
+
raise VersionConflict("candidate_source_changed")
|
|
114
|
+
count = conn.execute(
|
|
115
|
+
"SELECT COUNT(*) FROM memory_candidate_observations "
|
|
116
|
+
"WHERE namespace=? AND candidate_key=?",
|
|
117
|
+
(key, fingerprint),
|
|
118
|
+
).fetchone()[0]
|
|
119
|
+
return seal("duplicate_source", reason="original_source_already_consumed",
|
|
120
|
+
observations=count)
|
|
121
|
+
|
|
122
|
+
# Native writes have a slot key. For older records derive only the same
|
|
123
|
+
# deterministic slot inside the exact tenant/workspace/scope/type partition.
|
|
124
|
+
rows = conn.execute(
|
|
125
|
+
"SELECT * FROM memory_records WHERE tenant_id=? AND scope=? AND scope_id=? "
|
|
126
|
+
"AND memory_type=? AND status='active' "
|
|
127
|
+
"AND (scope='org' OR workspace_id=?)",
|
|
128
|
+
(tenant_id, candidate.scope, candidate.scope_id, candidate.memory_type, workspace_id),
|
|
129
|
+
).fetchall()
|
|
130
|
+
matches = [record for row in rows if (
|
|
131
|
+
(record := _row_to_record(row)).content_hash == fact_hash
|
|
132
|
+
or bool(candidate.slot_key) and (
|
|
133
|
+
record.metadata.get("slot_key") or derive_profile_slot_key(record.content)
|
|
134
|
+
) == candidate.slot_key
|
|
135
|
+
)]
|
|
136
|
+
if len(matches) > 1:
|
|
137
|
+
return seal("rejected", reason="candidate_slot_ambiguous")
|
|
138
|
+
existing = matches[0] if matches else None
|
|
139
|
+
if existing is not None and existing.write_policy == "locked":
|
|
140
|
+
return seal("rejected", reason="candidate_slot_locked")
|
|
141
|
+
if candidate.operation != "add" and existing is None:
|
|
142
|
+
return seal("rejected", reason="candidate_correction_target_missing")
|
|
143
|
+
conn.execute("INSERT INTO memory_candidate_sources VALUES (?,?,?)",
|
|
144
|
+
(key, source, source_digest))
|
|
145
|
+
conn.execute("INSERT INTO memory_candidate_observations VALUES (?,?,?,?,?,?)",
|
|
146
|
+
(key, fingerprint, source, fact_hash, candidate.slot_key, _now_iso()))
|
|
147
|
+
observed = conn.execute(
|
|
148
|
+
"SELECT source_event_id FROM memory_candidate_observations "
|
|
149
|
+
"WHERE namespace=? AND candidate_key=? ORDER BY source_event_id",
|
|
150
|
+
(key, fingerprint),
|
|
151
|
+
).fetchall()
|
|
152
|
+
count = len(observed)
|
|
153
|
+
effective = replace(candidate, operation="update") if existing else candidate
|
|
154
|
+
evaluated = policy.evaluate(effective, existing=existing, observations=count)
|
|
155
|
+
if evaluated.decision != "commit":
|
|
156
|
+
return seal(evaluated.decision, reason=evaluated.reason, observations=count)
|
|
157
|
+
if existing is not None and existing.content_hash == fact_hash:
|
|
158
|
+
return seal("duplicate", reason="same_current_fact", observations=count, record=existing)
|
|
159
|
+
from uuid import uuid4
|
|
160
|
+
|
|
161
|
+
record = MemoryRecord(
|
|
162
|
+
memory_id=existing.memory_id if existing else "memory_" + uuid4().hex,
|
|
163
|
+
tenant_id=tenant_id, workspace_id=workspace_id, scope=candidate.scope,
|
|
164
|
+
scope_id=candidate.scope_id, memory_type=candidate.memory_type,
|
|
165
|
+
content=candidate.content, summary=candidate.content, status="active",
|
|
166
|
+
confidence=candidate.confidence, importance=candidate.importance,
|
|
167
|
+
valid_from=existing.valid_from if existing else _now_iso(), valid_to="",
|
|
168
|
+
expires_at=candidate.expires_at,
|
|
169
|
+
source_session_id=existing.source_session_id if existing else source_session_id,
|
|
170
|
+
source_event_ids=list(dict.fromkeys((
|
|
171
|
+
*(existing.source_event_ids if existing else ()),
|
|
172
|
+
*(row["source_event_id"] for row in observed),
|
|
173
|
+
))), source_seq_range=None, content_hash=fact_hash, version=1,
|
|
174
|
+
metadata={**(existing.metadata if existing else {}),
|
|
175
|
+
"slot_key": candidate.slot_key,
|
|
176
|
+
"extraction_policy": NATIVE_USER_EXTRACTION_POLICY,
|
|
177
|
+
"mutation_session_id": source_session_id, "source_input_digest": source_digest},
|
|
178
|
+
write_policy=existing.write_policy if existing else
|
|
179
|
+
"user_confirm" if candidate.reason.startswith("explicit_user") else "auto",
|
|
180
|
+
)
|
|
181
|
+
saved = provider._upsert_locked(record, expected_version=existing.version if existing else None)
|
|
182
|
+
return seal("committed", reason=evaluated.reason, observations=count, record=saved,
|
|
183
|
+
action="correct" if existing else "remember")
|