agentengine-sdk-python 0.8.0__py3-none-any.whl → 0.8.2__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.0.dist-info → agentengine_sdk_python-0.8.2.dist-info}/METADATA +43 -7
- agentengine_sdk_python-0.8.2.dist-info/RECORD +778 -0
- {agentengine_sdk_python-0.8.0.dist-info → agentengine_sdk_python-0.8.2.dist-info}/WHEEL +1 -1
- ksadk/a2a/__init__.py +2 -0
- ksadk/a2a/_space_client_events.py +381 -0
- ksadk/a2a/bootstrap.py +0 -2
- ksadk/a2a/card.py +6 -111
- ksadk/a2a/control_plane.py +239 -1
- ksadk/a2a/event_adapter.py +156 -64
- ksadk/a2a/executor.py +195 -172
- ksadk/a2a/routes.py +12 -5
- ksadk/a2a/server.py +1 -8
- ksadk/a2a/service_env.py +33 -0
- ksadk/a2a/space_client.py +43 -349
- ksadk/a2a_card.py +101 -0
- ksadk/a2ui/core.py +222 -53
- ksadk/agui/_agent_helpers.py +243 -0
- ksadk/agui/a2ui_projection.py +12 -5
- ksadk/agui/agent.py +474 -275
- ksadk/agui/config.py +11 -5
- ksadk/agui/routes.py +7 -15
- ksadk/api/client.py +530 -40
- ksadk/api/environment.py +27 -0
- ksadk/builders/code_builder.py +352 -53
- ksadk/builders/container_builder.py +71 -44
- ksadk/builders/framework_requirements.py +2 -1
- ksadk/builders/ks3_uploader.py +2 -2
- ksadk/builders/managed_runtime_builder.py +60 -28
- ksadk/cli/__init__.py +25 -0
- ksadk/cli/cmd_a2a.py +18 -31
- ksadk/cli/cmd_create.py +13 -26
- ksadk/cli/cmd_dashboard.py +15 -1
- ksadk/cli/cmd_deploy.py +28 -3
- ksadk/cli/cmd_eval.py +443 -0
- ksadk/cli/cmd_evalset.py +284 -0
- ksadk/cli/cmd_files.py +5 -1
- ksadk/cli/cmd_hermes.py +137 -163
- ksadk/cli/cmd_invoke.py +28 -38
- ksadk/cli/cmd_launch.py +12 -0
- ksadk/cli/cmd_managed_runtime.py +95 -0
- ksadk/cli/cmd_observe.py +85 -0
- ksadk/cli/cmd_openclaw.py +85 -479
- ksadk/cli/cmd_replay.py +28 -4
- ksadk/cli/cmd_run.py +29 -67
- ksadk/cli/cmd_studio.py +201 -0
- ksadk/cli/cmd_web.py +15 -6
- ksadk/cli/dry_run.py +2 -1
- ksadk/cli/env_options.py +83 -0
- ksadk/cli/global_options.py +3 -1
- ksadk/cli/hermes_env.py +151 -0
- ksadk/cli/invoke_payload.py +49 -0
- ksadk/cli/network_options.py +7 -5
- ksadk/cli/openclaw_env.py +458 -0
- ksadk/cli/runtime_bootstrap.py +51 -0
- ksadk/cli/ui.py +6 -0
- ksadk/cli/workflow_common.py +5 -5
- ksadk/codex/__init__.py +3 -3
- ksadk/codex/client.py +833 -49
- ksadk/codex/phase.py +1 -1
- ksadk/codex/runtime.py +720 -127
- ksadk/common/kop_client.py +232 -0
- ksadk/configs/__init__.py +1 -3
- ksadk/configs/env_registry.py +244 -45
- ksadk/configs/env_registry_pcm.py +181 -0
- ksadk/configs/env_var_spec.py +18 -0
- ksadk/configs/global_config.py +6 -1
- ksadk/configs/settings.py +5 -108
- ksadk/context_engine/__init__.py +98 -0
- ksadk/context_engine/assembler.py +176 -0
- ksadk/context_engine/baseline.py +419 -0
- ksadk/context_engine/cache_observability.py +228 -0
- ksadk/context_engine/capabilities.py +443 -0
- ksadk/context_engine/contributors.py +344 -0
- ksadk/context_engine/hosted_pipeline.py +417 -0
- ksadk/context_engine/models.py +117 -0
- ksadk/context_engine/planner.py +698 -0
- ksadk/context_engine/policies.py +319 -0
- ksadk/context_engine/projection.py +31 -0
- ksadk/context_engine/shadow_plan.py +266 -0
- ksadk/context_engine/tokenizer.py +164 -0
- ksadk/conversations/__init__.py +85 -94
- ksadk/conversations/context.py +126 -6
- ksadk/conversations/message_projection.py +292 -28
- ksadk/conversations/model_context.py +49 -3
- ksadk/conversations/runtime_compaction.py +207 -5
- ksadk/conversations/runtime_input.py +193 -3
- ksadk/conversations/runtime_invocation.py +81 -3
- ksadk/conversations/runtime_observability.py +157 -4
- ksadk/conversations/runtime_payloads.py +42 -0
- ksadk/conversations/runtime_preparation.py +280 -2
- ksadk/conversations/runtime_resume.py +71 -32
- ksadk/conversations/runtime_stream_events.py +94 -9
- ksadk/conversations/runtime_streaming.py +175 -22
- ksadk/conversations/semantic_summary.py +311 -1
- ksadk/conversations/session_lock.py +51 -0
- ksadk/deployment/env_forward.py +72 -0
- ksadk/deployment/managed_runtime.py +38 -0
- ksadk/deployment/manager.py +24 -8
- ksadk/deployment/providers/serverless.py +228 -92
- ksadk/evaluation/__init__.py +116 -0
- ksadk/evaluation/a2a_adapter.py +452 -0
- ksadk/evaluation/adapters.py +67 -0
- ksadk/evaluation/agent_eval_client.py +395 -0
- ksadk/evaluation/cloud_binding.py +80 -0
- ksadk/evaluation/cloud_converter.py +194 -0
- ksadk/evaluation/cloud_service.py +199 -0
- ksadk/evaluation/contracts.py +478 -0
- ksadk/evaluation/evalset.py +351 -0
- ksadk/evaluation/evaluators.py +627 -0
- ksadk/evaluation/evidence.py +258 -0
- ksadk/evaluation/executor.py +143 -0
- ksadk/evaluation/local_adapter.py +550 -0
- ksadk/evaluation/service_env.py +27 -0
- ksadk/evaluation/storage.py +68 -0
- ksadk/evaluation/studio_build_adapter.py +253 -0
- ksadk/evaluation/target.py +82 -0
- ksadk/events/__init__.py +13 -13
- ksadk/events/_v1_compat/__init__.py +1 -0
- ksadk/events/_v1_compat/models.py +299 -0
- ksadk/events/_v1_compat/parser.py +247 -0
- ksadk/events/_v1_compat/projection.py +753 -0
- ksadk/events/adapters/__init__.py +34 -0
- ksadk/events/adapters/_a2a_snapshot.py +622 -0
- ksadk/events/adapters/_a2a_support.py +275 -0
- ksadk/events/adapters/_codex_interactions.py +375 -0
- ksadk/events/adapters/_codex_items.py +801 -0
- ksadk/events/adapters/_codex_validators.py +311 -0
- ksadk/events/adapters/_langgraph_support.py +785 -0
- ksadk/events/adapters/a2a.py +841 -0
- ksadk/events/adapters/adk.py +503 -0
- ksadk/events/adapters/codex.py +717 -0
- ksadk/events/adapters/langgraph.py +745 -0
- ksadk/events/canonical.py +440 -0
- ksadk/events/canonical_replay.py +497 -0
- ksadk/events/canonical_store.py +468 -0
- ksadk/events/cold_recovery.py +257 -0
- ksadk/events/content.py +89 -0
- ksadk/events/identity.py +86 -0
- ksadk/events/pipeline.py +455 -0
- ksadk/events/projections.py +66 -0
- ksadk/events/reducer.py +661 -0
- ksadk/events/replay.py +3 -36
- ksadk/events/runtime_event.py +20 -256
- ksadk/events/session_event.py +324 -0
- ksadk/events/store.py +3 -316
- ksadk/events/v1_compat.py +41 -0
- ksadk/harness/__init__.py +3 -2
- ksadk/harness/app.py +114 -123
- ksadk/harness/config.py +1 -1
- ksadk/harness/runtime.py +467 -0
- ksadk/hermes_terminal.py +5 -0
- ksadk/identity/resolver.py +4 -3
- ksadk/interaction/__init__.py +28 -0
- ksadk/interaction/contracts.py +136 -0
- ksadk/interaction/ledger.py +189 -0
- ksadk/interaction/provider.py +122 -0
- ksadk/interaction/providers/__init__.py +52 -0
- ksadk/interaction/providers/adk.py +40 -0
- ksadk/interaction/providers/codex.py +94 -0
- ksadk/interaction/providers/langgraph.py +58 -0
- ksadk/kernel/__init__.py +117 -0
- ksadk/kernel/authorization.py +258 -0
- ksadk/kernel/bootstrap.py +1074 -0
- ksadk/kernel/contract_fingerprints.py +61 -0
- ksadk/kernel/contracts.py +362 -0
- ksadk/kernel/control.py +219 -0
- ksadk/kernel/errors.py +79 -0
- ksadk/kernel/ingress.py +1102 -0
- ksadk/kernel/mapping.py +111 -0
- ksadk/kernel/memory_store.py +1133 -0
- ksadk/kernel/postgres_store.py +1756 -0
- ksadk/kernel/recovery.py +452 -0
- ksadk/kernel/runtime_identity.py +103 -0
- ksadk/kernel/sql/001_agent_kernel.sql +120 -0
- ksadk/kernel/sqlite_store.py +1410 -0
- ksadk/kernel/state.py +145 -0
- ksadk/kernel/store.py +232 -0
- ksadk/kernel/worker.py +953 -0
- ksadk/knowledge_base/client.py +22 -4
- ksadk/knowledge_base/service.py +32 -5
- ksadk/managed_a2a_card.py +160 -0
- ksadk/managed_runtime.py +16 -9
- ksadk/memory/__init__.py +61 -0
- ksadk/memory/adk/backends/base_ltm_backend.py +96 -1
- ksadk/memory/adk/backends/http_ltm_backend.py +14 -5
- ksadk/memory/adk/backends/inmemory_ltm_backend.py +136 -3
- ksadk/memory/adk/backends/sdk_ltm_backend.py +394 -5
- ksadk/memory/adk/backends/sqlite_ltm_backend.py +95 -0
- ksadk/memory/adk/long_term_memory.py +3 -2
- ksadk/memory/coordinator.py +461 -0
- ksadk/memory/events.py +202 -0
- ksadk/memory/extraction.py +228 -0
- ksadk/memory/ltm_backend_factory.py +16 -3
- ksadk/memory/models.py +316 -0
- ksadk/memory/policy.py +215 -0
- ksadk/memory/provider.py +47 -0
- ksadk/memory/provider_adapter.py +159 -0
- ksadk/memory/provider_resolver.py +71 -0
- ksadk/memory/providers/__init__.py +5 -0
- ksadk/memory/providers/local_sqlite.py +490 -0
- ksadk/memory/resolved_policy.py +145 -0
- ksadk/memory/service.py +198 -8
- ksadk/memory/tool.py +4 -0
- ksadk/model_proxy/bootstrap.py +16 -9
- ksadk/model_proxy/config.py +22 -1
- ksadk/model_proxy/detect.py +202 -9
- ksadk/model_proxy/namespace.py +67 -15
- ksadk/model_proxy/server.py +168 -6
- ksadk/model_proxy/transform.py +105 -3
- ksadk/observability/__init__.py +25 -0
- ksadk/observability/session_log.py +420 -0
- ksadk/observability/trajectory.py +105 -0
- ksadk/prompts/__init__.py +71 -0
- ksadk/prompts/compiler.py +215 -0
- ksadk/prompts/models.py +82 -0
- ksadk/prompts/projection.py +89 -0
- ksadk/prompts/resolved.py +137 -0
- ksadk/prompts/sources.py +233 -0
- ksadk/runners/_langgraph_runner_streams.py +822 -0
- ksadk/runners/adk_runner.py +14 -31
- ksadk/runners/base_runner.py +44 -3
- ksadk/runners/factory.py +3 -3
- ksadk/runners/langgraph_runner.py +252 -433
- ksadk/runners/utils/__init__.py +0 -10
- ksadk/runners/utils/loader.py +16 -0
- ksadk/runtime/__init__.py +67 -41
- ksadk/runtime/_runner_adapter/__init__.py +1 -0
- ksadk/runtime/_runner_adapter/stream_mapping.py +788 -0
- ksadk/runtime/adapter.py +199 -16
- ksadk/runtime/conversation_execution.py +687 -0
- ksadk/runtime/executor.py +392 -0
- ksadk/runtime/factory.py +342 -0
- ksadk/runtime/framework_adapters.py +6 -12
- ksadk/runtime/hosted_finalizer.py +268 -0
- ksadk/runtime/launch.py +61 -0
- ksadk/runtime/preprocessing.py +49 -14
- ksadk/runtime/runner_adapter.py +330 -391
- ksadk/runtime/runner_loading.py +36 -0
- ksadk/runtime/usage.py +33 -0
- ksadk/sandbox/backends/e2b.py +36 -3
- ksadk/server/__init__.py +39 -4
- ksadk/server/app.py +18 -492
- ksadk/server/composition.py +36 -2
- ksadk/server/factory.py +210 -149
- ksadk/server/routes/common.py +15 -46
- ksadk/server/routes/control.py +13 -9
- ksadk/server/routes/kernel_ingress.py +258 -0
- ksadk/server/routes/misc.py +10 -33
- ksadk/server/routes/models.py +14 -6
- ksadk/server/routes/openai_compat.py +129 -27
- ksadk/server/routes/projection.py +84 -16
- ksadk/server/routes/run.py +347 -460
- ksadk/server/routes/sessions.py +37 -40
- ksadk/server/routes/streaming.py +52 -7
- ksadk/server/routes/workspace.py +54 -21
- ksadk/server/static/assets/{ArtifactsPanel-Cjnqt-8W.js → ArtifactsPanel-DSU1sWD_.js} +1 -1
- ksadk/server/static/assets/{CodeBlock-BqyqZfuB.js → CodeBlock-DOH6MVcn.js} +4 -4
- ksadk/server/static/assets/{MathMessageMarkdown-BpH7jiFM.js → MathMessageMarkdown-BL8my1R2.js} +4 -4
- ksadk/server/static/assets/MathMessageMarkdown-BorAY7qD.css +1 -0
- ksadk/server/static/assets/MermaidBlock-Dz4IP-Tx.js +312 -0
- ksadk/server/static/assets/NativeTerminalPanel-DUrK0JpZ.js +1 -0
- ksadk/server/static/assets/abnfDiagram-VCTEODGH-g20pFzNV.js +1 -0
- ksadk/server/static/assets/arc-C4FzinUA.js +1 -0
- ksadk/server/static/assets/architecture-7GRP2DOG-DjKratdf.js +1 -0
- ksadk/server/static/assets/architectureDiagram-5GKGNRK7-DpIqL5h4.js +36 -0
- ksadk/server/static/assets/blockDiagram-NRAW4CY4-BjkljTNz.js +129 -0
- ksadk/server/static/assets/c4Diagram-UCG6FXSJ-Dk_ieq2X.js +38 -0
- ksadk/server/static/assets/channel-4cQHKtx1.js +1 -0
- ksadk/server/static/assets/chunk-2Q5K7J3B-DmgWkESh.js +1 -0
- ksadk/server/static/assets/{chunk-FMBD7UC4-BGhvzDYt.js → chunk-5VM5RSS4-BVHAchFb.js} +1 -1
- ksadk/server/static/assets/{chunk-ND2GUHAM-D98vPd-E.js → chunk-F27PBJKO-C-ipQzuS.js} +1 -1
- ksadk/server/static/assets/chunk-G27WJ6UU-C2EyJbIK.js +231 -0
- ksadk/server/static/assets/{chunk-4BX2VUAB-CsWIQRhc.js → chunk-JWPE2WC7-vYvVJb_M.js} +1 -1
- ksadk/server/static/assets/chunk-LCL6LL3I-BcI6ysPT.js +206 -0
- ksadk/server/static/assets/chunk-POPQ4Y6H-C030x_Z1.js +1 -0
- ksadk/server/static/assets/chunk-RHFEMEQ7-yyGpsz4n.js +168 -0
- ksadk/server/static/assets/chunk-SVP7TREG-BLTlmMU7.js +88 -0
- ksadk/server/static/assets/chunk-XXDRQBXY-C_32ArgP.js +1 -0
- ksadk/server/static/assets/classDiagram-DTDB5LWJ-DLFd9Xi0.js +1 -0
- ksadk/server/static/assets/classDiagram-v2-JRS7N3AN-DLFd9Xi0.js +1 -0
- ksadk/server/static/assets/{cose-bilkent-S5V4N54A-CX3yotHq.js → cose-bilkent-JH36ORCC-9YQYwdl0.js} +1 -1
- ksadk/server/static/assets/cynefin-OW5HDTMX-DjEM48Qp.js +1 -0
- ksadk/server/static/assets/cynefinDiagram-5FMLGOSQ-ErLF5N13.js +62 -0
- ksadk/server/static/assets/cytoscape.esm-CyCl8rPi.js +321 -0
- ksadk/server/static/assets/dagre-3AP2YEHR-DFiQF-6f.js +4 -0
- ksadk/server/static/assets/dagre-BuhGnRI1.js +1 -0
- ksadk/server/static/assets/diagram-S7CK7UJ4-Bt1v8-GC.js +30 -0
- ksadk/server/static/assets/diagram-UQ7AKVKN-DSCxJdBK.js +41 -0
- ksadk/server/static/assets/diagram-VSXAHHWV-DHfYwp_9.js +3 -0
- ksadk/server/static/assets/diagram-VX7I27RA-DdzD-4Le.js +24 -0
- ksadk/server/static/assets/diagram-Z3DM3KII-Du-nAJ9F.js +24 -0
- ksadk/server/static/assets/{dist-D82mtD2s.js → dist-8hqcx0sU.js} +1 -1
- ksadk/server/static/assets/dist-B7seoj3d.js +1 -0
- ksadk/server/static/assets/{dist-DHn6nBzr.js → dist-BE7bu-DL.js} +1 -1
- ksadk/server/static/assets/{dist-eoW6tZXA.js → dist-BVDffZ0U.js} +1 -1
- ksadk/server/static/assets/{dist-C2xjE7cO.js → dist-BX8z72IC.js} +1 -1
- ksadk/server/static/assets/{dist-CgF3fj15.js → dist-Bf2M8m3N.js} +1 -1
- ksadk/server/static/assets/{dist-Bs-jQcRX.js → dist-BjU6y-A2.js} +1 -1
- ksadk/server/static/assets/{dist-Djgl2wVZ.js → dist-BytlxIDT.js} +1 -1
- ksadk/server/static/assets/{dist-CVQRZjPu.js → dist-CSh_DE14.js} +1 -1
- ksadk/server/static/assets/{dist-6CVKuTAe.js → dist-CXYYBw3_.js} +1 -1
- ksadk/server/static/assets/{dist-DlzCRy0S.js → dist-C_wsv-Qd.js} +1 -1
- ksadk/server/static/assets/{dist-DF06ORQo.js → dist-ClxGviKw.js} +1 -1
- ksadk/server/static/assets/{dist-Cf23NY4L.js → dist-CttYUqzS.js} +1 -1
- ksadk/server/static/assets/{dist-ivOya1LD.js → dist-DNp6rLRA.js} +1 -1
- ksadk/server/static/assets/{dist-BibcG1V9.js → dist-DUX-id_F.js} +1 -1
- ksadk/server/static/assets/{dist-BUHRxvDG.js → dist-DmldrHd_.js} +1 -1
- ksadk/server/static/assets/{dist-BZ9n9AjO.js → dist-DnfXs8Vn.js} +2 -2
- ksadk/server/static/assets/{dist-DqNnNFrw.js → dist-Dq9gLD7L.js} +1 -1
- ksadk/server/static/assets/{dist-CGcp2ex8.js → dist-W-TIVntx.js} +1 -1
- ksadk/server/static/assets/{dist-Cwt7koer.js → dist-dDdADKFA.js} +1 -1
- ksadk/server/static/assets/{dist-C7-3jInC.js → dist-dydu68Fl.js} +1 -1
- ksadk/server/static/assets/{dist-C99THtIA.js → dist-ngtN0DJB.js} +1 -1
- ksadk/server/static/assets/ebnfDiagram-PWID7BFC-Da9C9NO1.js +1 -0
- ksadk/server/static/assets/erDiagram-SSCWMZ5O-EAGqqR79.js +99 -0
- ksadk/server/static/assets/eventmodeling-NTZA5JFV-CMgMaJrC.js +1 -0
- ksadk/server/static/assets/flowDiagram-A5DVABFB-BBKL0x3P.js +1 -0
- ksadk/server/static/assets/ganttDiagram-EL5Y4UJY-DqJsKb59.js +292 -0
- ksadk/server/static/assets/gitGraph-4MIJSDKK-Dw63mrhw.js +1 -0
- ksadk/server/static/assets/gitGraphDiagram-WWUBYQGX-B6g4dtDi.js +106 -0
- ksadk/server/static/assets/index-8ipRcQ-M.js +240 -0
- ksadk/server/static/assets/index-ByFCqjlh.css +2 -0
- ksadk/server/static/assets/info-A6RAGUB7-B6iAblII.js +1 -0
- ksadk/server/static/assets/infoDiagram-RXCK75RN-Djm_nbd5.js +2 -0
- ksadk/server/static/assets/{ishikawaDiagram-YF4QCWOH-DLOHGvWw.js → ishikawaDiagram-5VMMS53U-DWya9SG2.js} +6 -6
- ksadk/server/static/assets/{journeyDiagram-JHISSGLW-BIzKeejT.js → journeyDiagram-EYS64GPL-DutxhSNI.js} +6 -6
- ksadk/server/static/assets/{kanban-definition-UN3LZRKU-BWHoFSw2.js → kanban-definition-3QL26DDD-DJdOF8Fm.js} +10 -10
- ksadk/server/static/assets/katex-vFWytM5c.js +257 -0
- ksadk/server/static/assets/{linear-CvMBw9cA.js → linear-BHjG8b-J.js} +1 -1
- ksadk/server/static/assets/mermaid-parser.core-KGSy4jWT.js +166 -0
- ksadk/server/static/assets/{mindmap-definition-RKZ34NQL-CSAQxEAe.js → mindmap-definition-FBJOCRG2-9-HC88D4.js} +29 -29
- ksadk/server/static/assets/packet-AYTQ26CC-BOYAipuq.js +1 -0
- ksadk/server/static/assets/pegDiagram-XKGWAZYB-DrEZd4fD.js +1 -0
- ksadk/server/static/assets/pie-WAS4IAKB-Ctq0Kbku.js +1 -0
- ksadk/server/static/assets/pieDiagram-E7YTZNPT-DpjqOFFp.js +39 -0
- ksadk/server/static/assets/{quadrantDiagram-W4KKPZXB-CxIR4JXI.js → quadrantDiagram-AXDQQJYC-BiNhgOXX.js} +6 -6
- ksadk/server/static/assets/radar-RG4KPBEZ-DN0-ul_0.js +1 -0
- ksadk/server/static/assets/railroad-74A4TZTK-C6cC_to0.js +1 -0
- ksadk/server/static/assets/railroad-abnf-HS5TGJTU-BimjIuRc.js +1 -0
- ksadk/server/static/assets/railroad-ebnf-LZEXJU2U-CvY65ABq.js +1 -0
- ksadk/server/static/assets/railroad-peg-WCYAUIDC-D72Iq_Tv.js +1 -0
- ksadk/server/static/assets/railroadDiagram-O6MQD6OU-_Y3Ojg6G.js +1 -0
- ksadk/server/static/assets/requirementDiagram-EFPCY7ZU-DeCFdg9K.js +84 -0
- ksadk/server/static/assets/{sankeyDiagram-5OEKKPKP-DpGa6EAV.js → sankeyDiagram-P5KCCOFB-CPH75rhw.js} +7 -7
- ksadk/server/static/assets/{sequenceDiagram-3UESZ5HK-NTKNGGe5.js → sequenceDiagram-WJ2MYXX4-OEpQQdr4.js} +10 -10
- ksadk/server/static/assets/sizeCapture-X5ZJPWSS-heUErzhI.js +1 -0
- ksadk/server/static/assets/stateDiagram-HBIQ2CUA-DvA3jSMB.js +1 -0
- ksadk/server/static/assets/stateDiagram-v2-4QOOHH4V-BgQY03nz.js +1 -0
- ksadk/server/static/assets/swimlanes-XN3QIQJK-CR-dfN_t.js +1 -0
- ksadk/server/static/assets/swimlanesDiagram-VK2B7HYN-DLVw3q5Q.js +8 -0
- ksadk/server/static/assets/{timeline-definition-PNZ67QCA-jqoYs3jQ.js → timeline-definition-24CTP7MA-DAVJf1TX.js} +7 -7
- ksadk/server/static/assets/treeView-Q6P3EWNA-BMpO0wsB.js +1 -0
- ksadk/server/static/assets/treemap-WGGIJYW6-Et0aqzU3.js +1 -0
- ksadk/server/static/assets/vennDiagram-4TSXK5OY-DwM8eDkh.js +34 -0
- ksadk/server/static/assets/wardley-WFR3VGLG-CbY0mhpl.js +1 -0
- ksadk/server/static/assets/wardleyDiagram-VM6X3IG4-DZL-Zz2t.js +78 -0
- ksadk/server/static/assets/xychartDiagram-S5SC5T6Z-Dy6Yh_hu.js +7 -0
- ksadk/server/static/index.html +2 -2
- ksadk/sessions/__init__.py +26 -9
- ksadk/sessions/_local_service_sync.py +688 -0
- ksadk/sessions/_local_tables.py +21 -0
- ksadk/sessions/_postgres_schema.py +305 -0
- ksadk/sessions/_postgres_tables.py +15 -0
- ksadk/sessions/base.py +80 -1
- ksadk/sessions/in_memory.py +53 -2
- ksadk/sessions/local_service.py +63 -604
- ksadk/sessions/postgres_service.py +58 -148
- ksadk/sessions/resilient.py +35 -1
- ksadk/skills/runtime/backends/e2b.py +1 -1
- ksadk/skills/service_client.py +13 -3
- ksadk/studio/__init__.py +5 -0
- ksadk/studio/agent_avatar_assets.py +120 -0
- ksadk/studio/agent_lifecycle.py +51 -0
- ksadk/studio/api.py +1598 -0
- ksadk/studio/api_catalog_routes.py +324 -0
- ksadk/studio/api_contracts.py +286 -0
- ksadk/studio/api_helpers.py +132 -0
- ksadk/studio/api_memory_routes.py +82 -0
- ksadk/studio/authoring.py +749 -0
- ksadk/studio/authoring_coordinator.py +1017 -0
- ksadk/studio/builder.py +397 -0
- ksadk/studio/capabilities.py +368 -0
- ksadk/studio/cloud.py +2099 -0
- ksadk/studio/codex_agent_service.py +694 -0
- ksadk/studio/codex_authoring.py +469 -0
- ksadk/studio/codex_builder.py +437 -0
- ksadk/studio/codex_manifest.py +307 -0
- ksadk/studio/codex_run.py +395 -0
- ksadk/studio/compiler.py +190 -0
- ksadk/studio/contracts.py +755 -0
- ksadk/studio/errors.py +47 -0
- ksadk/studio/event_store.py +214 -0
- ksadk/studio/framework_run.py +342 -0
- ksadk/studio/hosted_kernel.py +317 -0
- ksadk/studio/identifiers.py +33 -0
- ksadk/studio/manifest_resolver.py +131 -0
- ksadk/studio/mcp_runtime.py +229 -0
- ksadk/studio/model_client.py +629 -0
- ksadk/studio/model_profile_service.py +214 -0
- ksadk/studio/operations.py +275 -0
- ksadk/studio/otel_trace.py +1060 -0
- ksadk/studio/pagination.py +182 -0
- ksadk/studio/pcm_memory.py +85 -0
- ksadk/studio/python_tool_inspection.py +183 -0
- ksadk/studio/repository.py +327 -0
- ksadk/studio/resource_catalog.py +1260 -0
- ksadk/studio/run_service.py +1133 -0
- ksadk/studio/runtime_catalog.py +74 -0
- ksadk/studio/runtime_source.py +365 -0
- ksadk/studio/service.py +2249 -0
- ksadk/studio/shared_web.py +1004 -0
- ksadk/studio/skill_discovery.py +628 -0
- ksadk/studio/static/assets/PrismRenderer-IYN-ffww.js +3 -0
- ksadk/studio/static/assets/index-BLpcdrgW.css +1 -0
- ksadk/studio/static/assets/index-CYekR2Xv.js +260 -0
- ksadk/studio/static/assets/react-vendor-BNmTiJ-I.js +9 -0
- ksadk/studio/static/assets/rolldown-runtime-hePW80VL.js +1 -0
- ksadk/studio/static/index.html +15 -0
- ksadk/studio/templates.py +722 -0
- ksadk/studio/validator.py +265 -0
- ksadk/studio/workspace.py +135 -0
- ksadk/tools/gateway.py +9 -0
- ksadk/toolsets/__init__.py +27 -1
- ksadk/toolsets/a2a.py +426 -0
- ksadk/tracing/__init__.py +19 -21
- ksadk/tracing/setup.py +317 -403
- ksadk/tracing/span_utils.py +0 -44
- ksadk/version.py +1 -1
- ksadk_runtime_common/memory_backend/registry.py +56 -56
- ksadk_runtime_common/schemas/runtime_event_v1.json +37 -0
- ksadk_runtime_common/schemas/runtime_event_v2.json +271 -0
- ksadk_runtime_common/workspace_files/router.py +8 -2
- agentengine_sdk_python-0.8.0.dist-info/RECORD +0 -578
- ksadk/a2a/card_builder.py +0 -64
- ksadk/a2a/client.py +0 -309
- ksadk/events/parser.py +0 -191
- ksadk/harness/runner.py +0 -211
- ksadk/runners/codex_runner.py +0 -132
- ksadk/runners/utils/langfuse.py +0 -230
- ksadk/server/static/assets/MathMessageMarkdown-DeYh8QPr.css +0 -1
- ksadk/server/static/assets/MermaidBlock-DWuxETaJ.js +0 -303
- ksadk/server/static/assets/NativeTerminalPanel-MDTopWMO.js +0 -1
- ksadk/server/static/assets/arc-CFKwSk_B.js +0 -1
- ksadk/server/static/assets/architecture-7EHR7CIX-CynqOWjZ.js +0 -1
- ksadk/server/static/assets/architectureDiagram-3BPJPVTR-Dej8jX8c.js +0 -36
- ksadk/server/static/assets/blockDiagram-GPEHLZMM-1F64jqvy.js +0 -132
- ksadk/server/static/assets/c4Diagram-AAUBKEIU-9q7AUAKb.js +0 -10
- ksadk/server/static/assets/channel-iUGv2N24.js +0 -1
- ksadk/server/static/assets/chunk-2J33WTMH-6qgwJm7Q.js +0 -1
- ksadk/server/static/assets/chunk-55IACEB6-BK8paA20.js +0 -1
- ksadk/server/static/assets/chunk-727SXJPM-B-JEhy7h.js +0 -206
- ksadk/server/static/assets/chunk-AQP2D5EJ-DYBvMHAa.js +0 -231
- ksadk/server/static/assets/chunk-QZHKN3VN-YQIEXeF6.js +0 -1
- ksadk/server/static/assets/classDiagram-4FO5ZUOK-CDAUuoXK.js +0 -1
- ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-CDAUuoXK.js +0 -1
- ksadk/server/static/assets/cytoscape.esm-DelgaX4f.js +0 -321
- ksadk/server/static/assets/dagre-BM42HDAG-C7TguAWo.js +0 -4
- ksadk/server/static/assets/dagre-Bx709z4p.js +0 -1
- ksadk/server/static/assets/diagram-2AECGRRQ-Dd17_kws.js +0 -43
- ksadk/server/static/assets/diagram-5GNKFQAL-DVIClIil.js +0 -10
- ksadk/server/static/assets/diagram-KO2AKTUF-Df_3gYQa.js +0 -3
- ksadk/server/static/assets/diagram-LMA3HP47-BhLk6ZtA.js +0 -24
- ksadk/server/static/assets/diagram-OG6HWLK6-D4GNGvuu.js +0 -24
- ksadk/server/static/assets/dist-DyvYLMW9.js +0 -1
- ksadk/server/static/assets/erDiagram-TEJ5UH35-CnfRcY2M.js +0 -85
- ksadk/server/static/assets/eventmodeling-FCH6USID-ChYO-ijM.js +0 -1
- ksadk/server/static/assets/flowDiagram-I6XJVG4X-DGaJjGNJ.js +0 -162
- ksadk/server/static/assets/ganttDiagram-6RSMTGT7-DpCoB2g9.js +0 -292
- ksadk/server/static/assets/gitGraph-WXDBUCRP-BN_0Fk-j.js +0 -1
- ksadk/server/static/assets/gitGraphDiagram-PVQCEYII-9IKVFBVT.js +0 -106
- ksadk/server/static/assets/graphlib-B8gBHxth.js +0 -1
- ksadk/server/static/assets/index-QSwCHuB8.js +0 -177
- ksadk/server/static/assets/index-jOPmTOZH.css +0 -2
- ksadk/server/static/assets/info-J43DQDTF-Cp6o3vId.js +0 -1
- ksadk/server/static/assets/infoDiagram-5YYISTIA-CYIIsb8y.js +0 -2
- ksadk/server/static/assets/katex-DATssjp1.js +0 -265
- ksadk/server/static/assets/mermaid-parser.core-mGAcEwcq.js +0 -161
- ksadk/server/static/assets/packet-YPE3B663-wQzct3cT.js +0 -1
- ksadk/server/static/assets/pie-LRSECV5Y-BgTQiv-9.js +0 -1
- ksadk/server/static/assets/pieDiagram-4H26LBE5-By-ra07M.js +0 -30
- ksadk/server/static/assets/radar-GUYGQ44K-BIq5fXsn.js +0 -1
- ksadk/server/static/assets/requirementDiagram-4Y6WPE33-DpW90iu3.js +0 -84
- ksadk/server/static/assets/stateDiagram-AJRCARHV-DGpd9yFG.js +0 -1
- ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-B3o579IQ.js +0 -1
- ksadk/server/static/assets/treeView-BLDUP644-Dqi-E0EM.js +0 -1
- ksadk/server/static/assets/treemap-LRROVOQU-ClQP9CJ9.js +0 -1
- ksadk/server/static/assets/vennDiagram-CIIHVFJN-l3Z7-wbP.js +0 -34
- ksadk/server/static/assets/wardley-L42UT6IY-8XLvnNIs.js +0 -1
- ksadk/server/static/assets/wardleyDiagram-YWT4CUSO-BR54GPmr.js +0 -78
- ksadk/server/static/assets/xychartDiagram-2RQKCTM6-SkiH8GsK.js +0 -7
- ksadk/tracing/exporters/langfuse_exporter.py +0 -460
- {agentengine_sdk_python-0.8.0.dist-info → agentengine_sdk_python-0.8.2.dist-info}/entry_points.txt +0 -0
- {agentengine_sdk_python-0.8.0.dist-info → agentengine_sdk_python-0.8.2.dist-info}/licenses/LICENSE +0 -0
- {agentengine_sdk_python-0.8.0.dist-info → agentengine_sdk_python-0.8.2.dist-info}/top_level.txt +0 -0
ksadk/kernel/recovery.py
ADDED
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""冷恢复决策表:RecoveryCoordinator(Phase 1 Task 7)。
|
|
3
|
+
|
|
4
|
+
接管一个 agent_instance 的 open run 时按固定决策表收口:
|
|
5
|
+
|
|
6
|
+
- run 已终态(或不存在 open run)→ ``no_op``;
|
|
7
|
+
- ``attach`` + ``durable_restore`` 能力可用且 durable handle digest 有效 →
|
|
8
|
+
``attach``(跨进程接回 live handle);
|
|
9
|
+
- ``resume`` 能力可用且存在 continuation → ``resume``(从最后 continuation 续跑);
|
|
10
|
+
- 否则 → 确定性 ``interrupted``(唯一 ``run.interrupted`` + open item close),
|
|
11
|
+
reason 固定为 ``runtime_not_durably_attachable``。
|
|
12
|
+
|
|
13
|
+
每个决定都追加一条 fenced ``control.recovery_decided`` 审计事实;
|
|
14
|
+
``RecoveryReport`` 只用于审计与测试,不进入公网 projection。
|
|
15
|
+
"""
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import time
|
|
19
|
+
from collections.abc import Callable
|
|
20
|
+
from dataclasses import dataclass, field
|
|
21
|
+
from typing import TYPE_CHECKING, Literal
|
|
22
|
+
|
|
23
|
+
from ksadk.events.canonical import RuntimeEvent
|
|
24
|
+
from ksadk.events.canonical_store import RuntimeEventStore
|
|
25
|
+
from ksadk.events.cold_recovery import scan_open_runs, settle_finding
|
|
26
|
+
from ksadk.events.pipeline import CanonicalEventPipeline
|
|
27
|
+
from ksadk.events.session_event import SessionEventStore
|
|
28
|
+
from ksadk.kernel.contracts import (
|
|
29
|
+
ActivationLease,
|
|
30
|
+
ActivationWriteGuard,
|
|
31
|
+
RuntimeCapabilityMatrix,
|
|
32
|
+
WriteContext,
|
|
33
|
+
)
|
|
34
|
+
from ksadk.kernel.state import RunState, is_terminal_run
|
|
35
|
+
from ksadk.kernel.store import AgentKernelStore, RunRecord, control_event
|
|
36
|
+
|
|
37
|
+
if TYPE_CHECKING: # pragma: no cover - import cycle guard
|
|
38
|
+
from ksadk.runtime.executor import RuntimeExecutor
|
|
39
|
+
from ksadk.runtime.launch import RuntimeLaunchContext
|
|
40
|
+
|
|
41
|
+
RecoveryOutcome = Literal["no_op", "attached", "resumed", "interrupted", "failed"]
|
|
42
|
+
|
|
43
|
+
CapabilityProvider = Callable[[], RuntimeCapabilityMatrix]
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@dataclass
|
|
47
|
+
class RecoveryReport:
|
|
48
|
+
"""一次 recover 决策的审计结果(不进入公网 projection)。"""
|
|
49
|
+
|
|
50
|
+
agent_instance_id: str
|
|
51
|
+
activation_id: str
|
|
52
|
+
run_id: str | None = None
|
|
53
|
+
outcome: RecoveryOutcome = "no_op"
|
|
54
|
+
reason: str | None = None
|
|
55
|
+
last_seq: int | None = None
|
|
56
|
+
written_events: list[RuntimeEvent] = field(default_factory=list)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _durable_handle_digest(handle_dump: dict) -> str | None:
|
|
60
|
+
"""durable handle 行携带的 digest;缺失/形状不符返回 None。"""
|
|
61
|
+
|
|
62
|
+
try:
|
|
63
|
+
from ksadk.runtime.adapter import RunHandle
|
|
64
|
+
from ksadk.runtime.executor import handle_digest
|
|
65
|
+
|
|
66
|
+
return handle_digest(RunHandle.model_validate(handle_dump))
|
|
67
|
+
except Exception:
|
|
68
|
+
return None
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class RecoveryCoordinator:
|
|
72
|
+
"""对 open run 做确定性收口或接管的协调器。"""
|
|
73
|
+
|
|
74
|
+
def __init__(
|
|
75
|
+
self,
|
|
76
|
+
store: AgentKernelStore,
|
|
77
|
+
session_events: SessionEventStore,
|
|
78
|
+
capabilities: CapabilityProvider,
|
|
79
|
+
*,
|
|
80
|
+
executor: "RuntimeExecutor | None" = None,
|
|
81
|
+
launch_context: "RuntimeLaunchContext | None" = None,
|
|
82
|
+
adapter_factory: Callable[[], object] | None = None,
|
|
83
|
+
clock: Callable[[], float] = time.time,
|
|
84
|
+
execution_sink: Callable[..., None] | None = None,
|
|
85
|
+
) -> None:
|
|
86
|
+
self._store = store
|
|
87
|
+
self._session_events = session_events
|
|
88
|
+
self._capabilities = capabilities
|
|
89
|
+
self._executor = executor
|
|
90
|
+
self._launch_context = launch_context
|
|
91
|
+
self._adapter_factory = adapter_factory
|
|
92
|
+
self._clock = clock
|
|
93
|
+
# Task 6:takeover 重建的 ActiveExecution 只在 lease 获取 +
|
|
94
|
+
# provider 支持的 attach/resume 成功后回调注册(worker.adopt_execution)。
|
|
95
|
+
self._execution_sink = execution_sink
|
|
96
|
+
|
|
97
|
+
async def recover(
|
|
98
|
+
self,
|
|
99
|
+
agent_instance_id: str,
|
|
100
|
+
activation: ActivationLease,
|
|
101
|
+
*,
|
|
102
|
+
run_id: str | None = None,
|
|
103
|
+
) -> RecoveryReport:
|
|
104
|
+
fence = activation.fencing_token
|
|
105
|
+
guard: WriteContext = ActivationWriteGuard(
|
|
106
|
+
activation_id=activation.activation_id, fencing_token=fence
|
|
107
|
+
)
|
|
108
|
+
run = await self._load_run(agent_instance_id, run_id)
|
|
109
|
+
if run is None or is_terminal_run(run.state):
|
|
110
|
+
return await self._decide(
|
|
111
|
+
agent_instance_id,
|
|
112
|
+
activation,
|
|
113
|
+
run,
|
|
114
|
+
outcome="no_op",
|
|
115
|
+
reason=(
|
|
116
|
+
"run_already_terminal"
|
|
117
|
+
if run is not None
|
|
118
|
+
else "no_open_run_for_agent_instance"
|
|
119
|
+
),
|
|
120
|
+
guard=guard,
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
capabilities = self._capabilities()
|
|
124
|
+
handle_dump = run.metadata.get("handle")
|
|
125
|
+
handle_digest_valid = (
|
|
126
|
+
isinstance(handle_dump, dict)
|
|
127
|
+
and isinstance(run.metadata.get("handle_digest"), str)
|
|
128
|
+
and _durable_handle_digest(handle_dump) == run.metadata.get("handle_digest")
|
|
129
|
+
)
|
|
130
|
+
if (
|
|
131
|
+
capabilities.attach.supported
|
|
132
|
+
and capabilities.durable_restore.supported
|
|
133
|
+
and handle_digest_valid
|
|
134
|
+
and self._executor is not None
|
|
135
|
+
and self._launch_context is not None
|
|
136
|
+
):
|
|
137
|
+
try:
|
|
138
|
+
handle = await self._executor.attach_record(run, self._launch_context)
|
|
139
|
+
except Exception as error:
|
|
140
|
+
return await self._decide(
|
|
141
|
+
agent_instance_id,
|
|
142
|
+
activation,
|
|
143
|
+
run,
|
|
144
|
+
outcome="failed",
|
|
145
|
+
reason=f"attach_failed:{type(error).__name__}",
|
|
146
|
+
guard=guard,
|
|
147
|
+
)
|
|
148
|
+
# attach 成功即把 live execution 交还 worker(lease 已在调用方获取,
|
|
149
|
+
# attach 已证明 runtime 支持接管),stream 消费失败时仍保留。
|
|
150
|
+
self._register_execution(
|
|
151
|
+
run.run_id,
|
|
152
|
+
handle.run_id,
|
|
153
|
+
getattr(self._executor, "adapter", None),
|
|
154
|
+
handle,
|
|
155
|
+
)
|
|
156
|
+
# attach 成功后重新消费剩余 stream:事实继续落库,自然结束收口。
|
|
157
|
+
try:
|
|
158
|
+
await self._consume_remaining_stream(
|
|
159
|
+
lambda: self._executor.stream(handle), # type: ignore[union-attr]
|
|
160
|
+
run,
|
|
161
|
+
guard,
|
|
162
|
+
)
|
|
163
|
+
except Exception as error:
|
|
164
|
+
return await self._decide(
|
|
165
|
+
agent_instance_id,
|
|
166
|
+
activation,
|
|
167
|
+
run,
|
|
168
|
+
outcome="failed",
|
|
169
|
+
reason=f"attach_stream_failed:{type(error).__name__}",
|
|
170
|
+
guard=guard,
|
|
171
|
+
)
|
|
172
|
+
return await self._decide(
|
|
173
|
+
agent_instance_id,
|
|
174
|
+
activation,
|
|
175
|
+
run,
|
|
176
|
+
outcome="attached",
|
|
177
|
+
reason="durable_handle_attached",
|
|
178
|
+
guard=guard,
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
if capabilities.resume.supported and run.metadata.get("continuation_ref"):
|
|
182
|
+
resumed_report = await self._try_real_resume(
|
|
183
|
+
agent_instance_id, activation, run, guard=guard
|
|
184
|
+
)
|
|
185
|
+
if resumed_report is not None:
|
|
186
|
+
return resumed_report
|
|
187
|
+
return await self._decide(
|
|
188
|
+
agent_instance_id,
|
|
189
|
+
activation,
|
|
190
|
+
run,
|
|
191
|
+
outcome="resumed",
|
|
192
|
+
reason="continuation_resume_delegated",
|
|
193
|
+
guard=guard,
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
return await self._interrupt_deterministically(
|
|
197
|
+
agent_instance_id, activation, run, guard=guard
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
async def settle_interrupted(
|
|
201
|
+
self,
|
|
202
|
+
agent_instance_id: str,
|
|
203
|
+
activation: ActivationLease,
|
|
204
|
+
*,
|
|
205
|
+
reason: str = "recover_error_settled_interrupted",
|
|
206
|
+
) -> RecoveryReport:
|
|
207
|
+
"""P0-1 兜底收口:``recover`` 抛错后的确定性 interrupted 决策。
|
|
208
|
+
|
|
209
|
+
不依赖任何 runtime 能力:直接对 open run 写唯一
|
|
210
|
+
``run.interrupted`` + open item close,并以当前 fencing 追加
|
|
211
|
+
``control.recovery_decided`` 审计事实。没有 open run 时退化为
|
|
212
|
+
``no_op``。持久化失败向上抛出,由调用方决定 degraded。
|
|
213
|
+
"""
|
|
214
|
+
|
|
215
|
+
guard: WriteContext = ActivationWriteGuard(
|
|
216
|
+
activation_id=activation.activation_id,
|
|
217
|
+
fencing_token=activation.fencing_token,
|
|
218
|
+
)
|
|
219
|
+
run = await self._load_run(agent_instance_id, None)
|
|
220
|
+
if run is None or is_terminal_run(run.state):
|
|
221
|
+
return await self._decide(
|
|
222
|
+
agent_instance_id,
|
|
223
|
+
activation,
|
|
224
|
+
run,
|
|
225
|
+
outcome="no_op",
|
|
226
|
+
reason=(
|
|
227
|
+
"run_already_terminal"
|
|
228
|
+
if run is not None
|
|
229
|
+
else "no_open_run_for_agent_instance"
|
|
230
|
+
),
|
|
231
|
+
guard=guard,
|
|
232
|
+
)
|
|
233
|
+
return await self._interrupt_deterministically(
|
|
234
|
+
agent_instance_id, activation, run, guard=guard, reason=reason
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
# ------------------------------------------------------------- internals
|
|
238
|
+
|
|
239
|
+
def _register_execution(
|
|
240
|
+
self,
|
|
241
|
+
durable_run_id: str,
|
|
242
|
+
runtime_run_id: str,
|
|
243
|
+
adapter: object | None,
|
|
244
|
+
handle: object,
|
|
245
|
+
) -> None:
|
|
246
|
+
"""把 takeover 重建的 live execution 交还 worker(best-effort)。"""
|
|
247
|
+
|
|
248
|
+
if self._execution_sink is None or adapter is None:
|
|
249
|
+
return
|
|
250
|
+
try:
|
|
251
|
+
self._execution_sink(
|
|
252
|
+
durable_run_id=durable_run_id,
|
|
253
|
+
runtime_run_id=runtime_run_id,
|
|
254
|
+
adapter=adapter,
|
|
255
|
+
handle=handle,
|
|
256
|
+
)
|
|
257
|
+
except Exception: # noqa: BLE001 - 审计/恢复路径绝不因 sink 失败中断
|
|
258
|
+
pass
|
|
259
|
+
|
|
260
|
+
async def _try_real_resume(
|
|
261
|
+
self,
|
|
262
|
+
agent_instance_id: str,
|
|
263
|
+
activation: ActivationLease,
|
|
264
|
+
run: RunRecord,
|
|
265
|
+
*,
|
|
266
|
+
guard: WriteContext,
|
|
267
|
+
) -> RecoveryReport | None:
|
|
268
|
+
"""用真实 adapter 从 continuation 恢复执行并继续消费 stream。
|
|
269
|
+
|
|
270
|
+
返回 ``None`` 表示没有可用 adapter(委托 worker 重放的旧路径)。
|
|
271
|
+
adapter 不支持 resume 时保持确定性收口(interrupted)。
|
|
272
|
+
"""
|
|
273
|
+
|
|
274
|
+
from ksadk.kernel.errors import UnsupportedControlError
|
|
275
|
+
from ksadk.runtime.adapter import ResumeTarget, RunHandle
|
|
276
|
+
|
|
277
|
+
if self._adapter_factory is None:
|
|
278
|
+
return None
|
|
279
|
+
handle_dump = run.metadata.get("handle")
|
|
280
|
+
continuation_ref = run.metadata.get("continuation_ref")
|
|
281
|
+
if not isinstance(handle_dump, dict) or not continuation_ref:
|
|
282
|
+
return None
|
|
283
|
+
adapter = self._adapter_factory()
|
|
284
|
+
try:
|
|
285
|
+
handle = RunHandle.model_validate(handle_dump)
|
|
286
|
+
resumed = await adapter.resume(
|
|
287
|
+
handle,
|
|
288
|
+
ResumeTarget(kind="invocation_id", id=str(continuation_ref)),
|
|
289
|
+
None,
|
|
290
|
+
)
|
|
291
|
+
except UnsupportedControlError:
|
|
292
|
+
# EchoAdapter 等不支持 resume 的 runtime:确定性收口,不重试。
|
|
293
|
+
return await self._interrupt_deterministically(
|
|
294
|
+
agent_instance_id, activation, run, guard=guard
|
|
295
|
+
)
|
|
296
|
+
except Exception as error:
|
|
297
|
+
return await self._decide(
|
|
298
|
+
agent_instance_id,
|
|
299
|
+
activation,
|
|
300
|
+
run,
|
|
301
|
+
outcome="failed",
|
|
302
|
+
reason=f"resume_failed:{type(error).__name__}",
|
|
303
|
+
guard=guard,
|
|
304
|
+
)
|
|
305
|
+
# lease 已获取 + provider 支持的 resume 已成功:takeover 重建
|
|
306
|
+
# ActiveExecution,后续控制命令/回包作用于同一 live execution。
|
|
307
|
+
self._register_execution(run.run_id, resumed.run_id, adapter, resumed)
|
|
308
|
+
try:
|
|
309
|
+
await self._consume_remaining_stream(
|
|
310
|
+
lambda: adapter.stream(resumed), run, guard
|
|
311
|
+
)
|
|
312
|
+
except Exception as error:
|
|
313
|
+
return await self._decide(
|
|
314
|
+
agent_instance_id,
|
|
315
|
+
activation,
|
|
316
|
+
run,
|
|
317
|
+
outcome="failed",
|
|
318
|
+
reason=f"resume_stream_failed:{type(error).__name__}",
|
|
319
|
+
guard=guard,
|
|
320
|
+
)
|
|
321
|
+
return await self._decide(
|
|
322
|
+
agent_instance_id,
|
|
323
|
+
activation,
|
|
324
|
+
run,
|
|
325
|
+
outcome="resumed",
|
|
326
|
+
reason="continuation_resumed",
|
|
327
|
+
guard=guard,
|
|
328
|
+
)
|
|
329
|
+
|
|
330
|
+
async def _consume_remaining_stream(
|
|
331
|
+
self, stream_factory, run: RunRecord, guard: WriteContext
|
|
332
|
+
) -> None:
|
|
333
|
+
"""消费剩余事件流(run_id 统一 durable id),自然结束收口 COMPLETED。"""
|
|
334
|
+
|
|
335
|
+
runtime_store = RuntimeEventStore(self._session_events, session_id=run.session_id)
|
|
336
|
+
async for event in stream_factory():
|
|
337
|
+
if event.run_id != run.run_id:
|
|
338
|
+
update: dict = {"run_id": run.run_id}
|
|
339
|
+
if getattr(event, "scope_id", None) == f"run:{event.run_id}":
|
|
340
|
+
update["scope_id"] = f"run:{run.run_id}"
|
|
341
|
+
event = event.model_copy(update=update)
|
|
342
|
+
await runtime_store.append(event, guard=guard) # type: ignore[arg-type]
|
|
343
|
+
await self._store.save_run_transition(
|
|
344
|
+
run.model_copy(update={"state": RunState.COMPLETED}),
|
|
345
|
+
expected_fence=guard.fencing_token, # type: ignore[attr-defined]
|
|
346
|
+
)
|
|
347
|
+
|
|
348
|
+
async def _load_run(
|
|
349
|
+
self, agent_instance_id: str, run_id: str | None
|
|
350
|
+
) -> RunRecord | None:
|
|
351
|
+
if run_id is not None:
|
|
352
|
+
return await self._store.load_run(run_id)
|
|
353
|
+
finder = getattr(self._store, "find_active_run", None)
|
|
354
|
+
if finder is not None:
|
|
355
|
+
return await finder(agent_instance_id)
|
|
356
|
+
return None
|
|
357
|
+
|
|
358
|
+
async def _interrupt_deterministically(
|
|
359
|
+
self,
|
|
360
|
+
agent_instance_id: str,
|
|
361
|
+
activation: ActivationLease,
|
|
362
|
+
run: RunRecord,
|
|
363
|
+
*,
|
|
364
|
+
guard: WriteContext,
|
|
365
|
+
reason: str = "runtime_not_durably_attachable",
|
|
366
|
+
) -> RecoveryReport:
|
|
367
|
+
fence = activation.fencing_token
|
|
368
|
+
runtime_store = RuntimeEventStore(self._session_events, session_id=run.session_id)
|
|
369
|
+
findings = await scan_open_runs(runtime_store, run.session_id)
|
|
370
|
+
finding = next((item for item in findings if item.run_id == run.run_id), None)
|
|
371
|
+
written: list[RuntimeEvent] = []
|
|
372
|
+
last_seq: int | None = None
|
|
373
|
+
if finding is not None:
|
|
374
|
+
events = settle_finding(
|
|
375
|
+
finding,
|
|
376
|
+
run.session_id,
|
|
377
|
+
allow_resume=False,
|
|
378
|
+
timestamp=self._clock(),
|
|
379
|
+
reason=reason,
|
|
380
|
+
)
|
|
381
|
+
pipeline = CanonicalEventPipeline(runtime_store, session_id=run.session_id)
|
|
382
|
+
for event in events:
|
|
383
|
+
persisted = await pipeline.emit(event, write_context=guard)
|
|
384
|
+
written.append(persisted)
|
|
385
|
+
last_seq = persisted.seq
|
|
386
|
+
await self._store.save_run_transition(
|
|
387
|
+
run.model_copy(update={"state": RunState.INTERRUPTED}),
|
|
388
|
+
expected_fence=fence,
|
|
389
|
+
)
|
|
390
|
+
return await self._decide(
|
|
391
|
+
agent_instance_id,
|
|
392
|
+
activation,
|
|
393
|
+
run,
|
|
394
|
+
outcome="interrupted",
|
|
395
|
+
reason=reason,
|
|
396
|
+
guard=guard,
|
|
397
|
+
written=written,
|
|
398
|
+
last_seq=last_seq,
|
|
399
|
+
)
|
|
400
|
+
|
|
401
|
+
async def _decide(
|
|
402
|
+
self,
|
|
403
|
+
agent_instance_id: str,
|
|
404
|
+
activation: ActivationLease,
|
|
405
|
+
run: RunRecord | None,
|
|
406
|
+
*,
|
|
407
|
+
outcome: RecoveryOutcome,
|
|
408
|
+
reason: str,
|
|
409
|
+
guard: WriteContext,
|
|
410
|
+
written: list[RuntimeEvent] | None = None,
|
|
411
|
+
last_seq: int | None = None,
|
|
412
|
+
) -> RecoveryReport:
|
|
413
|
+
if run is not None:
|
|
414
|
+
await self._store.append_event(
|
|
415
|
+
control_event(
|
|
416
|
+
session_id=run.session_id,
|
|
417
|
+
event_type="control.recovery_decided",
|
|
418
|
+
payload={
|
|
419
|
+
"agent_instance_id": agent_instance_id,
|
|
420
|
+
"activation_id": activation.activation_id,
|
|
421
|
+
"fencing_token": activation.fencing_token,
|
|
422
|
+
"run_id": run.run_id,
|
|
423
|
+
"outcome": outcome,
|
|
424
|
+
"reason": reason,
|
|
425
|
+
},
|
|
426
|
+
run_id=run.run_id,
|
|
427
|
+
),
|
|
428
|
+
expected_fence=activation.fencing_token,
|
|
429
|
+
agent_instance_id=agent_instance_id,
|
|
430
|
+
)
|
|
431
|
+
return RecoveryReport(
|
|
432
|
+
agent_instance_id=agent_instance_id,
|
|
433
|
+
activation_id=activation.activation_id,
|
|
434
|
+
run_id=run.run_id if run is not None else None,
|
|
435
|
+
outcome=outcome,
|
|
436
|
+
reason=reason,
|
|
437
|
+
last_seq=last_seq,
|
|
438
|
+
written_events=list(written or []),
|
|
439
|
+
)
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
def durable_handle_digest(handle_dump: dict) -> str | None:
|
|
443
|
+
"""Public shim kept for callers that only need digest validation."""
|
|
444
|
+
return _durable_handle_digest(handle_dump)
|
|
445
|
+
|
|
446
|
+
|
|
447
|
+
__all__ = [
|
|
448
|
+
"RecoveryCoordinator",
|
|
449
|
+
"RecoveryReport",
|
|
450
|
+
"RecoveryOutcome",
|
|
451
|
+
"durable_handle_digest",
|
|
452
|
+
]
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"""Non-secret provenance for the KsADK code that is actually imported.
|
|
2
|
+
|
|
3
|
+
The base runtime image may contain an older ``ksadk`` distribution while a
|
|
4
|
+
Code deployment shadows it from ``/app/code``. Health must therefore report
|
|
5
|
+
the source package identity, never the base image's distribution metadata.
|
|
6
|
+
|
|
7
|
+
``_bundle_identity.py`` is generated into Code archives by :class:`CodeBuilder`.
|
|
8
|
+
It is package content, not a user environment variable, and is intentionally
|
|
9
|
+
optional so legacy images report an honest incomplete provenance record.
|
|
10
|
+
|
|
11
|
+
The managed-runtime image also attests the exact wheel it installs. That
|
|
12
|
+
image provenance is used only when Python is importing KsADK from the image's
|
|
13
|
+
``site-packages`` directory. A Code archive that shadows KsADK must carry its
|
|
14
|
+
own bundle identity; an environment variable from the base image must never
|
|
15
|
+
claim provenance for user-supplied code.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import importlib
|
|
21
|
+
import os
|
|
22
|
+
import re
|
|
23
|
+
from functools import lru_cache
|
|
24
|
+
from pathlib import Path
|
|
25
|
+
from typing import Any
|
|
26
|
+
|
|
27
|
+
from ksadk.version import VERSION
|
|
28
|
+
|
|
29
|
+
_COMMIT_RE = re.compile(r"^[0-9a-f]{40,64}$")
|
|
30
|
+
_SHA256_RE = re.compile(r"^[0-9a-f]{64}$")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _imports_image_installed_ksadk() -> bool:
|
|
34
|
+
"""Whether ``ksadk`` is the distribution installed by the runtime image."""
|
|
35
|
+
|
|
36
|
+
try:
|
|
37
|
+
package = importlib.import_module("ksadk")
|
|
38
|
+
package_file = Path(str(package.__file__ or "")).resolve()
|
|
39
|
+
except (ImportError, OSError, RuntimeError):
|
|
40
|
+
return False
|
|
41
|
+
return "site-packages" in package_file.parts
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _image_identity() -> dict[str, str]:
|
|
45
|
+
"""Read image-attested provenance without trusting it for shadowed code."""
|
|
46
|
+
|
|
47
|
+
if not _imports_image_installed_ksadk():
|
|
48
|
+
return {}
|
|
49
|
+
return {
|
|
50
|
+
"ksadk_commit": str(
|
|
51
|
+
os.environ.get("KSADK_RUNTIME_IMAGE_SOURCE_COMMIT") or ""
|
|
52
|
+
).lower(),
|
|
53
|
+
"ksadk_wheel_sha256": str(
|
|
54
|
+
os.environ.get("KSADK_RUNTIME_IMAGE_WHEEL_SHA256") or ""
|
|
55
|
+
).lower(),
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@lru_cache(maxsize=1)
|
|
60
|
+
def runtime_identity() -> dict[str, str]:
|
|
61
|
+
"""Return the provenance embedded beside the imported KsADK package.
|
|
62
|
+
|
|
63
|
+
Missing or malformed optional provenance is represented by an empty field.
|
|
64
|
+
Image provenance is accepted only for the image-installed distribution;
|
|
65
|
+
callers must not substitute a process environment value for Code-bundled
|
|
66
|
+
KsADK or infer identity from an image tag.
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
identity: dict[str, str] = {
|
|
70
|
+
"ksadk_version": VERSION,
|
|
71
|
+
"ksadk_commit": "",
|
|
72
|
+
"ksadk_source_digest": "",
|
|
73
|
+
"ksadk_wheel_sha256": "",
|
|
74
|
+
}
|
|
75
|
+
try:
|
|
76
|
+
bundled: Any = importlib.import_module("ksadk._bundle_identity")
|
|
77
|
+
candidate = getattr(bundled, "BUNDLE_IDENTITY", {})
|
|
78
|
+
except (ImportError, AttributeError):
|
|
79
|
+
candidate = {}
|
|
80
|
+
if isinstance(candidate, dict) and str(candidate.get("ksadk_version") or "") == VERSION:
|
|
81
|
+
# A Code bundle is the closest provenance of the code Python imported.
|
|
82
|
+
commit = str(candidate.get("ksadk_commit") or "").lower()
|
|
83
|
+
digest = str(candidate.get("ksadk_source_digest") or "").lower()
|
|
84
|
+
if _COMMIT_RE.fullmatch(commit):
|
|
85
|
+
identity["ksadk_commit"] = commit
|
|
86
|
+
if _SHA256_RE.fullmatch(digest):
|
|
87
|
+
identity["ksadk_source_digest"] = digest
|
|
88
|
+
if identity["ksadk_commit"] or identity["ksadk_source_digest"]:
|
|
89
|
+
return identity
|
|
90
|
+
|
|
91
|
+
# No valid bundle identity means the installed image distribution is the
|
|
92
|
+
# imported source only when it has not been shadowed by a Code archive.
|
|
93
|
+
image = _image_identity()
|
|
94
|
+
commit = image.get("ksadk_commit", "")
|
|
95
|
+
wheel_digest = image.get("ksadk_wheel_sha256", "")
|
|
96
|
+
if _COMMIT_RE.fullmatch(commit):
|
|
97
|
+
identity["ksadk_commit"] = commit
|
|
98
|
+
if _SHA256_RE.fullmatch(wheel_digest):
|
|
99
|
+
identity["ksadk_wheel_sha256"] = wheel_digest
|
|
100
|
+
return identity
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
__all__ = ["runtime_identity"]
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
-- Agent Kernel durable state (Phase 1 Task 4).
|
|
2
|
+
-- BIGINT fencing_token / TIMESTAMPTZ lease / JSONB payload.
|
|
3
|
+
-- Idempotency: (tenant_id, session_id, idempotency_key) unique.
|
|
4
|
+
-- Claim ordering: FOR UPDATE SKIP LOCKED ordered by accepted_seq (see postgres_store.py).
|
|
5
|
+
|
|
6
|
+
CREATE TABLE IF NOT EXISTS kernel_inbox (
|
|
7
|
+
message_id UUID PRIMARY KEY,
|
|
8
|
+
tenant_id TEXT NOT NULL DEFAULT 'default',
|
|
9
|
+
agent_instance_id TEXT NOT NULL,
|
|
10
|
+
session_id TEXT NOT NULL,
|
|
11
|
+
idempotency_key TEXT NOT NULL,
|
|
12
|
+
request_digest TEXT NOT NULL,
|
|
13
|
+
accepted_seq BIGINT NOT NULL,
|
|
14
|
+
status TEXT NOT NULL CHECK (status IN ('accepted','claimed','completed','discarded')),
|
|
15
|
+
claimed_fence BIGINT,
|
|
16
|
+
payload JSONB NOT NULL,
|
|
17
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
18
|
+
UNIQUE (tenant_id, session_id, idempotency_key)
|
|
19
|
+
);
|
|
20
|
+
CREATE INDEX IF NOT EXISTS idx_kernel_inbox_claim
|
|
21
|
+
ON kernel_inbox (agent_instance_id, session_id, status, accepted_seq);
|
|
22
|
+
|
|
23
|
+
CREATE TABLE IF NOT EXISTS kernel_runs (
|
|
24
|
+
run_id TEXT PRIMARY KEY,
|
|
25
|
+
tenant_id TEXT NOT NULL DEFAULT 'default',
|
|
26
|
+
agent_instance_id TEXT NOT NULL,
|
|
27
|
+
session_id TEXT NOT NULL,
|
|
28
|
+
state TEXT NOT NULL CHECK (state IN (
|
|
29
|
+
'pending','running','paused','waiting','completed','failed','cancelled','interrupted'
|
|
30
|
+
)),
|
|
31
|
+
activation_fence BIGINT NOT NULL DEFAULT 0,
|
|
32
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
33
|
+
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
34
|
+
metadata JSONB NOT NULL DEFAULT '{}'::jsonb
|
|
35
|
+
);
|
|
36
|
+
CREATE INDEX IF NOT EXISTS idx_kernel_runs_session_state
|
|
37
|
+
ON kernel_runs (session_id, state);
|
|
38
|
+
|
|
39
|
+
-- Activation lease: one row per (agent_instance_id, session_id). Takeover uses
|
|
40
|
+
-- INSERT .. ON CONFLICT .. DO UPDATE .. WHERE lease_expires_at <= now()
|
|
41
|
+
-- (or released / same activation) and atomically bumps fencing_token + 1.
|
|
42
|
+
CREATE TABLE IF NOT EXISTS kernel_activations (
|
|
43
|
+
agent_instance_id TEXT NOT NULL,
|
|
44
|
+
session_id TEXT NOT NULL,
|
|
45
|
+
activation_id TEXT NOT NULL,
|
|
46
|
+
fencing_token BIGINT NOT NULL,
|
|
47
|
+
lease_expires_at TIMESTAMPTZ NOT NULL,
|
|
48
|
+
released BOOLEAN NOT NULL DEFAULT FALSE,
|
|
49
|
+
runtime_type TEXT NOT NULL DEFAULT 'ksadk',
|
|
50
|
+
bundle_digest TEXT NOT NULL DEFAULT '',
|
|
51
|
+
capability_digest TEXT NOT NULL DEFAULT '',
|
|
52
|
+
PRIMARY KEY (agent_instance_id, session_id)
|
|
53
|
+
);
|
|
54
|
+
CREATE INDEX IF NOT EXISTS idx_kernel_activations_expiry
|
|
55
|
+
ON kernel_activations (lease_expires_at);
|
|
56
|
+
|
|
57
|
+
CREATE TABLE IF NOT EXISTS kernel_accepted_seq (
|
|
58
|
+
tenant_id TEXT NOT NULL DEFAULT 'default',
|
|
59
|
+
session_id TEXT NOT NULL,
|
|
60
|
+
last_seq BIGINT NOT NULL DEFAULT 0,
|
|
61
|
+
PRIMARY KEY (tenant_id, session_id)
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
-- Mutation permit nonce 单次使用(durable replay 防护,跨 Pod / 重启共享)。
|
|
65
|
+
-- register 语义见 postgres_store.PostgresNonceStore:INSERT .. ON CONFLICT DO
|
|
66
|
+
-- NOTHING,冲突时读回 (command_id, idempotency_key) 判定网络重试 vs 重放。
|
|
67
|
+
CREATE TABLE IF NOT EXISTS kernel_permit_nonces (
|
|
68
|
+
nonce TEXT PRIMARY KEY,
|
|
69
|
+
command_id TEXT NOT NULL,
|
|
70
|
+
idempotency_key TEXT NOT NULL,
|
|
71
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
72
|
+
);
|
|
73
|
+
CREATE INDEX IF NOT EXISTS idx_kernel_permit_nonces_created
|
|
74
|
+
ON kernel_permit_nonces (created_at);
|
|
75
|
+
|
|
76
|
+
-- Durable Interaction ledger (Phase 1 Task 5). First-wins terminal CAS on
|
|
77
|
+
-- (revision, status); terminal decision and its SessionEvent append happen in
|
|
78
|
+
-- the same writer transaction (see postgres_store interaction methods).
|
|
79
|
+
-- Row key: (tenant_id, interaction_id); idempotency submissions are unique on
|
|
80
|
+
-- (tenant_id, interaction_id, idempotency_key).
|
|
81
|
+
CREATE TABLE IF NOT EXISTS kernel_interactions (
|
|
82
|
+
interaction_id TEXT NOT NULL,
|
|
83
|
+
tenant_id TEXT NOT NULL DEFAULT 'default',
|
|
84
|
+
agent_instance_id TEXT NOT NULL,
|
|
85
|
+
session_id TEXT NOT NULL,
|
|
86
|
+
run_id TEXT NOT NULL,
|
|
87
|
+
kind TEXT NOT NULL CHECK (kind IN ('approval','structured_input','plan_review','custom')),
|
|
88
|
+
request_schema JSONB NOT NULL,
|
|
89
|
+
presentation JSONB,
|
|
90
|
+
revision BIGINT NOT NULL,
|
|
91
|
+
status TEXT NOT NULL CHECK (status IN (
|
|
92
|
+
'pending','resolving','resolved','cancelled','expired'
|
|
93
|
+
)),
|
|
94
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
95
|
+
expires_at TIMESTAMPTZ,
|
|
96
|
+
provider_id TEXT NOT NULL DEFAULT '',
|
|
97
|
+
native_target JSONB,
|
|
98
|
+
continuation_metadata JSONB,
|
|
99
|
+
request_digest TEXT NOT NULL,
|
|
100
|
+
response JSONB,
|
|
101
|
+
outcome TEXT,
|
|
102
|
+
actor TEXT,
|
|
103
|
+
event_id UUID,
|
|
104
|
+
accepted_seq BIGINT,
|
|
105
|
+
fencing_token BIGINT,
|
|
106
|
+
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
107
|
+
PRIMARY KEY (tenant_id, interaction_id)
|
|
108
|
+
);
|
|
109
|
+
CREATE INDEX IF NOT EXISTS idx_kernel_interactions_pending
|
|
110
|
+
ON kernel_interactions (tenant_id, session_id, status);
|
|
111
|
+
|
|
112
|
+
CREATE TABLE IF NOT EXISTS kernel_interaction_submissions (
|
|
113
|
+
tenant_id TEXT NOT NULL DEFAULT 'default',
|
|
114
|
+
interaction_id TEXT NOT NULL,
|
|
115
|
+
idempotency_key TEXT NOT NULL,
|
|
116
|
+
submission_digest TEXT NOT NULL,
|
|
117
|
+
receipt JSONB NOT NULL,
|
|
118
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
119
|
+
PRIMARY KEY (tenant_id, interaction_id, idempotency_key)
|
|
120
|
+
);
|